add support for CALL statements (#1063)

This commit is contained in:
Ophir LOJKINE 2023-12-19 21:01:44 +01:00 committed by GitHub
parent 8d97330d42
commit 1933f194e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 89 additions and 11 deletions

View file

@ -1437,6 +1437,7 @@ pub enum Statement {
file_format: Option<FileFormat>,
source: Box<Query>,
},
Call(Function),
Copy {
/// The source of 'COPY TO', or the target of 'COPY FROM'
source: CopySource,
@ -1715,7 +1716,9 @@ pub enum Statement {
///
/// Note: this is a PostgreSQL-specific statement,
/// but may also compatible with other SQL.
Discard { object_type: DiscardObject },
Discard {
object_type: DiscardObject,
},
/// SET `[ SESSION | LOCAL ]` ROLE role_name. Examples: [ANSI][1], [Postgresql][2], [MySQL][3], and [Oracle][4].
///
/// [1]: https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#set-role-statement
@ -1747,7 +1750,10 @@ pub enum Statement {
///
/// Note: this is a PostgreSQL-specific statements
/// `SET TIME ZONE <value>` is an alias for `SET timezone TO <value>` in PostgreSQL
SetTimeZone { local: bool, value: Expr },
SetTimeZone {
local: bool,
value: Expr,
},
/// SET NAMES 'charset_name' [COLLATE 'collation_name']
///
/// Note: this is a MySQL-specific statement.
@ -1762,13 +1768,17 @@ pub enum Statement {
/// SHOW FUNCTIONS
///
/// Note: this is a Presto-specific statement.
ShowFunctions { filter: Option<ShowStatementFilter> },
ShowFunctions {
filter: Option<ShowStatementFilter>,
},
/// ```sql
/// SHOW <variable>
/// ```
///
/// Note: this is a PostgreSQL-specific statement.
ShowVariable { variable: Vec<Ident> },
ShowVariable {
variable: Vec<Ident>,
},
/// SHOW VARIABLES
///
/// Note: this is a MySQL-specific statement.
@ -1806,11 +1816,15 @@ pub enum Statement {
/// SHOW COLLATION
///
/// Note: this is a MySQL-specific statement.
ShowCollation { filter: Option<ShowStatementFilter> },
ShowCollation {
filter: Option<ShowStatementFilter>,
},
/// USE
///
/// Note: This is a MySQL-specific statement.
Use { db_name: Ident },
Use {
db_name: Ident,
},
/// `START [ TRANSACTION | WORK ] | START TRANSACTION } ...`
/// If `begin` is false.
///
@ -1838,7 +1852,9 @@ pub enum Statement {
if_exists: bool,
},
/// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
Commit { chain: bool },
Commit {
chain: bool,
},
/// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ] [ TO [ SAVEPOINT ] savepoint_name ]`
Rollback {
chain: bool,
@ -1934,11 +1950,17 @@ pub enum Statement {
/// `DEALLOCATE [ PREPARE ] { name | ALL }`
///
/// Note: this is a PostgreSQL-specific statement.
Deallocate { name: Ident, prepare: bool },
Deallocate {
name: Ident,
prepare: bool,
},
/// `EXECUTE name [ ( parameter [, ...] ) ]`
///
/// Note: this is a PostgreSQL-specific statement.
Execute { name: Ident, parameters: Vec<Expr> },
Execute {
name: Ident,
parameters: Vec<Expr>,
},
/// `PREPARE name [ ( data_type [, ...] ) ] AS statement`
///
/// Note: this is a PostgreSQL-specific statement.
@ -1979,9 +2001,13 @@ pub enum Statement {
format: Option<AnalyzeFormat>,
},
/// SAVEPOINT -- define a new savepoint within the current transaction
Savepoint { name: Ident },
Savepoint {
name: Ident,
},
/// RELEASE \[ SAVEPOINT \] savepoint_name
ReleaseSavepoint { name: Ident },
ReleaseSavepoint {
name: Ident,
},
// MERGE INTO statement, based on Snowflake. See <https://docs.snowflake.com/en/sql-reference/sql/merge.html>
Merge {
// optional INTO keyword
@ -2303,6 +2329,8 @@ impl fmt::Display for Statement {
Ok(())
}
Statement::Call(function) => write!(f, "CALL {function}"),
Statement::Copy {
source,
to,

View file

@ -493,6 +493,7 @@ impl<'a> Parser<'a> {
Keyword::UNCACHE => Ok(self.parse_uncache_table()?),
Keyword::UPDATE => Ok(self.parse_update()?),
Keyword::ALTER => Ok(self.parse_alter()?),
Keyword::CALL => Ok(self.parse_call()?),
Keyword::COPY => Ok(self.parse_copy()?),
Keyword::CLOSE => Ok(self.parse_close()?),
Keyword::SET => Ok(self.parse_set()?),
@ -4773,6 +4774,32 @@ impl<'a> Parser<'a> {
})
}
/// Parse a `CALL procedure_name(arg1, arg2, ...)`
/// or `CALL procedure_name` statement
pub fn parse_call(&mut self) -> Result<Statement, ParserError> {
let object_name = self.parse_object_name()?;
if self.peek_token().token == Token::LParen {
match self.parse_function(object_name)? {
Expr::Function(f) => Ok(Statement::Call(f)),
other => parser_err!(
format!("Expected a simple procedure call but found: {other}"),
self.peek_token().location
),
}
} else {
Ok(Statement::Call(Function {
name: object_name,
args: vec![],
over: None,
distinct: false,
filter: None,
null_treatment: None,
special: true,
order_by: vec![],
}))
}
}
/// Parse a copy statement
pub fn parse_copy(&mut self) -> Result<Statement, ParserError> {
let source;

View file

@ -7949,6 +7949,29 @@ fn parse_create_type() {
);
}
#[test]
fn parse_call() {
all_dialects().verified_stmt("CALL my_procedure()");
all_dialects().verified_stmt("CALL my_procedure(1, 'a')");
pg_and_generic().verified_stmt("CALL my_procedure(1, 'a', $1)");
all_dialects().verified_stmt("CALL my_procedure");
assert_eq!(
verified_stmt("CALL my_procedure('a')"),
Statement::Call(Function {
args: vec![FunctionArg::Unnamed(FunctionArgExpr::Expr(Expr::Value(
Value::SingleQuotedString("a".to_string())
))),],
name: ObjectName(vec![Ident::new("my_procedure")]),
filter: None,
null_treatment: None,
over: None,
distinct: false,
special: false,
order_by: vec![]
})
);
}
#[test]
fn parse_create_table_collate() {
pg_and_generic().verified_stmt("CREATE TABLE tbl (foo INT, bar TEXT COLLATE \"de_DE\")");