From 88da3b7bd57d25c90de543bd5b3ce4b49a648f47 Mon Sep 17 00:00:00 2001 From: Andrew Harper Date: Fri, 19 Sep 2025 17:33:27 -0400 Subject: [PATCH 1/2] Add support for procedure parameter default values --- src/ast/ddl.rs | 9 ++++- src/parser/mod.rs | 7 ++++ tests/sqlparser_common.rs | 80 +++++++++++++++++++++++++++++++++++++-- tests/sqlparser_mssql.rs | 8 ++++ 4 files changed, 99 insertions(+), 5 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index f998143c8..773b119fb 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -1458,12 +1458,19 @@ pub struct ProcedureParam { pub name: Ident, pub data_type: DataType, pub mode: Option, + pub default: Option, } impl fmt::Display for ProcedureParam { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(mode) = &self.mode { - write!(f, "{mode} {} {}", self.name, self.data_type) + if let Some(default) = &self.default { + write!(f, "{mode} {} {} = {}", self.name, self.data_type, default) + } else { + write!(f, "{mode} {} {}", self.name, self.data_type) + } + } else if let Some(default) = &self.default { + write!(f, "{} {} = {}", self.name, self.data_type, default) } else { write!(f, "{} {}", self.name, self.data_type) } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index d661efd4d..9682804c2 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7897,10 +7897,17 @@ impl<'a> Parser<'a> { }; let name = self.parse_identifier()?; let data_type = self.parse_data_type()?; + let default = if self.consume_token(&Token::Eq) { + Some(self.parse_expr()?) + } else { + None + }; + Ok(ProcedureParam { name, data_type, mode, + default, }) } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 720c1e492..09ba590e9 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -16497,7 +16497,8 @@ fn parse_create_procedure_with_parameter_modes() { span: fake_span, }, data_type: DataType::Integer(None), - mode: Some(ArgMode::In) + mode: Some(ArgMode::In), + default: None, }, ProcedureParam { name: Ident { @@ -16506,7 +16507,8 @@ fn parse_create_procedure_with_parameter_modes() { span: fake_span, }, data_type: DataType::Text, - mode: Some(ArgMode::Out) + mode: Some(ArgMode::Out), + default: None, }, ProcedureParam { name: Ident { @@ -16515,7 +16517,8 @@ fn parse_create_procedure_with_parameter_modes() { span: fake_span, }, data_type: DataType::Timestamp(None, TimezoneInfo::None), - mode: Some(ArgMode::InOut) + mode: Some(ArgMode::InOut), + default: None, }, ProcedureParam { name: Ident { @@ -16524,7 +16527,8 @@ fn parse_create_procedure_with_parameter_modes() { span: fake_span, }, data_type: DataType::Bool, - mode: None + mode: None, + default: None, }, ]) ); @@ -16533,6 +16537,74 @@ fn parse_create_procedure_with_parameter_modes() { } } +#[test] +fn create_procedure_with_parameter_default_value() { + let sql = r#"CREATE PROCEDURE test_proc (IN a INTEGER = 1, OUT b TEXT = '2', INOUT c TIMESTAMP = NULL, d BOOL = 0) 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), + default: Some(Expr::Value((number("1")).with_empty_span())), + }, + ProcedureParam { + name: Ident { + value: "b".into(), + quote_style: None, + span: fake_span, + }, + data_type: DataType::Text, + mode: Some(ArgMode::Out), + default: Some(Expr::Value( + Value::SingleQuotedString("2".into()).with_empty_span() + )), + }, + ProcedureParam { + name: Ident { + value: "c".into(), + quote_style: None, + span: fake_span, + }, + data_type: DataType::Timestamp(None, TimezoneInfo::None), + mode: Some(ArgMode::InOut), + default: Some(Expr::Value(Value::Null.with_empty_span())), + }, + ProcedureParam { + name: Ident { + value: "d".into(), + quote_style: None, + span: fake_span, + }, + data_type: DataType::Bool, + mode: None, + default: Some(Expr::Value((number("0")).with_empty_span())), + } + ]), + ); + } + _ => unreachable!(), + } +} + #[test] fn parse_not_null() { let _ = all_dialects().expr_parses_to("x NOT NULL", "x IS NOT NULL"); diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index b1ad422ec..6d8a85c52 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -156,6 +156,7 @@ fn parse_create_procedure() { }, data_type: DataType::Int(None), mode: None, + default: None, }, ProcedureParam { name: Ident { @@ -168,6 +169,7 @@ fn parse_create_procedure() { unit: None })), mode: None, + default: None, } ]), name: ObjectName::from(vec![Ident { @@ -198,6 +200,12 @@ fn parse_mssql_create_procedure() { let _ = ms().verified_stmt("CREATE PROCEDURE [foo] AS BEGIN UPDATE bar SET col = 'test'; SELECT [foo] FROM BAR WHERE [FOO] > 10; END"); } +#[test] +fn parse_mssql_create_procedure_with_parameter_default_value() { + let sql = r#"CREATE PROCEDURE foo (IN @a INTEGER = 1, OUT @b TEXT = '2', INOUT @c DATETIME = NULL, @d BOOL = 0) AS BEGIN SELECT 1; END"#; + let _ = ms().verified_stmt(sql); +} + #[test] fn parse_create_function() { let return_expression_function = "CREATE FUNCTION some_scalar_udf(@foo INT, @bar VARCHAR(256)) RETURNS INT AS BEGIN RETURN 1; END"; From 300f9e664665b591c8dba436b6db69e248af9bd6 Mon Sep 17 00:00:00 2001 From: Andrew Harper Date: Mon, 6 Oct 2025 10:00:12 -0400 Subject: [PATCH 2/2] Consolidate new examples into existing test functions --- tests/sqlparser_common.rs | 32 +++++--------------------------- tests/sqlparser_mssql.rs | 4 +--- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 09ba590e9..489ea3f04 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -16535,10 +16535,8 @@ fn parse_create_procedure_with_parameter_modes() { } _ => unreachable!(), } -} -#[test] -fn create_procedure_with_parameter_default_value() { + // parameters with default values let sql = r#"CREATE PROCEDURE test_proc (IN a INTEGER = 1, OUT b TEXT = '2', INOUT c TIMESTAMP = NULL, d BOOL = 0) AS BEGIN SELECT 1; END"#; match verified_stmt(sql) { Statement::CreateProcedure { @@ -16549,29 +16547,17 @@ fn create_procedure_with_parameter_default_value() { } => { 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, - }, + name: Ident::new("a"), data_type: DataType::Integer(None), mode: Some(ArgMode::In), default: Some(Expr::Value((number("1")).with_empty_span())), }, ProcedureParam { - name: Ident { - value: "b".into(), - quote_style: None, - span: fake_span, - }, + name: Ident::new("b"), data_type: DataType::Text, mode: Some(ArgMode::Out), default: Some(Expr::Value( @@ -16579,21 +16565,13 @@ fn create_procedure_with_parameter_default_value() { )), }, ProcedureParam { - name: Ident { - value: "c".into(), - quote_style: None, - span: fake_span, - }, + name: Ident::new("c"), data_type: DataType::Timestamp(None, TimezoneInfo::None), mode: Some(ArgMode::InOut), default: Some(Expr::Value(Value::Null.with_empty_span())), }, ProcedureParam { - name: Ident { - value: "d".into(), - quote_style: None, - span: fake_span, - }, + name: Ident::new("d"), data_type: DataType::Bool, mode: None, default: Some(Expr::Value((number("0")).with_empty_span())), diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 6d8a85c52..181899170 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -198,10 +198,8 @@ fn parse_mssql_create_procedure() { let _ = ms().verified_stmt("CREATE PROCEDURE [foo] AS BEGIN SELECT [foo], CASE WHEN [foo] IS NULL THEN 'empty' ELSE 'notempty' END AS [foo]; END"); // Multiple statements let _ = ms().verified_stmt("CREATE PROCEDURE [foo] AS BEGIN UPDATE bar SET col = 'test'; SELECT [foo] FROM BAR WHERE [FOO] > 10; END"); -} -#[test] -fn parse_mssql_create_procedure_with_parameter_default_value() { + // parameters with default values let sql = r#"CREATE PROCEDURE foo (IN @a INTEGER = 1, OUT @b TEXT = '2', INOUT @c DATETIME = NULL, @d BOOL = 0) AS BEGIN SELECT 1; END"#; let _ = ms().verified_stmt(sql); }