Skip to content

Commit d853c35

Browse files
authored
improve support for T-SQL EXECUTE statements (#1490)
1 parent 543ec6c commit d853c35

File tree

4 files changed

+77
-14
lines changed

4 files changed

+77
-14
lines changed

src/ast/mod.rs

+17-6
Original file line numberDiff line numberDiff line change
@@ -3113,10 +3113,14 @@ pub enum Statement {
31133113
/// EXECUTE name [ ( parameter [, ...] ) ] [USING <expr>]
31143114
/// ```
31153115
///
3116-
/// Note: this is a PostgreSQL-specific statement.
3116+
/// Note: this statement is supported by Postgres and MSSQL, with slight differences in syntax.
3117+
///
3118+
/// Postgres: <https://www.postgresql.org/docs/current/sql-execute.html>
3119+
/// MSSQL: <https://learn.microsoft.com/en-us/sql/relational-databases/stored-procedures/execute-a-stored-procedure>
31173120
Execute {
3118-
name: Ident,
3121+
name: ObjectName,
31193122
parameters: Vec<Expr>,
3123+
has_parentheses: bool,
31203124
using: Vec<Expr>,
31213125
},
31223126
/// ```sql
@@ -4585,12 +4589,19 @@ impl fmt::Display for Statement {
45854589
Statement::Execute {
45864590
name,
45874591
parameters,
4592+
has_parentheses,
45884593
using,
45894594
} => {
4590-
write!(f, "EXECUTE {name}")?;
4591-
if !parameters.is_empty() {
4592-
write!(f, "({})", display_comma_separated(parameters))?;
4593-
}
4595+
let (open, close) = if *has_parentheses {
4596+
("(", ")")
4597+
} else {
4598+
(if parameters.is_empty() { "" } else { " " }, "")
4599+
};
4600+
write!(
4601+
f,
4602+
"EXECUTE {name}{open}{}{close}",
4603+
display_comma_separated(parameters),
4604+
)?;
45944605
if !using.is_empty() {
45954606
write!(f, " USING {}", display_comma_separated(using))?;
45964607
};

src/parser/mod.rs

+15-5
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
// `LISTEN` and `NOTIFY` are Postgres-specific
@@ -11807,11 +11807,20 @@ impl<'a> Parser<'a> {
1180711807
}
1180811808

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

11812-
let mut parameters = vec![];
11813-
if self.consume_token(&Token::LParen) {
11814-
parameters = self.parse_comma_separated(Parser::parse_expr)?;
11812+
let has_parentheses = self.consume_token(&Token::LParen);
11813+
11814+
let end_token = match (has_parentheses, self.peek_token().token) {
11815+
(true, _) => Token::RParen,
11816+
(false, Token::EOF) => Token::EOF,
11817+
(false, Token::Word(w)) if w.keyword == Keyword::USING => Token::Word(w),
11818+
(false, _) => Token::SemiColon,
11819+
};
11820+
11821+
let parameters = self.parse_comma_separated0(Parser::parse_expr, end_token)?;
11822+
11823+
if has_parentheses {
1181511824
self.expect_token(&Token::RParen)?;
1181611825
}
1181711826

@@ -11827,6 +11836,7 @@ impl<'a> Parser<'a> {
1182711836
Ok(Statement::Execute {
1182811837
name,
1182911838
parameters,
11839+
has_parentheses,
1183011840
using,
1183111841
})
1183211842
}

tests/sqlparser_common.rs

+39
Original file line numberDiff line numberDiff line change
@@ -1396,6 +1396,10 @@ fn pg_and_generic() -> TestedDialects {
13961396
])
13971397
}
13981398

1399+
fn ms_and_generic() -> TestedDialects {
1400+
TestedDialects::new(vec![Box::new(MsSqlDialect {}), Box::new(GenericDialect {})])
1401+
}
1402+
13991403
#[test]
14001404
fn parse_json_ops_without_colon() {
14011405
use self::BinaryOperator::*;
@@ -9735,6 +9739,41 @@ fn parse_call() {
97359739
);
97369740
}
97379741

9742+
#[test]
9743+
fn parse_execute_stored_procedure() {
9744+
let expected = Statement::Execute {
9745+
name: ObjectName(vec![
9746+
Ident {
9747+
value: "my_schema".to_string(),
9748+
quote_style: None,
9749+
},
9750+
Ident {
9751+
value: "my_stored_procedure".to_string(),
9752+
quote_style: None,
9753+
},
9754+
]),
9755+
parameters: vec![
9756+
Expr::Value(Value::NationalStringLiteral("param1".to_string())),
9757+
Expr::Value(Value::NationalStringLiteral("param2".to_string())),
9758+
],
9759+
has_parentheses: false,
9760+
using: vec![],
9761+
};
9762+
assert_eq!(
9763+
// Microsoft SQL Server does not use parentheses around arguments for EXECUTE
9764+
ms_and_generic()
9765+
.verified_stmt("EXECUTE my_schema.my_stored_procedure N'param1', N'param2'"),
9766+
expected
9767+
);
9768+
assert_eq!(
9769+
ms_and_generic().one_statement_parses_to(
9770+
"EXEC my_schema.my_stored_procedure N'param1', N'param2';",
9771+
"EXECUTE my_schema.my_stored_procedure N'param1', N'param2'",
9772+
),
9773+
expected
9774+
);
9775+
}
9776+
97389777
#[test]
97399778
fn parse_create_table_collate() {
97409779
pg_and_generic().verified_stmt("CREATE TABLE tbl (foo INT, bar TEXT COLLATE \"de_DE\")");

tests/sqlparser_postgres.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -1539,8 +1539,9 @@ fn parse_execute() {
15391539
assert_eq!(
15401540
stmt,
15411541
Statement::Execute {
1542-
name: "a".into(),
1542+
name: ObjectName(vec!["a".into()]),
15431543
parameters: vec![],
1544+
has_parentheses: false,
15441545
using: vec![]
15451546
}
15461547
);
@@ -1549,11 +1550,12 @@ fn parse_execute() {
15491550
assert_eq!(
15501551
stmt,
15511552
Statement::Execute {
1552-
name: "a".into(),
1553+
name: ObjectName(vec!["a".into()]),
15531554
parameters: vec![
15541555
Expr::Value(number("1")),
15551556
Expr::Value(Value::SingleQuotedString("t".to_string()))
15561557
],
1558+
has_parentheses: true,
15571559
using: vec![]
15581560
}
15591561
);
@@ -1563,8 +1565,9 @@ fn parse_execute() {
15631565
assert_eq!(
15641566
stmt,
15651567
Statement::Execute {
1566-
name: "a".into(),
1568+
name: ObjectName(vec!["a".into()]),
15671569
parameters: vec![],
1570+
has_parentheses: false,
15681571
using: vec![
15691572
Expr::Cast {
15701573
kind: CastKind::Cast,

0 commit comments

Comments
 (0)