Skip to content

Commit 3d2db8c

Browse files
authored
Snowflake: Improve support for reserved keywords for table factor (#1942)
1 parent 85fa881 commit 3d2db8c

File tree

4 files changed

+103
-12
lines changed

4 files changed

+103
-12
lines changed

src/dialect/mod.rs

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

966-
/// Returns reserved keywords when looking to parse a `TableFactor`.
967-
/// See [Self::supports_from_trailing_commas]
968-
fn get_reserved_keywords_for_table_factor(&self) -> &[Keyword] {
969-
keywords::RESERVED_FOR_TABLE_FACTOR
970-
}
971-
972966
/// Returns reserved keywords that may prefix a select item expression
973967
/// e.g. `SELECT CONNECT_BY_ROOT name FROM Tbl2` (Snowflake)
974968
fn get_reserved_keywords_for_select_item_operator(&self) -> &[Keyword] {
@@ -1027,7 +1021,13 @@ pub trait Dialect: Debug + Any {
10271021
explicit || self.is_column_alias(kw, parser)
10281022
}
10291023

1030-
/// Returns true if the specified keyword should be parsed as a table identifier.
1024+
/// Returns true if the specified keyword should be parsed as a table factor identifier.
1025+
/// See [keywords::RESERVED_FOR_TABLE_FACTOR]
1026+
fn is_table_factor(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
1027+
!keywords::RESERVED_FOR_TABLE_FACTOR.contains(kw)
1028+
}
1029+
1030+
/// Returns true if the specified keyword should be parsed as a table factor alias.
10311031
/// See [keywords::RESERVED_FOR_TABLE_ALIAS]
10321032
fn is_table_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
10331033
!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
@@ -4449,11 +4449,7 @@ impl<'a> Parser<'a> {
44494449
self.parse_comma_separated_with_trailing_commas(
44504450
Parser::parse_table_and_joins,
44514451
trailing_commas,
4452-
|kw, _parser| {
4453-
self.dialect
4454-
.get_reserved_keywords_for_table_factor()
4455-
.contains(kw)
4456-
},
4452+
|kw, parser| !self.dialect.is_table_factor(kw, parser),
44574453
)
44584454
}
44594455

tests/sqlparser_snowflake.rs

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

3609+
#[test]
3610+
fn test_sql_keywords_as_table_factor() {
3611+
// LIMIT is a table factor, Snowflake does not reserve it
3612+
snowflake().one_statement_parses_to("SELECT * FROM tbl, LIMIT", "SELECT * FROM tbl, LIMIT");
3613+
// LIMIT is not a table factor
3614+
snowflake().one_statement_parses_to("SELECT * FROM tbl, LIMIT 1", "SELECT * FROM tbl LIMIT 1");
3615+
// ORDER is reserved
3616+
assert!(snowflake()
3617+
.parse_sql_statements("SELECT * FROM tbl, order")
3618+
.is_err());
3619+
}
3620+
36093621
#[test]
36103622
fn test_timetravel_at_before() {
36113623
snowflake().verified_only_select("SELECT * FROM tbl AT(TIMESTAMP => '2024-12-15 00:00:00')");

0 commit comments

Comments
 (0)