Skip to content

Commit d552f03

Browse files
Snowflake: parse parenthesized SHOW as RESULTSET/CURSOR declaration payload
1 parent ce2e5b9 commit d552f03

6 files changed

Lines changed: 111 additions & 3 deletions

File tree

src/ast/query.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ pub enum SetExpr {
177177
Merge(Statement),
178178
/// `TABLE` command
179179
Table(Box<Table>),
180+
/// `SHOW` statement used as the query payload of a Snowflake `RESULTSET` or
181+
/// `CURSOR` declaration (`RESULTSET DEFAULT (SHOW ...)`, `CURSOR FOR (SHOW ...)`).
182+
Show(Statement),
180183
}
181184

182185
impl SetExpr {
@@ -205,6 +208,7 @@ impl fmt::Display for SetExpr {
205208
SetExpr::Delete(v) => v.fmt(f),
206209
SetExpr::Merge(v) => v.fmt(f),
207210
SetExpr::Table(t) => t.fmt(f),
211+
SetExpr::Show(v) => v.fmt(f),
208212
SetExpr::SetOperation {
209213
left,
210214
right,

src/ast/spans.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ impl Spanned for SetExpr {
235235
SetExpr::Update(statement) => statement.span(),
236236
SetExpr::Delete(statement) => statement.span(),
237237
SetExpr::Merge(statement) => statement.span(),
238+
SetExpr::Show(statement) => statement.span(),
238239
}
239240
}
240241
}

src/dialect/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,13 @@ pub trait Dialect: Debug + Any {
432432
false
433433
}
434434

435+
/// Returns true if the dialect accepts a parenthesized `SHOW` statement as
436+
/// the query payload of a `RESULTSET` or `CURSOR` declaration
437+
/// (`res RESULTSET DEFAULT (SHOW ...)`, `cur CURSOR FOR (SHOW ...)`).
438+
fn supports_show_in_resultset_cursor(&self) -> bool {
439+
false
440+
}
441+
435442
/// Returns true if the dialect supports the MATCH_RECOGNIZE operation.
436443
fn supports_match_recognize(&self) -> bool {
437444
false

src/dialect/snowflake.rs

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

198+
/// See <https://docs.snowflake.com/en/developer-guide/snowflake-scripting/cursors>
199+
fn supports_show_in_resultset_cursor(&self) -> bool {
200+
true
201+
}
202+
198203
fn supports_match_recognize(&self) -> bool {
199204
true
200205
}

src/parser/mod.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8094,7 +8094,9 @@ impl<'a> Parser<'a> {
80948094
_ => (
80958095
Some(DeclareType::Cursor),
80968096
None,
8097-
Some(DeclareAssignment::For(Box::new(self.parse_expr()?))),
8097+
Some(DeclareAssignment::For(Box::new(
8098+
self.parse_snowflake_declaration_payload_expr()?,
8099+
))),
80988100
None,
80998101
),
81008102
}
@@ -8275,18 +8277,54 @@ impl<'a> Parser<'a> {
82758277
Ok(match &self.peek_token_ref().token {
82768278
Token::Word(w) if w.keyword == Keyword::DEFAULT => {
82778279
self.next_token(); // Skip `DEFAULT`
8278-
Some(DeclareAssignment::Default(Box::new(self.parse_expr()?)))
8280+
Some(DeclareAssignment::Default(Box::new(
8281+
self.parse_snowflake_declaration_payload_expr()?,
8282+
)))
82798283
}
82808284
Token::Assignment => {
82818285
self.next_token(); // Skip `:=`
82828286
Some(DeclareAssignment::DuckAssignment(Box::new(
8283-
self.parse_expr()?,
8287+
self.parse_snowflake_declaration_payload_expr()?,
82848288
)))
82858289
}
82868290
_ => None,
82878291
})
82888292
}
82898293

8294+
/// Parses the expression payload of a Snowflake `RESULTSET` / `CURSOR`
8295+
/// declaration. Identical to [`Parser::parse_expr`] except that, under a
8296+
/// dialect that allows it, a parenthesized `SHOW` statement is accepted as
8297+
/// the query payload and wrapped as an [`Expr::Subquery`] whose body is a
8298+
/// [`SetExpr::Show`]. The pre-existing `(SELECT ...)` subquery path is left
8299+
/// untouched.
8300+
fn parse_snowflake_declaration_payload_expr(&mut self) -> Result<Expr, ParserError> {
8301+
let is_paren_show = self.dialect.supports_show_in_resultset_cursor()
8302+
&& self.peek_nth_token_ref(0).token == Token::LParen
8303+
&& matches!(
8304+
&self.peek_nth_token_ref(1).token,
8305+
Token::Word(w) if w.keyword == Keyword::SHOW
8306+
);
8307+
if !is_paren_show {
8308+
return self.parse_expr();
8309+
}
8310+
self.expect_token(&Token::LParen)?;
8311+
self.expect_keyword_is(Keyword::SHOW)?;
8312+
let show = self.parse_show()?;
8313+
self.expect_token(&Token::RParen)?;
8314+
Ok(Expr::Subquery(Box::new(Query {
8315+
with: None,
8316+
body: Box::new(SetExpr::Show(show)),
8317+
order_by: None,
8318+
limit_clause: None,
8319+
fetch: None,
8320+
locks: vec![],
8321+
for_clause: None,
8322+
settings: None,
8323+
format_clause: None,
8324+
pipe_operators: vec![],
8325+
})))
8326+
}
8327+
82908328
/// Parses the assigned expression in a variable declaration.
82918329
///
82928330
/// Syntax:

tests/sqlparser_snowflake.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1915,6 +1915,59 @@ fn parse_snowflake_declare_result_set() {
19151915
);
19161916
}
19171917

1918+
#[test]
1919+
fn parse_snowflake_declare_show_payload() {
1920+
fn payload_show(stmt: Statement) -> Statement {
1921+
match stmt {
1922+
Statement::Declare { mut stmts } => {
1923+
assert_eq!(1, stmts.len());
1924+
let Declare {
1925+
assignment,
1926+
for_query,
1927+
..
1928+
} = stmts.swap_remove(0);
1929+
let query = match (assignment, for_query) {
1930+
(Some(DeclareAssignment::Default(expr)), None)
1931+
| (Some(DeclareAssignment::DuckAssignment(expr)), None)
1932+
| (Some(DeclareAssignment::For(expr)), None) => match *expr {
1933+
Expr::Subquery(query) => query,
1934+
other => panic!("expected subquery payload, got {other:?}"),
1935+
},
1936+
other => panic!("unexpected declaration payload: {other:?}"),
1937+
};
1938+
match *query.body {
1939+
SetExpr::Show(show) => show,
1940+
other => panic!("expected SHOW body, got {other:?}"),
1941+
}
1942+
}
1943+
other => panic!("expected DECLARE, got {other:?}"),
1944+
}
1945+
}
1946+
1947+
for sql in [
1948+
"DECLARE res RESULTSET DEFAULT (SHOW TASKS)",
1949+
"DECLARE res RESULTSET := (SHOW TASKS LIKE 'foo%')",
1950+
"DECLARE cur CURSOR FOR (SHOW TASKS)",
1951+
] {
1952+
let stmt = snowflake().verified_stmt(sql);
1953+
// The parsed SHOW must be recoverable from the declaration AST.
1954+
assert!(matches!(payload_show(stmt), Statement::ShowTasks { .. }));
1955+
}
1956+
1957+
// The pre-existing parenthesized-SELECT payload is unaffected.
1958+
snowflake().verified_stmt("DECLARE res RESULTSET DEFAULT (SELECT price FROM invoices)");
1959+
snowflake().verified_stmt("DECLARE cur CURSOR FOR (SELECT id FROM invoices)");
1960+
1961+
// Under a non-Snowflake dialect the SHOW payload still errors.
1962+
let generic = TestedDialects::new(vec![Box::new(GenericDialect {})]);
1963+
for sql in [
1964+
"DECLARE res RESULTSET DEFAULT (SHOW TASKS)",
1965+
"DECLARE cur CURSOR FOR (SHOW TASKS)",
1966+
] {
1967+
assert!(generic.parse_sql_statements(sql).is_err());
1968+
}
1969+
}
1970+
19181971
#[test]
19191972
fn parse_snowflake_declare_exception() {
19201973
for (sql, expected_name, expected_assigned_expr) in [

0 commit comments

Comments
 (0)