Skip to content

Commit 325f060

Browse files
yoavcloudayman-sigma
authored andcommitted
Snowflake: Improve support for reserved keywords for table factor (apache#1942)
1 parent 75a1e5f commit 325f060

4 files changed

Lines changed: 103 additions & 12 deletions

File tree

src/dialect/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -968,12 +968,6 @@ pub trait Dialect: Debug + Any {
968968
keywords::RESERVED_FOR_IDENTIFIER.contains(&kw)
969969
}
970970

971-
/// Returns reserved keywords when looking to parse a `TableFactor`.
972-
/// See [Self::supports_from_trailing_commas]
973-
fn get_reserved_keywords_for_table_factor(&self) -> &[Keyword] {
974-
keywords::RESERVED_FOR_TABLE_FACTOR
975-
}
976-
977971
/// Returns reserved keywords that may prefix a select item expression
978972
/// e.g. `SELECT CONNECT_BY_ROOT name FROM Tbl2` (Snowflake)
979973
fn get_reserved_keywords_for_select_item_operator(&self) -> &[Keyword] {
@@ -1032,7 +1026,13 @@ pub trait Dialect: Debug + Any {
10321026
explicit || self.is_column_alias(kw, parser)
10331027
}
10341028

1035-
/// Returns true if the specified keyword should be parsed as a table identifier.
1029+
/// Returns true if the specified keyword should be parsed as a table factor identifier.
1030+
/// See [keywords::RESERVED_FOR_TABLE_FACTOR]
1031+
fn is_table_factor(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
1032+
!keywords::RESERVED_FOR_TABLE_FACTOR.contains(kw)
1033+
}
1034+
1035+
/// Returns true if the specified keyword should be parsed as a table factor alias.
10361036
/// See [keywords::RESERVED_FOR_TABLE_ALIAS]
10371037
fn is_table_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
10381038
!keywords::RESERVED_FOR_TABLE_ALIAS.contains(kw)

src/dialect/snowflake.rs

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,82 @@ use super::keywords::RESERVED_FOR_IDENTIFIER;
4747
use sqlparser::ast::StorageSerializationPolicy;
4848

4949
const RESERVED_KEYWORDS_FOR_SELECT_ITEM_OPERATOR: [Keyword; 1] = [Keyword::CONNECT_BY_ROOT];
50+
51+
// See: <https://docs.snowflake.com/en/sql-reference/reserved-keywords>
52+
const RESERVED_KEYWORDS_FOR_TABLE_FACTOR: &[Keyword] = &[
53+
Keyword::ALL,
54+
Keyword::ALTER,
55+
Keyword::AND,
56+
Keyword::ANY,
57+
Keyword::AS,
58+
Keyword::BETWEEN,
59+
Keyword::BY,
60+
Keyword::CHECK,
61+
Keyword::COLUMN,
62+
Keyword::CONNECT,
63+
Keyword::CREATE,
64+
Keyword::CROSS,
65+
Keyword::CURRENT,
66+
Keyword::DELETE,
67+
Keyword::DISTINCT,
68+
Keyword::DROP,
69+
Keyword::ELSE,
70+
Keyword::EXISTS,
71+
Keyword::FOLLOWING,
72+
Keyword::FOR,
73+
Keyword::FROM,
74+
Keyword::FULL,
75+
Keyword::GRANT,
76+
Keyword::GROUP,
77+
Keyword::HAVING,
78+
Keyword::ILIKE,
79+
Keyword::IN,
80+
Keyword::INCREMENT,
81+
Keyword::INNER,
82+
Keyword::INSERT,
83+
Keyword::INTERSECT,
84+
Keyword::INTO,
85+
Keyword::IS,
86+
Keyword::JOIN,
87+
Keyword::LEFT,
88+
Keyword::LIKE,
89+
Keyword::MINUS,
90+
Keyword::NATURAL,
91+
Keyword::NOT,
92+
Keyword::NULL,
93+
Keyword::OF,
94+
Keyword::ON,
95+
Keyword::OR,
96+
Keyword::ORDER,
97+
Keyword::QUALIFY,
98+
Keyword::REGEXP,
99+
Keyword::REVOKE,
100+
Keyword::RIGHT,
101+
Keyword::RLIKE,
102+
Keyword::ROW,
103+
Keyword::ROWS,
104+
Keyword::SAMPLE,
105+
Keyword::SELECT,
106+
Keyword::SET,
107+
Keyword::SOME,
108+
Keyword::START,
109+
Keyword::TABLE,
110+
Keyword::TABLESAMPLE,
111+
Keyword::THEN,
112+
Keyword::TO,
113+
Keyword::TRIGGER,
114+
Keyword::UNION,
115+
Keyword::UNIQUE,
116+
Keyword::UPDATE,
117+
Keyword::USING,
118+
Keyword::VALUES,
119+
Keyword::WHEN,
120+
Keyword::WHENEVER,
121+
Keyword::WHERE,
122+
Keyword::WINDOW,
123+
Keyword::WITH,
124+
];
125+
50126
/// A [`Dialect`] for [Snowflake](https://www.snowflake.com/)
51127
#[derive(Debug, Default)]
52128
pub struct SnowflakeDialect;
@@ -433,6 +509,13 @@ impl Dialect for SnowflakeDialect {
433509
}
434510
}
435511

512+
fn is_table_factor(&self, kw: &Keyword, parser: &mut Parser) -> bool {
513+
match kw {
514+
Keyword::LIMIT if peek_for_limit_options(parser) => false,
515+
_ => !RESERVED_KEYWORDS_FOR_TABLE_FACTOR.contains(kw),
516+
}
517+
}
518+
436519
/// See: <https://docs.snowflake.com/en/sql-reference/constructs/at-before>
437520
fn supports_timestamp_versioning(&self) -> bool {
438521
true

src/parser/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4480,11 +4480,7 @@ impl<'a> Parser<'a> {
44804480
self.parse_comma_separated_with_trailing_commas(
44814481
Parser::parse_table_and_joins,
44824482
trailing_commas,
4483-
|kw, _parser| {
4484-
self.dialect
4485-
.get_reserved_keywords_for_table_factor()
4486-
.contains(kw)
4487-
},
4483+
|kw, parser| !self.dialect.is_table_factor(kw, parser),
44884484
)
44894485
}
44904486

tests/sqlparser_snowflake.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3625,6 +3625,18 @@ fn test_sql_keywords_as_table_aliases() {
36253625
snowflake().verified_stmt("SELECT * FROM tbl LIMIT $$$$");
36263626
}
36273627

3628+
#[test]
3629+
fn test_sql_keywords_as_table_factor() {
3630+
// LIMIT is a table factor, Snowflake does not reserve it
3631+
snowflake().one_statement_parses_to("SELECT * FROM tbl, LIMIT", "SELECT * FROM tbl, LIMIT");
3632+
// LIMIT is not a table factor
3633+
snowflake().one_statement_parses_to("SELECT * FROM tbl, LIMIT 1", "SELECT * FROM tbl LIMIT 1");
3634+
// ORDER is reserved
3635+
assert!(snowflake()
3636+
.parse_sql_statements("SELECT * FROM tbl, order")
3637+
.is_err());
3638+
}
3639+
36283640
#[test]
36293641
fn test_timetravel_at_before() {
36303642
snowflake().verified_only_select("SELECT * FROM tbl AT(TIMESTAMP => '2024-12-15 00:00:00')");

0 commit comments

Comments
 (0)