From a72ea5416c9d622f4c91f9a29a9d1fa95fee91c9 Mon Sep 17 00:00:00 2001 From: wugeer <1284057728@qq.com> Date: Fri, 1 Nov 2024 21:50:10 +0800 Subject: [PATCH 1/5] Fix the parsing error in MSSQL for multiple statements that include `DECLARE` statements. before parse sql in mssql ``` declare @a int; set @a = 2; select @a * 4; ``` raise error ``` Error during parsing: ParserError("Expected: end of statement, found: set at Line: 2, Column: 1") ``` --- src/parser/mod.rs | 10 ++++++ tests/sqlparser_mssql.rs | 66 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 942ff19fd..de6a0799c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -434,6 +434,16 @@ impl<'a> Parser<'a> { let statement = self.parse_statement()?; stmts.push(statement); expecting_statement_delimiter = true; + if self.peek_token().token != Token::SemiColon { + // If the next valid character is not a semicolon, + // check whether the previous valid character is a semicolon. + // If it is, revert to the previous valid character (i.e., the semicolon); + // otherwise, do nothing. + self.prev_token(); + if self.peek_token().token != Token::SemiColon { + self.next_token(); + } + }; } Ok(stmts) } diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index c5f43b072..2b402613d 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -628,6 +628,72 @@ fn parse_mssql_declare() { }], ast ); + + let sql = "DECLARE @bar INT;SET @bar = 2;SELECT @bar * 4"; + let ast = Parser::parse_sql(&MsSqlDialect {}, sql).unwrap(); + assert_eq!( + vec![ + Statement::Declare { + stmts: vec![Declare { + names: vec![Ident { + value: "@bar".to_string(), + quote_style: None + }], + data_type: Some(Int(None)), + assignment: None, + declare_type: None, + binary: None, + sensitive: None, + scroll: None, + hold: None, + for_query: None + }] + }, + Statement::SetVariable { + local: false, + hivevar: false, + variables: OneOrManyWithParens::One(ObjectName(vec![Ident::new("@bar")])), + value: vec![Expr::Value(Value::Number("2".parse().unwrap(), false))], + }, + Statement::Query(Box::new(Query { + with: None, + limit: None, + limit_by: vec![], + offset: None, + fetch: None, + locks: vec![], + for_clause: None, + order_by: None, + settings: None, + format_clause: None, + body: Box::new(SetExpr::Select(Box::new(Select { + distinct: None, + top: None, + projection: vec![SelectItem::UnnamedExpr(Expr::BinaryOp { + left: Box::new(Expr::Identifier(Ident::new("@bar"))), + op: BinaryOperator::Multiply, + right: Box::new(Expr::Value(Value::Number("4".parse().unwrap(), false))), + })], + into: None, + from: vec![], + lateral_views: vec![], + prewhere: None, + selection: None, + group_by: GroupByExpr::Expressions(vec![], vec![]), + cluster_by: vec![], + distribute_by: vec![], + sort_by: vec![], + having: None, + named_window: vec![], + window_before_qualify: false, + qualify: None, + value_table_mode: None, + connect_by: None, + }))) + })) + ], + ast + ); } #[test] From 612dc408161d6bffcac3bd3a4882aa0a7f38bd2d Mon Sep 17 00:00:00 2001 From: wugeer <1284057728@qq.com> Date: Sat, 2 Nov 2024 08:59:16 +0800 Subject: [PATCH 2/5] Make the logic for exiting the `parse_mssql_declare` loop more precise --- src/parser/mod.rs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index de6a0799c..97921abf6 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -434,16 +434,6 @@ impl<'a> Parser<'a> { let statement = self.parse_statement()?; stmts.push(statement); expecting_statement_delimiter = true; - if self.peek_token().token != Token::SemiColon { - // If the next valid character is not a semicolon, - // check whether the previous valid character is a semicolon. - // If it is, revert to the previous valid character (i.e., the semicolon); - // otherwise, do nothing. - self.prev_token(); - if self.peek_token().token != Token::SemiColon { - self.next_token(); - } - }; } Ok(stmts) } @@ -5364,7 +5354,7 @@ impl<'a> Parser<'a> { for_query: None, }); - if self.next_token() != Token::Comma { + if self.peek_token().token == Token::SemiColon || self.next_token() != Token::Comma { break; } } From 258645ac86cd4908a62e66e70e92cbcfd3a0ad6e Mon Sep 17 00:00:00 2001 From: wugeer <1284057728@qq.com> Date: Wed, 6 Nov 2024 21:06:58 +0800 Subject: [PATCH 3/5] Use a more consistent approach --- tests/sqlparser_mssql.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index 2b402613d..bac7667a7 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -29,7 +29,7 @@ use sqlparser::ast::DeclareAssignment::MsSqlAssignment; use sqlparser::ast::Value::SingleQuotedString; use sqlparser::ast::*; use sqlparser::dialect::{GenericDialect, MsSqlDialect}; -use sqlparser::parser::{Parser, ParserError}; +use sqlparser::parser::ParserError; #[test] fn parse_mssql_identifiers() { @@ -575,7 +575,7 @@ fn parse_substring_in_select() { #[test] fn parse_mssql_declare() { let sql = "DECLARE @foo CURSOR, @bar INT, @baz AS TEXT = 'foobar';"; - let ast = Parser::parse_sql(&MsSqlDialect {}, sql).unwrap(); + let ast = ms().parse_sql_statements(sql).unwrap(); assert_eq!( vec![Statement::Declare { @@ -630,7 +630,7 @@ fn parse_mssql_declare() { ); let sql = "DECLARE @bar INT;SET @bar = 2;SELECT @bar * 4"; - let ast = Parser::parse_sql(&MsSqlDialect {}, sql).unwrap(); + let ast = ms().parse_sql_statements(sql).unwrap(); assert_eq!( vec![ Statement::Declare { From f42952e47b2ee80b39ca2f377764538f7aad79dc Mon Sep 17 00:00:00 2001 From: wugeer <1284057728@qq.com> Date: Fri, 8 Nov 2024 07:43:13 +0800 Subject: [PATCH 4/5] Parse the MSSQL `DECLARE` statement in a more rustc-like manner --- src/parser/mod.rs | 94 +++++++++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 97921abf6..5cfe1372a 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -5311,55 +5311,61 @@ impl<'a> Parser<'a> { /// ``` /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/declare-local-variable-transact-sql?view=sql-server-ver16 pub fn parse_mssql_declare(&mut self) -> Result { - let mut stmts = vec![]; - - loop { - let name = { - let ident = self.parse_identifier(false)?; - if !ident.value.starts_with('@') { - Err(ParserError::TokenizerError( - "Invalid MsSql variable declaration.".to_string(), - )) - } else { - Ok(ident) - } - }?; + let stmts = self.parse_comma_separated(Parser::parse_mssql_declare_stmt)?; - let (declare_type, data_type) = match self.peek_token().token { - Token::Word(w) => match w.keyword { - Keyword::CURSOR => { - self.next_token(); - (Some(DeclareType::Cursor), None) - } - Keyword::AS => { - self.next_token(); - (None, Some(self.parse_data_type()?)) - } - _ => (None, Some(self.parse_data_type()?)), - }, - _ => (None, Some(self.parse_data_type()?)), - }; + Ok(Statement::Declare { stmts }) + } - let assignment = self.parse_mssql_variable_declaration_expression()?; + /// Parse the body of a [MsSql] `DECLARE`statement. + /// + /// Syntax: + /// ```text + // { + // { @local_variable [AS] data_type [ = value ] } + // | { @cursor_variable_name CURSOR } + // } [ ,...n ] + /// ``` + /// [MsSql]: https://learn.microsoft.com/en-us/sql/t-sql/language-elements/declare-local-variable-transact-sql?view=sql-server-ver16 + pub fn parse_mssql_declare_stmt(&mut self) -> Result { + let name = { + let ident = self.parse_identifier(false)?; + if !ident.value.starts_with('@') { + Err(ParserError::TokenizerError( + "Invalid MsSql variable declaration.".to_string(), + )) + } else { + Ok(ident) + } + }?; - stmts.push(Declare { - names: vec![name], - data_type, - assignment, - declare_type, - binary: None, - sensitive: None, - scroll: None, - hold: None, - for_query: None, - }); + let (declare_type, data_type) = match self.peek_token().token { + Token::Word(w) => match w.keyword { + Keyword::CURSOR => { + self.next_token(); + (Some(DeclareType::Cursor), None) + } + Keyword::AS => { + self.next_token(); + (None, Some(self.parse_data_type()?)) + } + _ => (None, Some(self.parse_data_type()?)), + }, + _ => (None, Some(self.parse_data_type()?)), + }; - if self.peek_token().token == Token::SemiColon || self.next_token() != Token::Comma { - break; - } - } + let assignment = self.parse_mssql_variable_declaration_expression()?; - Ok(Statement::Declare { stmts }) + Ok(Declare { + names: vec![name], + data_type, + assignment, + declare_type, + binary: None, + sensitive: None, + scroll: None, + hold: None, + for_query: None, + }) } /// Parses the assigned expression in a variable declaration. From 18c0d689a3602aee6ae935d414c2cfaf4e86fde4 Mon Sep 17 00:00:00 2001 From: wugeer <1284057728@qq.com> Date: Sun, 10 Nov 2024 00:29:08 +0800 Subject: [PATCH 5/5] fix compile issue --- tests/sqlparser_mssql.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/sqlparser_mssql.rs b/tests/sqlparser_mssql.rs index bac7667a7..53b7413b3 100644 --- a/tests/sqlparser_mssql.rs +++ b/tests/sqlparser_mssql.rs @@ -669,6 +669,7 @@ fn parse_mssql_declare() { body: Box::new(SetExpr::Select(Box::new(Select { distinct: None, top: None, + top_before_distinct: false, projection: vec![SelectItem::UnnamedExpr(Expr::BinaryOp { left: Box::new(Expr::Identifier(Ident::new("@bar"))), op: BinaryOperator::Multiply,