diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 059c61967..4e1d651fe 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -30,7 +30,7 @@ use sqlparser_derive::{Visit, VisitMut}; use crate::ast::value::escape_single_quote_string; use crate::ast::{ - display_comma_separated, display_separated, CommentDef, CreateFunctionBody, + display_comma_separated, display_separated, ArgMode, CommentDef, CreateFunctionBody, CreateFunctionUsing, DataType, Expr, FunctionBehavior, FunctionCalledOnNull, FunctionDeterminismSpecifier, FunctionParallel, Ident, MySQLColumnPosition, ObjectName, OperateFunctionArg, OrderByExpr, ProjectionSelect, SequenceOptions, SqlOption, Tag, Value, @@ -1367,11 +1367,16 @@ impl fmt::Display for NullsDistinctOption { pub struct ProcedureParam { pub name: Ident, pub data_type: DataType, + pub mode: Option, } impl fmt::Display for ProcedureParam { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} {}", self.name, self.data_type) + if let Some(mode) = &self.mode { + write!(f, "{mode} {} {}", self.name, self.data_type) + } else { + write!(f, "{} {}", self.name, self.data_type) + } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index be32093f3..046581aa8 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7626,9 +7626,22 @@ impl<'a> Parser<'a> { } pub fn parse_procedure_param(&mut self) -> Result { + let mode = if self.parse_keyword(Keyword::IN) { + Some(ArgMode::In) + } else if self.parse_keyword(Keyword::OUT) { + Some(ArgMode::Out) + } else if self.parse_keyword(Keyword::INOUT) { + Some(ArgMode::InOut) + } else { + None + }; let name = self.parse_identifier()?; let data_type = self.parse_data_type()?; - Ok(ProcedureParam { name, data_type }) + Ok(ProcedureParam { + name, + data_type, + mode, + }) } pub fn parse_column_def(&mut self) -> Result { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index e4363ff6e..815b74f50 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -15349,3 +15349,65 @@ fn check_enforced() { "CREATE TABLE t (a INT, b INT, c INT, CHECK (a > 0) NOT ENFORCED, CHECK (b > 0) ENFORCED, CHECK (c > 0))", ); } + +#[test] +fn parse_create_procedure_with_parameter_modes() { + let sql = r#"CREATE PROCEDURE test_proc (IN a INTEGER, OUT b TEXT, INOUT c TIMESTAMP, d BOOL) AS BEGIN SELECT 1; END"#; + match verified_stmt(sql) { + Statement::CreateProcedure { + or_alter, + name, + params, + .. + } => { + assert_eq!(or_alter, false); + assert_eq!(name.to_string(), "test_proc"); + let fake_span = Span { + start: Location { line: 0, column: 0 }, + end: Location { line: 0, column: 0 }, + }; + assert_eq!( + params, + Some(vec![ + ProcedureParam { + name: Ident { + value: "a".into(), + quote_style: None, + span: fake_span, + }, + data_type: DataType::Integer(None), + mode: Some(ArgMode::In) + }, + ProcedureParam { + name: Ident { + value: "b".into(), + quote_style: None, + span: fake_span, + }, + data_type: DataType::Text, + mode: Some(ArgMode::Out) + }, + ProcedureParam { + name: Ident { + value: "c".into(), + quote_style: None, + span: fake_span, + }, + data_type: DataType::Timestamp(None, TimezoneInfo::None), + mode: Some(ArgMode::InOut) + }, + ProcedureParam { + name: Ident { + value: "d".into(), + quote_style: None, + span: fake_span, + }, + data_type: DataType::Bool, + mode: None + }, + ]) + ); + } + _ => unreachable!(), + } +} diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 2a3145028..8edb100aa 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -153,7 +153,8 @@ fn parse_create_procedure() { quote_style: None, span: Span::empty(), }, - data_type: DataType::Int(None) + data_type: DataType::Int(None), + mode: None, }, ProcedureParam { name: Ident { @@ -164,7 +165,8 @@ fn parse_create_procedure() { data_type: DataType::Varchar(Some(CharacterLength::IntegerLength { length: 256, unit: None - })) + })), + mode: None, } ]), name: ObjectName::from(vec![Ident {