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 3ac56707 MSSQL: Support standalone BEGIN...END blocks (#2186)
3ac56707 is described below

commit 3ac567076ce505adb033f08a2044cfe5c3a7b229
Author: Guan-Ming (Wesley) Chiu <[email protected]>
AuthorDate: Wed Feb 4 00:11:38 2026 +0800

    MSSQL: Support standalone BEGIN...END blocks (#2186)
    
    Signed-off-by: Guan-Ming (Wesley) Chiu 
<[email protected]>
    Co-authored-by: Ifeanyi Ubah <[email protected]>
---
 src/dialect/mssql.rs     | 24 +++++++++++++++-
 src/parser/mod.rs        | 13 ++++++---
 tests/sqlparser_mssql.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 103 insertions(+), 5 deletions(-)

diff --git a/src/dialect/mssql.rs b/src/dialect/mssql.rs
index 24f7c7c4..4056bf87 100644
--- a/src/dialect/mssql.rs
+++ b/src/dialect/mssql.rs
@@ -145,7 +145,29 @@ impl Dialect for MsSqlDialect {
     }
 
     fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, 
ParserError>> {
-        if parser.peek_keyword(Keyword::IF) {
+        if parser.parse_keyword(Keyword::BEGIN) {
+            // Check if this is a BEGIN...END block rather than BEGIN 
TRANSACTION
+            let is_block = parser
+                .maybe_parse(|p| {
+                    if p.parse_transaction_modifier().is_some()
+                        || p.parse_one_of_keywords(&[Keyword::TRANSACTION, 
Keyword::WORK])
+                            .is_some()
+                        || matches!(p.peek_token_ref().token, Token::SemiColon 
| Token::EOF)
+                    {
+                        p.expected("statement", p.peek_token())
+                    } else {
+                        Ok(())
+                    }
+                })
+                .unwrap_or(None)
+                .is_some();
+            if is_block {
+                Some(parser.parse_begin_exception_end())
+            } else {
+                parser.prev_token();
+                None
+            }
+        } else if parser.peek_keyword(Keyword::IF) {
             Some(self.parse_if_stmt(parser))
         } else if parser.parse_keywords(&[Keyword::CREATE, Keyword::TRIGGER]) {
             Some(self.parse_create_trigger(parser, false))
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 80c70583..5fa224f9 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -17902,9 +17902,9 @@ impl<'a> Parser<'a> {
         })
     }
 
-    /// Parse a 'BEGIN' statement
-    pub fn parse_begin(&mut self) -> Result<Statement, ParserError> {
-        let modifier = if !self.dialect.supports_start_transaction_modifier() {
+    /// Parse a transaction modifier keyword that can follow a `BEGIN` 
statement.
+    pub(crate) fn parse_transaction_modifier(&mut self) -> 
Option<TransactionModifier> {
+        if !self.dialect.supports_start_transaction_modifier() {
             None
         } else if self.parse_keyword(Keyword::DEFERRED) {
             Some(TransactionModifier::Deferred)
@@ -17918,7 +17918,12 @@ impl<'a> Parser<'a> {
             Some(TransactionModifier::Catch)
         } else {
             None
-        };
+        }
+    }
+
+    /// Parse a 'BEGIN' statement
+    pub fn parse_begin(&mut self) -> Result<Statement, ParserError> {
+        let modifier = self.parse_transaction_modifier();
         let transaction = match 
self.parse_one_of_keywords(&[Keyword::TRANSACTION, Keyword::WORK]) {
             Some(Keyword::TRANSACTION) => 
Some(BeginTransactionKind::Transaction),
             Some(Keyword::WORK) => Some(BeginTransactionKind::Work),
diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs
index 7ef4ce85..d7700088 100644
--- a/tests/sqlparser_mssql.rs
+++ b/tests/sqlparser_mssql.rs
@@ -2554,3 +2554,74 @@ fn test_sql_keywords_as_column_aliases() {
         }
     }
 }
+
+#[test]
+fn parse_mssql_begin_end_block() {
+    // Single statement
+    let sql = "BEGIN SELECT 1; END";
+    let stmt = ms().verified_stmt(sql);
+    match &stmt {
+        Statement::StartTransaction {
+            begin,
+            has_end_keyword,
+            statements,
+            transaction,
+            modifier,
+            ..
+        } => {
+            assert!(begin);
+            assert!(has_end_keyword);
+            assert!(transaction.is_none());
+            assert!(modifier.is_none());
+            assert_eq!(statements.len(), 1);
+        }
+        _ => panic!("Expected StartTransaction, got: {stmt:?}"),
+    }
+
+    // Multiple statements
+    let sql = "BEGIN SELECT 1; SELECT 2; END";
+    let stmt = ms().verified_stmt(sql);
+    match &stmt {
+        Statement::StartTransaction {
+            statements,
+            has_end_keyword,
+            ..
+        } => {
+            assert!(has_end_keyword);
+            assert_eq!(statements.len(), 2);
+        }
+        _ => panic!("Expected StartTransaction, got: {stmt:?}"),
+    }
+
+    // DML inside BEGIN/END
+    let sql = "BEGIN INSERT INTO t VALUES (1); UPDATE t SET x = 2; END";
+    let stmt = ms().verified_stmt(sql);
+    match &stmt {
+        Statement::StartTransaction {
+            statements,
+            has_end_keyword,
+            ..
+        } => {
+            assert!(has_end_keyword);
+            assert_eq!(statements.len(), 2);
+        }
+        _ => panic!("Expected StartTransaction, got: {stmt:?}"),
+    }
+
+    // BEGIN TRANSACTION still works
+    let sql = "BEGIN TRANSACTION";
+    let stmt = ms().verified_stmt(sql);
+    match &stmt {
+        Statement::StartTransaction {
+            begin,
+            has_end_keyword,
+            transaction,
+            ..
+        } => {
+            assert!(begin);
+            assert!(!has_end_keyword);
+            assert!(transaction.is_some());
+        }
+        _ => panic!("Expected StartTransaction, got: {stmt:?}"),
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to