From e01f5a5206948a4a1ecb175d5465af1d2729d868 Mon Sep 17 00:00:00 2001 From: ZacJW Date: Sun, 22 Jun 2025 09:04:56 +0100 Subject: [PATCH 1/5] Support argmode in CREATE PROCEDURE --- src/ast/ddl.rs | 7 ++----- src/parser/mod.rs | 11 ++++++++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 059c61967..134338515 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -30,11 +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, - CreateFunctionUsing, DataType, Expr, FunctionBehavior, FunctionCalledOnNull, - FunctionDeterminismSpecifier, FunctionParallel, Ident, MySQLColumnPosition, ObjectName, - OperateFunctionArg, OrderByExpr, ProjectionSelect, SequenceOptions, SqlOption, Tag, Value, - ValueWithSpan, + 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, ValueWithSpan }; use crate::keywords::Keyword; use crate::tokenizer::Token; @@ -1367,6 +1363,7 @@ impl fmt::Display for NullsDistinctOption { pub struct ProcedureParam { pub name: Ident, pub data_type: DataType, + pub mode: Option, } impl fmt::Display for ProcedureParam { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index be32093f3..f22e88dbc 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7626,9 +7626,18 @@ 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 { From 9ccb082a09ebb02967faec4282f6a28d40a0c117 Mon Sep 17 00:00:00 2001 From: ZacJW Date: Sun, 22 Jun 2025 09:10:55 +0100 Subject: [PATCH 2/5] Cargo fmt --- src/ast/ddl.rs | 6 +++++- src/parser/mod.rs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 134338515..dd9c77dfd 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -30,7 +30,11 @@ use sqlparser_derive::{Visit, VisitMut}; use crate::ast::value::escape_single_quote_string; use crate::ast::{ - 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, ValueWithSpan + 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, + ValueWithSpan, }; use crate::keywords::Keyword; use crate::tokenizer::Token; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index f22e88dbc..046581aa8 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7637,7 +7637,11 @@ impl<'a> Parser<'a> { }; let name = self.parse_identifier()?; let data_type = self.parse_data_type()?; - Ok(ProcedureParam { name, data_type, mode }) + Ok(ProcedureParam { + name, + data_type, + mode, + }) } pub fn parse_column_def(&mut self) -> Result { From e32fab070c45b55969cbbe0117eda1d2ddf944aa Mon Sep 17 00:00:00 2001 From: ZacJW Date: Sun, 22 Jun 2025 09:14:57 +0100 Subject: [PATCH 3/5] Fix MS SQL test --- tests/sqlparser_mssql.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 { From 6be4fad5c4f2e11095de82fbdd914ba63c3e7232 Mon Sep 17 00:00:00 2001 From: ZacJW Date: Sun, 22 Jun 2025 12:05:31 +0100 Subject: [PATCH 4/5] Fix Display impl for ProcedureParam --- src/ast/ddl.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index dd9c77dfd..4e1d651fe 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -1372,7 +1372,11 @@ pub struct ProcedureParam { 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) + } } } From 21c6d4295cbe050563fccf4986093f3ca9051998 Mon Sep 17 00:00:00 2001 From: ZacJW Date: Sun, 22 Jun 2025 12:06:01 +0100 Subject: [PATCH 5/5] Add testcase for CREATE PROCEDURE with parameter modes --- tests/sqlparser_common.rs | 62 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) 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!(), + } +}