Skip to content

Commit 1c814f3

Browse files
Snowflake: accept :placeholder as SELECT ... INTO target
Extend parse_select_into to accept a colon placeholder (e.g. SELECT ... INTO :var) as the target in dialects that opt in via supports_select_into_placeholder_target (Snowflake scripting). The :name placeholder text is preserved as the object-name part so the round-trip is stable and downstream can distinguish it from a bare table target. Bare-identifier targets are unchanged.
1 parent 36b0974 commit 1c814f3

4 files changed

Lines changed: 53 additions & 1 deletion

File tree

src/dialect/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,13 @@ pub trait Dialect: Debug + Any {
11151115
false
11161116
}
11171117

1118+
/// Returns true if this dialect accepts a colon placeholder as a
1119+
/// `SELECT ... INTO` target, e.g. `SELECT ... INTO :var` in Snowflake
1120+
/// scripting, where the target is a local variable rather than a table.
1121+
fn supports_select_into_placeholder_target(&self) -> bool {
1122+
false
1123+
}
1124+
11181125
/// Returns true if this dialect supports `$` as a prefix for money literals
11191126
/// e.g. `SELECT $123.45` (SQL Server)
11201127
fn supports_dollar_as_money_prefix(&self) -> bool {

src/dialect/snowflake.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ impl Dialect for SnowflakeDialect {
195195
true
196196
}
197197

198+
/// Snowflake scripting accepts `SELECT ... INTO :var` where the target is a
199+
/// local variable placeholder rather than a table name.
200+
fn supports_select_into_placeholder_target(&self) -> bool {
201+
true
202+
}
203+
198204
/// See <https://docs.snowflake.com/en/developer-guide/snowflake-scripting/cursors>
199205
fn supports_for_loop_over_cursor(&self) -> bool {
200206
true

src/parser/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20606,7 +20606,20 @@ impl<'a> Parser<'a> {
2060620606
.is_some();
2060720607
let unlogged = self.parse_keyword(Keyword::UNLOGGED);
2060820608
let table = self.parse_keyword(Keyword::TABLE);
20609-
let name = self.parse_object_name(false)?;
20609+
let name = if self.dialect.supports_select_into_placeholder_target()
20610+
&& self.peek_token_ref().token == Token::Colon
20611+
{
20612+
// Snowflake scripting: `SELECT ... INTO :var` targets a local
20613+
// variable. Keep the `:name` placeholder text as the object-name
20614+
// part so the round-trip is preserved and downstream can tell it
20615+
// apart from a bare table target.
20616+
let placeholder = self.parse_value()?;
20617+
ObjectName(vec![ObjectNamePart::Identifier(Ident::new(
20618+
placeholder.to_string(),
20619+
))])
20620+
} else {
20621+
self.parse_object_name(false)?
20622+
};
2061020623

2061120624
Ok(SelectInto {
2061220625
temporary,

tests/sqlparser_snowflake.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8625,3 +8625,29 @@ fn test_show_terse_stages() {
86258625
_ => unreachable!(),
86268626
}
86278627
}
8628+
8629+
#[test]
8630+
fn test_select_into_placeholder_target() {
8631+
// Snowflake scripting: a colon placeholder is a valid `SELECT ... INTO`
8632+
// target (a local variable), and it round-trips as `:res`.
8633+
let stmt = snowflake().verified_stmt("SELECT :res + t.hmy INTO :res FROM tbl AS t");
8634+
let Statement::Query(query) = stmt else {
8635+
unreachable!()
8636+
};
8637+
let SetExpr::Select(select) = *query.body else {
8638+
unreachable!()
8639+
};
8640+
let into = select.into.expect("expected SELECT ... INTO clause");
8641+
assert_eq!(into.name.to_string(), ":res");
8642+
assert!(!into.table);
8643+
8644+
// Bare-identifier targets are unaffected.
8645+
let stmt = snowflake().verified_stmt("SELECT 1 INTO res FROM tbl");
8646+
let Statement::Query(query) = stmt else {
8647+
unreachable!()
8648+
};
8649+
let SetExpr::Select(select) = *query.body else {
8650+
unreachable!()
8651+
};
8652+
assert_eq!(select.into.unwrap().name.to_string(), "res");
8653+
}

0 commit comments

Comments
 (0)