This is an automated email from the ASF dual-hosted git repository.

iffyio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 8cfc4627 Add support for MySQL's INSERT INTO ... SET syntax (#1641)
8cfc4627 is described below

commit 8cfc46277fdfce4d7bd71efdce5cc3e669c34afc
Author: Yoav Cohen <[email protected]>
AuthorDate: Mon Jan 6 20:13:38 2025 +0100

    Add support for MySQL's INSERT INTO ... SET syntax (#1641)
---
 src/ast/dml.rs              | 14 +++++++++-----
 src/ast/spans.rs            |  2 ++
 src/dialect/mod.rs          |  7 +++++++
 src/dialect/mysql.rs        |  7 ++++++-
 src/parser/mod.rs           | 24 ++++++++++--------------
 tests/sqlparser_common.rs   |  6 ++++++
 tests/sqlparser_postgres.rs |  3 +++
 7 files changed, 43 insertions(+), 20 deletions(-)

diff --git a/src/ast/dml.rs b/src/ast/dml.rs
index 22309c8f..f64818e6 100644
--- a/src/ast/dml.rs
+++ b/src/ast/dml.rs
@@ -32,8 +32,8 @@ use sqlparser_derive::{Visit, VisitMut};
 pub use super::ddl::{ColumnDef, TableConstraint};
 
 use super::{
-    display_comma_separated, display_separated, ClusteredBy, CommentDef, Expr, 
FileFormat,
-    FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, 
Ident,
+    display_comma_separated, display_separated, Assignment, ClusteredBy, 
CommentDef, Expr,
+    FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat, 
HiveRowFormat, Ident,
     InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert, 
OneOrManyWithParens,
     OrderByExpr, Query, RowAccessPolicy, SelectItem, SqlOption, 
SqliteOnConflict, TableEngine,
     TableWithJoins, Tag, WrappedCollection,
@@ -480,6 +480,9 @@ pub struct Insert {
     pub overwrite: bool,
     /// A SQL query that specifies what to insert
     pub source: Option<Box<Query>>,
+    /// MySQL `INSERT INTO ... SET`
+    /// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
+    pub assignments: Vec<Assignment>,
     /// partitioned insert (Hive)
     pub partitioned: Option<Vec<Expr>>,
     /// Columns defined after PARTITION
@@ -545,9 +548,10 @@ impl Display for Insert {
 
         if let Some(source) = &self.source {
             write!(f, "{source}")?;
-        }
-
-        if self.source.is_none() && self.columns.is_empty() {
+        } else if !self.assignments.is_empty() {
+            write!(f, "SET ")?;
+            write!(f, "{}", display_comma_separated(&self.assignments))?;
+        } else if self.source.is_none() && self.columns.is_empty() {
             write!(f, "DEFAULT VALUES")?;
         }
 
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 1dd9118f..2ca65914 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -1153,6 +1153,7 @@ impl Spanned for Insert {
             replace_into: _, // bool
             priority: _,     // todo, mysql specific
             insert_alias: _, // todo, mysql specific
+            assignments,
         } = self;
 
         union_spans(
@@ -1160,6 +1161,7 @@ impl Spanned for Insert {
                 .chain(table_alias.as_ref().map(|i| i.span))
                 .chain(columns.iter().map(|i| i.span))
                 .chain(source.as_ref().map(|q| q.span()))
+                .chain(assignments.iter().map(|i| i.span()))
                 .chain(partitioned.iter().flat_map(|i| i.iter().map(|k| 
k.span())))
                 .chain(after_columns.iter().map(|i| i.span))
                 .chain(on.as_ref().map(|i| i.span()))
diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs
index 025b5b35..7b14f2db 100644
--- a/src/dialect/mod.rs
+++ b/src/dialect/mod.rs
@@ -775,6 +775,13 @@ pub trait Dialect: Debug + Any {
     fn supports_table_sample_before_alias(&self) -> bool {
         false
     }
+
+    /// Returns true if this dialect supports the `INSERT INTO ... SET col1 = 
1, ...` syntax.
+    ///
+    /// MySQL: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
+    fn supports_insert_set(&self) -> bool {
+        false
+    }
 }
 
 /// This represents the operators for which precedence must be defined
diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs
index 1ede59f5..3c3f2ee8 100644
--- a/src/dialect/mysql.rs
+++ b/src/dialect/mysql.rs
@@ -98,10 +98,15 @@ impl Dialect for MySqlDialect {
         true
     }
 
-    /// see <https://dev.mysql.com/doc/refman/8.4/en/create-table-select.html>
+    /// See: <https://dev.mysql.com/doc/refman/8.4/en/create-table-select.html>
     fn supports_create_table_select(&self) -> bool {
         true
     }
+
+    /// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
+    fn supports_insert_set(&self) -> bool {
+        true
+    }
 }
 
 /// `LOCK TABLES`
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 7776c66a..85ae6639 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -11899,9 +11899,9 @@ impl<'a> Parser<'a> {
 
             let is_mysql = dialect_of!(self is MySqlDialect);
 
-            let (columns, partitioned, after_columns, source) =
+            let (columns, partitioned, after_columns, source, assignments) =
                 if self.parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) {
-                    (vec![], None, vec![], None)
+                    (vec![], None, vec![], None, vec![])
                 } else {
                     let (columns, partitioned, after_columns) = if 
!self.peek_subquery_start() {
                         let columns = 
self.parse_parenthesized_column_list(Optional, is_mysql)?;
@@ -11918,9 +11918,14 @@ impl<'a> Parser<'a> {
                         Default::default()
                     };
 
-                    let source = Some(self.parse_query()?);
+                    let (source, assignments) =
+                        if self.dialect.supports_insert_set() && 
self.parse_keyword(Keyword::SET) {
+                            (None, 
self.parse_comma_separated(Parser::parse_assignment)?)
+                        } else {
+                            (Some(self.parse_query()?), vec![])
+                        };
 
-                    (columns, partitioned, after_columns, source)
+                    (columns, partitioned, after_columns, source, assignments)
                 };
 
             let insert_alias = if dialect_of!(self is MySqlDialect | 
GenericDialect)
@@ -12000,6 +12005,7 @@ impl<'a> Parser<'a> {
                 columns,
                 after_columns,
                 source,
+                assignments,
                 table,
                 on,
                 returning,
@@ -14228,16 +14234,6 @@ mod tests {
         assert!(Parser::parse_sql(&GenericDialect {}, sql).is_err());
     }
 
-    #[test]
-    fn test_replace_into_set() {
-        // NOTE: This is actually valid MySQL syntax, REPLACE and INSERT,
-        // but the parser does not yet support it.
-        // https://dev.mysql.com/doc/refman/8.3/en/insert.html
-        let sql = "REPLACE INTO t SET a='1'";
-
-        assert!(Parser::parse_sql(&MySqlDialect {}, sql).is_err());
-    }
-
     #[test]
     fn test_replace_into_set_placeholder() {
         let sql = "REPLACE INTO t SET ?";
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 791fa38c..7c8fd05a 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -119,6 +119,12 @@ fn parse_insert_values() {
     verified_stmt("INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM 
foo UNION VALUES (1)");
 }
 
+#[test]
+fn parse_insert_set() {
+    let dialects = all_dialects_where(|d| d.supports_insert_set());
+    dialects.verified_stmt("INSERT INTO tbl1 SET col1 = 1, col2 = 'abc', col3 
= current_date()");
+}
+
 #[test]
 fn parse_replace_into() {
     let dialect = PostgreSqlDialect {};
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index fd520d50..1a621ee7 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -4423,6 +4423,7 @@ fn test_simple_postgres_insert_with_alias() {
                 settings: None,
                 format_clause: None,
             })),
+            assignments: vec![],
             partitioned: None,
             after_columns: vec![],
             table: false,
@@ -4493,6 +4494,7 @@ fn test_simple_postgres_insert_with_alias() {
                 settings: None,
                 format_clause: None,
             })),
+            assignments: vec![],
             partitioned: None,
             after_columns: vec![],
             table: false,
@@ -4559,6 +4561,7 @@ fn test_simple_insert_with_quoted_alias() {
                 settings: None,
                 format_clause: None,
             })),
+            assignments: vec![],
             partitioned: None,
             after_columns: vec![],
             table: false,


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to