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]

Reply via email to