Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
340 changes: 337 additions & 3 deletions src/ast/dml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,16 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};

use crate::display_utils::{indented_list, Indent, SpaceOrNewline};
use crate::{
ast::display_separated,
display_utils::{indented_list, Indent, SpaceOrNewline},
};

use super::{
display_comma_separated, helpers::attached_token::AttachedToken, query::InputFormatClause,
Assignment, Expr, FromTable, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnInsert,
OrderByExpr, Query, SelectItem, Setting, SqliteOnConflict, TableObject, TableWithJoins,
UpdateTableFromKind,
OrderByExpr, Query, SelectInto, SelectItem, Setting, SqliteOnConflict, TableFactor,
TableObject, TableWithJoins, UpdateTableFromKind, Values,
};

/// INSERT statement.
Expand Down Expand Up @@ -310,3 +313,334 @@ impl Display for Update {
Ok(())
}
}

/// A `MERGE` statement.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Merge {
/// The `MERGE` token that starts the statement.
pub merge_token: AttachedToken,
/// optional INTO keyword
pub into: bool,
/// Specifies the table to merge
pub table: TableFactor,
/// Specifies the table or subquery to join with the target table
pub source: TableFactor,
/// Specifies the expression on which to join the target table and source
pub on: Box<Expr>,
/// Specifies the actions to perform when values match or do not match.
pub clauses: Vec<MergeClause>,
// Specifies the output to save changes in MSSQL
pub output: Option<OutputClause>,
}

impl Display for Merge {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"MERGE{int} {table} USING {source} ",
int = if self.into { " INTO" } else { "" },
table = self.table,
source = self.source,
)?;
write!(f, "ON {on} ", on = self.on)?;
write!(f, "{}", display_separated(&self.clauses, " "))?;
if let Some(ref output) = self.output {
write!(f, " {output}")?;
}
Ok(())
}
}

/// A `WHEN` clause within a `MERGE` Statement
///
/// Example:
/// ```sql
/// WHEN NOT MATCHED BY SOURCE AND product LIKE '%washer%' THEN DELETE
/// ```
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MergeClause {
/// The `WHEN` token that starts the sub-expression.
pub when_token: AttachedToken,
pub clause_kind: MergeClauseKind,
pub predicate: Option<Expr>,
pub action: MergeAction,
}

impl Display for MergeClause {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let MergeClause {
when_token: _,
clause_kind,
predicate,
action,
} = self;

write!(f, "WHEN {clause_kind}")?;
if let Some(pred) = predicate {
write!(f, " AND {pred}")?;
}
write!(f, " THEN {action}")
}
}

/// Variant of `WHEN` clause used within a `MERGE` Statement.
///
/// Example:
/// ```sql
/// MERGE INTO T USING U ON FALSE WHEN MATCHED THEN DELETE
/// ```
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MergeClauseKind {
/// `WHEN MATCHED`
Matched,
/// `WHEN NOT MATCHED`
NotMatched,
/// `WHEN MATCHED BY TARGET`
///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
NotMatchedByTarget,
/// `WHEN MATCHED BY SOURCE`
///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
NotMatchedBySource,
}

impl Display for MergeClauseKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MergeClauseKind::Matched => write!(f, "MATCHED"),
MergeClauseKind::NotMatched => write!(f, "NOT MATCHED"),
MergeClauseKind::NotMatchedByTarget => write!(f, "NOT MATCHED BY TARGET"),
MergeClauseKind::NotMatchedBySource => write!(f, "NOT MATCHED BY SOURCE"),
}
}
}

/// Underlying statement of a `WHEN` clause within a `MERGE` Statement
///
/// Example
/// ```sql
/// INSERT (product, quantity) VALUES(product, quantity)
/// ```
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MergeAction {
/// An `INSERT` clause
///
/// Example:
/// ```sql
/// INSERT (product, quantity) VALUES(product, quantity)
/// ```
Insert(MergeInsertExpr),
/// An `UPDATE` clause
///
/// Example:
/// ```sql
/// UPDATE SET quantity = T.quantity + S.quantity
/// ```
Update(MergeUpdateExpr),
/// A plain `DELETE` clause
Delete {
/// The `DELETE` token that starts the sub-expression.
delete_token: AttachedToken,
},
}

impl Display for MergeAction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MergeAction::Insert(insert) => {
write!(f, "INSERT {insert}")
}
MergeAction::Update(update) => {
write!(f, "UPDATE {update}")
}
MergeAction::Delete { .. } => {
write!(f, "DELETE")
}
}
}
}

/// The type of expression used to insert rows within a `MERGE` statement.
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MergeInsertKind {
/// The insert expression is defined from an explicit `VALUES` clause
///
/// Example:
/// ```sql
/// INSERT VALUES(product, quantity)
/// ```
Values(Values),
/// The insert expression is defined using only the `ROW` keyword.
///
/// Example:
/// ```sql
/// INSERT ROW
/// ```
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
Row,
}

impl Display for MergeInsertKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MergeInsertKind::Values(values) => {
write!(f, "{values}")
}
MergeInsertKind::Row => {
write!(f, "ROW")
}
}
}
}

/// The expression used to insert rows within a `MERGE` statement.
///
/// Examples
/// ```sql
/// INSERT (product, quantity) VALUES(product, quantity)
/// INSERT ROW
/// ```
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MergeInsertExpr {
/// The `INSERT` token that starts the sub-expression.
pub insert_token: AttachedToken,
/// Columns (if any) specified by the insert.
///
/// Example:
/// ```sql
/// INSERT (product, quantity) VALUES(product, quantity)
/// INSERT (product, quantity) ROW
/// ```
pub columns: Vec<ObjectName>,
/// The token, `[VALUES | ROW]` starting `kind`.
pub kind_token: AttachedToken,
/// The insert type used by the statement.
pub kind: MergeInsertKind,
/// An optional condition to restrict the insertion (Oracle specific)
pub insert_predicate: Option<Expr>,
}

impl Display for MergeInsertExpr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if !self.columns.is_empty() {
write!(f, "({}) ", display_comma_separated(self.columns.as_slice()))?;
}
write!(f, "{}", self.kind)?;
if let Some(predicate) = self.insert_predicate.as_ref() {
write!(f, " WHERE {}", predicate)?;
}
Ok(())
}
}

/// The expression used to update rows within a `MERGE` statement.
///
/// Examples
/// ```sql
/// UPDATE SET quantity = T.quantity + S.quantity
/// ```
///
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MergeUpdateExpr {
/// The `UPDATE` token that starts the sub-expression.
pub update_token: AttachedToken,
/// The update assiment expressions
pub assignments: Vec<Assignment>,
/// `where_clause` for the update (Oralce specific)
pub update_predicate: Option<Expr>,
/// `delete_clause` for the update "delete where" (Oracle specific)
pub delete_predicate: Option<Expr>,
}

impl Display for MergeUpdateExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SET {}", display_comma_separated(&self.assignments))?;
if let Some(predicate) = self.update_predicate.as_ref() {
write!(f, " WHERE {predicate}")?;
}
if let Some(predicate) = self.delete_predicate.as_ref() {
write!(f, " DELETE WHERE {predicate}")?;
}
Ok(())
}
}

/// A `OUTPUT` Clause in the end of a `MERGE` Statement
///
/// Example:
/// OUTPUT $action, deleted.* INTO dbo.temp_products;
/// [mssql](https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum OutputClause {
Output {
output_token: AttachedToken,
select_items: Vec<SelectItem>,
into_table: Option<SelectInto>,
},
Returning {
returning_token: AttachedToken,
select_items: Vec<SelectItem>,
},
}

impl fmt::Display for OutputClause {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
OutputClause::Output {
output_token: _,
select_items,
into_table,
} => {
f.write_str("OUTPUT ")?;
display_comma_separated(select_items).fmt(f)?;
if let Some(into_table) = into_table {
f.write_str(" ")?;
into_table.fmt(f)?;
}
Ok(())
}
OutputClause::Returning {
returning_token: _,
select_items,
} => {
f.write_str("RETURNING ")?;
display_comma_separated(select_items).fmt(f)
}
}
}
}
Loading