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