@@ -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),
@@ -1946,6 +1951,13 @@ impl<'a> Parser<'a> {
19461951 chain.push(AccessExpr::Dot(expr));
19471952 self.advance_token(); // The consumed string
19481953 }
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+ }
19491961 // Fallback to parsing an arbitrary expression, but restrict to expression
19501962 // types that are valid after the dot operator. This ensures that e.g.
19511963 // `T.interval` is parsed as a compound identifier, not as an interval
@@ -15435,6 +15447,9 @@ impl<'a> Parser<'a> {
1543515447 && self.peek_keyword_with_tokens(Keyword::SEMANTIC_VIEW, &[Token::LParen])
1543615448 {
1543715449 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()
1543815453 } else {
1543915454 let name = self.parse_object_name(true)?;
1544015455
@@ -15531,6 +15546,37 @@ impl<'a> Parser<'a> {
1553115546 }
1553215547 }
1553315548
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+
1553415580 fn maybe_parse_table_sample(&mut self) -> Result<Option<Box<TableSample>>, ParserError> {
1553515581 let modifier = if self.parse_keyword(Keyword::TABLESAMPLE) {
1553615582 TableSampleModifier::TableSample
0 commit comments