improve support for T-SQL EXECUTE statements (#1490)

This commit is contained in:
Ophir LOJKINE 2024-11-07 16:59:14 +01:00 committed by GitHub
parent 543ec6c584
commit d853c35391
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 77 additions and 14 deletions

View file

@ -3113,10 +3113,14 @@ pub enum Statement {
/// EXECUTE name [ ( parameter [, ...] ) ] [USING <expr>]
/// ```
///
/// Note: this is a PostgreSQL-specific statement.
/// Note: this statement is supported by Postgres and MSSQL, with slight differences in syntax.
///
/// Postgres: <https://www.postgresql.org/docs/current/sql-execute.html>
/// MSSQL: <https://learn.microsoft.com/en-us/sql/relational-databases/stored-procedures/execute-a-stored-procedure>
Execute {
name: Ident,
name: ObjectName,
parameters: Vec<Expr>,
has_parentheses: bool,
using: Vec<Expr>,
},
/// ```sql
@ -4585,12 +4589,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))?;
};

View file

@ -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(),
// `LISTEN` and `NOTIFY` are Postgres-specific
@ -11807,11 +11807,20 @@ impl<'a> Parser<'a> {
}
pub fn parse_execute(&mut self) -> Result<Statement, ParserError> {
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)?;
}
@ -11827,6 +11836,7 @@ impl<'a> Parser<'a> {
Ok(Statement::Execute {
name,
parameters,
has_parentheses,
using,
})
}

View file

@ -1396,6 +1396,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::*;
@ -9735,6 +9739,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\")");

View file

@ -1539,8 +1539,9 @@ fn parse_execute() {
assert_eq!(
stmt,
Statement::Execute {
name: "a".into(),
name: ObjectName(vec!["a".into()]),
parameters: vec![],
has_parentheses: false,
using: vec![]
}
);
@ -1549,11 +1550,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![]
}
);
@ -1563,8 +1565,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,