dml.rs - source

sqlparser/ast/

dml.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18#[cfg(not(feature = "std"))]
19use alloc::{boxed::Box, format, string::ToString, vec::Vec};
20
21use core::fmt::{self, Display};
22#[cfg(feature = "serde")]
23use serde::{Deserialize, Serialize};
24#[cfg(feature = "visitor")]
25use sqlparser_derive::{Visit, VisitMut};
26
27use crate::{
28    ast::display_separated,
29    display_utils::{indented_list, Indent, SpaceOrNewline},
30};
31
32use super::{
33    display_comma_separated, helpers::attached_token::AttachedToken, query::InputFormatClause,
34    Assignment, Expr, FromTable, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnInsert,
35    OptimizerHint, OrderByExpr, Query, SelectInto, SelectItem, Setting, SqliteOnConflict,
36    TableFactor, TableObject, TableWithJoins, UpdateTableFromKind, Values,
37};
38
39/// INSERT statement.
40#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
41#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
42#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
43pub struct Insert {
44    /// Token for the `INSERT` keyword (or its substitutes)
45    pub insert_token: AttachedToken,
46    /// A query optimizer hint
47    ///
48    /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
49    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
50    pub optimizer_hint: Option<OptimizerHint>,
51    /// Only for Sqlite
52    pub or: Option<SqliteOnConflict>,
53    /// Only for mysql
54    pub ignore: bool,
55    /// INTO - optional keyword
56    pub into: bool,
57    /// TABLE
58    pub table: TableObject,
59    /// table_name as foo (for PostgreSQL)
60    pub table_alias: Option<Ident>,
61    /// COLUMNS
62    pub columns: Vec<Ident>,
63    /// Overwrite (Hive)
64    pub overwrite: bool,
65    /// A SQL query that specifies what to insert
66    pub source: Option<Box<Query>>,
67    /// MySQL `INSERT INTO ... SET`
68    /// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
69    pub assignments: Vec<Assignment>,
70    /// partitioned insert (Hive)
71    pub partitioned: Option<Vec<Expr>>,
72    /// Columns defined after PARTITION
73    pub after_columns: Vec<Ident>,
74    /// whether the insert has the table keyword (Hive)
75    pub has_table_keyword: bool,
76    /// ON INSERT
77    pub on: Option<OnInsert>,
78    /// RETURNING
79    pub returning: Option<Vec<SelectItem>>,
80    /// Only for mysql
81    pub replace_into: bool,
82    /// Only for mysql
83    pub priority: Option<MysqlInsertPriority>,
84    /// Only for mysql
85    pub insert_alias: Option<InsertAliases>,
86    /// Settings used for ClickHouse.
87    ///
88    /// ClickHouse syntax: `INSERT INTO tbl SETTINGS format_template_resultset = '/some/path/resultset.format'`
89    ///
90    /// [ClickHouse `INSERT INTO`](https://clickhouse.com/docs/en/sql-reference/statements/insert-into)
91    pub settings: Option<Vec<Setting>>,
92    /// Format for `INSERT` statement when not using standard SQL format. Can be e.g. `CSV`,
93    /// `JSON`, `JSONAsString`, `LineAsString` and more.
94    ///
95    /// ClickHouse syntax: `INSERT INTO tbl FORMAT JSONEachRow {"foo": 1, "bar": 2}, {"foo": 3}`
96    ///
97    /// [ClickHouse formats JSON insert](https://clickhouse.com/docs/en/interfaces/formats#json-inserting-data)
98    pub format_clause: Option<InputFormatClause>,
99}
100
101impl Display for Insert {
102    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
103        let table_name = if let Some(alias) = &self.table_alias {
104            format!("{0} AS {alias}", self.table)
105        } else {
106            self.table.to_string()
107        };
108
109        if let Some(on_conflict) = self.or {
110            f.write_str("INSERT")?;
111            if let Some(hint) = self.optimizer_hint.as_ref() {
112                write!(f, " {hint}")?;
113            }
114            write!(f, " {on_conflict} INTO {table_name} ")?;
115        } else {
116            write!(
117                f,
118                "{start}",
119                start = if self.replace_into {
120                    "REPLACE"
121                } else {
122                    "INSERT"
123                }
124            )?;
125            if let Some(hint) = self.optimizer_hint.as_ref() {
126                write!(f, " {hint}")?;
127            }
128            if let Some(priority) = self.priority {
129                write!(f, " {priority}",)?;
130            }
131
132            write!(
133                f,
134                "{ignore}{over}{int}{tbl} {table_name} ",
135                table_name = table_name,
136                ignore = if self.ignore { " IGNORE" } else { "" },
137                over = if self.overwrite { " OVERWRITE" } else { "" },
138                int = if self.into { " INTO" } else { "" },
139                tbl = if self.has_table_keyword { " TABLE" } else { "" },
140            )?;
141        }
142        if !self.columns.is_empty() {
143            write!(f, "({})", display_comma_separated(&self.columns))?;
144            SpaceOrNewline.fmt(f)?;
145        }
146        if let Some(ref parts) = self.partitioned {
147            if !parts.is_empty() {
148                write!(f, "PARTITION ({})", display_comma_separated(parts))?;
149                SpaceOrNewline.fmt(f)?;
150            }
151        }
152        if !self.after_columns.is_empty() {
153            write!(f, "({})", display_comma_separated(&self.after_columns))?;
154            SpaceOrNewline.fmt(f)?;
155        }
156
157        if let Some(settings) = &self.settings {
158            write!(f, "SETTINGS {}", display_comma_separated(settings))?;
159            SpaceOrNewline.fmt(f)?;
160        }
161
162        if let Some(source) = &self.source {
163            source.fmt(f)?;
164        } else if !self.assignments.is_empty() {
165            write!(f, "SET")?;
166            indented_list(f, &self.assignments)?;
167        } else if let Some(format_clause) = &self.format_clause {
168            format_clause.fmt(f)?;
169        } else if self.columns.is_empty() {
170            write!(f, "DEFAULT VALUES")?;
171        }
172
173        if let Some(insert_alias) = &self.insert_alias {
174            write!(f, " AS {0}", insert_alias.row_alias)?;
175
176            if let Some(col_aliases) = &insert_alias.col_aliases {
177                if !col_aliases.is_empty() {
178                    write!(f, " ({})", display_comma_separated(col_aliases))?;
179                }
180            }
181        }
182
183        if let Some(on) = &self.on {
184            write!(f, "{on}")?;
185        }
186
187        if let Some(returning) = &self.returning {
188            SpaceOrNewline.fmt(f)?;
189            f.write_str("RETURNING")?;
190            indented_list(f, returning)?;
191        }
192        Ok(())
193    }
194}
195
196/// DELETE statement.
197#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
198#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
199#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
200pub struct Delete {
201    /// Token for the `DELETE` keyword
202    pub delete_token: AttachedToken,
203    /// A query optimizer hint
204    ///
205    /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
206    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
207    pub optimizer_hint: Option<OptimizerHint>,
208    /// Multi tables delete are supported in mysql
209    pub tables: Vec<ObjectName>,
210    /// FROM
211    pub from: FromTable,
212    /// USING (Snowflake, Postgres, MySQL)
213    pub using: Option<Vec<TableWithJoins>>,
214    /// WHERE
215    pub selection: Option<Expr>,
216    /// RETURNING
217    pub returning: Option<Vec<SelectItem>>,
218    /// ORDER BY (MySQL)
219    pub order_by: Vec<OrderByExpr>,
220    /// LIMIT (MySQL)
221    pub limit: Option<Expr>,
222}
223
224impl Display for Delete {
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        f.write_str("DELETE")?;
227        if let Some(hint) = self.optimizer_hint.as_ref() {
228            f.write_str(" ")?;
229            hint.fmt(f)?;
230        }
231        if !self.tables.is_empty() {
232            indented_list(f, &self.tables)?;
233        }
234        match &self.from {
235            FromTable::WithFromKeyword(from) => {
236                f.write_str(" FROM")?;
237                indented_list(f, from)?;
238            }
239            FromTable::WithoutKeyword(from) => {
240                indented_list(f, from)?;
241            }
242        }
243        if let Some(using) = &self.using {
244            SpaceOrNewline.fmt(f)?;
245            f.write_str("USING")?;
246            indented_list(f, using)?;
247        }
248        if let Some(selection) = &self.selection {
249            SpaceOrNewline.fmt(f)?;
250            f.write_str("WHERE")?;
251            SpaceOrNewline.fmt(f)?;
252            Indent(selection).fmt(f)?;
253        }
254        if let Some(returning) = &self.returning {
255            SpaceOrNewline.fmt(f)?;
256            f.write_str("RETURNING")?;
257            indented_list(f, returning)?;
258        }
259        if !self.order_by.is_empty() {
260            SpaceOrNewline.fmt(f)?;
261            f.write_str("ORDER BY")?;
262            indented_list(f, &self.order_by)?;
263        }
264        if let Some(limit) = &self.limit {
265            SpaceOrNewline.fmt(f)?;
266            f.write_str("LIMIT")?;
267            SpaceOrNewline.fmt(f)?;
268            Indent(limit).fmt(f)?;
269        }
270        Ok(())
271    }
272}
273
274/// UPDATE statement.
275#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
276#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
277#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
278pub struct Update {
279    /// Token for the `UPDATE` keyword
280    pub update_token: AttachedToken,
281    /// A query optimizer hint
282    ///
283    /// [MySQL](https://dev.mysql.com/doc/refman/8.4/en/optimizer-hints.html)
284    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
285    pub optimizer_hint: Option<OptimizerHint>,
286    /// TABLE
287    pub table: TableWithJoins,
288    /// Column assignments
289    pub assignments: Vec<Assignment>,
290    /// Table which provide value to be set
291    pub from: Option<UpdateTableFromKind>,
292    /// WHERE
293    pub selection: Option<Expr>,
294    /// RETURNING
295    pub returning: Option<Vec<SelectItem>>,
296    /// SQLite-specific conflict resolution clause
297    pub or: Option<SqliteOnConflict>,
298    /// LIMIT
299    pub limit: Option<Expr>,
300}
301
302impl Display for Update {
303    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
304        f.write_str("UPDATE ")?;
305        if let Some(hint) = self.optimizer_hint.as_ref() {
306            hint.fmt(f)?;
307            f.write_str(" ")?;
308        }
309        if let Some(or) = &self.or {
310            or.fmt(f)?;
311            f.write_str(" ")?;
312        }
313        self.table.fmt(f)?;
314        if let Some(UpdateTableFromKind::BeforeSet(from)) = &self.from {
315            SpaceOrNewline.fmt(f)?;
316            f.write_str("FROM")?;
317            indented_list(f, from)?;
318        }
319        if !self.assignments.is_empty() {
320            SpaceOrNewline.fmt(f)?;
321            f.write_str("SET")?;
322            indented_list(f, &self.assignments)?;
323        }
324        if let Some(UpdateTableFromKind::AfterSet(from)) = &self.from {
325            SpaceOrNewline.fmt(f)?;
326            f.write_str("FROM")?;
327            indented_list(f, from)?;
328        }
329        if let Some(selection) = &self.selection {
330            SpaceOrNewline.fmt(f)?;
331            f.write_str("WHERE")?;
332            SpaceOrNewline.fmt(f)?;
333            Indent(selection).fmt(f)?;
334        }
335        if let Some(returning) = &self.returning {
336            SpaceOrNewline.fmt(f)?;
337            f.write_str("RETURNING")?;
338            indented_list(f, returning)?;
339        }
340        if let Some(limit) = &self.limit {
341            SpaceOrNewline.fmt(f)?;
342            write!(f, "LIMIT {limit}")?;
343        }
344        Ok(())
345    }
346}
347
348/// A `MERGE` statement.
349#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
350#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
351#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
352pub struct Merge {
353    /// The `MERGE` token that starts the statement.
354    pub merge_token: AttachedToken,
355    /// A query optimizer hint
356    ///
357    /// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/Comments.html#GUID-D316D545-89E2-4D54-977F-FC97815CD62E)
358    pub optimizer_hint: Option<OptimizerHint>,
359    /// optional INTO keyword
360    pub into: bool,
361    /// Specifies the table to merge
362    pub table: TableFactor,
363    /// Specifies the table or subquery to join with the target table
364    pub source: TableFactor,
365    /// Specifies the expression on which to join the target table and source
366    pub on: Box<Expr>,
367    /// Specifies the actions to perform when values match or do not match.
368    pub clauses: Vec<MergeClause>,
369    /// Specifies the output to save changes in MSSQL
370    pub output: Option<OutputClause>,
371}
372
373impl Display for Merge {
374    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375        f.write_str("MERGE")?;
376        if let Some(hint) = self.optimizer_hint.as_ref() {
377            write!(f, " {hint}")?;
378        }
379        if self.into {
380            write!(f, " INTO")?;
381        }
382        write!(
383            f,
384            " {table} USING {source} ",
385            table = self.table,
386            source = self.source
387        )?;
388        write!(f, "ON {on} ", on = self.on)?;
389        write!(f, "{}", display_separated(&self.clauses, " "))?;
390        if let Some(ref output) = self.output {
391            write!(f, " {output}")?;
392        }
393        Ok(())
394    }
395}
396
397/// A `WHEN` clause within a `MERGE` Statement
398///
399/// Example:
400/// ```sql
401/// WHEN NOT MATCHED BY SOURCE AND product LIKE '%washer%' THEN DELETE
402/// ```
403/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
404/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
405#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
406#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
407#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
408pub struct MergeClause {
409    /// The `WHEN` token that starts the sub-expression.
410    pub when_token: AttachedToken,
411    /// The type of `WHEN` clause.
412    pub clause_kind: MergeClauseKind,
413    /// An optional predicate to further restrict the clause.
414    pub predicate: Option<Expr>,
415    /// The action to perform when the clause is matched.
416    pub action: MergeAction,
417}
418
419impl Display for MergeClause {
420    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
421        let MergeClause {
422            when_token: _,
423            clause_kind,
424            predicate,
425            action,
426        } = self;
427
428        write!(f, "WHEN {clause_kind}")?;
429        if let Some(pred) = predicate {
430            write!(f, " AND {pred}")?;
431        }
432        write!(f, " THEN {action}")
433    }
434}
435
436/// Variant of `WHEN` clause used within a `MERGE` Statement.
437///
438/// Example:
439/// ```sql
440/// MERGE INTO T USING U ON FALSE WHEN MATCHED THEN DELETE
441/// ```
442/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
443/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
444#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
445#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
446#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
447pub enum MergeClauseKind {
448    /// `WHEN MATCHED`
449    Matched,
450    /// `WHEN NOT MATCHED`
451    NotMatched,
452    /// `WHEN MATCHED BY TARGET`
453    ///
454    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
455    NotMatchedByTarget,
456    /// `WHEN MATCHED BY SOURCE`
457    ///
458    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
459    NotMatchedBySource,
460}
461
462impl Display for MergeClauseKind {
463    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
464        match self {
465            MergeClauseKind::Matched => write!(f, "MATCHED"),
466            MergeClauseKind::NotMatched => write!(f, "NOT MATCHED"),
467            MergeClauseKind::NotMatchedByTarget => write!(f, "NOT MATCHED BY TARGET"),
468            MergeClauseKind::NotMatchedBySource => write!(f, "NOT MATCHED BY SOURCE"),
469        }
470    }
471}
472
473/// Underlying statement of a `WHEN` clause within a `MERGE` Statement
474///
475/// Example
476/// ```sql
477/// INSERT (product, quantity) VALUES(product, quantity)
478/// ```
479///
480/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
481/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
482/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
483#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
484#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
485#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
486pub enum MergeAction {
487    /// An `INSERT` clause
488    ///
489    /// Example:
490    /// ```sql
491    /// INSERT (product, quantity) VALUES(product, quantity)
492    /// ```
493    Insert(MergeInsertExpr),
494    /// An `UPDATE` clause
495    ///
496    /// Example:
497    /// ```sql
498    /// UPDATE SET quantity = T.quantity + S.quantity
499    /// ```
500    Update(MergeUpdateExpr),
501    /// A plain `DELETE` clause
502    Delete {
503        /// The `DELETE` token that starts the sub-expression.
504        delete_token: AttachedToken,
505    },
506}
507
508impl Display for MergeAction {
509    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
510        match self {
511            MergeAction::Insert(insert) => {
512                write!(f, "INSERT {insert}")
513            }
514            MergeAction::Update(update) => {
515                write!(f, "UPDATE {update}")
516            }
517            MergeAction::Delete { .. } => {
518                write!(f, "DELETE")
519            }
520        }
521    }
522}
523
524/// The type of expression used to insert rows within a `MERGE` statement.
525///
526/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
527/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
528#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
529#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
530#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
531pub enum MergeInsertKind {
532    /// The insert expression is defined from an explicit `VALUES` clause
533    ///
534    /// Example:
535    /// ```sql
536    /// INSERT VALUES(product, quantity)
537    /// ```
538    Values(Values),
539    /// The insert expression is defined using only the `ROW` keyword.
540    ///
541    /// Example:
542    /// ```sql
543    /// INSERT ROW
544    /// ```
545    /// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
546    Row,
547}
548
549impl Display for MergeInsertKind {
550    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
551        match self {
552            MergeInsertKind::Values(values) => {
553                write!(f, "{values}")
554            }
555            MergeInsertKind::Row => {
556                write!(f, "ROW")
557            }
558        }
559    }
560}
561
562/// The expression used to insert rows within a `MERGE` statement.
563///
564/// Examples
565/// ```sql
566/// INSERT (product, quantity) VALUES(product, quantity)
567/// INSERT ROW
568/// ```
569///
570/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
571/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
572/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
573#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
574#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
575#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
576pub struct MergeInsertExpr {
577    /// The `INSERT` token that starts the sub-expression.
578    pub insert_token: AttachedToken,
579    /// Columns (if any) specified by the insert.
580    ///
581    /// Example:
582    /// ```sql
583    /// INSERT (product, quantity) VALUES(product, quantity)
584    /// INSERT (product, quantity) ROW
585    /// ```
586    pub columns: Vec<ObjectName>,
587    /// The token, `[VALUES | ROW]` starting `kind`.
588    pub kind_token: AttachedToken,
589    /// The insert type used by the statement.
590    pub kind: MergeInsertKind,
591    /// An optional condition to restrict the insertion (Oracle specific)
592    pub insert_predicate: Option<Expr>,
593}
594
595impl Display for MergeInsertExpr {
596    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
597        if !self.columns.is_empty() {
598            write!(f, "({}) ", display_comma_separated(self.columns.as_slice()))?;
599        }
600        write!(f, "{}", self.kind)?;
601        if let Some(predicate) = self.insert_predicate.as_ref() {
602            write!(f, " WHERE {}", predicate)?;
603        }
604        Ok(())
605    }
606}
607
608/// The expression used to update rows within a `MERGE` statement.
609///
610/// Examples
611/// ```sql
612/// UPDATE SET quantity = T.quantity + S.quantity
613/// ```
614///
615/// [Snowflake](https://docs.snowflake.com/en/sql-reference/sql/merge)
616/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax#merge_statement)
617/// [Oracle](https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/MERGE.html)
618#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
619#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
620#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
621pub struct MergeUpdateExpr {
622    /// The `UPDATE` token that starts the sub-expression.
623    pub update_token: AttachedToken,
624    /// The update assiment expressions
625    pub assignments: Vec<Assignment>,
626    /// `where_clause` for the update (Oralce specific)
627    pub update_predicate: Option<Expr>,
628    /// `delete_clause` for the update "delete where" (Oracle specific)
629    pub delete_predicate: Option<Expr>,
630}
631
632impl Display for MergeUpdateExpr {
633    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
634        write!(f, "SET {}", display_comma_separated(&self.assignments))?;
635        if let Some(predicate) = self.update_predicate.as_ref() {
636            write!(f, " WHERE {predicate}")?;
637        }
638        if let Some(predicate) = self.delete_predicate.as_ref() {
639            write!(f, " DELETE WHERE {predicate}")?;
640        }
641        Ok(())
642    }
643}
644
645/// A `OUTPUT` Clause in the end of a `MERGE` Statement
646///
647/// Example:
648/// OUTPUT $action, deleted.* INTO dbo.temp_products;
649/// [mssql](https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql)
650#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
651#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
652#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
653pub enum OutputClause {
654    /// `OUTPUT` clause
655    Output {
656        /// The `OUTPUT` token that starts the sub-expression.
657        output_token: AttachedToken,
658        /// The select items to output
659        select_items: Vec<SelectItem>,
660        /// Optional `INTO` table to direct the output
661        into_table: Option<SelectInto>,
662    },
663    /// `RETURNING` clause
664    Returning {
665        /// The `RETURNING` token that starts the sub-expression.
666        returning_token: AttachedToken,
667        /// The select items to return
668        select_items: Vec<SelectItem>,
669    },
670}
671
672impl fmt::Display for OutputClause {
673    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
674        match self {
675            OutputClause::Output {
676                output_token: _,
677                select_items,
678                into_table,
679            } => {
680                f.write_str("OUTPUT ")?;
681                display_comma_separated(select_items).fmt(f)?;
682                if let Some(into_table) = into_table {
683                    f.write_str(" ")?;
684                    into_table.fmt(f)?;
685                }
686                Ok(())
687            }
688            OutputClause::Returning {
689                returning_token: _,
690                select_items,
691            } => {
692                f.write_str("RETURNING ")?;
693                display_comma_separated(select_items).fmt(f)
694            }
695        }
696    }
697}