From 095d10850a9892ec155b058832761de91f950513 Mon Sep 17 00:00:00 2001 From: Drew Thomas Date: Fri, 3 Nov 2023 11:44:11 +1100 Subject: [PATCH 1/3] Support `INSERT INTO ... DEFAULT VALUES ...` --- src/ast/mod.rs | 11 ++++-- src/parser/mod.rs | 16 +++++++-- tests/sqlparser_common.rs | 69 +++++++++++++++++++++++++++++++++++-- tests/sqlparser_mysql.rs | 16 ++++----- tests/sqlparser_postgres.rs | 2 +- 5 files changed, 99 insertions(+), 15 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index ab917dc4c..f6f029026 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1385,7 +1385,7 @@ pub enum Statement { /// Overwrite (Hive) overwrite: bool, /// A SQL query that specifies what to insert - source: Box, + source: Option>, /// partitioned insert (Hive) partitioned: Option>, /// Columns defined after PARTITION @@ -2241,7 +2241,14 @@ impl fmt::Display for Statement { if !after_columns.is_empty() { write!(f, "({}) ", display_comma_separated(after_columns))?; } - write!(f, "{source}")?; + + if let Some(source) = source { + write!(f, "{source}")?; + } + + if source.is_none() && columns.is_empty() { + write!(f, "DEFAULT VALUES")?; + } if let Some(on) = on { write!(f, "{on}")?; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 1964437eb..58d30f5ee 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7090,7 +7090,14 @@ impl<'a> Parser<'a> { let table = self.parse_keyword(Keyword::TABLE); let table_name = self.parse_object_name()?; let is_mysql = dialect_of!(self is MySqlDialect); - let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?; + + let is_default_values = self.parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]); + + let columns = if is_default_values { + vec![] + } else { + self.parse_parenthesized_column_list(Optional, is_mysql)? + }; let partitioned = if self.parse_keyword(Keyword::PARTITION) { self.expect_token(&Token::LParen)?; @@ -7104,7 +7111,12 @@ 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)?; - let source = Box::new(self.parse_query()?); + let source = if is_default_values { + None + } else { + Some(Box::new(self.parse_query()?)) + }; + let on = if self.parse_keyword(Keyword::ON) { if self.parse_keyword(Keyword::CONFLICT) { let conflict_target = diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index befdf5129..8bfe09c09 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -85,7 +85,7 @@ fn parse_insert_values() { Statement::Insert { table_name, columns, - source, + source: Some(source), .. } => { assert_eq!(table_name.to_string(), expected_table_name); @@ -93,7 +93,7 @@ fn parse_insert_values() { for (index, column) in columns.iter().enumerate() { assert_eq!(column, &Ident::new(expected_columns[index].clone())); } - match &*source.body { + match *source.body { SetExpr::Values(Values { rows, .. }) => { assert_eq!(rows.as_slice(), expected_rows) } @@ -107,6 +107,71 @@ fn parse_insert_values() { verified_stmt("INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1)"); } +#[test] +fn parse_insert_default_values() { + let insert_with_default_values = verified_stmt("INSERT INTO test_table DEFAULT VALUES"); + + match insert_with_default_values { + Statement::Insert { + columns, + on, + returning, + source, + table_name, + .. + } => { + assert_eq!(columns, vec![]); + assert_eq!(on, None); + assert_eq!(returning, None); + assert_eq!(source, None); + assert_eq!(table_name, ObjectName(vec!["test_table".into()])); + } + _ => unreachable!(), + } + + let insert_with_default_values_and_returning = + verified_stmt("INSERT INTO test_table DEFAULT VALUES RETURNING test_column"); + + match insert_with_default_values_and_returning { + Statement::Insert { + columns, + on, + returning, + source, + table_name, + .. + } => { + assert_eq!(columns, vec![]); + assert_eq!(on, None); + assert!(returning.is_some()); + assert_eq!(source, None); + assert_eq!(table_name, ObjectName(vec!["test_table".into()])); + } + _ => unreachable!(), + } + + let insert_with_default_values_and_on_conflict = + verified_stmt("INSERT INTO test_table DEFAULT VALUES ON CONFLICT DO NOTHING"); + + match insert_with_default_values_and_on_conflict { + Statement::Insert { + columns, + on, + returning, + source, + table_name, + .. + } => { + assert_eq!(columns, vec![]); + assert!(on.is_some()); + assert_eq!(returning, None); + assert_eq!(source, None); + assert_eq!(table_name, ObjectName(vec!["test_table".into()])); + } + _ => unreachable!(), + } +} + #[test] fn parse_insert_sqlite() { let dialect = SQLiteDialect {}; diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 2788dfabe..c727fcd98 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -937,7 +937,7 @@ fn parse_simple_insert() { assert_eq!(vec![Ident::new("title"), Ident::new("priority")], columns); assert!(on.is_none()); assert_eq!( - Box::new(Query { + Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { explicit_row: false, @@ -964,7 +964,7 @@ fn parse_simple_insert() { offset: None, fetch: None, locks: vec![], - }), + })), source ); } @@ -990,7 +990,7 @@ fn parse_ignore_insert() { assert!(on.is_none()); assert!(ignore); assert_eq!( - Box::new(Query { + Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { explicit_row: false, @@ -1005,7 +1005,7 @@ fn parse_ignore_insert() { offset: None, fetch: None, locks: vec![] - }), + })), source ); } @@ -1029,7 +1029,7 @@ fn parse_empty_row_insert() { assert!(columns.is_empty()); assert!(on.is_none()); assert_eq!( - Box::new(Query { + Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { explicit_row: false, @@ -1041,7 +1041,7 @@ fn parse_empty_row_insert() { offset: None, fetch: None, locks: vec![], - }), + })), source ); } @@ -1077,7 +1077,7 @@ fn parse_insert_with_on_duplicate_update() { columns ); assert_eq!( - Box::new(Query { + Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { explicit_row: false, @@ -1100,7 +1100,7 @@ fn parse_insert_with_on_duplicate_update() { offset: None, fetch: None, locks: vec![], - }), + })), source ); assert_eq!( diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 606d835f1..1fecc3562 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -1372,7 +1372,7 @@ fn parse_prepare() { Statement::Insert { table_name, columns, - source, + source: Some(source), .. } => { assert_eq!(table_name.to_string(), "customers"); From 4f334bd7370e2805090c08cd1e3a7fca3d5ee6db Mon Sep 17 00:00:00 2001 From: Drew Thomas Date: Sat, 11 Nov 2023 10:39:26 +1100 Subject: [PATCH 2/3] Fix bug where columns could be listed after `DEFAULT VALUES` Fix a bug where parsing `DEFAULT VALUES` in inserts didn't consider Hive syntax for partitions and columns. Also add tests for syntax errors when using `DEFAULT VALUES`. --- src/parser/mod.rs | 43 ++++++++++++++++++++------------------- tests/sqlparser_common.rs | 40 ++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 58d30f5ee..20ee10dee 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -7091,31 +7091,21 @@ impl<'a> Parser<'a> { let table_name = self.parse_object_name()?; let is_mysql = dialect_of!(self is MySqlDialect); - let is_default_values = self.parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]); + 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 = if is_default_values { - vec![] - } else { - self.parse_parenthesized_column_list(Optional, is_mysql)? - }; + let partitioned = self.parse_insert_partition()?; - let partitioned = if self.parse_keyword(Keyword::PARTITION) { - self.expect_token(&Token::LParen)?; - let r = Some(self.parse_comma_separated(Parser::parse_expr)?); - self.expect_token(&Token::RParen)?; - r - } else { - None - }; + // Hive allows you to specify columns after partitions as well if you want. + let after_columns = self.parse_parenthesized_column_list(Optional, false)?; - // Hive allows you to specify columns after partitions as well if you want. - let after_columns = self.parse_parenthesized_column_list(Optional, false)?; + let source = Some(Box::new(self.parse_query()?)); - let source = if is_default_values { - None - } else { - Some(Box::new(self.parse_query()?)) - }; + (columns, partitioned, after_columns, source) + }; let on = if self.parse_keyword(Keyword::ON) { if self.parse_keyword(Keyword::CONFLICT) { @@ -7187,6 +7177,17 @@ impl<'a> Parser<'a> { } } + pub fn parse_insert_partition(&mut self) -> Result>, ParserError> { + if self.parse_keyword(Keyword::PARTITION) { + self.expect_token(&Token::LParen)?; + let partition_cols = Some(self.parse_comma_separated(Parser::parse_expr)?); + self.expect_token(&Token::RParen)?; + Ok(partition_cols) + } else { + Ok(None) + } + } + pub fn parse_update(&mut self) -> Result { let table = self.parse_table_and_joins()?; self.expect_keyword(Keyword::SET)?; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 8bfe09c09..48020ee09 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -113,15 +113,19 @@ fn parse_insert_default_values() { match insert_with_default_values { Statement::Insert { + after_columns, columns, on, + partitioned, returning, source, table_name, .. } => { assert_eq!(columns, vec![]); + assert_eq!(after_columns, vec![]); assert_eq!(on, None); + assert_eq!(partitioned, None); assert_eq!(returning, None); assert_eq!(source, None); assert_eq!(table_name, ObjectName(vec!["test_table".into()])); @@ -134,15 +138,19 @@ fn parse_insert_default_values() { match insert_with_default_values_and_returning { Statement::Insert { + after_columns, columns, on, + partitioned, returning, source, table_name, .. } => { + assert_eq!(after_columns, vec![]); assert_eq!(columns, vec![]); assert_eq!(on, None); + assert_eq!(partitioned, None); assert!(returning.is_some()); assert_eq!(source, None); assert_eq!(table_name, ObjectName(vec!["test_table".into()])); @@ -155,21 +163,53 @@ fn parse_insert_default_values() { match insert_with_default_values_and_on_conflict { Statement::Insert { + after_columns, columns, on, + partitioned, returning, source, table_name, .. } => { + assert_eq!(after_columns, vec![]); assert_eq!(columns, vec![]); assert!(on.is_some()); + assert_eq!(partitioned, None); assert_eq!(returning, None); assert_eq!(source, None); assert_eq!(table_name, ObjectName(vec!["test_table".into()])); } _ => unreachable!(), } + + let insert_with_columns_and_default_values = "INSERT INTO test_table (test_col) DEFAULT VALUES"; + assert_eq!( + ParserError::ParserError( + "Expected SELECT, VALUES, or a subquery in the query body, found: DEFAULT".to_string() + ), + parse_sql_statements(insert_with_columns_and_default_values).unwrap_err() + ); + + let insert_with_default_values_and_hive_after_columns = + "INSERT INTO test_table DEFAULT VALUES (some_column)"; + assert_eq!( + ParserError::ParserError("Expected end of statement, found: (".to_string()), + parse_sql_statements(insert_with_default_values_and_hive_after_columns).unwrap_err() + ); + + let insert_with_default_values_and_hive_partition = + "INSERT INTO test_table DEFAULT VALUES PARTITION (some_column)"; + assert_eq!( + ParserError::ParserError("Expected end of statement, found: PARTITION".to_string()), + parse_sql_statements(insert_with_default_values_and_hive_partition).unwrap_err() + ); + + let insert_with_default_values_and_values_list = "INSERT INTO test_table DEFAULT VALUES (1)"; + assert_eq!( + ParserError::ParserError("Expected end of statement, found: (".to_string()), + parse_sql_statements(insert_with_default_values_and_values_list).unwrap_err() + ); } #[test] From 63f8a6c1ad4ccda04aa822590b689eaf884ed354 Mon Sep 17 00:00:00 2001 From: Drew Thomas Date: Tue, 21 Nov 2023 07:26:54 +1100 Subject: [PATCH 3/3] Remove extra whitespace in `DEFAULT VALUES ON CONFLICT` test https://github.com/sqlparser-rs/sqlparser-rs/pull/1037 removed the extra whitespace, so we need to remove it here as well now. --- tests/sqlparser_common.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 7e49a9eeb..4acd642de 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -159,7 +159,7 @@ fn parse_insert_default_values() { } let insert_with_default_values_and_on_conflict = - verified_stmt("INSERT INTO test_table DEFAULT VALUES ON CONFLICT DO NOTHING"); + verified_stmt("INSERT INTO test_table DEFAULT VALUES ON CONFLICT DO NOTHING"); match insert_with_default_values_and_on_conflict { Statement::Insert {