From 7d43ac043febdb37a01607bd7198501b666e70bb Mon Sep 17 00:00:00 2001 From: jonathanlehto Date: Thu, 29 Feb 2024 11:01:15 -0500 Subject: [PATCH 1/9] added bugfix --- src/parser/mod.rs | 11 +++++++++-- tests/sqlparser_common.rs | 3 +++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 6d7ac3604..635498e8c 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8411,8 +8411,15 @@ impl<'a> Parser<'a> { let partitioned = self.parse_insert_partition()?; - // Hive allows you to specify columns after partitions as well if you want. - let after_columns = self.parse_parenthesized_column_list(Optional, false)?; + // make sure we are not about to consume a query + let mut after_columns = vec![]; + match self.peek_nth_token(1).token { + Token::Word(w) if w.keyword != Keyword::SELECT => { + // Hive allows you to specify columns after partitions as well if you want. + after_columns = self.parse_parenthesized_column_list(Optional, false)?; + }, + _ => {} + }; let source = Some(Box::new(self.parse_query()?)); diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index f81456849..a8ada3399 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -107,6 +107,9 @@ fn parse_insert_values() { } verified_stmt("INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1)"); + + // allow parenthesis query after insert into + verified_stmt("INSERT INTO tbla (cola) (SELECT cola FROM tblb)"); } #[test] From ade716a4fa2db2fa3926a9e21b91f3316e641d82 Mon Sep 17 00:00:00 2001 From: jonathanlehto Date: Thu, 29 Feb 2024 16:44:46 -0500 Subject: [PATCH 2/9] fixing codestyle --- src/parser/mod.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 635498e8c..50e38c1df 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8403,28 +8403,28 @@ impl<'a> Parser<'a> { let is_mysql = dialect_of!(self is MySqlDialect); - let (columns, partitioned, after_columns, source) = - if self.parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) { - (vec![], None, vec![], None) - } else { - let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?; + let (columns, partitioned, after_columns, source) = if self + .parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) { + (vec![], None, vec![], None) + } else { + let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?; - let partitioned = self.parse_insert_partition()?; + let partitioned = self.parse_insert_partition()?; - // make sure we are not about to consume a query - let mut after_columns = vec![]; - match self.peek_nth_token(1).token { - Token::Word(w) if w.keyword != Keyword::SELECT => { - // Hive allows you to specify columns after partitions as well if you want. - after_columns = self.parse_parenthesized_column_list(Optional, false)?; - }, - _ => {} - }; + // make sure we are not about to consume a query + let mut after_columns = vec![]; + match self.peek_nth_token(1).token { + Token::Word(w) if w.keyword != Keyword::SELECT => { + // Hive allows you to specify columns after partitions as well if you want. + after_columns = self.parse_parenthesized_column_list(Optional, false)?; + }, + _ => {} + }; - let source = Some(Box::new(self.parse_query()?)); + let source = Some(Box::new(self.parse_query()?)); - (columns, partitioned, after_columns, source) - }; + (columns, partitioned, after_columns, source) + }; let on = if self.parse_keyword(Keyword::ON) { if self.parse_keyword(Keyword::CONFLICT) { From fad429ef4e690bbd4563d954f89e2098fa754ea3 Mon Sep 17 00:00:00 2001 From: jonathanlehto Date: Thu, 29 Feb 2024 16:51:47 -0500 Subject: [PATCH 3/9] second pass at codestyle --- src/parser/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 50e38c1df..56f25dcf7 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8404,7 +8404,8 @@ impl<'a> Parser<'a> { let is_mysql = dialect_of!(self is MySqlDialect); let (columns, partitioned, after_columns, source) = if self - .parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) { + .parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) + { (vec![], None, vec![], None) } else { let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?; @@ -8417,7 +8418,7 @@ impl<'a> Parser<'a> { Token::Word(w) if w.keyword != Keyword::SELECT => { // Hive allows you to specify columns after partitions as well if you want. after_columns = self.parse_parenthesized_column_list(Optional, false)?; - }, + } _ => {} }; From 95241bf0f6d0c0f3cb590d8d24dedacb591187a1 Mon Sep 17 00:00:00 2001 From: jonathanlehto Date: Fri, 1 Mar 2024 15:40:11 -0500 Subject: [PATCH 4/9] moved partition check into partition statement --- src/parser/mod.rs | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 56f25dcf7..fbacbf4aa 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8410,16 +8410,9 @@ impl<'a> Parser<'a> { } else { let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?; - let partitioned = self.parse_insert_partition()?; - - // make sure we are not about to consume a query - let mut after_columns = vec![]; - match self.peek_nth_token(1).token { - Token::Word(w) if w.keyword != Keyword::SELECT => { - // Hive allows you to specify columns after partitions as well if you want. - after_columns = self.parse_parenthesized_column_list(Optional, false)?; - } - _ => {} + let (partitioned, after_columns) = match self.parse_insert_partition()? { + Some((partitioned, after_columns)) => (Some(partitioned), after_columns), + None => (None, vec![]) }; let source = Some(Box::new(self.parse_query()?)); @@ -8500,12 +8493,16 @@ impl<'a> Parser<'a> { } } - pub fn parse_insert_partition(&mut self) -> Result>, ParserError> { + pub fn parse_insert_partition(&mut self) -> Result, Vec)>, ParserError> { if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; - let partition_cols = Some(self.parse_comma_separated(Parser::parse_expr)?); + let partition_cols = self.parse_comma_separated(Parser::parse_expr)?; self.expect_token(&Token::RParen)?; - Ok(partition_cols) + + // Hive allows you to specify columns after partitions as well if you want. + let after_columns = self.parse_parenthesized_column_list(Optional, false)?; + + Ok(Some((partition_cols, after_columns))) } else { Ok(None) } From ca8d6345a6c417b3ee5d5a3dcc0e24371b3391ea Mon Sep 17 00:00:00 2001 From: jonathanlehto Date: Fri, 1 Mar 2024 15:42:29 -0500 Subject: [PATCH 5/9] adding another test case --- tests/sqlparser_common.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index a8ada3399..91313e098 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -110,6 +110,9 @@ fn parse_insert_values() { // allow parenthesis query after insert into verified_stmt("INSERT INTO tbla (cola) (SELECT cola FROM tblb)"); + + // allow parenthesis query after insert into + verified_stmt("INSERT INTO tbla (cola) (VALUES (1))"); } #[test] From 7443e109604be369b9f3a7539f7204fe8b5343e8 Mon Sep 17 00:00:00 2001 From: jonathanlehto Date: Fri, 1 Mar 2024 15:51:37 -0500 Subject: [PATCH 6/9] simplified change --- src/parser/mod.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index fbacbf4aa..90b326529 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8410,10 +8410,7 @@ impl<'a> Parser<'a> { } else { let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?; - let (partitioned, after_columns) = match self.parse_insert_partition()? { - Some((partitioned, after_columns)) => (Some(partitioned), after_columns), - None => (None, vec![]) - }; + let (partitioned, after_columns) = self.parse_insert_partition()?; let source = Some(Box::new(self.parse_query()?)); @@ -8493,7 +8490,7 @@ impl<'a> Parser<'a> { } } - pub fn parse_insert_partition(&mut self) -> Result, Vec)>, ParserError> { + pub fn parse_insert_partition(&mut self) -> Result<(Option>, Vec), ParserError> { if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; let partition_cols = self.parse_comma_separated(Parser::parse_expr)?; @@ -8502,9 +8499,9 @@ impl<'a> Parser<'a> { // Hive allows you to specify columns after partitions as well if you want. let after_columns = self.parse_parenthesized_column_list(Optional, false)?; - Ok(Some((partition_cols, after_columns))) + Ok((Some(partition_cols), after_columns)) } else { - Ok(None) + Ok((None, vec![])) } } From 5c946a0cbe0f9c30f1d67759ea1215ff76476d59 Mon Sep 17 00:00:00 2001 From: jonathanlehto Date: Tue, 5 Mar 2024 11:40:24 -0500 Subject: [PATCH 7/9] rebased, updated to not allow values as valid subquery --- src/parser/mod.rs | 40 ++++++++++++++++++++++++++++++++++++++- tests/sqlparser_common.rs | 3 --- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 90b326529..e28f2f9f0 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7135,6 +7135,16 @@ impl<'a> Parser<'a> { } else if self.consume_token(&Token::LParen) { // CTEs are not allowed here, but the parser currently accepts them let subquery = self.parse_query()?; + + let is_generic = dialect_of!(self is GenericDialect); + if !is_generic { + if let SetExpr::Values(_) = *subquery.body { + return Err(ParserError::ParserError(format!( + "VALUES is not a recognized subquery" + ))) + } + } + self.expect_token(&Token::RParen)?; SetExpr::Query(Box::new(subquery)) } else if self.parse_keyword(Keyword::VALUES) { @@ -10152,7 +10162,35 @@ mod tests { fn test_replace_into_select() { let sql = r#"REPLACE INTO t1 (a, b, c) (SELECT * FROM t2)"#; - assert!(Parser::parse_sql(&GenericDialect {}, sql).is_err()); + assert!(Parser::parse_sql(&GenericDialect {}, sql).is_ok()); + } + + #[test] + fn test_insert_into_select() { + let sql = r#"INSERT INTO t1 (a, b, c) (SELECT * FROM t2)"#; + + assert!(Parser::parse_sql(&GenericDialect {}, sql).is_ok()); + } + + #[test] + fn test_insert_into_values() { + let sql = r#"INSERT INTO t1 (a) VALUES(1)"#; + + assert!(Parser::parse_sql(&GenericDialect {}, sql).is_ok()); + } + + #[test] + fn test_insert_into_values_wrapped() { + let sql = r#"INSERT INTO t1 (a) (VALUES(1))"#; + + assert!(Parser::parse_sql(&GenericDialect {}, sql).is_ok()); + } + + #[test] + fn test_insert_into_values_wrapped_not_generic() { + let sql = r#"INSERT INTO t1 (a) (VALUES(1))"#; + + assert!(Parser::parse_sql(&MySqlDialect {}, sql).is_err()); } #[test] diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 91313e098..a8ada3399 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -110,9 +110,6 @@ fn parse_insert_values() { // allow parenthesis query after insert into verified_stmt("INSERT INTO tbla (cola) (SELECT cola FROM tblb)"); - - // allow parenthesis query after insert into - verified_stmt("INSERT INTO tbla (cola) (VALUES (1))"); } #[test] From 1252b100a8aa4552561332c72a1dc463c826cf9a Mon Sep 17 00:00:00 2001 From: jonathanlehto Date: Mon, 11 Mar 2024 15:05:45 -0400 Subject: [PATCH 8/9] fixing lint and codestyle --- src/parser/mod.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index e28f2f9f0..045a573e5 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7139,9 +7139,9 @@ impl<'a> Parser<'a> { let is_generic = dialect_of!(self is GenericDialect); if !is_generic { if let SetExpr::Values(_) = *subquery.body { - return Err(ParserError::ParserError(format!( - "VALUES is not a recognized subquery" - ))) + return Err(ParserError::ParserError( + "VALUES is not a recognized subquery".to_string() + )); } } @@ -8413,19 +8413,18 @@ impl<'a> Parser<'a> { let is_mysql = dialect_of!(self is MySqlDialect); - let (columns, partitioned, after_columns, source) = if self - .parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) - { - (vec![], None, vec![], None) - } else { - let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?; + let (columns, partitioned, after_columns, source) = + if self.parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) { + (vec![], None, vec![], None) + } else { + let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?; - let (partitioned, after_columns) = self.parse_insert_partition()?; + let (partitioned, after_columns) = self.parse_insert_partition()?; - let source = Some(Box::new(self.parse_query()?)); + let source = Some(Box::new(self.parse_query()?)); - (columns, partitioned, after_columns, source) - }; + (columns, partitioned, after_columns, source) + }; let on = if self.parse_keyword(Keyword::ON) { if self.parse_keyword(Keyword::CONFLICT) { @@ -8500,7 +8499,9 @@ impl<'a> Parser<'a> { } } - pub fn parse_insert_partition(&mut self) -> Result<(Option>, Vec), ParserError> { + pub fn parse_insert_partition( + &mut self + ) -> Result<(Option>, Vec), ParserError> { if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; let partition_cols = self.parse_comma_separated(Parser::parse_expr)?; From 9738562cb8f78ca8aed5072622061fba58ffb996 Mon Sep 17 00:00:00 2001 From: jonathanlehto Date: Mon, 11 Mar 2024 15:07:30 -0400 Subject: [PATCH 9/9] adding commas --- src/parser/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 045a573e5..0704578a4 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7140,7 +7140,7 @@ impl<'a> Parser<'a> { if !is_generic { if let SetExpr::Values(_) = *subquery.body { return Err(ParserError::ParserError( - "VALUES is not a recognized subquery".to_string() + "VALUES is not a recognized subquery".to_string(), )); } } @@ -8500,7 +8500,7 @@ impl<'a> Parser<'a> { } pub fn parse_insert_partition( - &mut self + &mut self, ) -> Result<(Option>, Vec), ParserError> { if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?;