From b634c00e6d8a26e4976698fa4d6b09fe40e636c7 Mon Sep 17 00:00:00 2001 From: LucaCappelletti94 Date: Wed, 23 Sep 2026 01:08:06 +0200 Subject: [PATCH] MsSql: Keep the comma separator when displaying DECLARE lists --- src/ast/mod.rs | 27 ++++++++++++++++++++++++--- src/parser/mod.rs | 12 ++++++++++-- tests/sqlparser_bigquery.rs | 2 +- tests/sqlparser_mssql.rs | 13 +++++++++++-- tests/sqlparser_snowflake.rs | 10 +++++----- 5 files changed, 51 insertions(+), 13 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index f585121add..a5518b161e 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -3142,6 +3142,22 @@ impl fmt::Display for DeclareType { } } +/// Separator written between the declarations collected by one `DECLARE` keyword. +#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub enum DeclareSeparator { + /// Each declaration ends with `;`, as in a Snowflake Scripting `DECLARE` section. + /// + /// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/declare) + #[default] + Semicolon, + /// One comma-separated list, as in `DECLARE @a INT, @b INT`. + /// + /// [MsSql](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/declare-local-variable-transact-sql) + Comma, +} + /// A `DECLARE` statement. /// [PostgreSQL] [Snowflake] [BigQuery] /// @@ -4086,6 +4102,8 @@ pub enum Statement { Declare { /// Cursor declaration statements collected by `DECLARE`. stmts: Vec, + /// Separator between `stmts` in the rendered SQL. + separator: DeclareSeparator, }, /// ```sql /// CREATE EXTENSION [ IF NOT EXISTS ] extension_name @@ -5264,9 +5282,12 @@ impl fmt::Display for Statement { write!(f, "{statement}") } Statement::Query(s) => s.fmt(f), - Statement::Declare { stmts } => { - write!(f, "DECLARE ")?; - write!(f, "{}", display_separated(stmts, "; ")) + Statement::Declare { stmts, separator } => { + let sep = match separator { + DeclareSeparator::Comma => ", ", + DeclareSeparator::Semicolon => "; ", + }; + write!(f, "DECLARE {}", display_separated(stmts, sep)) } Statement::Fetch { name, diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 340b2d0779..fa77b1caf3 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8009,6 +8009,7 @@ impl<'a> Parser<'a> { hold, for_query: query, }], + separator: DeclareSeparator::Semicolon, }) } @@ -8052,6 +8053,7 @@ impl<'a> Parser<'a> { hold: None, for_query: None, }], + separator: DeclareSeparator::Semicolon, }) } @@ -8168,7 +8170,10 @@ impl<'a> Parser<'a> { break; } - Ok(Statement::Declare { stmts }) + Ok(Statement::Declare { + stmts, + separator: DeclareSeparator::Semicolon, + }) } /// Parse a [MsSql] `DECLARE` statement. @@ -8185,7 +8190,10 @@ impl<'a> Parser<'a> { pub fn parse_mssql_declare(&mut self) -> Result { let stmts = self.parse_comma_separated(Parser::parse_mssql_declare_stmt)?; - Ok(Statement::Declare { stmts }) + Ok(Statement::Declare { + stmts, + separator: DeclareSeparator::Comma, + }) } /// Parse the body of a [MsSql] `DECLARE`statement. diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index daebe63dd7..612d3c2b00 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -2191,7 +2191,7 @@ fn parse_big_query_declare() { ), ] { match bigquery().verified_stmt(sql) { - Statement::Declare { mut stmts } => { + Statement::Declare { mut stmts, .. } => { assert_eq!(1, stmts.len()); let Declare { names, diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index e07d87007d..9bf015fc00 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -1475,7 +1475,8 @@ fn parse_mssql_declare() { hold: None, for_query: None } - ] + ], + separator: DeclareSeparator::Comma, }], ast ); @@ -1495,7 +1496,8 @@ fn parse_mssql_declare() { scroll: None, hold: None, for_query: None - }] + }], + separator: DeclareSeparator::Comma, }, Statement::Set(Set::SingleAssignment { scope: None, @@ -3004,3 +3006,10 @@ fn parse_create_proc() { .expect_err("PROC should remain MSSQL-specific"); ms_and_generic().verified_stmt("SELECT proc FROM jobs"); } + +#[test] +fn parse_mssql_declare_comma_list_display() { + ms().verified_stmt("DECLARE @a INT, @b INT"); + ms().verified_stmt("DECLARE @a INT = 1, @b TEXT = 'x'"); + ms().verified_stmt("DECLARE @foo CURSOR, @bar INT"); +} diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 6c8c73c6dd..0c37194467 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -1739,7 +1739,7 @@ fn parse_snowflake_declare_cursor() { ), ] { match snowflake().verified_stmt(sql) { - Statement::Declare { mut stmts } => { + Statement::Declare { mut stmts, .. } => { assert_eq!(1, stmts.len()); let Declare { names, @@ -1807,7 +1807,7 @@ fn parse_snowflake_declare_result_set() { ("DECLARE res RESULTSET", "res", None), ] { match snowflake().verified_stmt(sql) { - Statement::Declare { mut stmts } => { + Statement::Declare { mut stmts, .. } => { assert_eq!(1, stmts.len()); let Declare { names, @@ -1860,7 +1860,7 @@ fn parse_snowflake_declare_exception() { ("DECLARE ex EXCEPTION", "ex", None), ] { match snowflake().verified_stmt(sql) { - Statement::Declare { mut stmts } => { + Statement::Declare { mut stmts, .. } => { assert_eq!(1, stmts.len()); let Declare { names, @@ -1900,7 +1900,7 @@ fn parse_snowflake_declare_variable() { ("DECLARE profit", "profit", None, None), ] { match snowflake().verified_stmt(sql) { - Statement::Declare { mut stmts } => { + Statement::Declare { mut stmts, .. } => { assert_eq!(1, stmts.len()); let Declare { names, @@ -1950,7 +1950,7 @@ fn parse_snowflake_declare_multi_statements() { "ex EXCEPTION (-20003, 'ERROR: Could not create table.')" ); match snowflake().verified_stmt(sql) { - Statement::Declare { stmts } => { + Statement::Declare { stmts, .. } => { let actual = stmts .iter() .map(|stmt| (stmt.names[0].value.as_str(), stmt.declare_type.clone()))