Skip to content

Commit d1cd72f

Browse files
committed
Snowflake: Improve support for reserved keywords for table factor
1 parent bc2c4e2 commit d1cd72f

File tree

4 files changed

+124
-12
lines changed

4 files changed

+124
-12
lines changed

src/dialect/mod.rs

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

951-
/// Returns reserved keywords when looking to parse a `TableFactor`.
952-
/// See [Self::supports_from_trailing_commas]
953-
fn get_reserved_keywords_for_table_factor(&self) -> &[Keyword] {
954-
keywords::RESERVED_FOR_TABLE_FACTOR
955-
}
956-
957951
/// Returns reserved keywords that may prefix a select item expression
958952
/// e.g. `SELECT CONNECT_BY_ROOT name FROM Tbl2` (Snowflake)
959953
fn get_reserved_keywords_for_select_item_operator(&self) -> &[Keyword] {
@@ -1012,7 +1006,13 @@ pub trait Dialect: Debug + Any {
10121006
explicit || self.is_column_alias(kw, parser)
10131007
}
10141008

1015-
/// Returns true if the specified keyword should be parsed as a table identifier.
1009+
/// Returns true if the specified keyword should be parsed as a table factor identifier.
1010+
/// See [keywords::RESERVED_FOR_TABLE_FACTOR]
1011+
fn is_table_factor(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
1012+
!keywords::RESERVED_FOR_TABLE_FACTOR.contains(kw)
1013+
}
1014+
1015+
/// Returns true if the specified keyword should be parsed as a table factor alias.
10161016
/// See [keywords::RESERVED_FOR_TABLE_ALIAS]
10171017
fn is_table_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
10181018
!keywords::RESERVED_FOR_TABLE_ALIAS.contains(kw)

src/dialect/snowflake.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,82 @@ use super::keywords::RESERVED_FOR_IDENTIFIER;
4545
use sqlparser::ast::StorageSerializationPolicy;
4646

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

506+
fn is_table_factor(&self, kw: &Keyword, parser: &mut Parser) -> bool {
507+
match kw {
508+
Keyword::LIMIT
509+
if matches!(
510+
parser.peek_token().token,
511+
Token::Number(_, _) | Token::Placeholder(_)
512+
) =>
513+
{
514+
false
515+
}
516+
517+
_ => !RESERVED_KEYWORDS_FOR_TABLE_FACTOR.contains(kw),
518+
}
519+
}
520+
430521
/// See: <https://docs.snowflake.com/en/sql-reference/constructs/at-before>
431522
fn supports_timestamp_versioning(&self) -> bool {
432523
true

src/parser/mod.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4369,11 +4369,7 @@ impl<'a> Parser<'a> {
43694369
self.parse_comma_separated_with_trailing_commas(
43704370
Parser::parse_table_and_joins,
43714371
trailing_commas,
4372-
|kw, _parser| {
4373-
self.dialect
4374-
.get_reserved_keywords_for_table_factor()
4375-
.contains(kw)
4376-
},
4372+
|kw, parser| !self.dialect.is_table_factor(kw, parser),
43774373
)
43784374
}
43794375

tests/sqlparser_snowflake.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3543,6 +3543,18 @@ fn test_sql_keywords_as_table_aliases() {
35433543
}
35443544
}
35453545

3546+
#[test]
3547+
fn test_sql_keywords_as_table_factor() {
3548+
// LIMIT is a table factor, Snowflake does not reserve it
3549+
snowflake().one_statement_parses_to("SELECT * FROM tbl, LIMIT", "SELECT * FROM tbl, LIMIT");
3550+
// LIMIT is not a table factor
3551+
snowflake().one_statement_parses_to("SELECT * FROM tbl, LIMIT 1", "SELECT * FROM tbl LIMIT 1");
3552+
// ORDER is reserved
3553+
assert!(snowflake()
3554+
.parse_sql_statements("SELECT * FROM tbl, order")
3555+
.is_err());
3556+
}
3557+
35463558
#[test]
35473559
fn test_timetravel_at_before() {
35483560
snowflake().verified_only_select("SELECT * FROM tbl AT(TIMESTAMP => '2024-12-15 00:00:00')");
@@ -4438,3 +4450,16 @@ fn test_snowflake_identifier_function() {
44384450
true
44394451
);
44404452
}
4453+
4454+
#[test]
4455+
fn test_x() {
4456+
println!(
4457+
"{:#?}",
4458+
snowflake()
4459+
.parse_sql_statements(
4460+
r#"
4461+
SELECT email from customers, order by 1"#
4462+
)
4463+
.unwrap()
4464+
);
4465+
}

0 commit comments

Comments
 (0)