diff --git a/fuzz/fuzz_seeds/generic_procedure_empty_parameter_list.sql b/fuzz/fuzz_seeds/generic_procedure_empty_parameter_list.sql new file mode 100644 index 0000000000..f93054854b --- /dev/null +++ b/fuzz/fuzz_seeds/generic_procedure_empty_parameter_list.sql @@ -0,0 +1 @@ +CREATE PROCEDURE p () AS BEGIN SELECT 1; END diff --git a/src/ast/mod.rs b/src/ast/mod.rs index e924ea2f37..ec6fb1489e 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4577,7 +4577,7 @@ pub enum Statement { or_alter: bool, /// Procedure name. name: ObjectName, - /// Optional procedure parameters. + /// Parenthesized parameter list, `None` when absent. params: Option>, /// Optional language identifier. language: Option, @@ -5591,9 +5591,7 @@ impl fmt::Display for Statement { )?; if let Some(p) = params { - if !p.is_empty() { - write!(f, " ({})", display_comma_separated(p))?; - } + write!(f, " ({})", display_comma_separated(p))?; } if let Some(language) = language { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index b630070939..5c057f6d07 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9940,8 +9940,11 @@ impl<'a> Parser<'a> { pub fn parse_optional_procedure_parameters( &mut self, ) -> Result>, ParserError> { + if !self.consume_token(&Token::LParen) { + return Ok(None); + } let mut params = vec![]; - if !self.consume_token(&Token::LParen) || self.consume_token(&Token::RParen) { + if self.consume_token(&Token::RParen) { return Ok(Some(params)); } loop { diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index c7ed5893b2..379e37a660 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -19326,7 +19326,7 @@ fn parse_create_procedure_with_language() { } => { assert_eq!(or_alter, false); assert_eq!(name.to_string(), "test_proc"); - assert_eq!(params, Some(vec![])); + assert_eq!(params, None); assert_eq!( language, Some(Ident { @@ -22714,3 +22714,33 @@ fn parse_grantee_after_public() { ParserError::ParserError("Expected: identifier, found: EOF".to_string()) ); } + +#[test] +fn parse_create_procedure_empty_parameter_list() { + verified_stmt("CREATE PROCEDURE p () AS BEGIN SELECT 1; END"); + match verified_stmt("CREATE PROCEDURE p AS BEGIN SELECT 1; END") { + Statement::CreateProcedure { params, .. } => assert_eq!(params, None), + stmt => panic!("unexpected statement: {stmt:?}"), + } +} + +#[test] +fn display_create_procedure_parameter_list_propagates_write_errors() { + use core::fmt::{self, Write}; + + struct RejectingWriter(&'static str); + + impl Write for RejectingWriter { + fn write_str(&mut self, value: &str) -> fmt::Result { + if value == self.0 { + Err(fmt::Error) + } else { + Ok(()) + } + } + } + + let statement = verified_stmt("CREATE PROCEDURE p () AS BEGIN SELECT 1; END"); + let mut writer = RejectingWriter(" ("); + assert_eq!(write!(writer, "{statement}"), Err(fmt::Error)); +}