Skip to content

Commit cf09440

Browse files
committed
improve support of T-SQL EXECUTE statements
This adds the following features from the Microsoft SQL Server EXECUTE statement: Support EXEC as an alias to EXECUTE fixes apache#1275 Support schema qualified stored procedures fixes apache#1276 support unnamed parameters in mssql EXECUTE stored procedure calls fixes apache#1489
1 parent ee90373 commit cf09440

File tree

4 files changed

+73
-14
lines changed

4 files changed

+73
-14
lines changed

src/ast/mod.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3095,10 +3095,14 @@ pub enum Statement {
30953095
/// EXECUTE name [ ( parameter [, ...] ) ] [USING <expr>]
30963096
/// ```
30973097
///
3098-
/// Note: this is a PostgreSQL-specific statement.
3098+
/// Note: this statement is supported by Postgres and MSSQL, with slight differences in syntax.
3099+
///
3100+
/// Postgres: <https://www.postgresql.org/docs/current/sql-execute.html>
3101+
/// MSSQL: <https://learn.microsoft.com/en-us/sql/relational-databases/stored-procedures/execute-a-stored-procedure>
30993102
Execute {
3100-
name: Ident,
3103+
name: ObjectName,
31013104
parameters: Vec<Expr>,
3105+
has_parentheses: bool,
31023106
using: Vec<Expr>,
31033107
},
31043108
/// ```sql
@@ -4510,12 +4514,19 @@ impl fmt::Display for Statement {
45104514
Statement::Execute {
45114515
name,
45124516
parameters,
4517+
has_parentheses,
45134518
using,
45144519
} => {
4515-
write!(f, "EXECUTE {name}")?;
4516-
if !parameters.is_empty() {
4517-
write!(f, "({})", display_comma_separated(parameters))?;
4518-
}
4520+
let (open, close) = if *has_parentheses {
4521+
("(", ")")
4522+
} else {
4523+
(if parameters.is_empty() { "" } else { " " }, "")
4524+
};
4525+
write!(
4526+
f,
4527+
"EXECUTE {name}{open}{}{close}",
4528+
display_comma_separated(parameters),
4529+
)?;
45194530
if !using.is_empty() {
45204531
write!(f, " USING {}", display_comma_separated(using))?;
45214532
};

src/parser/mod.rs

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ impl<'a> Parser<'a> {
529529
// `PREPARE`, `EXECUTE` and `DEALLOCATE` are Postgres-specific
530530
// syntaxes. They are used for Postgres prepared statement.
531531
Keyword::DEALLOCATE => self.parse_deallocate(),
532-
Keyword::EXECUTE => self.parse_execute(),
532+
Keyword::EXECUTE | Keyword::EXEC => self.parse_execute(),
533533
Keyword::PREPARE => self.parse_prepare(),
534534
Keyword::MERGE => self.parse_merge(),
535535
// `PRAGMA` is sqlite specific https://www.sqlite.org/pragma.html
@@ -11726,11 +11726,20 @@ impl<'a> Parser<'a> {
1172611726
}
1172711727

1172811728
pub fn parse_execute(&mut self) -> Result<Statement, ParserError> {
11729-
let name = self.parse_identifier(false)?;
11729+
let name = self.parse_object_name(false)?;
1173011730

11731-
let mut parameters = vec![];
11732-
if self.consume_token(&Token::LParen) {
11733-
parameters = self.parse_comma_separated(Parser::parse_expr)?;
11731+
let has_parentheses = self.consume_token(&Token::LParen);
11732+
11733+
let end_token = match (has_parentheses, self.peek_token().token) {
11734+
(true, _) => Token::RParen,
11735+
(false, Token::EOF) => Token::EOF,
11736+
(false, Token::Word(w)) if w.keyword == Keyword::USING => Token::Word(w),
11737+
(false, _) => Token::SemiColon,
11738+
};
11739+
11740+
let parameters = self.parse_comma_separated0(Parser::parse_expr, end_token)?;
11741+
11742+
if has_parentheses {
1173411743
self.expect_token(&Token::RParen)?;
1173511744
}
1173611745

@@ -11746,6 +11755,7 @@ impl<'a> Parser<'a> {
1174611755
Ok(Statement::Execute {
1174711756
name,
1174811757
parameters,
11758+
has_parentheses,
1174911759
using,
1175011760
})
1175111761
}

tests/sqlparser_mssql.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,41 @@ fn parse_substring_in_select() {
570570
}
571571
}
572572

573+
#[test]
574+
fn parse_mssql_execute_stored_procedure() {
575+
let expected = Statement::Execute {
576+
name: ObjectName(vec![
577+
Ident {
578+
value: "my_schema".to_string(),
579+
quote_style: None,
580+
},
581+
Ident {
582+
value: "my_stored_procedure".to_string(),
583+
quote_style: None,
584+
},
585+
]),
586+
parameters: vec![
587+
Expr::Value(Value::NationalStringLiteral("param1".to_string())),
588+
Expr::Value(Value::NationalStringLiteral("param2".to_string())),
589+
],
590+
has_parentheses: false,
591+
using: vec![],
592+
};
593+
assert_eq!(
594+
ms().verified_stmt("EXECUTE my_schema.my_stored_procedure N'param1', N'param2'"),
595+
expected
596+
);
597+
assert_eq!(
598+
Parser::parse_sql(
599+
&MsSqlDialect {},
600+
"EXEC my_schema.my_stored_procedure N'param1', N'param2';"
601+
)
602+
.unwrap()[0],
603+
expected,
604+
"EXEC should be parsed the same as EXECUTE"
605+
);
606+
}
607+
573608
#[test]
574609
fn parse_mssql_declare() {
575610
let sql = "DECLARE @foo CURSOR, @bar INT, @baz AS TEXT = 'foobar';";

tests/sqlparser_postgres.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,8 +1538,9 @@ fn parse_execute() {
15381538
assert_eq!(
15391539
stmt,
15401540
Statement::Execute {
1541-
name: "a".into(),
1541+
name: ObjectName(vec!["a".into()]),
15421542
parameters: vec![],
1543+
has_parentheses: false,
15431544
using: vec![]
15441545
}
15451546
);
@@ -1548,11 +1549,12 @@ fn parse_execute() {
15481549
assert_eq!(
15491550
stmt,
15501551
Statement::Execute {
1551-
name: "a".into(),
1552+
name: ObjectName(vec!["a".into()]),
15521553
parameters: vec![
15531554
Expr::Value(number("1")),
15541555
Expr::Value(Value::SingleQuotedString("t".to_string()))
15551556
],
1557+
has_parentheses: true,
15561558
using: vec![]
15571559
}
15581560
);
@@ -1562,8 +1564,9 @@ fn parse_execute() {
15621564
assert_eq!(
15631565
stmt,
15641566
Statement::Execute {
1565-
name: "a".into(),
1567+
name: ObjectName(vec!["a".into()]),
15661568
parameters: vec![],
1569+
has_parentheses: false,
15671570
using: vec![
15681571
Expr::Cast {
15691572
kind: CastKind::Cast,

0 commit comments

Comments
 (0)