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