diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 4769ea9bd..fba7e5588 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1437,6 +1437,7 @@ pub enum Statement { file_format: Option, source: Box, }, + 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 ` is an alias for `SET timezone TO ` 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 }, + ShowFunctions { + filter: Option, + }, /// ```sql /// SHOW /// ``` /// /// Note: this is a PostgreSQL-specific statement. - ShowVariable { variable: Vec }, + ShowVariable { + variable: Vec, + }, /// 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 }, + ShowCollation { + filter: Option, + }, /// 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 }, + Execute { + name: Ident, + parameters: Vec, + }, /// `PREPARE name [ ( data_type [, ...] ) ] AS statement` /// /// Note: this is a PostgreSQL-specific statement. @@ -1979,9 +2001,13 @@ pub enum Statement { format: Option, }, /// 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 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, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d9d4761c3..5497d0f84 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -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 { + 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 { let source; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 1d0923b4f..fea90c4c6 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -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\")");