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