Skip to content

Commit 0adf4c6

Browse files
authored
Support BigQuery MERGE syntax (apache#1217)
1 parent b51f2a0 commit 0adf4c6

5 files changed

Lines changed: 554 additions & 140 deletions

File tree

src/ast/mod.rs

Lines changed: 182 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -2549,20 +2549,23 @@ pub enum Statement {
25492549
/// RELEASE [ SAVEPOINT ] savepoint_name
25502550
/// ```
25512551
ReleaseSavepoint { name: Ident },
2552+
/// A `MERGE` statement.
2553+
///
25522554
/// ```sql
2553-
/// MERGE INTO <statement>
2555+
/// MERGE INTO <target_table> USING <source> ON <join_expr> { matchedClause | notMatchedClause } [ ... ]
25542556
/// ```
2555-
/// Based on Snowflake. See <https://docs.snowflake.com/en/sql-reference/sql/merge.html>
2557+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
2558+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
25562559
Merge {
2557-
// optional INTO keyword
2560+
/// optional INTO keyword
25582561
into: bool,
2559-
// Specifies the table to merge
2562+
/// Specifies the table to merge
25602563
table: TableFactor,
2561-
// Specifies the table or subquery to join with the target table
2564+
/// Specifies the table or subquery to join with the target table
25622565
source: TableFactor,
2563-
// Specifies the expression on which to join the target table and source
2566+
/// Specifies the expression on which to join the target table and source
25642567
on: Box<Expr>,
2565-
// Specifies the actions to perform when values match or do not match.
2568+
/// Specifies the actions to perform when values match or do not match.
25662569
clauses: Vec<MergeClause>,
25672570
},
25682571
/// ```sql
@@ -5549,75 +5552,196 @@ impl fmt::Display for CopyLegacyCsvOption {
55495552
}
55505553
}
55515554

5552-
/// `MERGE` Statement
5555+
/// Variant of `WHEN` clause used within a `MERGE` Statement.
55535556
///
5557+
/// Example:
55545558
/// ```sql
5555-
/// MERGE INTO <target_table> USING <source> ON <join_expr> { matchedClause | notMatchedClause } [ ... ]
5559+
/// MERGE INTO T USING U ON FALSE WHEN MATCHED THEN DELETE
55565560
/// ```
5561+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
5562+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
5563+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
5564+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5565+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
5566+
pub enum MergeClauseKind {
5567+
/// `WHEN MATCHED`
5568+
Matched,
5569+
/// `WHEN NOT MATCHED`
5570+
NotMatched,
5571+
/// `WHEN MATCHED BY TARGET`
5572+
///
5573+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
5574+
NotMatchedByTarget,
5575+
/// `WHEN MATCHED BY SOURCE`
5576+
///
5577+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
5578+
NotMatchedBySource,
5579+
}
5580+
5581+
impl Display for MergeClauseKind {
5582+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5583+
match self {
5584+
MergeClauseKind::Matched => write!(f, "MATCHED"),
5585+
MergeClauseKind::NotMatched => write!(f, "NOT MATCHED"),
5586+
MergeClauseKind::NotMatchedByTarget => write!(f, "NOT MATCHED BY TARGET"),
5587+
MergeClauseKind::NotMatchedBySource => write!(f, "NOT MATCHED BY SOURCE"),
5588+
}
5589+
}
5590+
}
5591+
5592+
/// The type of expression used to insert rows within a `MERGE` statement.
55575593
///
5558-
/// See [Snowflake documentation](https://docs.snowflake.com/en/sql-reference/sql/merge)
5594+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
5595+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
55595596
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
55605597
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
55615598
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
5562-
pub enum MergeClause {
5563-
MatchedUpdate {
5564-
predicate: Option<Expr>,
5565-
assignments: Vec<Assignment>,
5566-
},
5567-
MatchedDelete(Option<Expr>),
5568-
NotMatched {
5569-
predicate: Option<Expr>,
5570-
columns: Vec<Ident>,
5571-
values: Values,
5572-
},
5599+
pub enum MergeInsertKind {
5600+
/// The insert expression is defined from an explicit `VALUES` clause
5601+
///
5602+
/// Example:
5603+
/// ```sql
5604+
/// INSERT VALUES(product, quantity)
5605+
/// ```
5606+
Values(Values),
5607+
/// The insert expression is defined using only the `ROW` keyword.
5608+
///
5609+
/// Example:
5610+
/// ```sql
5611+
/// INSERT ROW
5612+
/// ```
5613+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
5614+
Row,
55735615
}
55745616

5575-
impl fmt::Display for MergeClause {
5617+
impl Display for MergeInsertKind {
55765618
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5577-
use MergeClause::*;
5578-
write!(f, "WHEN")?;
55795619
match self {
5580-
MatchedUpdate {
5581-
predicate,
5582-
assignments,
5583-
} => {
5584-
write!(f, " MATCHED")?;
5585-
if let Some(pred) = predicate {
5586-
write!(f, " AND {pred}")?;
5587-
}
5588-
write!(
5589-
f,
5590-
" THEN UPDATE SET {}",
5591-
display_comma_separated(assignments)
5592-
)
5620+
MergeInsertKind::Values(values) => {
5621+
write!(f, "{values}")
55935622
}
5594-
MatchedDelete(predicate) => {
5595-
write!(f, " MATCHED")?;
5596-
if let Some(pred) = predicate {
5597-
write!(f, " AND {pred}")?;
5598-
}
5599-
write!(f, " THEN DELETE")
5623+
MergeInsertKind::Row => {
5624+
write!(f, "ROW")
56005625
}
5601-
NotMatched {
5602-
predicate,
5603-
columns,
5604-
values,
5605-
} => {
5606-
write!(f, " NOT MATCHED")?;
5607-
if let Some(pred) = predicate {
5608-
write!(f, " AND {pred}")?;
5609-
}
5610-
write!(
5611-
f,
5612-
" THEN INSERT ({}) {}",
5613-
display_comma_separated(columns),
5614-
values
5615-
)
5626+
}
5627+
}
5628+
}
5629+
5630+
/// The expression used to insert rows within a `MERGE` statement.
5631+
///
5632+
/// Examples
5633+
/// ```sql
5634+
/// INSERT (product, quantity) VALUES(product, quantity)
5635+
/// INSERT ROW
5636+
/// ```
5637+
///
5638+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
5639+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
5640+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
5641+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5642+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
5643+
pub struct MergeInsertExpr {
5644+
/// Columns (if any) specified by the insert.
5645+
///
5646+
/// Example:
5647+
/// ```sql
5648+
/// INSERT (product, quantity) VALUES(product, quantity)
5649+
/// INSERT (product, quantity) ROW
5650+
/// ```
5651+
pub columns: Vec<Ident>,
5652+
/// The insert type used by the statement.
5653+
pub kind: MergeInsertKind,
5654+
}
5655+
5656+
impl Display for MergeInsertExpr {
5657+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5658+
if !self.columns.is_empty() {
5659+
write!(f, "({}) ", display_comma_separated(self.columns.as_slice()))?;
5660+
}
5661+
write!(f, "{}", self.kind)
5662+
}
5663+
}
5664+
5665+
/// Underlying statement of a when clause within a `MERGE` Statement
5666+
///
5667+
/// Example
5668+
/// ```sql
5669+
/// INSERT (product, quantity) VALUES(product, quantity)
5670+
/// ```
5671+
///
5672+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
5673+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
5674+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
5675+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5676+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
5677+
pub enum MergeAction {
5678+
/// An `INSERT` clause
5679+
///
5680+
/// Example:
5681+
/// ```sql
5682+
/// INSERT (product, quantity) VALUES(product, quantity)
5683+
/// ```
5684+
Insert(MergeInsertExpr),
5685+
/// An `UPDATE` clause
5686+
///
5687+
/// Example:
5688+
/// ```sql
5689+
/// UPDATE SET quantity = T.quantity + S.quantity
5690+
/// ```
5691+
Update { assignments: Vec<Assignment> },
5692+
/// A plain `DELETE` clause
5693+
Delete,
5694+
}
5695+
5696+
impl Display for MergeAction {
5697+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5698+
match self {
5699+
MergeAction::Insert(insert) => {
5700+
write!(f, "INSERT {insert}")
5701+
}
5702+
MergeAction::Update { assignments } => {
5703+
write!(f, "UPDATE SET {}", display_comma_separated(assignments))
5704+
}
5705+
MergeAction::Delete => {
5706+
write!(f, "DELETE")
56165707
}
56175708
}
56185709
}
56195710
}
56205711

5712+
/// A when clause within a `MERGE` Statement
5713+
///
5714+
/// Example:
5715+
/// ```sql
5716+
/// WHEN NOT MATCHED BY SOURCE AND product LIKE '%washer%' THEN DELETE
5717+
/// ```
5718+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
5719+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
5720+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
5721+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
5722+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
5723+
pub struct MergeClause {
5724+
pub clause_kind: MergeClauseKind,
5725+
pub predicate: Option<Expr>,
5726+
pub action: MergeAction,
5727+
}
5728+
5729+
impl Display for MergeClause {
5730+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
5731+
let MergeClause {
5732+
clause_kind,
5733+
predicate,
5734+
action,
5735+
} = self;
5736+
5737+
write!(f, "WHEN {clause_kind}")?;
5738+
if let Some(pred) = predicate {
5739+
write!(f, " AND {pred}")?;
5740+
}
5741+
write!(f, " THEN {action}")
5742+
}
5743+
}
5744+
56215745
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
56225746
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
56235747
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]

src/keywords.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -637,6 +637,7 @@ define_keywords!(
637637
SNAPSHOT,
638638
SOME,
639639
SORT,
640+
SOURCE,
640641
SPATIAL,
641642
SPECIFIC,
642643
SPECIFICTYPE,
@@ -676,6 +677,7 @@ define_keywords!(
676677
TABLE,
677678
TABLES,
678679
TABLESAMPLE,
680+
TARGET,
679681
TBLPROPERTIES,
680682
TEMP,
681683
TEMPORARY,

0 commit comments

Comments
 (0)