Skip to content

Commit 7240269

Browse files
committed
Improve the look ahead accuracy
1 parent 5eaab33 commit 7240269

File tree

2 files changed

+35
-21
lines changed

2 files changed

+35
-21
lines changed

src/dialect/snowflake.rs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ use crate::ast::helpers::stmt_data_loading::{
2323
FileStagingCommand, StageLoadSelectItem, StageLoadSelectItemKind, StageParamsObject,
2424
};
2525
use 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
}
@@ -358,7 +358,6 @@ impl Dialect for SnowflakeDialect {
358358
| Keyword::UNPIVOT
359359
| Keyword::EXCEPT
360360
| Keyword::MATCH_RECOGNIZE
361-
| Keyword::OFFSET
362361
if !matches!(parser.peek_token_ref().token, Token::SemiColon | Token::EOF) =>
363362
{
364363
false
@@ -367,14 +366,7 @@ impl Dialect for SnowflakeDialect {
367366
// `LIMIT` can be considered an alias as long as it's not followed by a value. For example:
368367
// `SELECT * FROM tbl LIMIT WHERE 1=1` - alias
369368
// `SELECT * FROM tbl LIMIT 3` - not an alias
370-
Keyword::LIMIT
371-
if matches!(
372-
parser.peek_token().token,
373-
Token::Number(_, _) | Token::Placeholder(_)
374-
) =>
375-
{
376-
false
377-
}
369+
Keyword::LIMIT | Keyword::OFFSET if peek_for_limit_options(parser) => false,
378370

379371
// `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
380372
// which would give it a different meanings, for example:
@@ -384,10 +376,7 @@ impl Dialect for SnowflakeDialect {
384376
if parser
385377
.peek_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT])
386378
.is_some()
387-
|| matches!(
388-
parser.peek_token().token,
389-
Token::Number(_, _) | Token::Placeholder(_)
390-
) =>
379+
|| peek_for_limit_options(parser) =>
391380
{
392381
false
393382
}
@@ -487,6 +476,18 @@ impl Dialect for SnowflakeDialect {
487476
}
488477
}
489478

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+
490491
fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statement, ParserError> {
491492
let stage = parse_snowflake_stage_name(parser)?;
492493
let pattern = if parser.parse_keyword(Keyword::PATTERN) {

tests/sqlparser_snowflake.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3535,6 +3535,15 @@ fn test_sql_keywords_as_select_item_aliases() {
35353535
.parse_sql_statements(&format!("SELECT 1 {kw}"))
35363536
.is_err());
35373537
}
3538+
3539+
// LIMIT is alias
3540+
snowflake().one_statement_parses_to("SELECT 1 LIMIT", "SELECT 1 AS LIMIT");
3541+
// LIMIT is not an alias
3542+
snowflake().verified_stmt("SELECT 1 LIMIT 1");
3543+
snowflake().verified_stmt("SELECT 1 LIMIT $1");
3544+
snowflake().verified_stmt("SELECT 1 LIMIT ''");
3545+
snowflake().verified_stmt("SELECT 1 LIMIT NULL");
3546+
snowflake().verified_stmt("SELECT 1 LIMIT $$$$");
35383547
}
35393548

35403549
#[test]
@@ -3587,10 +3596,14 @@ fn test_sql_keywords_as_table_aliases() {
35873596
.is_err());
35883597
}
35893598

3590-
// LIMIT as alias and not as alias
3599+
// LIMIT is alias
35913600
snowflake().one_statement_parses_to("SELECT * FROM tbl LIMIT", "SELECT * FROM tbl AS LIMIT");
3601+
// LIMIT is not an alias
35923602
snowflake().verified_stmt("SELECT * FROM tbl LIMIT 1");
35933603
snowflake().verified_stmt("SELECT * FROM tbl LIMIT $1");
3604+
snowflake().verified_stmt("SELECT * FROM tbl LIMIT ''");
3605+
snowflake().verified_stmt("SELECT * FROM tbl LIMIT NULL");
3606+
snowflake().verified_stmt("SELECT * FROM tbl LIMIT $$$$");
35943607
}
35953608

35963609
#[test]

0 commit comments

Comments
 (0)