This is an automated email from the ASF dual-hosted git repository.
github-bot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git
The following commit(s) were added to refs/heads/main by this push:
new 1b842d3b MSSQL: Parse IF/ELSE without semicolon delimiters (#2128)
1b842d3b is described below
commit 1b842d3b6a76eadd0a8dc9bfecc8cb1dcd0bd3c0
Author: Yoav Cohen <[email protected]>
AuthorDate: Wed Dec 10 07:04:22 2025 -0500
MSSQL: Parse IF/ELSE without semicolon delimiters (#2128)
---
src/dialect/mssql.rs | 22 +++++++++++++++++-----
src/parser/mod.rs | 13 +++++++------
tests/sqlparser_mssql.rs | 39 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 62 insertions(+), 12 deletions(-)
diff --git a/src/dialect/mssql.rs b/src/dialect/mssql.rs
index e1902b38..faf3402c 100644
--- a/src/dialect/mssql.rs
+++ b/src/dialect/mssql.rs
@@ -21,14 +21,12 @@ use crate::ast::{
GranteesType, IfStatement, Statement,
};
use crate::dialect::Dialect;
-use crate::keywords::{self, Keyword};
+use crate::keywords::Keyword;
use crate::parser::{Parser, ParserError};
use crate::tokenizer::Token;
#[cfg(not(feature = "std"))]
use alloc::{vec, vec::Vec};
-const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[Keyword::IF, Keyword::ELSE];
-
/// A [`Dialect`] for [Microsoft SQL
Server](https://www.microsoft.com/en-us/sql-server/)
#[derive(Debug)]
pub struct MsSqlDialect {}
@@ -128,8 +126,22 @@ impl Dialect for MsSqlDialect {
&[GranteesType::Public]
}
- fn is_column_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
- !keywords::RESERVED_FOR_COLUMN_ALIAS.contains(kw) &&
!RESERVED_FOR_COLUMN_ALIAS.contains(kw)
+ fn is_select_item_alias(&self, explicit: bool, kw: &Keyword, parser: &mut
Parser) -> bool {
+ match kw {
+ // List of keywords that cannot be used as select item aliases in
MSSQL
+ // regardless of whether the alias is explicit or implicit
+ Keyword::IF | Keyword::ELSE => false,
+ _ => explicit || self.is_column_alias(kw, parser),
+ }
+ }
+
+ fn is_table_factor_alias(&self, explicit: bool, kw: &Keyword, parser: &mut
Parser) -> bool {
+ match kw {
+ // List of keywords that cannot be used as table aliases in MSSQL
+ // regardless of whether the alias is explicit or implicit
+ Keyword::IF | Keyword::ELSE => false,
+ _ => explicit || self.is_table_alias(kw, parser),
+ }
}
fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement,
ParserError>> {
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index dc529945..54fb3273 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -11503,16 +11503,17 @@ impl<'a> Parser<'a> {
let next_token = self.next_token();
match next_token.token {
- // By default, if a word is located after the `AS` keyword we
consider it an alias
- // as long as it's not reserved.
+ // Accepts a keyword as an alias if the AS keyword explicitly
indicate an alias or if the
+ // caller provided a list of reserved keywords and the keyword is
not on that list.
Token::Word(w)
- if after_as || reserved_kwds.is_some_and(|x|
!x.contains(&w.keyword)) =>
+ if reserved_kwds.is_some()
+ && (after_as || reserved_kwds.is_some_and(|x|
!x.contains(&w.keyword))) =>
{
Ok(Some(w.into_ident(next_token.span)))
}
- // This pattern allows for customizing the acceptance of words as
aliases based on the caller's
- // context, such as to what SQL element this word is a potential
alias of (select item alias, table name
- // alias, etc.) or dialect-specific logic that goes beyond a
simple list of reserved keywords.
+ // Accepts a keyword as alias based on the caller's context, such
as to what SQL element
+ // this word is a potential alias of using the validator
call-back. This allows for
+ // dialect-specific logic.
Token::Word(w) if validator(after_as, &w.keyword, self) => {
Ok(Some(w.into_ident(next_token.span)))
}
diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs
index 37e8e962..70e0aab4 100644
--- a/tests/sqlparser_mssql.rs
+++ b/tests/sqlparser_mssql.rs
@@ -2501,8 +2501,45 @@ fn test_tsql_no_semicolon_delimiter() {
DECLARE @X AS NVARCHAR(MAX)='x'
DECLARE @Y AS NVARCHAR(MAX)='y'
"#;
-
let stmts = tsql().parse_sql_statements(sql).unwrap();
assert_eq!(stmts.len(), 2);
assert!(stmts.iter().all(|s| matches!(s, Statement::Declare { .. })));
+
+ let sql = r#"
+SELECT col FROM tbl
+IF x=1
+ SELECT 1
+ELSE
+ SELECT 2
+ "#;
+ let stmts = tsql().parse_sql_statements(sql).unwrap();
+ assert_eq!(stmts.len(), 2);
+ assert!(matches!(&stmts[0], Statement::Query(_)));
+ assert!(matches!(&stmts[1], Statement::If(_)));
+}
+
+#[test]
+fn test_sql_keywords_as_table_aliases() {
+ // Some keywords that should not be parsed as an alias implicitly or
explicitly
+ let reserved_kws = vec!["IF", "ELSE"];
+ for kw in reserved_kws {
+ for explicit in &["", "AS "] {
+ assert!(tsql()
+ .parse_sql_statements(&format!("SELECT * FROM tbl
{explicit}{kw}"))
+ .is_err());
+ }
+ }
+}
+
+#[test]
+fn test_sql_keywords_as_column_aliases() {
+ // Some keywords that should not be parsed as an alias implicitly or
explicitly
+ let reserved_kws = vec!["IF", "ELSE"];
+ for kw in reserved_kws {
+ for explicit in &["", "AS "] {
+ assert!(tsql()
+ .parse_sql_statements(&format!("SELECT col {explicit}{kw} FROM
tbl"))
+ .is_err());
+ }
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]