blob: 3e2f7ee09f996b7cbcd376027974fe8c02cd3616 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, format, string::ToString, vec::Vec};
use core::fmt::{self, Display};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};
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,
OptimizerHint, OrderByExpr, Query, SelectInto, SelectItem, Setting, SqliteOnConflict,
TableAliasWithoutColumns, TableFactor, TableObject, TableWithJoins, UpdateTableFromKind,
Values,
};
/// INSERT 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 Insert {
/// Token for the `INSERT` keyword (or its substitutes)
pub insert_token: AttachedToken,
/// Query optimizer hints
///
/// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
pub optimizer_hints: Vec<OptimizerHint>,
/// Only for Sqlite
pub or: Option<SqliteOnConflict>,
/// Only for mysql
pub ignore: bool,
/// INTO - optional keyword
pub into: bool,
/// TABLE
pub table: TableObject,
/// `table_name as foo` (for PostgreSQL)
/// `table_name foo` (for Oracle)
pub table_alias: Option<TableAliasWithoutColumns>,
/// COLUMNS
pub columns: Vec<ObjectName>,
/// Overwrite (Hive)
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
pub after_columns: Vec<Ident>,
/// whether the insert has the table keyword (Hive)
pub has_table_keyword: bool,
/// ON INSERT
pub on: Option<OnInsert>,
/// RETURNING
pub returning: Option<Vec<SelectItem>>,
/// OUTPUT (MSSQL)
/// See <https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql>
pub output: Option<OutputClause>,
/// Only for mysql
pub replace_into: bool,
/// Only for mysql
pub priority: Option<MysqlInsertPriority>,
/// Only for mysql
pub insert_alias: Option<InsertAliases>,
/// Settings used for ClickHouse.
///
/// ClickHouse syntax: `INSERT INTO tbl SETTINGS format_template_resultset = '/some/path/resultset.format'`
///
/// [ClickHouse `INSERT INTO`](https://clickhouse.com/docs/en/sql-reference/statements/insert-into)
pub settings: Option<Vec<Setting>>,
/// Format for `INSERT` statement when not using standard SQL format. Can be e.g. `CSV`,
/// `JSON`, `JSONAsString`, `LineAsString` and more.
///
/// ClickHouse syntax: `INSERT INTO tbl FORMAT JSONEachRow {"foo": 1, "bar": 2}, {"foo": 3}`
///
/// [ClickHouse formats JSON insert](https://clickhouse.com/docs/en/interfaces/formats#json-inserting-data)
pub format_clause: Option<InputFormatClause>,
/// For Snowflake multi-table insert: specifies the type (`ALL` or `FIRST`)
///
/// - `None` means this is a regular single-table INSERT
/// - `Some(All)` means `INSERT ALL` (all matching WHEN clauses are executed)
/// - `Some(First)` means `INSERT FIRST` (only the first matching WHEN clause is executed)
///
/// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
pub multi_table_insert_type: Option<MultiTableInsertType>,
/// For multi-table insert: additional INTO clauses (unconditional)
///
/// Used for `INSERT ALL INTO t1 INTO t2 ... SELECT ...`
///
/// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
pub multi_table_into_clauses: Vec<MultiTableInsertIntoClause>,
/// For conditional multi-table insert: WHEN clauses
///
/// Used for `INSERT ALL/FIRST WHEN cond THEN INTO t1 ... SELECT ...`
///
/// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
pub multi_table_when_clauses: Vec<MultiTableInsertWhenClause>,
/// For conditional multi-table insert: ELSE clause
///
/// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
pub multi_table_else_clause: Option<Vec<MultiTableInsertIntoClause>>,
}
impl Display for Insert {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// SQLite OR conflict has a special format: INSERT OR ... INTO table_name
let table_name = if let Some(table_alias) = &self.table_alias {
format!(
"{table} {as_keyword}{alias}",
table = self.table,
as_keyword = if table_alias.explicit { "AS " } else { "" },
alias = table_alias.alias
)
} else {
self.table.to_string()
};
if let Some(on_conflict) = self.or {
f.write_str("INSERT")?;
for hint in &self.optimizer_hints {
write!(f, " {hint}")?;
}
write!(f, " {on_conflict} INTO {table_name} ")?;
} else {
write!(
f,
"{start}",
start = if self.replace_into {
"REPLACE"
} else {
"INSERT"
}
)?;
for hint in &self.optimizer_hints {
write!(f, " {hint}")?;
}
if let Some(priority) = self.priority {
write!(f, " {priority}")?;
}
if self.ignore {
write!(f, " IGNORE")?;
}
if self.overwrite {
write!(f, " OVERWRITE")?;
}
if let Some(insert_type) = &self.multi_table_insert_type {
write!(f, " {}", insert_type)?;
}
if self.into {
write!(f, " INTO")?;
}
if self.has_table_keyword {
write!(f, " TABLE")?;
}
if !table_name.is_empty() {
write!(f, " {table_name} ")?;
}
}
if !self.columns.is_empty() {
write!(f, "({})", display_comma_separated(&self.columns))?;
SpaceOrNewline.fmt(f)?;
}
if let Some(ref parts) = self.partitioned {
if !parts.is_empty() {
write!(f, "PARTITION ({})", display_comma_separated(parts))?;
SpaceOrNewline.fmt(f)?;
}
}
if !self.after_columns.is_empty() {
write!(f, "({})", display_comma_separated(&self.after_columns))?;
SpaceOrNewline.fmt(f)?;
}
if let Some(output) = &self.output {
write!(f, "{output}")?;
SpaceOrNewline.fmt(f)?;
}
if let Some(settings) = &self.settings {
write!(f, "SETTINGS {}", display_comma_separated(settings))?;
SpaceOrNewline.fmt(f)?;
}
for into_clause in &self.multi_table_into_clauses {
SpaceOrNewline.fmt(f)?;
write!(f, "{}", into_clause)?;
}
for when_clause in &self.multi_table_when_clauses {
SpaceOrNewline.fmt(f)?;
write!(f, "{}", when_clause)?;
}
if let Some(else_clauses) = &self.multi_table_else_clause {
SpaceOrNewline.fmt(f)?;
write!(f, "ELSE")?;
for into_clause in else_clauses {
SpaceOrNewline.fmt(f)?;
write!(f, "{}", into_clause)?;
}
}
if let Some(source) = &self.source {
if !self.multi_table_into_clauses.is_empty()
|| !self.multi_table_when_clauses.is_empty()
{
SpaceOrNewline.fmt(f)?;
}
source.fmt(f)?;
} else if !self.assignments.is_empty() {
write!(f, "SET")?;
indented_list(f, &self.assignments)?;
} else if let Some(format_clause) = &self.format_clause {
format_clause.fmt(f)?;
} else if self.columns.is_empty() {
write!(f, "DEFAULT VALUES")?;
}
if let Some(insert_alias) = &self.insert_alias {
write!(f, " AS {0}", insert_alias.row_alias)?;
if let Some(col_aliases) = &insert_alias.col_aliases {
if !col_aliases.is_empty() {
write!(f, " ({})", display_comma_separated(col_aliases))?;
}
}
}
if let Some(on) = &self.on {
write!(f, "{on}")?;
}
if let Some(returning) = &self.returning {
SpaceOrNewline.fmt(f)?;
f.write_str("RETURNING")?;
indented_list(f, returning)?;
}
Ok(())
}
}
/// DELETE 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 Delete {
/// Token for the `DELETE` keyword
pub delete_token: AttachedToken,
/// Query optimizer hints
///
/// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
pub optimizer_hints: Vec<OptimizerHint>,
/// Multi tables delete are supported in mysql
pub tables: Vec<ObjectName>,
/// FROM
pub from: FromTable,
/// USING (Snowflake, Postgres, MySQL)
pub using: Option<Vec<TableWithJoins>>,
/// WHERE
pub selection: Option<Expr>,
/// RETURNING
pub returning: Option<Vec<SelectItem>>,
/// OUTPUT (MSSQL)
/// See <https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql>
pub output: Option<OutputClause>,
/// ORDER BY (MySQL)
pub order_by: Vec<OrderByExpr>,
/// LIMIT (MySQL)
pub limit: Option<Expr>,
}
impl Display for Delete {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("DELETE")?;
for hint in &self.optimizer_hints {
f.write_str(" ")?;
hint.fmt(f)?;
}
if !self.tables.is_empty() {
indented_list(f, &self.tables)?;
}
match &self.from {
FromTable::WithFromKeyword(from) => {
f.write_str(" FROM")?;
indented_list(f, from)?;
}
FromTable::WithoutKeyword(from) => {
indented_list(f, from)?;
}
}
if let Some(output) = &self.output {
SpaceOrNewline.fmt(f)?;
write!(f, "{output}")?;
}
if let Some(using) = &self.using {
SpaceOrNewline.fmt(f)?;
f.write_str("USING")?;
indented_list(f, using)?;
}
if let Some(selection) = &self.selection {
SpaceOrNewline.fmt(f)?;
f.write_str("WHERE")?;
SpaceOrNewline.fmt(f)?;
Indent(selection).fmt(f)?;
}
if let Some(returning) = &self.returning {
SpaceOrNewline.fmt(f)?;
f.write_str("RETURNING")?;
indented_list(f, returning)?;
}
if !self.order_by.is_empty() {
SpaceOrNewline.fmt(f)?;
f.write_str("ORDER BY")?;
indented_list(f, &self.order_by)?;
}
if let Some(limit) = &self.limit {
SpaceOrNewline.fmt(f)?;
f.write_str("LIMIT")?;
SpaceOrNewline.fmt(f)?;
Indent(limit).fmt(f)?;
}
Ok(())
}
}
/// UPDATE 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 Update {
/// Token for the `UPDATE` keyword
pub update_token: AttachedToken,
/// Query optimizer hints
///
/// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
pub optimizer_hints: Vec<OptimizerHint>,
/// TABLE
pub table: TableWithJoins,
/// Column assignments
pub assignments: Vec<Assignment>,
/// Table which provide value to be set
pub from: Option<UpdateTableFromKind>,
/// WHERE
pub selection: Option<Expr>,
/// RETURNING
pub returning: Option<Vec<SelectItem>>,
/// OUTPUT (MSSQL)
/// See <https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql>
pub output: Option<OutputClause>,
/// SQLite-specific conflict resolution clause
pub or: Option<SqliteOnConflict>,
/// ORDER BY (MySQL extension for single-table UPDATE)
/// See <https://dev.mysql.com/doc/refman/8.4/en/update.html>
pub order_by: Vec<OrderByExpr>,
/// LIMIT
pub limit: Option<Expr>,
}
impl Display for Update {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("UPDATE")?;
for hint in &self.optimizer_hints {
f.write_str(" ")?;
hint.fmt(f)?;
}
f.write_str(" ")?;
if let Some(or) = &self.or {
or.fmt(f)?;
f.write_str(" ")?;
}
self.table.fmt(f)?;
if let Some(UpdateTableFromKind::BeforeSet(from)) = &self.from {
SpaceOrNewline.fmt(f)?;
f.write_str("FROM")?;
indented_list(f, from)?;
}
if !self.assignments.is_empty() {
SpaceOrNewline.fmt(f)?;
f.write_str("SET")?;
indented_list(f, &self.assignments)?;
}
if let Some(output) = &self.output {
SpaceOrNewline.fmt(f)?;
write!(f, "{output}")?;
}
if let Some(UpdateTableFromKind::AfterSet(from)) = &self.from {
SpaceOrNewline.fmt(f)?;
f.write_str("FROM")?;
indented_list(f, from)?;
}
if let Some(selection) = &self.selection {
SpaceOrNewline.fmt(f)?;
f.write_str("WHERE")?;
SpaceOrNewline.fmt(f)?;
Indent(selection).fmt(f)?;
}
if let Some(returning) = &self.returning {
SpaceOrNewline.fmt(f)?;
f.write_str("RETURNING")?;
indented_list(f, returning)?;
}
if !self.order_by.is_empty() {
SpaceOrNewline.fmt(f)?;
f.write_str("ORDER BY")?;
indented_list(f, &self.order_by)?;
}
if let Some(limit) = &self.limit {
SpaceOrNewline.fmt(f)?;
write!(f, "LIMIT {limit}")?;
}
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,
/// Query optimizer hints
///
/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
pub optimizer_hints: Vec<OptimizerHint>,
/// 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 {
f.write_str("MERGE")?;
for hint in &self.optimizer_hints {
write!(f, " {hint}")?;
}
if self.into {
write!(f, " INTO")?;
}
write!(
f,
" {table} USING {source} ",
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,
/// The type of `WHEN` clause.
pub clause_kind: MergeClauseKind,
/// An optional predicate to further restrict the clause.
pub predicate: Option<Expr>,
/// The action to perform when the clause is matched.
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, Copy, 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(())
}
}
/// An `OUTPUT` clause on `MERGE`, `INSERT`, `UPDATE`, or `DELETE` (MSSQL).
///
/// Example:
/// OUTPUT $action, deleted.* INTO dbo.temp_products;
/// <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` clause
Output {
/// The `OUTPUT` token that starts the sub-expression.
output_token: AttachedToken,
/// The select items to output
select_items: Vec<SelectItem>,
/// Optional `INTO` table to direct the output
into_table: Option<SelectInto>,
},
/// `RETURNING` clause
Returning {
/// The `RETURNING` token that starts the sub-expression.
returning_token: AttachedToken,
/// The select items to return
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)
}
}
}
}
/// A WHEN clause in a conditional multi-table INSERT.
///
/// Syntax:
/// ```sql
/// WHEN n1 > 100 THEN
/// INTO t1
/// INTO t2 (c1, c2) VALUES (n1, n2)
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MultiTableInsertWhenClause {
/// The condition for this WHEN clause
pub condition: Expr,
/// The INTO clauses to execute when the condition is true
pub into_clauses: Vec<MultiTableInsertIntoClause>,
}
impl Display for MultiTableInsertWhenClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "WHEN {} THEN", self.condition)?;
for into_clause in &self.into_clauses {
SpaceOrNewline.fmt(f)?;
write!(f, "{}", into_clause)?;
}
Ok(())
}
}
/// An INTO clause in a multi-table INSERT.
///
/// Syntax:
/// ```sql
/// INTO <target_table> [ ( <target_col_name> [ , ... ] ) ] [ VALUES ( { <source_col_name> | DEFAULT | NULL } [ , ... ] ) ]
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MultiTableInsertIntoClause {
/// The target table
pub table_name: ObjectName,
/// The target columns (optional)
pub columns: Vec<Ident>,
/// The VALUES clause (optional)
pub values: Option<MultiTableInsertValues>,
}
impl Display for MultiTableInsertIntoClause {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "INTO {}", self.table_name)?;
if !self.columns.is_empty() {
write!(f, " ({})", display_comma_separated(&self.columns))?;
}
if let Some(values) = &self.values {
write!(f, " VALUES ({})", display_comma_separated(&values.values))?;
}
Ok(())
}
}
/// The VALUES clause in a multi-table INSERT INTO clause.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct MultiTableInsertValues {
/// The values to insert (can be column references, DEFAULT, or NULL)
pub values: Vec<MultiTableInsertValue>,
}
/// A value in a multi-table INSERT VALUES clause.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MultiTableInsertValue {
/// A column reference or expression from the source
Expr(Expr),
/// The DEFAULT keyword
Default,
}
impl Display for MultiTableInsertValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MultiTableInsertValue::Expr(expr) => write!(f, "{}", expr),
MultiTableInsertValue::Default => write!(f, "DEFAULT"),
}
}
}
/// The type of multi-table INSERT statement(Snowflake).
///
/// See: <https://docs.snowflake.com/en/sql-reference/sql/insert-multi-table>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum MultiTableInsertType {
/// `INSERT ALL` - all matching WHEN clauses are executed
All,
/// `INSERT FIRST` - only the first matching WHEN clause is executed
First,
}
impl Display for MultiTableInsertType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MultiTableInsertType::All => write!(f, "ALL"),
MultiTableInsertType::First => write!(f, "FIRST"),
}
}
}