diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 0573240a2..8a5a36b0d 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -3095,10 +3095,14 @@ pub enum Statement { /// EXECUTE name [ ( parameter [, ...] ) ] [USING ] /// ``` /// - /// Note: this is a PostgreSQL-specific statement. + /// Note: this statement is supported by Postgres and MSSQL, with slight differences in syntax. + /// + /// Postgres: + /// MSSQL: Execute { - name: Ident, + name: ObjectName, parameters: Vec, + has_parentheses: bool, using: Vec, }, /// ```sql @@ -4510,12 +4514,19 @@ impl fmt::Display for Statement { Statement::Execute { name, parameters, + has_parentheses, using, } => { - write!(f, "EXECUTE {name}")?; - if !parameters.is_empty() { - write!(f, "({})", display_comma_separated(parameters))?; - } + let (open, close) = if *has_parentheses { + ("(", ")") + } else { + (if parameters.is_empty() { "" } else { " " }, "") + }; + write!( + f, + "EXECUTE {name}{open}{}{close}", + display_comma_separated(parameters), + )?; if !using.is_empty() { write!(f, " USING {}", display_comma_separated(using))?; }; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index a9a5b1df4..a2c57bb4d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -529,7 +529,7 @@ impl<'a> Parser<'a> { // `PREPARE`, `EXECUTE` and `DEALLOCATE` are Postgres-specific // syntaxes. They are used for Postgres prepared statement. Keyword::DEALLOCATE => self.parse_deallocate(), - Keyword::EXECUTE => self.parse_execute(), + Keyword::EXECUTE | Keyword::EXEC => self.parse_execute(), Keyword::PREPARE => self.parse_prepare(), Keyword::MERGE => self.parse_merge(), // `PRAGMA` is sqlite specific https://www.sqlite.org/pragma.html @@ -11726,11 +11726,20 @@ impl<'a> Parser<'a> { } pub fn parse_execute(&mut self) -> Result { - let name = self.parse_identifier(false)?; + let name = self.parse_object_name(false)?; - let mut parameters = vec![]; - if self.consume_token(&Token::LParen) { - parameters = self.parse_comma_separated(Parser::parse_expr)?; + let has_parentheses = self.consume_token(&Token::LParen); + + let end_token = match (has_parentheses, self.peek_token().token) { + (true, _) => Token::RParen, + (false, Token::EOF) => Token::EOF, + (false, Token::Word(w)) if w.keyword == Keyword::USING => Token::Word(w), + (false, _) => Token::SemiColon, + }; + + let parameters = self.parse_comma_separated0(Parser::parse_expr, end_token)?; + + if has_parentheses { self.expect_token(&Token::RParen)?; } @@ -11746,6 +11755,7 @@ impl<'a> Parser<'a> { Ok(Statement::Execute { name, parameters, + has_parentheses, using, }) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a2eb5070d..6c8c1884b 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -1395,6 +1395,10 @@ fn pg_and_generic() -> TestedDialects { ]) } +fn ms_and_generic() -> TestedDialects { + TestedDialects::new(vec![Box::new(MsSqlDialect {}), Box::new(GenericDialect {})]) +} + #[test] fn parse_json_ops_without_colon() { use self::BinaryOperator::*; @@ -9730,6 +9734,41 @@ fn parse_call() { ); } +#[test] +fn parse_execute_stored_procedure() { + let expected = Statement::Execute { + name: ObjectName(vec![ + Ident { + value: "my_schema".to_string(), + quote_style: None, + }, + Ident { + value: "my_stored_procedure".to_string(), + quote_style: None, + }, + ]), + parameters: vec![ + Expr::Value(Value::NationalStringLiteral("param1".to_string())), + Expr::Value(Value::NationalStringLiteral("param2".to_string())), + ], + has_parentheses: false, + using: vec![], + }; + assert_eq!( + // Microsoft SQL Server does not use parentheses around arguments for EXECUTE + ms_and_generic() + .verified_stmt("EXECUTE my_schema.my_stored_procedure N'param1', N'param2'"), + expected + ); + assert_eq!( + ms_and_generic().one_statement_parses_to( + "EXEC my_schema.my_stored_procedure N'param1', N'param2';", + "EXECUTE my_schema.my_stored_procedure N'param1', N'param2'", + ), + expected + ); +} + #[test] fn parse_create_table_collate() { pg_and_generic().verified_stmt("CREATE TABLE tbl (foo INT, bar TEXT COLLATE \"de_DE\")"); diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index b9b3811ba..f4c7ab7cd 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1538,8 +1538,9 @@ fn parse_execute() { assert_eq!( stmt, Statement::Execute { - name: "a".into(), + name: ObjectName(vec!["a".into()]), parameters: vec![], + has_parentheses: false, using: vec![] } ); @@ -1548,11 +1549,12 @@ fn parse_execute() { assert_eq!( stmt, Statement::Execute { - name: "a".into(), + name: ObjectName(vec!["a".into()]), parameters: vec![ Expr::Value(number("1")), Expr::Value(Value::SingleQuotedString("t".to_string())) ], + has_parentheses: true, using: vec![] } ); @@ -1562,8 +1564,9 @@ fn parse_execute() { assert_eq!( stmt, Statement::Execute { - name: "a".into(), + name: ObjectName(vec!["a".into()]), parameters: vec![], + has_parentheses: false, using: vec![ Expr::Cast { kind: CastKind::Cast,