@@ -1284,6 +1284,11 @@ impl<'a> Parser<'a> {
12841284 // SQLite has single-quoted identifiers
12851285 id_parts.push(Ident::with_quote('\'', s))
12861286 }
1287+ Token::Placeholder(s) => {
1288+ // Snowflake uses $1, $2, etc. for positional column references
1289+ // in staged data queries like: SELECT t.$1 FROM @stage t
1290+ id_parts.push(Ident::new(s))
1291+ }
12871292 Token::Mul => {
12881293 return Ok(Expr::QualifiedWildcard(
12891294 ObjectName::from(id_parts),
@@ -1606,10 +1611,34 @@ impl<'a> Parser<'a> {
16061611 value: self.parse_introduced_string_expr()?.into(),
16071612 })
16081613 }
1614+ // An unreserved word (likely an identifier) is followed by an arrow,
1615+ // which indicates a lambda function with a single, untyped parameter.
1616+ // For example: `a -> a * 2`.
16091617 Token::Arrow if self.dialect.supports_lambda_functions() => {
16101618 self.expect_token(&Token::Arrow)?;
16111619 Ok(Expr::Lambda(LambdaFunction {
1612- params: OneOrManyWithParens::One(w.to_ident(w_span)),
1620+ params: OneOrManyWithParens::One(LambdaFunctionParameter {
1621+ name: w.to_ident(w_span),
1622+ data_type: None,
1623+ }),
1624+ body: Box::new(self.parse_expr()?),
1625+ syntax: LambdaSyntax::Arrow,
1626+ }))
1627+ }
1628+ // An unreserved word (likely an identifier) that is followed by another word (likley a data type)
1629+ // which is then followed by an arrow, which indicates a lambda function with a single, typed parameter.
1630+ // For example: `a INT -> a * 2`.
1631+ Token::Word(_)
1632+ if self.dialect.supports_lambda_functions()
1633+ && self.peek_nth_token_ref(1).token == Token::Arrow =>
1634+ {
1635+ let data_type = self.parse_data_type()?;
1636+ self.expect_token(&Token::Arrow)?;
1637+ Ok(Expr::Lambda(LambdaFunction {
1638+ params: OneOrManyWithParens::One(LambdaFunctionParameter {
1639+ name: w.to_ident(w_span),
1640+ data_type: Some(data_type),
1641+ }),
16131642 body: Box::new(self.parse_expr()?),
16141643 syntax: LambdaSyntax::Arrow,
16151644 }))
@@ -1922,6 +1951,13 @@ impl<'a> Parser<'a> {
19221951 chain.push(AccessExpr::Dot(expr));
19231952 self.advance_token(); // The consumed string
19241953 }
1954+ Token::Placeholder(s) => {
1955+ // Snowflake uses $1, $2, etc. for positional column references
1956+ // in staged data queries like: SELECT t.$1 FROM @stage t
1957+ let expr = Expr::Identifier(Ident::with_span(next_token.span, s));
1958+ chain.push(AccessExpr::Dot(expr));
1959+ self.advance_token(); // The consumed placeholder
1960+ }
19251961 // Fallback to parsing an arbitrary expression, but restrict to expression
19261962 // types that are valid after the dot operator. This ensures that e.g.
19271963 // `T.interval` is parsed as a compound identifier, not as an interval
@@ -2195,7 +2231,7 @@ impl<'a> Parser<'a> {
21952231 return Ok(None);
21962232 }
21972233 self.maybe_parse(|p| {
2198- let params = p.parse_comma_separated(|p| p.parse_identifier ())?;
2234+ let params = p.parse_comma_separated(|p| p.parse_lambda_function_parameter ())?;
21992235 p.expect_token(&Token::RParen)?;
22002236 p.expect_token(&Token::Arrow)?;
22012237 let expr = p.parse_expr()?;
@@ -2207,7 +2243,7 @@ impl<'a> Parser<'a> {
22072243 })
22082244 }
22092245
2210- /// Parses a lambda expression using the `LAMBDA` keyword syntax.
2246+ /// Parses a lambda expression following the `LAMBDA` keyword syntax.
22112247 ///
22122248 /// Syntax: `LAMBDA <params> : <expr>`
22132249 ///
@@ -2217,30 +2253,49 @@ impl<'a> Parser<'a> {
22172253 ///
22182254 /// See <https://duckdb.org/docs/stable/sql/functions/lambda>
22192255 fn parse_lambda_expr(&mut self) -> Result<Expr, ParserError> {
2256+ // Parse the parameters: either a single identifier or comma-separated identifiers
2257+ let params = self.parse_lambda_function_parameters()?;
2258+ // Expect the colon separator
2259+ self.expect_token(&Token::Colon)?;
2260+ // Parse the body expression
2261+ let body = self.parse_expr()?;
2262+ Ok(Expr::Lambda(LambdaFunction {
2263+ params,
2264+ body: Box::new(body),
2265+ syntax: LambdaSyntax::LambdaKeyword,
2266+ }))
2267+ }
2268+
2269+ /// Parses the parameters of a lambda function with optional typing.
2270+ fn parse_lambda_function_parameters(
2271+ &mut self,
2272+ ) -> Result<OneOrManyWithParens<LambdaFunctionParameter>, ParserError> {
22202273 // Parse the parameters: either a single identifier or comma-separated identifiers
22212274 let params = if self.consume_token(&Token::LParen) {
22222275 // Parenthesized parameters: (x, y)
2223- let params = self.parse_comma_separated(|p| p.parse_identifier ())?;
2276+ let params = self.parse_comma_separated(|p| p.parse_lambda_function_parameter ())?;
22242277 self.expect_token(&Token::RParen)?;
22252278 OneOrManyWithParens::Many(params)
22262279 } else {
22272280 // Unparenthesized parameters: x or x, y
2228- let params = self.parse_comma_separated(|p| p.parse_identifier ())?;
2281+ let params = self.parse_comma_separated(|p| p.parse_lambda_function_parameter ())?;
22292282 if params.len() == 1 {
22302283 OneOrManyWithParens::One(params.into_iter().next().unwrap())
22312284 } else {
22322285 OneOrManyWithParens::Many(params)
22332286 }
22342287 };
2235- // Expect the colon separator
2236- self.expect_token(&Token::Colon)?;
2237- // Parse the body expression
2238- let body = self.parse_expr()?;
2239- Ok(Expr::Lambda(LambdaFunction {
2240- params,
2241- body: Box::new(body),
2242- syntax: LambdaSyntax::LambdaKeyword,
2243- }))
2288+ Ok(params)
2289+ }
2290+
2291+ /// Parses a single parameter of a lambda function, with optional typing.
2292+ fn parse_lambda_function_parameter(&mut self) -> Result<LambdaFunctionParameter, ParserError> {
2293+ let name = self.parse_identifier()?;
2294+ let data_type = match self.peek_token().token {
2295+ Token::Word(_) => self.maybe_parse(|p| p.parse_data_type())?,
2296+ _ => None,
2297+ };
2298+ Ok(LambdaFunctionParameter { name, data_type })
22442299 }
22452300
22462301 /// Tries to parse the body of an [ODBC escaping sequence]
@@ -15392,6 +15447,9 @@ impl<'a> Parser<'a> {
1539215447 && self.peek_keyword_with_tokens(Keyword::SEMANTIC_VIEW, &[Token::LParen])
1539315448 {
1539415449 self.parse_semantic_view_table_factor()
15450+ } else if self.peek_token_ref().token == Token::AtSign {
15451+ // Stage reference: @mystage or @namespace.stage (e.g. Snowflake)
15452+ self.parse_snowflake_stage_table_factor()
1539515453 } else {
1539615454 let name = self.parse_object_name(true)?;
1539715455
@@ -15488,6 +15546,37 @@ impl<'a> Parser<'a> {
1548815546 }
1548915547 }
1549015548
15549+ /// Parse a Snowflake stage reference as a table factor.
15550+ /// Handles syntax like: `@mystage1 (file_format => 'myformat', pattern => '...')`
15551+ ///
15552+ /// See: <https://docs.snowflake.com/en/user-guide/querying-stage>
15553+ fn parse_snowflake_stage_table_factor(&mut self) -> Result<TableFactor, ParserError> {
15554+ // Parse the stage name starting with @
15555+ let name = crate::dialect::parse_snowflake_stage_name(self)?;
15556+
15557+ // Parse optional stage options like (file_format => 'myformat', pattern => '...')
15558+ let args = if self.consume_token(&Token::LParen) {
15559+ Some(self.parse_table_function_args()?)
15560+ } else {
15561+ None
15562+ };
15563+
15564+ let alias = self.maybe_parse_table_alias()?;
15565+
15566+ Ok(TableFactor::Table {
15567+ name,
15568+ alias,
15569+ args,
15570+ with_hints: vec![],
15571+ version: None,
15572+ partitions: vec![],
15573+ with_ordinality: false,
15574+ json_path: None,
15575+ sample: None,
15576+ index_hints: vec![],
15577+ })
15578+ }
15579+
1549115580 fn maybe_parse_table_sample(&mut self) -> Result<Option<Box<TableSample>>, ParserError> {
1549215581 let modifier = if self.parse_keyword(Keyword::TABLESAMPLE) {
1549315582 TableSampleModifier::TableSample
@@ -18114,11 +18203,14 @@ impl<'a> Parser<'a> {
1811418203 /// Parse a 'BEGIN' statement
1811518204 pub fn parse_begin(&mut self) -> Result<Statement, ParserError> {
1811618205 let modifier = self.parse_transaction_modifier();
18117- let transaction = match self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]) {
18118- Some(Keyword::TRANSACTION) => Some(BeginTransactionKind::Transaction),
18119- Some(Keyword::WORK) => Some(BeginTransactionKind::Work),
18120- _ => None,
18121- };
18206+ let transaction =
18207+ match self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK, Keyword::TRAN])
18208+ {
18209+ Some(Keyword::TRANSACTION) => Some(BeginTransactionKind::Transaction),
18210+ Some(Keyword::WORK) => Some(BeginTransactionKind::Work),
18211+ Some(Keyword::TRAN) => Some(BeginTransactionKind::Tran),
18212+ _ => None,
18213+ };
1812218214 Ok(Statement::StartTransaction {
1812318215 modes: self.parse_transaction_modes()?,
1812418216 begin: true,
@@ -18252,7 +18344,7 @@ impl<'a> Parser<'a> {
1825218344
1825318345 /// Parse an optional `AND [NO] CHAIN` clause for `COMMIT` and `ROLLBACK` statements
1825418346 pub fn parse_commit_rollback_chain(&mut self) -> Result<bool, ParserError> {
18255- let _ = self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]);
18347+ let _ = self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK, Keyword::TRAN ]);
1825618348 if self.parse_keyword(Keyword::AND) {
1825718349 let chain = !self.parse_keyword(Keyword::NO);
1825818350 self.expect_keyword_is(Keyword::CHAIN)?;
0 commit comments