Skip to main content

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::{
20    boxed::Box,
21    format,
22    string::{String, ToString},
23    vec::Vec,
24};
25
26use core::fmt::{self, Display};
27#[cfg(feature = "serde")]
28use serde::{Deserialize, Serialize};
29#[cfg(feature = "visitor")]
30use sqlparser_derive::{Visit, VisitMut};
31
32pub use super::ddl::{ColumnDef, TableConstraint};
33
34use super::{
35    display_comma_separated, display_separated, query::InputFormatClause, Assignment, ClusteredBy,
36    CommentDef, Expr, FileFormat, FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat,
37    HiveRowFormat, Ident, InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert,
38    OneOrManyWithParens, OrderByExpr, Query, RowAccessPolicy, SelectItem, Setting, SqlOption,
39    SqliteOnConflict, StorageSerializationPolicy, TableEngine, TableObject, TableWithJoins, Tag,
40    WrappedCollection,
41};
42
43/// CREATE INDEX statement.
44#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
47pub struct CreateIndex {
48    /// index name
49    pub name: Option<ObjectName>,
50    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
51    pub table_name: ObjectName,
52    pub using: Option<Ident>,
53    pub columns: Vec<OrderByExpr>,
54    pub unique: bool,
55    pub concurrently: bool,
56    pub if_not_exists: bool,
57    pub include: Vec<Ident>,
58    pub nulls_distinct: Option<bool>,
59    /// WITH clause: <https://www.postgresql.org/docs/current/sql-createindex.html>
60    pub with: Vec<Expr>,
61    pub predicate: Option<Expr>,
62}
63
64impl Display for CreateIndex {
65    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
66        write!(
67            f,
68            "CREATE {unique}INDEX {concurrently}{if_not_exists}",
69            unique = if self.unique { "UNIQUE " } else { "" },
70            concurrently = if self.concurrently {
71                "CONCURRENTLY "
72            } else {
73                ""
74            },
75            if_not_exists = if self.if_not_exists {
76                "IF NOT EXISTS "
77            } else {
78                ""
79            },
80        )?;
81        if let Some(value) = &self.name {
82            write!(f, "{value} ")?;
83        }
84        write!(f, "ON {}", self.table_name)?;
85        if let Some(value) = &self.using {
86            write!(f, " USING {value} ")?;
87        }
88        write!(f, "({})", display_separated(&self.columns, ","))?;
89        if !self.include.is_empty() {
90            write!(f, " INCLUDE ({})", display_separated(&self.include, ","))?;
91        }
92        if let Some(value) = self.nulls_distinct {
93            if value {
94                write!(f, " NULLS DISTINCT")?;
95            } else {
96                write!(f, " NULLS NOT DISTINCT")?;
97            }
98        }
99        if !self.with.is_empty() {
100            write!(f, " WITH ({})", display_comma_separated(&self.with))?;
101        }
102        if let Some(predicate) = &self.predicate {
103            write!(f, " WHERE {predicate}")?;
104        }
105        Ok(())
106    }
107}
108
109/// CREATE TABLE statement.
110#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
111#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
112#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
113pub struct CreateTable {
114    pub or_replace: bool,
115    pub temporary: bool,
116    pub external: bool,
117    pub global: Option<bool>,
118    pub if_not_exists: bool,
119    pub transient: bool,
120    pub volatile: bool,
121    pub iceberg: bool,
122    /// Table name
123    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
124    pub name: ObjectName,
125    /// Optional schema
126    pub columns: Vec<ColumnDef>,
127    pub constraints: Vec<TableConstraint>,
128    pub hive_distribution: HiveDistributionStyle,
129    pub hive_formats: Option<HiveFormat>,
130    pub table_properties: Vec<SqlOption>,
131    pub with_options: Vec<SqlOption>,
132    pub file_format: Option<FileFormat>,
133    pub location: Option<String>,
134    pub query: Option<Box<Query>>,
135    pub without_rowid: bool,
136    pub like: Option<ObjectName>,
137    pub clone: Option<ObjectName>,
138    pub engine: Option<TableEngine>,
139    pub comment: Option<CommentDef>,
140    pub auto_increment_offset: Option<u32>,
141    pub default_charset: Option<String>,
142    pub collation: Option<String>,
143    pub on_commit: Option<OnCommit>,
144    /// ClickHouse "ON CLUSTER" clause:
145    /// <https://clickhouse.com/docs/en/sql-reference/distributed-ddl/>
146    pub on_cluster: Option<Ident>,
147    /// ClickHouse "PRIMARY KEY " clause.
148    /// <https://clickhouse.com/docs/en/sql-reference/statements/create/table/>
149    pub primary_key: Option<Box<Expr>>,
150    /// ClickHouse "ORDER BY " clause. Note that omitted ORDER BY is different
151    /// than empty (represented as ()), the latter meaning "no sorting".
152    /// <https://clickhouse.com/docs/en/sql-reference/statements/create/table/>
153    pub order_by: Option<OneOrManyWithParens<Expr>>,
154    /// BigQuery: A partition expression for the table.
155    /// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#partition_expression>
156    pub partition_by: Option<Box<Expr>>,
157    /// BigQuery: Table clustering column list.
158    /// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#table_option_list>
159    pub cluster_by: Option<WrappedCollection<Vec<Ident>>>,
160    /// Hive: Table clustering column list.
161    /// <https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable>
162    pub clustered_by: Option<ClusteredBy>,
163    /// BigQuery: Table options list.
164    /// <https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language#table_option_list>
165    pub options: Option<Vec<SqlOption>>,
166    /// SQLite "STRICT" clause.
167    /// if the "STRICT" table-option keyword is added to the end, after the closing ")",
168    /// then strict typing rules apply to that table.
169    pub strict: bool,
170    /// Snowflake "COPY GRANTS" clause
171    /// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
172    pub copy_grants: bool,
173    /// Snowflake "ENABLE_SCHEMA_EVOLUTION" clause
174    /// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
175    pub enable_schema_evolution: Option<bool>,
176    /// Snowflake "CHANGE_TRACKING" clause
177    /// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
178    pub change_tracking: Option<bool>,
179    /// Snowflake "DATA_RETENTION_TIME_IN_DAYS" clause
180    /// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
181    pub data_retention_time_in_days: Option<u64>,
182    /// Snowflake "MAX_DATA_EXTENSION_TIME_IN_DAYS" clause
183    /// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
184    pub max_data_extension_time_in_days: Option<u64>,
185    /// Snowflake "DEFAULT_DDL_COLLATION" clause
186    /// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
187    pub default_ddl_collation: Option<String>,
188    /// Snowflake "WITH AGGREGATION POLICY" clause
189    /// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
190    pub with_aggregation_policy: Option<ObjectName>,
191    /// Snowflake "WITH ROW ACCESS POLICY" clause
192    /// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
193    pub with_row_access_policy: Option<RowAccessPolicy>,
194    /// Snowflake "WITH TAG" clause
195    /// <https://docs.snowflake.com/en/sql-reference/sql/create-table>
196    pub with_tags: Option<Vec<Tag>>,
197    /// Snowflake "EXTERNAL_VOLUME" clause for Iceberg tables
198    /// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
199    pub external_volume: Option<String>,
200    /// Snowflake "BASE_LOCATION" clause for Iceberg tables
201    /// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
202    pub base_location: Option<String>,
203    /// Snowflake "CATALOG" clause for Iceberg tables
204    /// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
205    pub catalog: Option<String>,
206    /// Snowflake "CATALOG_SYNC" clause for Iceberg tables
207    /// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
208    pub catalog_sync: Option<String>,
209    /// Snowflake "STORAGE_SERIALIZATION_POLICY" clause for Iceberg tables
210    /// <https://docs.snowflake.com/en/sql-reference/sql/create-iceberg-table>
211    pub storage_serialization_policy: Option<StorageSerializationPolicy>,
212}
213
214impl Display for CreateTable {
215    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216        // We want to allow the following options
217        // Empty column list, allowed by PostgreSQL:
218        //   `CREATE TABLE t ()`
219        // No columns provided for CREATE TABLE AS:
220        //   `CREATE TABLE t AS SELECT a from t2`
221        // Columns provided for CREATE TABLE AS:
222        //   `CREATE TABLE t (a INT) AS SELECT a from t2`
223        write!(
224            f,
225            "CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}{iceberg}TABLE {if_not_exists}{name}",
226            or_replace = if self.or_replace { "OR REPLACE " } else { "" },
227            external = if self.external { "EXTERNAL " } else { "" },
228            global = self.global
229                .map(|global| {
230                    if global {
231                        "GLOBAL "
232                    } else {
233                        "LOCAL "
234                    }
235                })
236                .unwrap_or(""),
237            if_not_exists = if self.if_not_exists { "IF NOT EXISTS " } else { "" },
238            temporary = if self.temporary { "TEMPORARY " } else { "" },
239            transient = if self.transient { "TRANSIENT " } else { "" },
240            volatile = if self.volatile { "VOLATILE " } else { "" },
241            // Only for Snowflake
242            iceberg = if self.iceberg { "ICEBERG " } else { "" },
243            name = self.name,
244        )?;
245        if let Some(on_cluster) = &self.on_cluster {
246            write!(f, " ON CLUSTER {}", on_cluster)?;
247        }
248        if !self.columns.is_empty() || !self.constraints.is_empty() {
249            write!(f, " ({}", display_comma_separated(&self.columns))?;
250            if !self.columns.is_empty() && !self.constraints.is_empty() {
251                write!(f, ", ")?;
252            }
253            write!(f, "{})", display_comma_separated(&self.constraints))?;
254        } else if self.query.is_none() && self.like.is_none() && self.clone.is_none() {
255            // PostgreSQL allows `CREATE TABLE t ();`, but requires empty parens
256            write!(f, " ()")?;
257        }
258
259        // Hive table comment should be after column definitions, please refer to:
260        // [Hive](https://cwiki.apache.org/confluence/display/Hive/LanguageManual+DDL#LanguageManualDDL-CreateTable)
261        if let Some(CommentDef::AfterColumnDefsWithoutEq(comment)) = &self.comment {
262            write!(f, " COMMENT '{comment}'")?;
263        }
264
265        // Only for SQLite
266        if self.without_rowid {
267            write!(f, " WITHOUT ROWID")?;
268        }
269
270        // Only for Hive
271        if let Some(l) = &self.like {
272            write!(f, " LIKE {l}")?;
273        }
274
275        if let Some(c) = &self.clone {
276            write!(f, " CLONE {c}")?;
277        }
278
279        match &self.hive_distribution {
280            HiveDistributionStyle::PARTITIONED { columns } => {
281                write!(f, " PARTITIONED BY ({})", display_comma_separated(columns))?;
282            }
283            HiveDistributionStyle::SKEWED {
284                columns,
285                on,
286                stored_as_directories,
287            } => {
288                write!(
289                    f,
290                    " SKEWED BY ({})) ON ({})",
291                    display_comma_separated(columns),
292                    display_comma_separated(on)
293                )?;
294                if *stored_as_directories {
295                    write!(f, " STORED AS DIRECTORIES")?;
296                }
297            }
298            _ => (),
299        }
300
301        if let Some(clustered_by) = &self.clustered_by {
302            write!(f, " {clustered_by}")?;
303        }
304
305        if let Some(HiveFormat {
306            row_format,
307            serde_properties,
308            storage,
309            location,
310        }) = &self.hive_formats
311        {
312            match row_format {
313                Some(HiveRowFormat::SERDE { class }) => write!(f, " ROW FORMAT SERDE '{class}'")?,
314                Some(HiveRowFormat::DELIMITED { delimiters }) => {
315                    write!(f, " ROW FORMAT DELIMITED")?;
316                    if !delimiters.is_empty() {
317                        write!(f, " {}", display_separated(delimiters, " "))?;
318                    }
319                }
320                None => (),
321            }
322            match storage {
323                Some(HiveIOFormat::IOF {
324                    input_format,
325                    output_format,
326                }) => write!(
327                    f,
328                    " STORED AS INPUTFORMAT {input_format} OUTPUTFORMAT {output_format}"
329                )?,
330                Some(HiveIOFormat::FileFormat { format }) if !self.external => {
331                    write!(f, " STORED AS {format}")?
332                }
333                _ => (),
334            }
335            if let Some(serde_properties) = serde_properties.as_ref() {
336                write!(
337                    f,
338                    " WITH SERDEPROPERTIES ({})",
339                    display_comma_separated(serde_properties)
340                )?;
341            }
342            if !self.external {
343                if let Some(loc) = location {
344                    write!(f, " LOCATION '{loc}'")?;
345                }
346            }
347        }
348        if self.external {
349            if let Some(file_format) = self.file_format {
350                write!(f, " STORED AS {file_format}")?;
351            }
352            write!(f, " LOCATION '{}'", self.location.as_ref().unwrap())?;
353        }
354        if !self.table_properties.is_empty() {
355            write!(
356                f,
357                " TBLPROPERTIES ({})",
358                display_comma_separated(&self.table_properties)
359            )?;
360        }
361        if !self.with_options.is_empty() {
362            write!(f, " WITH ({})", display_comma_separated(&self.with_options))?;
363        }
364        if let Some(engine) = &self.engine {
365            write!(f, " ENGINE={engine}")?;
366        }
367        if let Some(comment_def) = &self.comment {
368            match comment_def {
369                CommentDef::WithEq(comment) => {
370                    write!(f, " COMMENT = '{comment}'")?;
371                }
372                CommentDef::WithoutEq(comment) => {
373                    write!(f, " COMMENT '{comment}'")?;
374                }
375                // For CommentDef::AfterColumnDefsWithoutEq will be displayed after column definition
376                CommentDef::AfterColumnDefsWithoutEq(_) => (),
377            }
378        }
379
380        if let Some(auto_increment_offset) = self.auto_increment_offset {
381            write!(f, " AUTO_INCREMENT {auto_increment_offset}")?;
382        }
383        if let Some(primary_key) = &self.primary_key {
384            write!(f, " PRIMARY KEY {}", primary_key)?;
385        }
386        if let Some(order_by) = &self.order_by {
387            write!(f, " ORDER BY {}", order_by)?;
388        }
389        if let Some(partition_by) = self.partition_by.as_ref() {
390            write!(f, " PARTITION BY {partition_by}")?;
391        }
392        if let Some(cluster_by) = self.cluster_by.as_ref() {
393            write!(f, " CLUSTER BY {cluster_by}")?;
394        }
395
396        if let Some(options) = self.options.as_ref() {
397            write!(
398                f,
399                " OPTIONS({})",
400                display_comma_separated(options.as_slice())
401            )?;
402        }
403
404        if let Some(external_volume) = self.external_volume.as_ref() {
405            write!(f, " EXTERNAL_VOLUME = '{external_volume}'")?;
406        }
407
408        if let Some(catalog) = self.catalog.as_ref() {
409            write!(f, " CATALOG = '{catalog}'")?;
410        }
411
412        if self.iceberg {
413            if let Some(base_location) = self.base_location.as_ref() {
414                write!(f, " BASE_LOCATION = '{base_location}'")?;
415            }
416        }
417
418        if let Some(catalog_sync) = self.catalog_sync.as_ref() {
419            write!(f, " CATALOG_SYNC = '{catalog_sync}'")?;
420        }
421
422        if let Some(storage_serialization_policy) = self.storage_serialization_policy.as_ref() {
423            write!(
424                f,
425                " STORAGE_SERIALIZATION_POLICY = {storage_serialization_policy}"
426            )?;
427        }
428
429        if self.copy_grants {
430            write!(f, " COPY GRANTS")?;
431        }
432
433        if let Some(is_enabled) = self.enable_schema_evolution {
434            write!(
435                f,
436                " ENABLE_SCHEMA_EVOLUTION={}",
437                if is_enabled { "TRUE" } else { "FALSE" }
438            )?;
439        }
440
441        if let Some(is_enabled) = self.change_tracking {
442            write!(
443                f,
444                " CHANGE_TRACKING={}",
445                if is_enabled { "TRUE" } else { "FALSE" }
446            )?;
447        }
448
449        if let Some(data_retention_time_in_days) = self.data_retention_time_in_days {
450            write!(
451                f,
452                " DATA_RETENTION_TIME_IN_DAYS={data_retention_time_in_days}",
453            )?;
454        }
455
456        if let Some(max_data_extension_time_in_days) = self.max_data_extension_time_in_days {
457            write!(
458                f,
459                " MAX_DATA_EXTENSION_TIME_IN_DAYS={max_data_extension_time_in_days}",
460            )?;
461        }
462
463        if let Some(default_ddl_collation) = &self.default_ddl_collation {
464            write!(f, " DEFAULT_DDL_COLLATION='{default_ddl_collation}'",)?;
465        }
466
467        if let Some(with_aggregation_policy) = &self.with_aggregation_policy {
468            write!(f, " WITH AGGREGATION POLICY {with_aggregation_policy}",)?;
469        }
470
471        if let Some(row_access_policy) = &self.with_row_access_policy {
472            write!(f, " {row_access_policy}",)?;
473        }
474
475        if let Some(tag) = &self.with_tags {
476            write!(f, " WITH TAG ({})", display_comma_separated(tag.as_slice()))?;
477        }
478
479        if let Some(default_charset) = &self.default_charset {
480            write!(f, " DEFAULT CHARSET={default_charset}")?;
481        }
482        if let Some(collation) = &self.collation {
483            write!(f, " COLLATE={collation}")?;
484        }
485
486        if self.on_commit.is_some() {
487            let on_commit = match self.on_commit {
488                Some(OnCommit::DeleteRows) => "ON COMMIT DELETE ROWS",
489                Some(OnCommit::PreserveRows) => "ON COMMIT PRESERVE ROWS",
490                Some(OnCommit::Drop) => "ON COMMIT DROP",
491                None => "",
492            };
493            write!(f, " {on_commit}")?;
494        }
495        if self.strict {
496            write!(f, " STRICT")?;
497        }
498        if let Some(query) = &self.query {
499            write!(f, " AS {query}")?;
500        }
501        Ok(())
502    }
503}
504
505/// INSERT statement.
506#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
507#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
508#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
509pub struct Insert {
510    /// Only for Sqlite
511    pub or: Option<SqliteOnConflict>,
512    /// Only for mysql
513    pub ignore: bool,
514    /// INTO - optional keyword
515    pub into: bool,
516    /// TABLE
517    pub table: TableObject,
518    /// table_name as foo (for PostgreSQL)
519    pub table_alias: Option<Ident>,
520    /// COLUMNS
521    pub columns: Vec<Ident>,
522    /// Overwrite (Hive)
523    pub overwrite: bool,
524    /// A SQL query that specifies what to insert
525    pub source: Option<Box<Query>>,
526    /// MySQL `INSERT INTO ... SET`
527    /// See: <https://dev.mysql.com/doc/refman/8.4/en/insert.html>
528    pub assignments: Vec<Assignment>,
529    /// partitioned insert (Hive)
530    pub partitioned: Option<Vec<Expr>>,
531    /// Columns defined after PARTITION
532    pub after_columns: Vec<Ident>,
533    /// whether the insert has the table keyword (Hive)
534    pub has_table_keyword: bool,
535    pub on: Option<OnInsert>,
536    /// RETURNING
537    pub returning: Option<Vec<SelectItem>>,
538    /// Only for mysql
539    pub replace_into: bool,
540    /// Only for mysql
541    pub priority: Option<MysqlInsertPriority>,
542    /// Only for mysql
543    pub insert_alias: Option<InsertAliases>,
544    /// Settings used for ClickHouse.
545    ///
546    /// ClickHouse syntax: `INSERT INTO tbl SETTINGS format_template_resultset = '/some/path/resultset.format'`
547    ///
548    /// [ClickHouse `INSERT INTO`](https://clickhouse.com/docs/en/sql-reference/statements/insert-into)
549    pub settings: Option<Vec<Setting>>,
550    /// Format for `INSERT` statement when not using standard SQL format. Can be e.g. `CSV`,
551    /// `JSON`, `JSONAsString`, `LineAsString` and more.
552    ///
553    /// ClickHouse syntax: `INSERT INTO tbl FORMAT JSONEachRow {"foo": 1, "bar": 2}, {"foo": 3}`
554    ///
555    /// [ClickHouse formats JSON insert](https://clickhouse.com/docs/en/interfaces/formats#json-inserting-data)
556    pub format_clause: Option<InputFormatClause>,
557}
558
559impl Display for Insert {
560    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
561        let table_name = if let Some(alias) = &self.table_alias {
562            format!("{0} AS {alias}", self.table)
563        } else {
564            self.table.to_string()
565        };
566
567        if let Some(on_conflict) = self.or {
568            write!(f, "INSERT {on_conflict} INTO {table_name} ")?;
569        } else {
570            write!(
571                f,
572                "{start}",
573                start = if self.replace_into {
574                    "REPLACE"
575                } else {
576                    "INSERT"
577                },
578            )?;
579            if let Some(priority) = self.priority {
580                write!(f, " {priority}",)?;
581            }
582
583            write!(
584                f,
585                "{ignore}{over}{int}{tbl} {table_name} ",
586                table_name = table_name,
587                ignore = if self.ignore { " IGNORE" } else { "" },
588                over = if self.overwrite { " OVERWRITE" } else { "" },
589                int = if self.into { " INTO" } else { "" },
590                tbl = if self.has_table_keyword { " TABLE" } else { "" },
591            )?;
592        }
593        if !self.columns.is_empty() {
594            write!(f, "({}) ", display_comma_separated(&self.columns))?;
595        }
596        if let Some(ref parts) = self.partitioned {
597            if !parts.is_empty() {
598                write!(f, "PARTITION ({}) ", display_comma_separated(parts))?;
599            }
600        }
601        if !self.after_columns.is_empty() {
602            write!(f, "({}) ", display_comma_separated(&self.after_columns))?;
603        }
604
605        if let Some(settings) = &self.settings {
606            write!(f, "SETTINGS {} ", display_comma_separated(settings))?;
607        }
608
609        if let Some(source) = &self.source {
610            write!(f, "{source}")?;
611        } else if !self.assignments.is_empty() {
612            write!(f, "SET ")?;
613            write!(f, "{}", display_comma_separated(&self.assignments))?;
614        } else if let Some(format_clause) = &self.format_clause {
615            write!(f, "{format_clause}")?;
616        } else if self.columns.is_empty() {
617            write!(f, "DEFAULT VALUES")?;
618        }
619
620        if let Some(insert_alias) = &self.insert_alias {
621            write!(f, " AS {0}", insert_alias.row_alias)?;
622
623            if let Some(col_aliases) = &insert_alias.col_aliases {
624                if !col_aliases.is_empty() {
625                    write!(f, " ({})", display_comma_separated(col_aliases))?;
626                }
627            }
628        }
629
630        if let Some(on) = &self.on {
631            write!(f, "{on}")?;
632        }
633
634        if let Some(returning) = &self.returning {
635            write!(f, " RETURNING {}", display_comma_separated(returning))?;
636        }
637        Ok(())
638    }
639}
640
641/// DELETE statement.
642#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
643#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
644#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
645pub struct Delete {
646    /// Multi tables delete are supported in mysql
647    pub tables: Vec<ObjectName>,
648    /// FROM
649    pub from: FromTable,
650    /// USING (Snowflake, Postgres, MySQL)
651    pub using: Option<Vec<TableWithJoins>>,
652    /// WHERE
653    pub selection: Option<Expr>,
654    /// RETURNING
655    pub returning: Option<Vec<SelectItem>>,
656    /// ORDER BY (MySQL)
657    pub order_by: Vec<OrderByExpr>,
658    /// LIMIT (MySQL)
659    pub limit: Option<Expr>,
660}
661
662impl Display for Delete {
663    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
664        write!(f, "DELETE ")?;
665        if !self.tables.is_empty() {
666            write!(f, "{} ", display_comma_separated(&self.tables))?;
667        }
668        match &self.from {
669            FromTable::WithFromKeyword(from) => {
670                write!(f, "FROM {}", display_comma_separated(from))?;
671            }
672            FromTable::WithoutKeyword(from) => {
673                write!(f, "{}", display_comma_separated(from))?;
674            }
675        }
676        if let Some(using) = &self.using {
677            write!(f, " USING {}", display_comma_separated(using))?;
678        }
679        if let Some(selection) = &self.selection {
680            write!(f, " WHERE {selection}")?;
681        }
682        if let Some(returning) = &self.returning {
683            write!(f, " RETURNING {}", display_comma_separated(returning))?;
684        }
685        if !self.order_by.is_empty() {
686            write!(f, " ORDER BY {}", display_comma_separated(&self.order_by))?;
687        }
688        if let Some(limit) = &self.limit {
689            write!(f, " LIMIT {limit}")?;
690        }
691        Ok(())
692    }
693}