From 5f70144866fea42a26f453cf15917bf3b001728a Mon Sep 17 00:00:00 2001 From: Etgar Perets Date: Mon, 3 Nov 2025 13:20:15 +0200 Subject: [PATCH 1/5] SGA-12672 Added support for INSERT INTO VALUE, previously only VALUES was allowed --- src/ast/mod.rs | 5 ++-- src/ast/query.rs | 20 ++++++++++++++- src/ast/spans.rs | 1 + src/parser/mod.rs | 22 ++++++++++++---- tests/sqlparser_bigquery.rs | 3 +++ tests/sqlparser_common.rs | 1 + tests/sqlparser_databricks.rs | 1 + tests/sqlparser_mysql.rs | 47 +++++++++++++++++++++++++++++++++++ tests/sqlparser_postgres.rs | 3 +++ 9 files changed, 95 insertions(+), 8 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 176d36545..fc86af90d 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -92,8 +92,9 @@ pub use self::query::{ TableIndexType, TableSample, TableSampleBucket, TableSampleKind, TableSampleMethod, TableSampleModifier, TableSampleQuantity, TableSampleSeed, TableSampleSeedModifier, TableSampleUnit, TableVersion, TableWithJoins, Top, TopQuantity, UpdateTableFromKind, - ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill, XmlNamespaceDefinition, - XmlPassingArgument, XmlPassingClause, XmlTableColumn, XmlTableColumnOption, + ValueTableMode, Values, ValuesKeyword, WildcardAdditionalOptions, With, WithFill, + XmlNamespaceDefinition, XmlPassingArgument, XmlPassingClause, XmlTableColumn, + XmlTableColumnOption, }; pub use self::trigger::{ diff --git a/src/ast/query.rs b/src/ast/query.rs index 599b013ab..adc0489fa 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -3135,12 +3135,30 @@ pub struct Values { /// Was there an explicit ROWs keyword (MySQL)? /// pub explicit_row: bool, + // MySql supports both VALUES and VALUE keywords. + // + pub keyword: ValuesKeyword, pub rows: Vec>, } +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +pub enum ValuesKeyword { + Values, + Value, +} + +impl fmt::Display for ValuesKeyword { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ValuesKeyword::Values => write!(f, "VALUES"), + ValuesKeyword::Value => write!(f, "VALUE"), + } + } +} + impl fmt::Display for Values { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("VALUES")?; + write!(f, "{}", self.keyword)?; let prefix = if self.explicit_row { "ROW" } else { "" }; let mut delim = ""; for row in &self.rows { diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 7d2a00095..f96442cdb 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -223,6 +223,7 @@ impl Spanned for Values { fn span(&self) -> Span { let Values { explicit_row: _, // bool, + keyword: _, rows, } = self; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 9a01e510b..61bdd106e 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -39,6 +39,7 @@ use crate::ast::helpers::{ stmt_create_table::{CreateTableBuilder, CreateTableConfiguration}, }; use crate::ast::Statement::CreatePolicy; +use crate::ast::ValuesKeyword; use crate::ast::*; use crate::dialect::*; use crate::keywords::{Keyword, ALL_KEYWORDS}; @@ -12528,7 +12529,10 @@ impl<'a> Parser<'a> { SetExpr::Query(subquery) } else if self.parse_keyword(Keyword::VALUES) { let is_mysql = dialect_of!(self is MySqlDialect); - SetExpr::Values(self.parse_values(is_mysql)?) + SetExpr::Values(self.parse_values(is_mysql, ValuesKeyword::Values)?) + } else if self.parse_keyword(Keyword::VALUE) { + let is_mysql = dialect_of!(self is MySqlDialect); + SetExpr::Values(self.parse_values(is_mysql, ValuesKeyword::Value)?) } else if self.parse_keyword(Keyword::TABLE) { SetExpr::Table(Box::new(self.parse_as_table()?)) } else { @@ -13832,7 +13836,7 @@ impl<'a> Parser<'a> { // Snowflake and Databricks allow syntax like below: // SELECT * FROM VALUES (1, 'a'), (2, 'b') AS t (col1, col2) // where there are no parentheses around the VALUES clause. - let values = SetExpr::Values(self.parse_values(false)?); + let values = SetExpr::Values(self.parse_values(false, ValuesKeyword::Values)?); let alias = self.maybe_parse_table_alias()?; Ok(TableFactor::Derived { lateral: false, @@ -16499,7 +16503,11 @@ impl<'a> Parser<'a> { }) } - pub fn parse_values(&mut self, allow_empty: bool) -> Result { + pub fn parse_values( + &mut self, + allow_empty: bool, + keyword: ValuesKeyword, + ) -> Result { let mut explicit_row = false; let rows = self.parse_comma_separated(|parser| { @@ -16517,7 +16525,11 @@ impl<'a> Parser<'a> { Ok(exprs) } })?; - Ok(Values { explicit_row, rows }) + Ok(Values { + explicit_row, + rows, + keyword, + }) } pub fn parse_start_transaction(&mut self) -> Result { @@ -16932,7 +16944,7 @@ impl<'a> Parser<'a> { MergeInsertKind::Row } else { self.expect_keyword_is(Keyword::VALUES)?; - let values = self.parse_values(is_mysql)?; + let values = self.parse_values(is_mysql, ValuesKeyword::Values)?; MergeInsertKind::Values(values) }; MergeAction::Insert(MergeInsertExpr { columns, kind }) diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 03a0ac813..84e0c17c8 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -1807,6 +1807,7 @@ fn parse_merge() { let insert_action = MergeAction::Insert(MergeInsertExpr { columns: vec![Ident::new("product"), Ident::new("quantity")], kind: MergeInsertKind::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![Expr::value(number("1")), Expr::value(number("2"))]], }), @@ -1951,6 +1952,7 @@ fn parse_merge() { action: MergeAction::Insert(MergeInsertExpr { columns: vec![Ident::new("a"), Ident::new("b"),], kind: MergeInsertKind::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::value(number("1")), @@ -1965,6 +1967,7 @@ fn parse_merge() { action: MergeAction::Insert(MergeInsertExpr { columns: vec![], kind: MergeInsertKind::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::value(number("1")), diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index f1ba5df04..03afd55d5 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -9908,6 +9908,7 @@ fn parse_merge() { action: MergeAction::Insert(MergeInsertExpr { columns: vec![Ident::new("A"), Ident::new("B"), Ident::new("C")], kind: MergeInsertKind::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::CompoundIdentifier(vec![ diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index 92b635339..fe5614e5e 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -157,6 +157,7 @@ fn test_databricks_lambdas() { #[test] fn test_values_clause() { let values = Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![ vec![ diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index e43df87ab..7145d95e6 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1885,6 +1885,7 @@ fn parse_simple_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![ vec![ @@ -1950,6 +1951,7 @@ fn parse_ignore_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::Value( @@ -1999,6 +2001,7 @@ fn parse_priority_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::Value( @@ -2045,6 +2048,7 @@ fn parse_priority_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::Value( @@ -2097,6 +2101,7 @@ fn parse_insert_as() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![Expr::Value( (Value::SingleQuotedString("2024-01-01".to_string())).with_empty_span() @@ -2156,6 +2161,7 @@ fn parse_insert_as() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::value(number("1")), @@ -2206,6 +2212,7 @@ fn parse_replace_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::Value( @@ -2253,6 +2260,7 @@ fn parse_empty_row_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![], vec![]] })), @@ -2303,6 +2311,7 @@ fn parse_insert_with_on_duplicate_update() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::Value( @@ -4353,3 +4362,41 @@ fn test_create_index_options() { "CREATE INDEX idx_name ON t(c1, c2) USING BTREE LOCK = EXCLUSIVE ALGORITHM = DEFAULT", ); } + +#[test] +fn test_insert_into_value() { + let sql = r"INSERT INTO t (id, name) VALUE ('AAA', 'BBB')"; + match mysql_and_generic().verified_stmt(sql) { + Statement::Insert(Insert { source, .. }) => { + assert_eq!( + Some(Box::new(Query { + with: None, + body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Value, + explicit_row: false, + rows: vec![vec![ + Expr::Value(ValueWithSpan { + value: Value::SingleQuotedString("AAA".to_string()), + span: Span::empty(), + }), + Expr::Value(ValueWithSpan { + value: Value::SingleQuotedString("BBB".to_string()), + span: Span::empty(), + }), + ]], + })), + order_by: None, + limit_clause: None, + fetch: None, + locks: vec![], + for_clause: None, + settings: None, + format_clause: None, + pipe_operators: vec![], + })), + source + ); + } + _ => unreachable!(), + } +} diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 9ba0fb978..40e66340e 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -5169,6 +5169,7 @@ fn test_simple_postgres_insert_with_alias() { source: Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::Identifier(Ident::new("DEFAULT")), @@ -5238,6 +5239,7 @@ fn test_simple_postgres_insert_with_alias() { source: Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::Identifier(Ident::new("DEFAULT")), @@ -5309,6 +5311,7 @@ fn test_simple_insert_with_quoted_alias() { source: Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { + keyword: ValuesKeyword::Values, explicit_row: false, rows: vec![vec![ Expr::Identifier(Ident::new("DEFAULT")), From ad88d84e853e39345bfa052cdc4547c5e7eb53e5 Mon Sep 17 00:00:00 2001 From: Etgar Perets Date: Mon, 3 Nov 2025 13:26:15 +0200 Subject: [PATCH 2/5] SGA-12671 added cfg_attr to enum --- src/ast/query.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ast/query.rs b/src/ast/query.rs index adc0489fa..c9ebb9162 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -3142,6 +3142,8 @@ pub struct Values { } #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub enum ValuesKeyword { Values, Value, From 0ebcaae646d4f52c23462b9715fd92f9fd335b7b Mon Sep 17 00:00:00 2001 From: Etgar Perets Date: Mon, 3 Nov 2025 14:07:36 +0200 Subject: [PATCH 3/5] SGA-12672 cargo lint fix --- src/ast/mod.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index fc86af90d..32f724cf5 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -2788,10 +2788,11 @@ impl fmt::Display for Declare { } /// Sql options of a `CREATE TABLE` statement. -#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] pub enum CreateTableOptions { + #[default] None, /// Options specified using the `WITH` keyword. /// e.g. `WITH (description = "123")` @@ -2820,12 +2821,6 @@ pub enum CreateTableOptions { TableProperties(Vec), } -impl Default for CreateTableOptions { - fn default() -> Self { - Self::None - } -} - impl fmt::Display for CreateTableOptions { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { From 158d1a10ab9fb82bad6eb6dd26da0cce72195df3 Mon Sep 17 00:00:00 2001 From: Etgar Perets Date: Tue, 4 Nov 2025 17:27:15 +0200 Subject: [PATCH 4/5] SGA-12672 changed enum to bool, removed the object structure in the test and moved it to another test --- src/ast/mod.rs | 2 +- src/ast/query.rs | 24 ++++----------- src/ast/spans.rs | 2 +- src/parser/mod.rs | 13 ++++---- tests/sqlparser_bigquery.rs | 6 ++-- tests/sqlparser_common.rs | 28 +++++++++++++----- tests/sqlparser_databricks.rs | 2 +- tests/sqlparser_mysql.rs | 56 ++++++----------------------------- tests/sqlparser_postgres.rs | 6 ++-- 9 files changed, 49 insertions(+), 90 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 32f724cf5..a5264640d 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -92,7 +92,7 @@ pub use self::query::{ TableIndexType, TableSample, TableSampleBucket, TableSampleKind, TableSampleMethod, TableSampleModifier, TableSampleQuantity, TableSampleSeed, TableSampleSeedModifier, TableSampleUnit, TableVersion, TableWithJoins, Top, TopQuantity, UpdateTableFromKind, - ValueTableMode, Values, ValuesKeyword, WildcardAdditionalOptions, With, WithFill, + ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill, XmlNamespaceDefinition, XmlPassingArgument, XmlPassingClause, XmlTableColumn, XmlTableColumnOption, }; diff --git a/src/ast/query.rs b/src/ast/query.rs index c9ebb9162..33c92614f 100644 --- a/src/ast/query.rs +++ b/src/ast/query.rs @@ -3137,30 +3137,16 @@ pub struct Values { pub explicit_row: bool, // MySql supports both VALUES and VALUE keywords. // - pub keyword: ValuesKeyword, + pub value_keyword: bool, pub rows: Vec>, } -#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] -#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] -pub enum ValuesKeyword { - Values, - Value, -} - -impl fmt::Display for ValuesKeyword { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - ValuesKeyword::Values => write!(f, "VALUES"), - ValuesKeyword::Value => write!(f, "VALUE"), - } - } -} - impl fmt::Display for Values { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.keyword)?; + match self.value_keyword { + true => f.write_str("VALUE")?, + false => f.write_str("VALUES")?, + }; let prefix = if self.explicit_row { "ROW" } else { "" }; let mut delim = ""; for row in &self.rows { diff --git a/src/ast/spans.rs b/src/ast/spans.rs index f96442cdb..c1ec5d10b 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -223,7 +223,7 @@ impl Spanned for Values { fn span(&self) -> Span { let Values { explicit_row: _, // bool, - keyword: _, + value_keyword: _, rows, } = self; diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 61bdd106e..30bf7ece5 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -39,7 +39,6 @@ use crate::ast::helpers::{ stmt_create_table::{CreateTableBuilder, CreateTableConfiguration}, }; use crate::ast::Statement::CreatePolicy; -use crate::ast::ValuesKeyword; use crate::ast::*; use crate::dialect::*; use crate::keywords::{Keyword, ALL_KEYWORDS}; @@ -12529,10 +12528,10 @@ impl<'a> Parser<'a> { SetExpr::Query(subquery) } else if self.parse_keyword(Keyword::VALUES) { let is_mysql = dialect_of!(self is MySqlDialect); - SetExpr::Values(self.parse_values(is_mysql, ValuesKeyword::Values)?) + SetExpr::Values(self.parse_values(is_mysql, false)?) } else if self.parse_keyword(Keyword::VALUE) { let is_mysql = dialect_of!(self is MySqlDialect); - SetExpr::Values(self.parse_values(is_mysql, ValuesKeyword::Value)?) + SetExpr::Values(self.parse_values(is_mysql, true)?) } else if self.parse_keyword(Keyword::TABLE) { SetExpr::Table(Box::new(self.parse_as_table()?)) } else { @@ -13836,7 +13835,7 @@ impl<'a> Parser<'a> { // Snowflake and Databricks allow syntax like below: // SELECT * FROM VALUES (1, 'a'), (2, 'b') AS t (col1, col2) // where there are no parentheses around the VALUES clause. - let values = SetExpr::Values(self.parse_values(false, ValuesKeyword::Values)?); + let values = SetExpr::Values(self.parse_values(false, false)?); let alias = self.maybe_parse_table_alias()?; Ok(TableFactor::Derived { lateral: false, @@ -16506,7 +16505,7 @@ impl<'a> Parser<'a> { pub fn parse_values( &mut self, allow_empty: bool, - keyword: ValuesKeyword, + value_keyword: bool, ) -> Result { let mut explicit_row = false; @@ -16528,7 +16527,7 @@ impl<'a> Parser<'a> { Ok(Values { explicit_row, rows, - keyword, + value_keyword, }) } @@ -16944,7 +16943,7 @@ impl<'a> Parser<'a> { MergeInsertKind::Row } else { self.expect_keyword_is(Keyword::VALUES)?; - let values = self.parse_values(is_mysql, ValuesKeyword::Values)?; + let values = self.parse_values(is_mysql, false)?; MergeInsertKind::Values(values) }; MergeAction::Insert(MergeInsertExpr { columns, kind }) diff --git a/tests/sqlparser_bigquery.rs b/tests/sqlparser_bigquery.rs index 84e0c17c8..0ef1c4f0c 100644 --- a/tests/sqlparser_bigquery.rs +++ b/tests/sqlparser_bigquery.rs @@ -1807,7 +1807,7 @@ fn parse_merge() { let insert_action = MergeAction::Insert(MergeInsertExpr { columns: vec![Ident::new("product"), Ident::new("quantity")], kind: MergeInsertKind::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![Expr::value(number("1")), Expr::value(number("2"))]], }), @@ -1952,7 +1952,7 @@ fn parse_merge() { action: MergeAction::Insert(MergeInsertExpr { columns: vec![Ident::new("a"), Ident::new("b"),], kind: MergeInsertKind::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::value(number("1")), @@ -1967,7 +1967,7 @@ fn parse_merge() { action: MergeAction::Insert(MergeInsertExpr { columns: vec![], kind: MergeInsertKind::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::value(number("1")), diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 03afd55d5..cbbbccd3d 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -106,19 +106,19 @@ fn parse_insert_values() { let rows2 = vec![row.clone(), row]; let sql = "INSERT customer VALUES (1, 2, 3)"; - check_one(sql, "customer", &[], &rows1); + check_one(sql, "customer", &[], &rows1, false); let sql = "INSERT INTO customer VALUES (1, 2, 3)"; - check_one(sql, "customer", &[], &rows1); + check_one(sql, "customer", &[], &rows1, false); let sql = "INSERT INTO customer VALUES (1, 2, 3), (1, 2, 3)"; - check_one(sql, "customer", &[], &rows2); + check_one(sql, "customer", &[], &rows2, false); let sql = "INSERT INTO public.customer VALUES (1, 2, 3)"; - check_one(sql, "public.customer", &[], &rows1); + check_one(sql, "public.customer", &[], &rows1, false); let sql = "INSERT INTO db.public.customer VALUES (1, 2, 3)"; - check_one(sql, "db.public.customer", &[], &rows1); + check_one(sql, "db.public.customer", &[], &rows1, false); let sql = "INSERT INTO public.customer (id, name, active) VALUES (1, 2, 3)"; check_one( @@ -126,6 +126,16 @@ fn parse_insert_values() { "public.customer", &["id".to_string(), "name".to_string(), "active".to_string()], &rows1, + false, + ); + + let sql = r"INSERT INTO t (id, name, active) VALUE (1, 2, 3)"; + check_one( + sql, + "t", + &["id".to_string(), "name".to_string(), "active".to_string()], + &rows1, + true, ); fn check_one( @@ -133,6 +143,7 @@ fn parse_insert_values() { expected_table_name: &str, expected_columns: &[String], expected_rows: &[Vec], + expected_value_keyword: bool, ) { match verified_stmt(sql) { Statement::Insert(Insert { @@ -147,8 +158,9 @@ fn parse_insert_values() { assert_eq!(column, &Ident::new(expected_columns[index].clone())); } match *source.body { - SetExpr::Values(Values { rows, .. }) => { - assert_eq!(rows.as_slice(), expected_rows) + SetExpr::Values(Values { rows, value_keyword, .. }) => { + assert_eq!(rows.as_slice(), expected_rows); + assert!(value_keyword == expected_value_keyword); } _ => unreachable!(), } @@ -9908,7 +9920,7 @@ fn parse_merge() { action: MergeAction::Insert(MergeInsertExpr { columns: vec![Ident::new("A"), Ident::new("B"), Ident::new("C")], kind: MergeInsertKind::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::CompoundIdentifier(vec![ diff --git a/tests/sqlparser_databricks.rs b/tests/sqlparser_databricks.rs index fe5614e5e..065e8f9e7 100644 --- a/tests/sqlparser_databricks.rs +++ b/tests/sqlparser_databricks.rs @@ -157,7 +157,7 @@ fn test_databricks_lambdas() { #[test] fn test_values_clause() { let values = Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![ vec![ diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index 7145d95e6..a5ca03bd8 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -1885,7 +1885,7 @@ fn parse_simple_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![ vec![ @@ -1951,7 +1951,7 @@ fn parse_ignore_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::Value( @@ -2001,7 +2001,7 @@ fn parse_priority_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::Value( @@ -2048,7 +2048,7 @@ fn parse_priority_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::Value( @@ -2101,7 +2101,7 @@ fn parse_insert_as() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![Expr::Value( (Value::SingleQuotedString("2024-01-01".to_string())).with_empty_span() @@ -2161,7 +2161,7 @@ fn parse_insert_as() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::value(number("1")), @@ -2212,7 +2212,7 @@ fn parse_replace_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::Value( @@ -2260,7 +2260,7 @@ fn parse_empty_row_insert() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![], vec![]] })), @@ -2311,7 +2311,7 @@ fn parse_insert_with_on_duplicate_update() { Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::Value( @@ -4362,41 +4362,3 @@ fn test_create_index_options() { "CREATE INDEX idx_name ON t(c1, c2) USING BTREE LOCK = EXCLUSIVE ALGORITHM = DEFAULT", ); } - -#[test] -fn test_insert_into_value() { - let sql = r"INSERT INTO t (id, name) VALUE ('AAA', 'BBB')"; - match mysql_and_generic().verified_stmt(sql) { - Statement::Insert(Insert { source, .. }) => { - assert_eq!( - Some(Box::new(Query { - with: None, - body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Value, - explicit_row: false, - rows: vec![vec![ - Expr::Value(ValueWithSpan { - value: Value::SingleQuotedString("AAA".to_string()), - span: Span::empty(), - }), - Expr::Value(ValueWithSpan { - value: Value::SingleQuotedString("BBB".to_string()), - span: Span::empty(), - }), - ]], - })), - order_by: None, - limit_clause: None, - fetch: None, - locks: vec![], - for_clause: None, - settings: None, - format_clause: None, - pipe_operators: vec![], - })), - source - ); - } - _ => unreachable!(), - } -} diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 40e66340e..87cb43edd 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -5169,7 +5169,7 @@ fn test_simple_postgres_insert_with_alias() { source: Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::Identifier(Ident::new("DEFAULT")), @@ -5239,7 +5239,7 @@ fn test_simple_postgres_insert_with_alias() { source: Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::Identifier(Ident::new("DEFAULT")), @@ -5311,7 +5311,7 @@ fn test_simple_insert_with_quoted_alias() { source: Some(Box::new(Query { with: None, body: Box::new(SetExpr::Values(Values { - keyword: ValuesKeyword::Values, + value_keyword: false, explicit_row: false, rows: vec![vec![ Expr::Identifier(Ident::new("DEFAULT")), From 1819a8fc4a580c29ec3cc393ab76f904b3f5360e Mon Sep 17 00:00:00 2001 From: Etgar Perets Date: Wed, 5 Nov 2025 09:18:33 +0200 Subject: [PATCH 5/5] SGA-12672 cargo fmt fix --- src/ast/mod.rs | 5 ++--- tests/sqlparser_common.rs | 6 +++++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a5264640d..3a448312b 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -92,9 +92,8 @@ pub use self::query::{ TableIndexType, TableSample, TableSampleBucket, TableSampleKind, TableSampleMethod, TableSampleModifier, TableSampleQuantity, TableSampleSeed, TableSampleSeedModifier, TableSampleUnit, TableVersion, TableWithJoins, Top, TopQuantity, UpdateTableFromKind, - ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill, - XmlNamespaceDefinition, XmlPassingArgument, XmlPassingClause, XmlTableColumn, - XmlTableColumnOption, + ValueTableMode, Values, WildcardAdditionalOptions, With, WithFill, XmlNamespaceDefinition, + XmlPassingArgument, XmlPassingClause, XmlTableColumn, XmlTableColumnOption, }; pub use self::trigger::{ diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index cbbbccd3d..5e682119f 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -158,7 +158,11 @@ fn parse_insert_values() { assert_eq!(column, &Ident::new(expected_columns[index].clone())); } match *source.body { - SetExpr::Values(Values { rows, value_keyword, .. }) => { + SetExpr::Values(Values { + rows, + value_keyword, + .. + }) => { assert_eq!(rows.as_slice(), expected_rows); assert!(value_keyword == expected_value_keyword); }