mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-01 20:52:16 +00:00
Snowflake: Improve accuracy of lookahead in implicit LIMIT alias (#1941)
This commit is contained in:
parent
4d9338638f
commit
92db20673b
2 changed files with 45 additions and 11 deletions
|
@ -23,8 +23,8 @@ use crate::ast::helpers::stmt_data_loading::{
|
||||||
FileStagingCommand, StageLoadSelectItem, StageLoadSelectItemKind, StageParamsObject,
|
FileStagingCommand, StageLoadSelectItem, StageLoadSelectItemKind, StageParamsObject,
|
||||||
};
|
};
|
||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
ColumnOption, ColumnPolicy, ColumnPolicyProperty, CopyIntoSnowflakeKind, Ident,
|
ColumnOption, ColumnPolicy, ColumnPolicyProperty, CopyIntoSnowflakeKind, DollarQuotedString,
|
||||||
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
|
Ident, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
|
||||||
IdentityPropertyOrder, ObjectName, ObjectNamePart, RowAccessPolicy, ShowObjects, SqlOption,
|
IdentityPropertyOrder, ObjectName, ObjectNamePart, RowAccessPolicy, ShowObjects, SqlOption,
|
||||||
Statement, TagsColumnOption, WrappedCollection,
|
Statement, TagsColumnOption, WrappedCollection,
|
||||||
};
|
};
|
||||||
|
@ -307,22 +307,22 @@ impl Dialect for SnowflakeDialect {
|
||||||
// they are not followed by other tokens that may change their meaning
|
// they are not followed by other tokens that may change their meaning
|
||||||
// e.g. `SELECT * EXCEPT (col1) FROM tbl`
|
// e.g. `SELECT * EXCEPT (col1) FROM tbl`
|
||||||
Keyword::EXCEPT
|
Keyword::EXCEPT
|
||||||
// e.g. `SELECT 1 LIMIT 5`
|
|
||||||
| Keyword::LIMIT
|
|
||||||
// e.g. `SELECT 1 OFFSET 5 ROWS`
|
|
||||||
| Keyword::OFFSET
|
|
||||||
// e.g. `INSERT INTO t SELECT 1 RETURNING *`
|
// e.g. `INSERT INTO t SELECT 1 RETURNING *`
|
||||||
| Keyword::RETURNING if !matches!(parser.peek_token_ref().token, Token::Comma | Token::EOF) =>
|
| Keyword::RETURNING if !matches!(parser.peek_token_ref().token, Token::Comma | Token::EOF) =>
|
||||||
{
|
{
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// e.g. `SELECT 1 LIMIT 5` - not an alias
|
||||||
|
// e.g. `SELECT 1 OFFSET 5 ROWS` - not an alias
|
||||||
|
Keyword::LIMIT | Keyword::OFFSET if peek_for_limit_options(parser) => false,
|
||||||
|
|
||||||
// `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
|
// `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
|
||||||
// which would give it a different meanings, for example:
|
// which would give it a different meanings, for example:
|
||||||
// `SELECT 1 FETCH FIRST 10 ROWS` - not an alias
|
// `SELECT 1 FETCH FIRST 10 ROWS` - not an alias
|
||||||
// `SELECT 1 FETCH 10` - not an alias
|
// `SELECT 1 FETCH 10` - not an alias
|
||||||
Keyword::FETCH if parser.peek_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT]).is_some()
|
Keyword::FETCH if parser.peek_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT]).is_some()
|
||||||
|| matches!(parser.peek_token().token, Token::Number(_, _)) =>
|
|| peek_for_limit_options(parser) =>
|
||||||
{
|
{
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -351,20 +351,23 @@ impl Dialect for SnowflakeDialect {
|
||||||
match kw {
|
match kw {
|
||||||
// The following keywords can be considered an alias as long as
|
// The following keywords can be considered an alias as long as
|
||||||
// they are not followed by other tokens that may change their meaning
|
// they are not followed by other tokens that may change their meaning
|
||||||
Keyword::LIMIT
|
Keyword::RETURNING
|
||||||
| Keyword::RETURNING
|
|
||||||
| Keyword::INNER
|
| Keyword::INNER
|
||||||
| Keyword::USING
|
| Keyword::USING
|
||||||
| Keyword::PIVOT
|
| Keyword::PIVOT
|
||||||
| Keyword::UNPIVOT
|
| Keyword::UNPIVOT
|
||||||
| Keyword::EXCEPT
|
| Keyword::EXCEPT
|
||||||
| Keyword::MATCH_RECOGNIZE
|
| Keyword::MATCH_RECOGNIZE
|
||||||
| Keyword::OFFSET
|
|
||||||
if !matches!(parser.peek_token_ref().token, Token::SemiColon | Token::EOF) =>
|
if !matches!(parser.peek_token_ref().token, Token::SemiColon | Token::EOF) =>
|
||||||
{
|
{
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// `LIMIT` can be considered an alias as long as it's not followed by a value. For example:
|
||||||
|
// `SELECT * FROM tbl LIMIT WHERE 1=1` - alias
|
||||||
|
// `SELECT * FROM tbl LIMIT 3` - not an alias
|
||||||
|
Keyword::LIMIT | Keyword::OFFSET if peek_for_limit_options(parser) => false,
|
||||||
|
|
||||||
// `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
|
// `FETCH` can be considered an alias as long as it's not followed by `FIRST`` or `NEXT`
|
||||||
// which would give it a different meanings, for example:
|
// which would give it a different meanings, for example:
|
||||||
// `SELECT * FROM tbl FETCH FIRST 10 ROWS` - not an alias
|
// `SELECT * FROM tbl FETCH FIRST 10 ROWS` - not an alias
|
||||||
|
@ -373,7 +376,7 @@ impl Dialect for SnowflakeDialect {
|
||||||
if parser
|
if parser
|
||||||
.peek_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT])
|
.peek_one_of_keywords(&[Keyword::FIRST, Keyword::NEXT])
|
||||||
.is_some()
|
.is_some()
|
||||||
|| matches!(parser.peek_token().token, Token::Number(_, _)) =>
|
|| peek_for_limit_options(parser) =>
|
||||||
{
|
{
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
@ -387,6 +390,7 @@ impl Dialect for SnowflakeDialect {
|
||||||
{
|
{
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
Keyword::GLOBAL if parser.peek_keyword(Keyword::FULL) => false,
|
Keyword::GLOBAL if parser.peek_keyword(Keyword::FULL) => false,
|
||||||
|
|
||||||
// Reserved keywords by the Snowflake dialect, which seem to be less strictive
|
// Reserved keywords by the Snowflake dialect, which seem to be less strictive
|
||||||
|
@ -472,6 +476,18 @@ impl Dialect for SnowflakeDialect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Peeks ahead to identify tokens that are expected after
|
||||||
|
// a LIMIT/FETCH keyword.
|
||||||
|
fn peek_for_limit_options(parser: &Parser) -> bool {
|
||||||
|
match &parser.peek_token_ref().token {
|
||||||
|
Token::Number(_, _) | Token::Placeholder(_) => true,
|
||||||
|
Token::SingleQuotedString(val) if val.is_empty() => true,
|
||||||
|
Token::DollarQuotedString(DollarQuotedString { value, .. }) if value.is_empty() => true,
|
||||||
|
Token::Word(w) if w.keyword == Keyword::NULL => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statement, ParserError> {
|
fn parse_file_staging_command(kw: Keyword, parser: &mut Parser) -> Result<Statement, ParserError> {
|
||||||
let stage = parse_snowflake_stage_name(parser)?;
|
let stage = parse_snowflake_stage_name(parser)?;
|
||||||
let pattern = if parser.parse_keyword(Keyword::PATTERN) {
|
let pattern = if parser.parse_keyword(Keyword::PATTERN) {
|
||||||
|
|
|
@ -3535,6 +3535,15 @@ fn test_sql_keywords_as_select_item_aliases() {
|
||||||
.parse_sql_statements(&format!("SELECT 1 {kw}"))
|
.parse_sql_statements(&format!("SELECT 1 {kw}"))
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LIMIT is alias
|
||||||
|
snowflake().one_statement_parses_to("SELECT 1 LIMIT", "SELECT 1 AS LIMIT");
|
||||||
|
// LIMIT is not an alias
|
||||||
|
snowflake().verified_stmt("SELECT 1 LIMIT 1");
|
||||||
|
snowflake().verified_stmt("SELECT 1 LIMIT $1");
|
||||||
|
snowflake().verified_stmt("SELECT 1 LIMIT ''");
|
||||||
|
snowflake().verified_stmt("SELECT 1 LIMIT NULL");
|
||||||
|
snowflake().verified_stmt("SELECT 1 LIMIT $$$$");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -3586,6 +3595,15 @@ fn test_sql_keywords_as_table_aliases() {
|
||||||
.parse_sql_statements(&format!("SELECT * FROM tbl {kw}"))
|
.parse_sql_statements(&format!("SELECT * FROM tbl {kw}"))
|
||||||
.is_err());
|
.is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LIMIT is alias
|
||||||
|
snowflake().one_statement_parses_to("SELECT * FROM tbl LIMIT", "SELECT * FROM tbl AS LIMIT");
|
||||||
|
// LIMIT is not an alias
|
||||||
|
snowflake().verified_stmt("SELECT * FROM tbl LIMIT 1");
|
||||||
|
snowflake().verified_stmt("SELECT * FROM tbl LIMIT $1");
|
||||||
|
snowflake().verified_stmt("SELECT * FROM tbl LIMIT ''");
|
||||||
|
snowflake().verified_stmt("SELECT * FROM tbl LIMIT NULL");
|
||||||
|
snowflake().verified_stmt("SELECT * FROM tbl LIMIT $$$$");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue