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 6f0e803a MSSQL: support EXEC (@sql) dynamic SQL execution (#2234)
6f0e803a is described below

commit 6f0e803aa77414e83aefd326e23231c51b60ae32
Author: Yoabot <[email protected]>
AuthorDate: Fri Feb 27 11:44:59 2026 +0100

    MSSQL: support EXEC (@sql) dynamic SQL execution (#2234)
---
 src/parser/mod.rs        | 23 ++++++++++++++++++++---
 tests/sqlparser_mssql.rs | 23 +++++++++++++++++++++++
 2 files changed, 43 insertions(+), 3 deletions(-)

diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index bea566bb..75db4d24 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -18583,6 +18583,9 @@ impl<'a> Parser<'a> {
 
     /// Parse a SQL `EXECUTE` statement
     pub fn parse_execute(&mut self) -> Result<Statement, ParserError> {
+        // Track whether the procedure/expression name itself was wrapped in 
parens,
+        // i.e. `EXEC (@sql)` (dynamic string execution) vs `EXEC sp_name`.
+        // When the name has parens there are no additional parameters.
         let name = if self.dialect.supports_execute_immediate()
             && self.parse_keyword(Keyword::IMMEDIATE)
         {
@@ -18593,10 +18596,18 @@ impl<'a> Parser<'a> {
             if has_parentheses {
                 self.expect_token(&Token::RParen)?;
             }
-            Some(name)
+            Some((name, has_parentheses))
         };
 
-        let has_parentheses = self.consume_token(&Token::LParen);
+        let name_had_parentheses = name.as_ref().map(|(_, p)| 
*p).unwrap_or(false);
+
+        // Only look for a parameter list when the name was NOT wrapped in 
parens.
+        // `EXEC (@sql)` is dynamic SQL execution and takes no parameters here.
+        let has_parentheses = if name_had_parentheses {
+            false
+        } else {
+            self.consume_token(&Token::LParen)
+        };
 
         let end_kws = &[Keyword::USING, Keyword::OUTPUT, Keyword::DEFAULT];
         let end_token = match (has_parentheses, self.peek_token().token) {
@@ -18606,12 +18617,18 @@ impl<'a> Parser<'a> {
             (false, _) => Token::SemiColon,
         };
 
-        let parameters = self.parse_comma_separated0(Parser::parse_expr, 
end_token)?;
+        let parameters = if name_had_parentheses {
+            vec![]
+        } else {
+            self.parse_comma_separated0(Parser::parse_expr, end_token)?
+        };
 
         if has_parentheses {
             self.expect_token(&Token::RParen)?;
         }
 
+        let name = name.map(|(n, _)| n);
+
         let into = if self.parse_keyword(Keyword::INTO) {
             self.parse_comma_separated(Self::parse_identifier)?
         } else {
diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs
index 6c8412a4..8bdb1c20 100644
--- a/tests/sqlparser_mssql.rs
+++ b/tests/sqlparser_mssql.rs
@@ -2783,3 +2783,26 @@ fn test_tsql_statement_keywords_not_implicit_aliases() {
         );
     }
 }
+
+#[test]
+fn test_exec_dynamic_sql() {
+    // EXEC (@sql) executes a dynamic SQL string held in a variable.
+    // It must parse as a single Execute statement and not attempt to parse
+    // parameters after the closing paren.
+    let stmts = tsql()
+        .parse_sql_statements("EXEC (@sql)")
+        .expect("EXEC (@sql) should parse");
+    assert_eq!(stmts.len(), 1);
+    assert!(
+        matches!(&stmts[0], Statement::Execute { .. }),
+        "expected Execute, got: {:?}",
+        stmts[0]
+    );
+
+    // Verify that a statement following EXEC (@sql) on the next line is parsed
+    // as a separate statement and not consumed as a parameter.
+    let stmts = tsql()
+        .parse_sql_statements("EXEC (@sql)\nDROP TABLE #tmp")
+        .expect("EXEC (@sql) followed by DROP TABLE should parse");
+    assert_eq!(stmts.len(), 2);
+}


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

Reply via email to