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]