Skip to content

Commit a281171

Browse files
finchxxiaiffyio
andauthored
Databricks: Add support for UPDATE SET * and INSERT * in MERGE statements (#2325)
Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
1 parent 3dd0e30 commit a281171

6 files changed

Lines changed: 156 additions & 49 deletions

File tree

src/ast/dml.rs

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,14 @@ pub enum MergeInsertKind {
649649
/// ```
650650
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
651651
Row,
652+
/// The insert expression uses the `*` wildcard to insert all columns.
653+
///
654+
/// Example:
655+
/// ```sql
656+
/// INSERT *
657+
/// ```
658+
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/delta-merge-into.html)
659+
Wildcard,
652660
}
653661

654662
impl Display for MergeInsertKind {
@@ -660,6 +668,9 @@ impl Display for MergeInsertKind {
660668
MergeInsertKind::Row => {
661669
write!(f, "ROW")
662670
}
671+
MergeInsertKind::Wildcard => {
672+
write!(f, "*")
673+
}
663674
}
664675
}
665676
}
@@ -710,33 +721,70 @@ impl Display for MergeInsertExpr {
710721
}
711722
}
712723

724+
/// The kind of update used within a `MERGE` statement.
725+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
726+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
727+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
728+
pub enum MergeUpdateKind {
729+
/// Standard update with explicit assignments.
730+
///
731+
/// Example:
732+
/// ```sql
733+
/// UPDATE SET quantity = source.quantity, name = source.name
734+
/// ```
735+
Set(Vec<Assignment>),
736+
/// The `*` wildcard to update all columns from the source.
737+
///
738+
/// Example:
739+
/// ```sql
740+
/// UPDATE SET *
741+
/// ```
742+
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/delta-merge-into.html)
743+
Wildcard,
744+
}
745+
746+
impl Display for MergeUpdateKind {
747+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
748+
match self {
749+
MergeUpdateKind::Set(assignments) => {
750+
write!(f, "SET {}", display_comma_separated(assignments))
751+
}
752+
MergeUpdateKind::Wildcard => {
753+
write!(f, "SET *")
754+
}
755+
}
756+
}
757+
}
758+
713759
/// The expression used to update rows within a `MERGE` statement.
714760
///
715761
/// Examples
716762
/// ```sql
717763
/// UPDATE SET quantity = T.quantity + S.quantity
764+
/// UPDATE SET *
718765
/// ```
719766
///
720767
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
721768
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
722769
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
770+
/// [Databricks](https://docs.databricks.com/en/sql/language-manual/delta-merge-into.html)
723771
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
724772
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
725773
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
726774
pub struct MergeUpdateExpr {
727775
/// The `UPDATE` token that starts the sub-expression.
728776
pub update_token: AttachedToken,
729-
/// The update assiment expressions
730-
pub assignments: Vec<Assignment>,
731-
/// `where_clause` for the update (Oralce specific)
777+
/// The kind of update: explicit assignments or `*` shorthand.
778+
pub kind: MergeUpdateKind,
779+
/// `where_clause` for the update (Oracle specific)
732780
pub update_predicate: Option<Expr>,
733781
/// `delete_clause` for the update "delete where" (Oracle specific)
734782
pub delete_predicate: Option<Expr>,
735783
}
736784

737785
impl Display for MergeUpdateExpr {
738786
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
739-
write!(f, "SET {}", display_comma_separated(&self.assignments))?;
787+
write!(f, "{}", self.kind)?;
740788
if let Some(predicate) = self.update_predicate.as_ref() {
741789
write!(f, " WHERE {predicate}")?;
742790
}

src/ast/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ pub use self::ddl::{
8585
};
8686
pub use self::dml::{
8787
Delete, Insert, Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr,
88-
MergeInsertKind, MergeUpdateExpr, MultiTableInsertIntoClause, MultiTableInsertType,
89-
MultiTableInsertValue, MultiTableInsertValues, MultiTableInsertWhenClause, OutputClause,
90-
Update,
88+
MergeInsertKind, MergeUpdateExpr, MergeUpdateKind, MultiTableInsertIntoClause,
89+
MultiTableInsertType, MultiTableInsertValue, MultiTableInsertValues,
90+
MultiTableInsertWhenClause, OutputClause, Update,
9191
};
9292
pub use self::operator::{BinaryOperator, UnaryOperator};
9393
pub use self::query::{

src/ast/spans.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,15 @@ use super::{
3939
IfStatement, IlikeSelectItem, IndexColumn, Insert, Interpolate, InterpolateExpr, Join,
4040
JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause,
4141
MatchRecognizePattern, Measure, Merge, MergeAction, MergeClause, MergeInsertExpr,
42-
MergeInsertKind, MergeUpdateExpr, NamedParenthesizedList, NamedWindowDefinition, ObjectName,
43-
ObjectNamePart, Offset, OnConflict, OnConflictAction, OnInsert, OpenStatement, OrderBy,
44-
OrderByExpr, OrderByKind, OutputClause, Parens, Partition, PartitionBoundValue,
45-
PivotValueSource, ProjectionSelect, Query, RaiseStatement, RaiseStatementValue,
46-
ReferentialAction, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select,
47-
SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias,
48-
TableAliasColumnDef, TableConstraint, TableFactor, TableObject, TableOptionsClustered,
49-
TableWithJoins, Update, UpdateTableFromKind, Use, Values, ViewColumnDef, WhileStatement,
50-
WildcardAdditionalOptions, With, WithFill,
42+
MergeInsertKind, MergeUpdateExpr, MergeUpdateKind, NamedParenthesizedList,
43+
NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction,
44+
OnInsert, OpenStatement, OrderBy, OrderByExpr, OrderByKind, OutputClause, Parens, Partition,
45+
PartitionBoundValue, PivotValueSource, ProjectionSelect, Query, RaiseStatement,
46+
RaiseStatementValue, ReferentialAction, RenameSelectItem, ReplaceSelectElement,
47+
ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript,
48+
SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint, TableFactor, TableObject,
49+
TableOptionsClustered, TableWithJoins, Update, UpdateTableFromKind, Use, Values, ViewColumnDef,
50+
WhileStatement, WildcardAdditionalOptions, With, WithFill,
5151
};
5252

5353
/// Given an iterator of spans, return the [Span::union] of all spans.
@@ -2534,7 +2534,7 @@ impl Spanned for MergeInsertExpr {
25342534
self.kind_token.0.span,
25352535
match self.kind {
25362536
MergeInsertKind::Values(ref values) => values.span(),
2537-
MergeInsertKind::Row => Span::empty(), // ~ covered by `kind_token`
2537+
MergeInsertKind::Row | MergeInsertKind::Wildcard => Span::empty(),
25382538
},
25392539
]
25402540
.into_iter()
@@ -2546,9 +2546,13 @@ impl Spanned for MergeInsertExpr {
25462546

25472547
impl Spanned for MergeUpdateExpr {
25482548
fn span(&self) -> Span {
2549+
let kind_span = match &self.kind {
2550+
MergeUpdateKind::Set(assignments) => union_spans(assignments.iter().map(Spanned::span)),
2551+
MergeUpdateKind::Wildcard => Span::empty(),
2552+
};
25492553
union_spans(
25502554
core::iter::once(self.update_token.0.span)
2551-
.chain(self.assignments.iter().map(Spanned::span))
2555+
.chain(core::iter::once(kind_span))
25522556
.chain(self.update_predicate.iter().map(Spanned::span))
25532557
.chain(self.delete_predicate.iter().map(Spanned::span)),
25542558
)
@@ -2930,7 +2934,7 @@ WHERE id = 1
29302934
);
29312935
if let MergeAction::Update(MergeUpdateExpr {
29322936
update_token,
2933-
assignments: _,
2937+
kind: _,
29342938
update_predicate: _,
29352939
delete_predicate: _,
29362940
}) = &clauses[1].action

src/parser/merge.rs

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,12 @@ use alloc::{boxed::Box, format, vec, vec::Vec};
1818
use crate::{
1919
ast::{
2020
Merge, MergeAction, MergeClause, MergeClauseKind, MergeInsertExpr, MergeInsertKind,
21-
MergeUpdateExpr, ObjectName, OutputClause, SetExpr,
21+
MergeUpdateExpr, MergeUpdateKind, ObjectName, OutputClause, SetExpr,
2222
},
2323
dialect::{BigQueryDialect, GenericDialect, MySqlDialect},
2424
keywords::Keyword,
2525
parser::IsOptional,
26+
tokenizer::Token,
2627
tokenizer::TokenWithSpan,
2728
};
2829

@@ -120,7 +121,11 @@ impl Parser<'_> {
120121

121122
let update_token = self.get_current_token().clone();
122123
self.expect_keyword_is(Keyword::SET)?;
123-
let assignments = self.parse_comma_separated(Parser::parse_assignment)?;
124+
let kind = if self.consume_token(&Token::Mul) {
125+
MergeUpdateKind::Wildcard
126+
} else {
127+
MergeUpdateKind::Set(self.parse_comma_separated(Parser::parse_assignment)?)
128+
};
124129
let update_predicate = if self.parse_keyword(Keyword::WHERE) {
125130
Some(self.parse_expr()?)
126131
} else {
@@ -134,7 +139,7 @@ impl Parser<'_> {
134139
};
135140
MergeAction::Update(MergeUpdateExpr {
136141
update_token: update_token.into(),
137-
assignments,
142+
kind,
138143
update_predicate,
139144
delete_predicate,
140145
})
@@ -167,32 +172,43 @@ impl Parser<'_> {
167172
};
168173

169174
let insert_token = self.get_current_token().clone();
170-
let is_mysql = dialect_of!(self is MySqlDialect);
171175

172-
let columns = self.parse_merge_clause_insert_columns(is_mysql)?;
173-
let (kind, kind_token) = if dialect_of!(self is BigQueryDialect | GenericDialect)
174-
&& self.parse_keyword(Keyword::ROW)
175-
{
176-
(MergeInsertKind::Row, self.get_current_token().clone())
177-
} else {
178-
self.expect_keyword_is(Keyword::VALUES)?;
179-
let values_token = self.get_current_token().clone();
180-
let values = self.parse_values(is_mysql, false)?;
181-
(MergeInsertKind::Values(values), values_token)
182-
};
183-
let insert_predicate = if self.parse_keyword(Keyword::WHERE) {
184-
Some(self.parse_expr()?)
176+
if self.consume_token(&Token::Mul) {
177+
let star_token = self.get_current_token().clone();
178+
MergeAction::Insert(MergeInsertExpr {
179+
insert_token: insert_token.into(),
180+
columns: vec![],
181+
kind_token: star_token.into(),
182+
kind: MergeInsertKind::Wildcard,
183+
insert_predicate: None,
184+
})
185185
} else {
186-
None
187-
};
186+
let is_mysql = dialect_of!(self is MySqlDialect);
187+
let columns = self.parse_merge_clause_insert_columns(is_mysql)?;
188+
let (kind, kind_token) = if dialect_of!(self is BigQueryDialect | GenericDialect)
189+
&& self.parse_keyword(Keyword::ROW)
190+
{
191+
(MergeInsertKind::Row, self.get_current_token().clone())
192+
} else {
193+
self.expect_keyword_is(Keyword::VALUES)?;
194+
let values_token = self.get_current_token().clone();
195+
let values = self.parse_values(is_mysql, false)?;
196+
(MergeInsertKind::Values(values), values_token)
197+
};
198+
let insert_predicate = if self.parse_keyword(Keyword::WHERE) {
199+
Some(self.parse_expr()?)
200+
} else {
201+
None
202+
};
188203

189-
MergeAction::Insert(MergeInsertExpr {
190-
insert_token: insert_token.into(),
191-
columns,
192-
kind_token: kind_token.into(),
193-
kind,
194-
insert_predicate,
195-
})
204+
MergeAction::Insert(MergeInsertExpr {
205+
insert_token: insert_token.into(),
206+
columns,
207+
kind_token: kind_token.into(),
208+
kind,
209+
insert_predicate,
210+
})
211+
}
196212
}
197213
_ => {
198214
return parser_err!(

tests/sqlparser_bigquery.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,7 +1830,7 @@ fn parse_merge() {
18301830
});
18311831
let update_action = MergeAction::Update(MergeUpdateExpr {
18321832
update_token: AttachedToken::empty(),
1833-
assignments: vec![
1833+
kind: MergeUpdateKind::Set(vec![
18341834
Assignment {
18351835
target: AssignmentTarget::ColumnName(ObjectName::from(vec![Ident::new("a")])),
18361836
value: Expr::value(number("1")),
@@ -1839,7 +1839,7 @@ fn parse_merge() {
18391839
target: AssignmentTarget::ColumnName(ObjectName::from(vec![Ident::new("b")])),
18401840
value: Expr::value(number("2")),
18411841
},
1842-
],
1842+
]),
18431843
update_predicate: None,
18441844
delete_predicate: None,
18451845
});

tests/sqlparser_common.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10157,7 +10157,7 @@ fn parse_merge() {
1015710157
}),
1015810158
action: MergeAction::Update(MergeUpdateExpr {
1015910159
update_token: AttachedToken::empty(),
10160-
assignments: vec![
10160+
kind: MergeUpdateKind::Set(vec![
1016110161
Assignment {
1016210162
target: AssignmentTarget::ColumnName(ObjectName::from(vec![
1016310163
Ident::new("dest"),
@@ -10178,7 +10178,7 @@ fn parse_merge() {
1017810178
Ident::new("G"),
1017910179
]),
1018010180
},
10181-
],
10181+
]),
1018210182
update_predicate: None,
1018310183
delete_predicate: None,
1018410184
}),
@@ -10239,6 +10239,45 @@ WHEN NOT MATCHED THEN \
1023910239
INSERT (PLAYGROUND.FOO.ID, PLAYGROUND.FOO.NAME) \
1024010240
VALUES (1, 'abc')";
1024110241
all_dialects().verified_stmt(sql);
10242+
10243+
// MERGE with wildcard (UPDATE SET * and INSERT *)
10244+
let sql = "MERGE INTO target USING source ON target.id = source.id WHEN MATCHED THEN UPDATE SET * WHEN NOT MATCHED THEN INSERT *";
10245+
match verified_stmt(sql) {
10246+
Statement::Merge(merge) => {
10247+
assert_eq!(merge.clauses.len(), 2);
10248+
10249+
match &merge.clauses[0].action {
10250+
MergeAction::Update(update_expr) => {
10251+
assert!(matches!(update_expr.kind, MergeUpdateKind::Wildcard));
10252+
}
10253+
_ => panic!("Expected UPDATE action"),
10254+
}
10255+
10256+
match &merge.clauses[1].action {
10257+
MergeAction::Insert(insert_expr) => {
10258+
assert!(matches!(insert_expr.kind, MergeInsertKind::Wildcard));
10259+
assert!(insert_expr.columns.is_empty());
10260+
}
10261+
_ => panic!("Expected INSERT action"),
10262+
}
10263+
}
10264+
_ => panic!("Expected MERGE statement"),
10265+
}
10266+
10267+
verified_stmt("MERGE INTO target USING source ON target.id = source.id WHEN MATCHED AND source.active = 1 THEN UPDATE SET *");
10268+
10269+
verified_stmt("MERGE INTO target USING source ON target.id = source.id WHEN NOT MATCHED BY TARGET THEN INSERT *");
10270+
10271+
verified_stmt("MERGE INTO target USING source ON target.id = source.id WHEN MATCHED THEN UPDATE SET * WHEN NOT MATCHED THEN INSERT (a, b) VALUES (source.a, source.b)");
10272+
10273+
let sql = concat!(
10274+
"MERGE INTO t1 AS target ",
10275+
"USING (SELECT * FROM t2) AS source ",
10276+
"ON target.id = source.id ",
10277+
"WHEN MATCHED THEN UPDATE SET * ",
10278+
"WHEN NOT MATCHED THEN INSERT *"
10279+
);
10280+
verified_stmt(sql);
1024210281
}
1024310282

1024410283
#[test]

0 commit comments

Comments
 (0)