Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18536,6 +18536,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)
{
Expand All @@ -18546,10 +18549,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) {
Expand All @@ -18559,12 +18570,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 {
Expand Down
23 changes: 23 additions & 0 deletions tests/sqlparser_mssql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2730,3 +2730,26 @@ fn parse_mssql_tran_shorthand() {
// ROLLBACK TRAN normalizes to ROLLBACK (same as ROLLBACK TRANSACTION)
ms().one_statement_parses_to("ROLLBACK TRAN", "ROLLBACK");
}

#[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);
}
Loading