Skip to content

Commit 0d18dfc

Browse files
committed
Reduce ambiguity for consecutive statements
- disallow keywords as table aliases for parsing statements without semicolons
1 parent b6971e8 commit 0d18dfc

File tree

3 files changed

+67
-4
lines changed

3 files changed

+67
-4
lines changed

src/dialect/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1028,8 +1028,14 @@ pub trait Dialect: Debug + Any {
10281028
/// Returns true if the specified keyword should be parsed as a table factor alias.
10291029
/// When explicit is true, the keyword is preceded by an `AS` word. Parser is provided
10301030
/// to enable looking ahead if needed.
1031-
fn is_table_factor_alias(&self, explicit: bool, kw: &Keyword, parser: &mut Parser) -> bool {
1032-
explicit || self.is_table_alias(kw, parser)
1031+
///
1032+
/// When the dialect supports statements without semicolon delimiter, actual keywords aren't parsed as aliases.
1033+
fn is_table_factor_alias(&self, explicit: bool, kw: &Keyword, _parser: &mut Parser) -> bool {
1034+
if self.supports_statements_without_semicolon_delimiter() {
1035+
kw == &Keyword::NoKeyword
1036+
} else {
1037+
explicit || self.is_table_alias(kw, _parser)
1038+
}
10331039
}
10341040

10351041
/// Returns true if this dialect supports querying historical table data

src/keywords.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,11 @@ pub const RESERVED_FOR_TABLE_ALIAS: &[Keyword] = &[
10971097
Keyword::TABLESAMPLE,
10981098
Keyword::FROM,
10991099
Keyword::OPEN,
1100+
Keyword::INSERT,
1101+
Keyword::UPDATE,
1102+
Keyword::DELETE,
1103+
Keyword::EXEC,
1104+
Keyword::EXECUTE,
11001105
];
11011106

11021107
/// Can't be used as a column alias, so that `SELECT <expr> alias`

tests/sqlparser_common.rs

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,45 @@ fn parse_select_with_table_alias() {
668668
);
669669
}
670670

671+
#[test]
672+
fn parse_select_with_table_alias_keyword() {
673+
// note: DECLARE isn't included in RESERVED_FOR_TABLE_ALIAS
674+
let table_alias_non_reserved_keyword = "SELECT a FROM lineitem DECLARE";
675+
let statements = all_dialects_requiring_semicolon_statement_delimiter()
676+
.parse_sql_statements(table_alias_non_reserved_keyword)
677+
.unwrap();
678+
assert_eq!(1, statements.len());
679+
assert_eq!(
680+
ParserError::ParserError("Expected: identifier, found: EOF".to_string()),
681+
all_dialects_not_requiring_semicolon_statement_delimiter()
682+
.parse_sql_statements(table_alias_non_reserved_keyword)
683+
.unwrap_err()
684+
);
685+
686+
let table_alias_quoted_keyword = "SELECT a FROM lineitem \"DECLARE\"";
687+
let statements = all_dialects()
688+
.parse_sql_statements(table_alias_quoted_keyword)
689+
.unwrap();
690+
assert_eq!(1, statements.len());
691+
}
692+
693+
#[test]
694+
fn parse_consecutive_queries() {
695+
let select_then_exec = "SELECT * FROM deleted; EXECUTE my_sp 'some', 'params'";
696+
let _ = all_dialects()
697+
.parse_sql_statements(select_then_exec)
698+
.unwrap();
699+
let _ = all_dialects_not_requiring_semicolon_statement_delimiter()
700+
.statements_without_semicolons_parse_to(select_then_exec, "");
701+
702+
let select_then_update = "SELECT 1 FROM x; UPDATE y SET z = 1";
703+
let _ = all_dialects()
704+
.parse_sql_statements(select_then_update)
705+
.unwrap();
706+
let _ = all_dialects_not_requiring_semicolon_statement_delimiter()
707+
.statements_without_semicolons_parse_to(select_then_update, "");
708+
}
709+
671710
#[test]
672711
fn parse_analyze() {
673712
verified_stmt("ANALYZE TABLE test_table");
@@ -934,7 +973,18 @@ fn parse_limit() {
934973

935974
#[test]
936975
fn parse_invalid_limit_by() {
937-
assert_err_parse_statements("SELECT * FROM user BY name", "name");
976+
assert_eq!(
977+
ParserError::ParserError("Expected: end of statement, found: name".to_string()),
978+
all_dialects_requiring_semicolon_statement_delimiter()
979+
.parse_sql_statements("SELECT * FROM user BY name")
980+
.unwrap_err()
981+
);
982+
assert_eq!(
983+
ParserError::ParserError("Expected: an SQL statement, found: BY".to_string()),
984+
all_dialects_not_requiring_semicolon_statement_delimiter()
985+
.parse_sql_statements("SELECT * FROM user BY name")
986+
.unwrap_err()
987+
);
938988
}
939989

940990
#[test]
@@ -11003,7 +11053,9 @@ fn parse_select_table_with_index_hints() {
1100311053

1100411054
// Test that dialects that don't support table hints will keep parsing the USE as table alias
1100511055
let sql = "SELECT * FROM T USE LIMIT 1";
11006-
let unsupported_dialects = all_dialects_where(|d| !d.supports_table_hints());
11056+
let unsupported_dialects = all_dialects_where(|d| {
11057+
!d.supports_table_hints() && !d.supports_statements_without_semicolon_delimiter()
11058+
});
1100711059
let select = unsupported_dialects
1100811060
.verified_only_select_with_canonical(sql, "SELECT * FROM T AS USE LIMIT 1");
1100911061
assert_eq!(

0 commit comments

Comments
 (0)