Skip to content

Commit b2ba29b

Browse files
yoavcloudayman-sigma
authored andcommitted
Add support for MySQL's INSERT INTO ... SET syntax (apache#1641)
1 parent a7e57ad commit b2ba29b

File tree

7 files changed

+43
-20
lines changed

7 files changed

+43
-20
lines changed

src/ast/dml.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ use sqlparser_derive::{Visit, VisitMut};
3232
pub use super::ddl::{ColumnDef, TableConstraint};
3333

3434
use super::{
35-
display_comma_separated, display_separated, ClusteredBy, CommentDef, Expr, FileFormat,
36-
FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident,
35+
display_comma_separated, display_separated, Assignment, ClusteredBy, CommentDef, Expr,
36+
FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident,
3737
InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert, OneOrManyWithParens,
3838
OrderByExpr, Query, RowAccessPolicy, SelectItem, SqlOption, SqliteOnConflict, TableEngine,
3939
TableWithJoins, Tag, WrappedCollection,
@@ -480,6 +480,9 @@ pub struct Insert {
480480
pub overwrite: bool,
481481
/// A SQL query that specifies what to insert
482482
pub source: Option<Box<Query>>,
483+
/// MySQL `INSERT INTO ... SET`
484+
/// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
485+
pub assignments: Vec<Assignment>,
483486
/// partitioned insert (Hive)
484487
pub partitioned: Option<Vec<Expr>>,
485488
/// Columns defined after PARTITION
@@ -545,9 +548,10 @@ impl Display for Insert {
545548

546549
if let Some(source) = &self.source {
547550
write!(f, "{source}")?;
548-
}
549-
550-
if self.source.is_none() && self.columns.is_empty() {
551+
} else if !self.assignments.is_empty() {
552+
write!(f, "SET ")?;
553+
write!(f, "{}", display_comma_separated(&self.assignments))?;
554+
} else if self.source.is_none() && self.columns.is_empty() {
551555
write!(f, "DEFAULT VALUES")?;
552556
}
553557

src/ast/spans.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,13 +1153,15 @@ impl Spanned for Insert {
11531153
replace_into: _, // bool
11541154
priority: _, // todo, mysql specific
11551155
insert_alias: _, // todo, mysql specific
1156+
assignments,
11561157
} = self;
11571158

11581159
union_spans(
11591160
core::iter::once(table_name.span())
11601161
.chain(table_alias.as_ref().map(|i| i.span))
11611162
.chain(columns.iter().map(|i| i.span))
11621163
.chain(source.as_ref().map(|q| q.span()))
1164+
.chain(assignments.iter().map(|i| i.span()))
11631165
.chain(partitioned.iter().flat_map(|i| i.iter().map(|k| k.span())))
11641166
.chain(after_columns.iter().map(|i| i.span))
11651167
.chain(on.as_ref().map(|i| i.span()))

src/dialect/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,13 @@ pub trait Dialect: Debug + Any {
775775
fn supports_table_sample_before_alias(&self) -> bool {
776776
false
777777
}
778+
779+
/// Returns true if this dialect supports the `INSERT INTO ... SET col1 = 1, ...` syntax.
780+
///
781+
/// MySQL: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
782+
fn supports_insert_set(&self) -> bool {
783+
false
784+
}
778785
}
779786

780787
/// This represents the operators for which precedence must be defined

src/dialect/mysql.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,15 @@ impl Dialect for MySqlDialect {
9898
true
9999
}
100100

101-
/// see <https://dev.mysql.com/doc/refman/8.4/en/create-table-select.html>
101+
/// See: <https://dev.mysql.com/doc/refman/8.4/en/create-table-select.html>
102102
fn supports_create_table_select(&self) -> bool {
103103
true
104104
}
105+
106+
/// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
107+
fn supports_insert_set(&self) -> bool {
108+
true
109+
}
105110
}
106111

107112
/// `LOCK TABLES`

src/parser/mod.rs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11909,9 +11909,9 @@ impl<'a> Parser<'a> {
1190911909

1191011910
let is_mysql = dialect_of!(self is MySqlDialect);
1191111911

11912-
let (columns, partitioned, after_columns, source) =
11912+
let (columns, partitioned, after_columns, source, assignments) =
1191311913
if self.parse_keywords(&[Keyword::DEFAULT, Keyword::VALUES]) {
11914-
(vec![], None, vec![], None)
11914+
(vec![], None, vec![], None, vec![])
1191511915
} else {
1191611916
let (columns, partitioned, after_columns) = if !self.peek_subquery_start() {
1191711917
let columns = self.parse_parenthesized_column_list(Optional, is_mysql)?;
@@ -11928,9 +11928,14 @@ impl<'a> Parser<'a> {
1192811928
Default::default()
1192911929
};
1193011930

11931-
let source = Some(self.parse_query()?);
11931+
let (source, assignments) =
11932+
if self.dialect.supports_insert_set() && self.parse_keyword(Keyword::SET) {
11933+
(None, self.parse_comma_separated(Parser::parse_assignment)?)
11934+
} else {
11935+
(Some(self.parse_query()?), vec![])
11936+
};
1193211937

11933-
(columns, partitioned, after_columns, source)
11938+
(columns, partitioned, after_columns, source, assignments)
1193411939
};
1193511940

1193611941
let insert_alias = if dialect_of!(self is MySqlDialect | GenericDialect)
@@ -12010,6 +12015,7 @@ impl<'a> Parser<'a> {
1201012015
columns,
1201112016
after_columns,
1201212017
source,
12018+
assignments,
1201312019
table,
1201412020
on,
1201512021
returning,
@@ -14238,16 +14244,6 @@ mod tests {
1423814244
assert!(Parser::parse_sql(&GenericDialect {}, sql).is_err());
1423914245
}
1424014246

14241-
#[test]
14242-
fn test_replace_into_set() {
14243-
// NOTE: This is actually valid MySQL syntax, REPLACE and INSERT,
14244-
// but the parser does not yet support it.
14245-
// https://dev.mysql.com/doc/refman/8.3/en/insert.html
14246-
let sql = "REPLACE INTO t SET a='1'";
14247-
14248-
assert!(Parser::parse_sql(&MySqlDialect {}, sql).is_err());
14249-
}
14250-
1425114247
#[test]
1425214248
fn test_replace_into_set_placeholder() {
1425314249
let sql = "REPLACE INTO t SET ?";

tests/sqlparser_common.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ fn parse_insert_values() {
119119
verified_stmt("INSERT INTO customer WITH foo AS (SELECT 1) SELECT * FROM foo UNION VALUES (1)");
120120
}
121121

122+
#[test]
123+
fn parse_insert_set() {
124+
let dialects = all_dialects_where(|d| d.supports_insert_set());
125+
dialects.verified_stmt("INSERT INTO tbl1 SET col1 = 1, col2 = 'abc', col3 = current_date()");
126+
}
127+
122128
#[test]
123129
fn parse_replace_into() {
124130
let dialect = PostgreSqlDialect {};

tests/sqlparser_postgres.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4423,6 +4423,7 @@ fn test_simple_postgres_insert_with_alias() {
44234423
settings: None,
44244424
format_clause: None,
44254425
})),
4426+
assignments: vec![],
44264427
partitioned: None,
44274428
after_columns: vec![],
44284429
table: false,
@@ -4493,6 +4494,7 @@ fn test_simple_postgres_insert_with_alias() {
44934494
settings: None,
44944495
format_clause: None,
44954496
})),
4497+
assignments: vec![],
44964498
partitioned: None,
44974499
after_columns: vec![],
44984500
table: false,
@@ -4559,6 +4561,7 @@ fn test_simple_insert_with_quoted_alias() {
45594561
settings: None,
45604562
format_clause: None,
45614563
})),
4564+
assignments: vec![],
45624565
partitioned: None,
45634566
after_columns: vec![],
45644567
table: false,

0 commit comments

Comments
 (0)