mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-07-07 17:04:59 +00:00
Add support for EXECUTE IMMEDIATE
(#1717)
This commit is contained in:
parent
3e90a18f6d
commit
b482562618
7 changed files with 122 additions and 35 deletions
|
@ -3269,18 +3269,21 @@ pub enum Statement {
|
|||
/// Note: this is a PostgreSQL-specific statement.
|
||||
Deallocate { name: Ident, prepare: bool },
|
||||
/// ```sql
|
||||
/// EXECUTE name [ ( parameter [, ...] ) ] [USING <expr>]
|
||||
/// An `EXECUTE` 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>
|
||||
/// BigQuery: <https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#execute_immediate>
|
||||
/// Snowflake: <https://docs.snowflake.com/en/sql-reference/sql/execute-immediate>
|
||||
Execute {
|
||||
name: ObjectName,
|
||||
name: Option<ObjectName>,
|
||||
parameters: Vec<Expr>,
|
||||
has_parentheses: bool,
|
||||
using: Vec<Expr>,
|
||||
/// Is this an `EXECUTE IMMEDIATE`
|
||||
immediate: bool,
|
||||
into: Vec<Ident>,
|
||||
using: Vec<ExprWithAlias>,
|
||||
},
|
||||
/// ```sql
|
||||
/// PREPARE name [ ( data_type [, ...] ) ] AS statement
|
||||
|
@ -4889,6 +4892,8 @@ impl fmt::Display for Statement {
|
|||
name,
|
||||
parameters,
|
||||
has_parentheses,
|
||||
immediate,
|
||||
into,
|
||||
using,
|
||||
} => {
|
||||
let (open, close) = if *has_parentheses {
|
||||
|
@ -4896,11 +4901,17 @@ impl fmt::Display for Statement {
|
|||
} else {
|
||||
(if parameters.is_empty() { "" } else { " " }, "")
|
||||
};
|
||||
write!(
|
||||
f,
|
||||
"EXECUTE {name}{open}{}{close}",
|
||||
display_comma_separated(parameters),
|
||||
)?;
|
||||
write!(f, "EXECUTE")?;
|
||||
if *immediate {
|
||||
write!(f, " IMMEDIATE")?;
|
||||
}
|
||||
if let Some(name) = name {
|
||||
write!(f, " {name}")?;
|
||||
}
|
||||
write!(f, "{open}{}{close}", display_comma_separated(parameters),)?;
|
||||
if !into.is_empty() {
|
||||
write!(f, " INTO {}", display_comma_separated(into))?;
|
||||
}
|
||||
if !using.is_empty() {
|
||||
write!(f, " USING {}", display_comma_separated(using))?;
|
||||
};
|
||||
|
|
|
@ -110,6 +110,11 @@ impl Dialect for BigQueryDialect {
|
|||
true
|
||||
}
|
||||
|
||||
/// See <https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#execute_immediate>
|
||||
fn supports_execute_immediate(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
// See <https://cloud.google.com/bigquery/docs/access-historical-data>
|
||||
fn supports_timestamp_versioning(&self) -> bool {
|
||||
true
|
||||
|
|
|
@ -261,6 +261,11 @@ pub trait Dialect: Debug + Any {
|
|||
false
|
||||
}
|
||||
|
||||
/// Returns true if the dialect supports `EXECUTE IMMEDIATE` statements.
|
||||
fn supports_execute_immediate(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Returns true if the dialect supports the MATCH_RECOGNIZE operation.
|
||||
fn supports_match_recognize(&self) -> bool {
|
||||
false
|
||||
|
|
|
@ -96,6 +96,11 @@ impl Dialect for SnowflakeDialect {
|
|||
true
|
||||
}
|
||||
|
||||
/// See <https://docs.snowflake.com/en/sql-reference/sql/execute-immediate>
|
||||
fn supports_execute_immediate(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn supports_match_recognize(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
|
|
@ -13849,7 +13849,14 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
pub fn parse_execute(&mut self) -> Result<Statement, ParserError> {
|
||||
let name = self.parse_object_name(false)?;
|
||||
let name = if self.dialect.supports_execute_immediate()
|
||||
&& self.parse_keyword(Keyword::IMMEDIATE)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
let name = self.parse_object_name(false)?;
|
||||
Some(name)
|
||||
};
|
||||
|
||||
let has_parentheses = self.consume_token(&Token::LParen);
|
||||
|
||||
|
@ -13866,19 +13873,24 @@ impl<'a> Parser<'a> {
|
|||
self.expect_token(&Token::RParen)?;
|
||||
}
|
||||
|
||||
let mut using = vec![];
|
||||
if self.parse_keyword(Keyword::USING) {
|
||||
using.push(self.parse_expr()?);
|
||||
let into = if self.parse_keyword(Keyword::INTO) {
|
||||
self.parse_comma_separated(Self::parse_identifier)?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
while self.consume_token(&Token::Comma) {
|
||||
using.push(self.parse_expr()?);
|
||||
}
|
||||
let using = if self.parse_keyword(Keyword::USING) {
|
||||
self.parse_comma_separated(Self::parse_expr_with_alias)?
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
Ok(Statement::Execute {
|
||||
immediate: name.is_none(),
|
||||
name,
|
||||
parameters,
|
||||
has_parentheses,
|
||||
into,
|
||||
using,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -10745,7 +10745,7 @@ fn parse_call() {
|
|||
#[test]
|
||||
fn parse_execute_stored_procedure() {
|
||||
let expected = Statement::Execute {
|
||||
name: ObjectName::from(vec![
|
||||
name: Some(ObjectName::from(vec![
|
||||
Ident {
|
||||
value: "my_schema".to_string(),
|
||||
quote_style: None,
|
||||
|
@ -10756,13 +10756,15 @@ fn parse_execute_stored_procedure() {
|
|||
quote_style: None,
|
||||
span: Span::empty(),
|
||||
},
|
||||
]),
|
||||
])),
|
||||
parameters: vec![
|
||||
Expr::Value(Value::NationalStringLiteral("param1".to_string())),
|
||||
Expr::Value(Value::NationalStringLiteral("param2".to_string())),
|
||||
],
|
||||
has_parentheses: false,
|
||||
immediate: false,
|
||||
using: vec![],
|
||||
into: vec![],
|
||||
};
|
||||
assert_eq!(
|
||||
// Microsoft SQL Server does not use parentheses around arguments for EXECUTE
|
||||
|
@ -10779,6 +10781,41 @@ fn parse_execute_stored_procedure() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_execute_immediate() {
|
||||
let dialects = all_dialects_where(|d| d.supports_execute_immediate());
|
||||
|
||||
let expected = Statement::Execute {
|
||||
parameters: vec![Expr::Value(Value::SingleQuotedString(
|
||||
"SELECT 1".to_string(),
|
||||
))],
|
||||
immediate: true,
|
||||
using: vec![ExprWithAlias {
|
||||
expr: Expr::Value(number("1")),
|
||||
alias: Some(Ident::new("b")),
|
||||
}],
|
||||
into: vec![Ident::new("a")],
|
||||
name: None,
|
||||
has_parentheses: false,
|
||||
};
|
||||
|
||||
let stmt = dialects.verified_stmt("EXECUTE IMMEDIATE 'SELECT 1' INTO a USING 1 AS b");
|
||||
assert_eq!(expected, stmt);
|
||||
|
||||
dialects.verified_stmt("EXECUTE IMMEDIATE 'SELECT 1' INTO a, b USING 1 AS x, y");
|
||||
dialects.verified_stmt("EXECUTE IMMEDIATE 'SELECT 1' USING 1 AS x, y");
|
||||
dialects.verified_stmt("EXECUTE IMMEDIATE 'SELECT 1' INTO a, b");
|
||||
dialects.verified_stmt("EXECUTE IMMEDIATE 'SELECT 1'");
|
||||
dialects.verified_stmt("EXECUTE 'SELECT 1'");
|
||||
|
||||
assert_eq!(
|
||||
ParserError::ParserError("Expected: identifier, found: ,".to_string()),
|
||||
dialects
|
||||
.parse_sql_statements("EXECUTE IMMEDIATE 'SELECT 1' USING 1 AS, y")
|
||||
.unwrap_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_create_table_collate() {
|
||||
pg_and_generic().verified_stmt("CREATE TABLE tbl (foo INT, bar TEXT COLLATE \"de_DE\")");
|
||||
|
|
|
@ -1659,10 +1659,12 @@ fn parse_execute() {
|
|||
assert_eq!(
|
||||
stmt,
|
||||
Statement::Execute {
|
||||
name: ObjectName::from(vec!["a".into()]),
|
||||
name: Some(ObjectName::from(vec!["a".into()])),
|
||||
parameters: vec![],
|
||||
has_parentheses: false,
|
||||
using: vec![]
|
||||
using: vec![],
|
||||
immediate: false,
|
||||
into: vec![]
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -1670,13 +1672,15 @@ fn parse_execute() {
|
|||
assert_eq!(
|
||||
stmt,
|
||||
Statement::Execute {
|
||||
name: ObjectName::from(vec!["a".into()]),
|
||||
name: Some(ObjectName::from(vec!["a".into()])),
|
||||
parameters: vec![
|
||||
Expr::Value(number("1")),
|
||||
Expr::Value(Value::SingleQuotedString("t".to_string()))
|
||||
],
|
||||
has_parentheses: true,
|
||||
using: vec![]
|
||||
using: vec![],
|
||||
immediate: false,
|
||||
into: vec![]
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -1685,23 +1689,31 @@ fn parse_execute() {
|
|||
assert_eq!(
|
||||
stmt,
|
||||
Statement::Execute {
|
||||
name: ObjectName::from(vec!["a".into()]),
|
||||
name: Some(ObjectName::from(vec!["a".into()])),
|
||||
parameters: vec![],
|
||||
has_parentheses: false,
|
||||
using: vec![
|
||||
Expr::Cast {
|
||||
kind: CastKind::Cast,
|
||||
expr: Box::new(Expr::Value(Value::Number("1337".parse().unwrap(), false))),
|
||||
data_type: DataType::SmallInt(None),
|
||||
format: None
|
||||
ExprWithAlias {
|
||||
expr: Expr::Cast {
|
||||
kind: CastKind::Cast,
|
||||
expr: Box::new(Expr::Value(Value::Number("1337".parse().unwrap(), false))),
|
||||
data_type: DataType::SmallInt(None),
|
||||
format: None
|
||||
},
|
||||
alias: None
|
||||
},
|
||||
Expr::Cast {
|
||||
kind: CastKind::Cast,
|
||||
expr: Box::new(Expr::Value(Value::Number("7331".parse().unwrap(), false))),
|
||||
data_type: DataType::SmallInt(None),
|
||||
format: None
|
||||
ExprWithAlias {
|
||||
expr: Expr::Cast {
|
||||
kind: CastKind::Cast,
|
||||
expr: Box::new(Expr::Value(Value::Number("7331".parse().unwrap(), false))),
|
||||
data_type: DataType::SmallInt(None),
|
||||
format: None
|
||||
},
|
||||
alias: None
|
||||
},
|
||||
]
|
||||
],
|
||||
immediate: false,
|
||||
into: vec![]
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue