@@ -23,8 +23,8 @@ use crate::ast::helpers::stmt_data_loading::{
2323 FileStagingCommand , StageLoadSelectItem , StageLoadSelectItemKind , StageParamsObject ,
2424} ;
2525use crate :: ast:: {
26- ColumnOption , ColumnPolicy , ColumnPolicyProperty , CopyIntoSnowflakeKind , Ident ,
27- IdentityParameters , IdentityProperty , IdentityPropertyFormatKind , IdentityPropertyKind ,
26+ ColumnOption , ColumnPolicy , ColumnPolicyProperty , CopyIntoSnowflakeKind , DollarQuotedString ,
27+ Ident , IdentityParameters , IdentityProperty , IdentityPropertyFormatKind , IdentityPropertyKind ,
2828 IdentityPropertyOrder , ObjectName , ObjectNamePart , RowAccessPolicy , ShowObjects , SqlOption ,
2929 Statement , TagsColumnOption , WrappedCollection ,
3030} ;
@@ -307,22 +307,22 @@ impl Dialect for SnowflakeDialect {
307307 // they are not followed by other tokens that may change their meaning
308308 // e.g. `SELECT * EXCEPT (col1) FROM tbl`
309309 Keyword :: EXCEPT
310- // e.g. `SELECT 1 LIMIT 5`
311- | Keyword :: LIMIT
312- // e.g. `SELECT 1 OFFSET 5 ROWS`
313- | Keyword :: OFFSET
314310 // e.g. `INSERT INTO t SELECT 1 RETURNING *`
315311 | Keyword :: RETURNING if !matches ! ( parser. peek_token_ref( ) . token, Token :: Comma | Token :: EOF ) =>
316312 {
317313 false
318314 }
319315
316+ // e.g. `SELECT 1 LIMIT 5` - not an alias
317+ // e.g. `SELECT 1 OFFSET 5 ROWS` - not an alias
318+ Keyword :: LIMIT | Keyword :: OFFSET if peek_for_limit_options ( parser) => false ,
319+
320320 // `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
321321 // which would give it a different meanings, for example:
322322 // `SELECT 1 FETCH FIRST 10 ROWS` - not an alias
323323 // `SELECT 1 FETCH 10` - not an alias
324324 Keyword :: FETCH if parser. peek_one_of_keywords ( & [ Keyword :: FIRST , Keyword :: NEXT ] ) . is_some ( )
325- || matches ! ( parser. peek_token ( ) . token , Token :: Number ( _ , _ ) ) =>
325+ || peek_for_limit_options ( parser) =>
326326 {
327327 false
328328 }
@@ -351,20 +351,23 @@ impl Dialect for SnowflakeDialect {
351351 match kw {
352352 // The following keywords can be considered an alias as long as
353353 // they are not followed by other tokens that may change their meaning
354- Keyword :: LIMIT
355- | Keyword :: RETURNING
354+ Keyword :: RETURNING
356355 | Keyword :: INNER
357356 | Keyword :: USING
358357 | Keyword :: PIVOT
359358 | Keyword :: UNPIVOT
360359 | Keyword :: EXCEPT
361360 | Keyword :: MATCH_RECOGNIZE
362- | Keyword :: OFFSET
363361 if !matches ! ( parser. peek_token_ref( ) . token, Token :: SemiColon | Token :: EOF ) =>
364362 {
365363 false
366364 }
367365
366+ // `LIMIT` can be considered an alias as long as it's not followed by a value. For example:
367+ // `SELECT * FROM tbl LIMIT WHERE 1=1` - alias
368+ // `SELECT * FROM tbl LIMIT 3` - not an alias
369+ Keyword :: LIMIT | Keyword :: OFFSET if peek_for_limit_options ( parser) => false ,
370+
368371 // `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
369372 // which would give it a different meanings, for example:
370373 // `SELECT * FROM tbl FETCH FIRST 10 ROWS` - not an alias
@@ -373,7 +376,7 @@ impl Dialect for SnowflakeDialect {
373376 if parser
374377 . peek_one_of_keywords ( & [ Keyword :: FIRST , Keyword :: NEXT ] )
375378 . is_some ( )
376- || matches ! ( parser. peek_token ( ) . token , Token :: Number ( _ , _ ) ) =>
379+ || peek_for_limit_options ( parser) =>
377380 {
378381 false
379382 }
@@ -387,6 +390,7 @@ impl Dialect for SnowflakeDialect {
387390 {
388391 false
389392 }
393+
390394 Keyword :: GLOBAL if parser. peek_keyword ( Keyword :: FULL ) => false ,
391395
392396 // Reserved keywords by the Snowflake dialect, which seem to be less strictive
@@ -472,6 +476,18 @@ impl Dialect for SnowflakeDialect {
472476 }
473477}
474478
479+ // Peeks ahead to identify tokens that are expected after
480+ // a LIMIT/FETCH keyword.
481+ fn peek_for_limit_options ( parser : & Parser ) -> bool {
482+ match & parser. peek_token_ref ( ) . token {
483+ Token :: Number ( _, _) | Token :: Placeholder ( _) => true ,
484+ Token :: SingleQuotedString ( val) if val. is_empty ( ) => true ,
485+ Token :: DollarQuotedString ( DollarQuotedString { value, .. } ) if value. is_empty ( ) => true ,
486+ Token :: Word ( w) if w. keyword == Keyword :: NULL => true ,
487+ _ => false ,
488+ }
489+ }
490+
475491fn parse_file_staging_command ( kw : Keyword , parser : & mut Parser ) -> Result < Statement , ParserError > {
476492 let stage = parse_snowflake_stage_name ( parser) ?;
477493 let pattern = if parser. parse_keyword ( Keyword :: PATTERN ) {
0 commit comments