sqlparser/ast/
ddl.rs

1// Licensed under the Apache License, Version 2.0 (the "License");
2// you may not use this file except in compliance with the License.
3// You may obtain a copy of the License at
4//
5// http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS,
9// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10// See the License for the specific language governing permissions and
11// limitations under the License.
12
13//! AST types specific to CREATE/ALTER variants of [`Statement`](crate::ast::Statement)
14//! (commonly referred to as Data Definition Language, or DDL)
15
16#[cfg(not(feature = "std"))]
17use alloc::{boxed::Box, string::String, vec::Vec};
18use core::fmt;
19
20#[cfg(feature = "serde")]
21use serde::{Deserialize, Serialize};
22
23#[cfg(feature = "visitor")]
24use sqlparser_derive::{Visit, VisitMut};
25
26use crate::ast::value::escape_single_quote_string;
27use crate::ast::{
28    display_comma_separated, display_separated, DataType, Expr, Ident, ObjectName, SequenceOptions,
29};
30use crate::tokenizer::Token;
31
32/// An `ALTER TABLE` (`Statement::AlterTable`) operation
33#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
34#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
35#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
36pub enum AlterTableOperation {
37    /// `ADD <table_constraint>`
38    AddConstraint(TableConstraint),
39    /// `ADD [COLUMN] [IF NOT EXISTS] <column_def>`
40    AddColumn {
41        /// `[COLUMN]`.
42        column_keyword: bool,
43        /// `[IF NOT EXISTS]`
44        if_not_exists: bool,
45        /// <column_def>.
46        column_def: ColumnDef,
47    },
48    /// `DROP CONSTRAINT [ IF EXISTS ] <name>`
49    DropConstraint {
50        if_exists: bool,
51        name: Ident,
52        cascade: bool,
53    },
54    /// `DROP [ COLUMN ] [ IF EXISTS ] <column_name> [ CASCADE ]`
55    DropColumn {
56        column_name: Ident,
57        if_exists: bool,
58        cascade: bool,
59    },
60    /// `DROP PRIMARY KEY`
61    ///
62    /// Note: this is a MySQL-specific operation.
63    DropPrimaryKey,
64    /// `RENAME TO PARTITION (partition=val)`
65    RenamePartitions {
66        old_partitions: Vec<Expr>,
67        new_partitions: Vec<Expr>,
68    },
69    /// Add Partitions
70    AddPartitions {
71        if_not_exists: bool,
72        new_partitions: Vec<Partition>,
73    },
74    DropPartitions {
75        partitions: Vec<Expr>,
76        if_exists: bool,
77    },
78    /// `RENAME [ COLUMN ] <old_column_name> TO <new_column_name>`
79    RenameColumn {
80        old_column_name: Ident,
81        new_column_name: Ident,
82    },
83    /// `RENAME TO <table_name>`
84    RenameTable { table_name: ObjectName },
85    // CHANGE [ COLUMN ] <old_name> <new_name> <data_type> [ <options> ]
86    ChangeColumn {
87        old_name: Ident,
88        new_name: Ident,
89        data_type: DataType,
90        options: Vec<ColumnOption>,
91    },
92    /// `RENAME CONSTRAINT <old_constraint_name> TO <new_constraint_name>`
93    ///
94    /// Note: this is a PostgreSQL-specific operation.
95    RenameConstraint { old_name: Ident, new_name: Ident },
96    /// `ALTER [ COLUMN ]`
97    AlterColumn {
98        column_name: Ident,
99        op: AlterColumnOperation,
100    },
101    /// 'SWAP WITH <table_name>'
102    ///
103    /// Note: this is Snowflake specific <https://docs.snowflake.com/en/sql-reference/sql/alter-table>
104    SwapWith { table_name: ObjectName },
105}
106
107#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
108#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
109#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
110pub enum AlterIndexOperation {
111    RenameIndex { index_name: ObjectName },
112}
113
114impl fmt::Display for AlterTableOperation {
115    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116        match self {
117            AlterTableOperation::AddPartitions {
118                if_not_exists,
119                new_partitions,
120            } => write!(
121                f,
122                "ADD{ine} {}",
123                display_separated(new_partitions, " "),
124                ine = if *if_not_exists { " IF NOT EXISTS" } else { "" }
125            ),
126            AlterTableOperation::AddConstraint(c) => write!(f, "ADD {c}"),
127            AlterTableOperation::AddColumn {
128                column_keyword,
129                if_not_exists,
130                column_def,
131            } => {
132                write!(f, "ADD")?;
133                if *column_keyword {
134                    write!(f, " COLUMN")?;
135                }
136                if *if_not_exists {
137                    write!(f, " IF NOT EXISTS")?;
138                }
139                write!(f, " {column_def}")?;
140
141                Ok(())
142            }
143            AlterTableOperation::AlterColumn { column_name, op } => {
144                write!(f, "ALTER COLUMN {column_name} {op}")
145            }
146            AlterTableOperation::DropPartitions {
147                partitions,
148                if_exists,
149            } => write!(
150                f,
151                "DROP{ie} PARTITION ({})",
152                display_comma_separated(partitions),
153                ie = if *if_exists { " IF EXISTS" } else { "" }
154            ),
155            AlterTableOperation::DropConstraint {
156                if_exists,
157                name,
158                cascade,
159            } => {
160                write!(
161                    f,
162                    "DROP CONSTRAINT {}{}{}",
163                    if *if_exists { "IF EXISTS " } else { "" },
164                    name,
165                    if *cascade { " CASCADE" } else { "" },
166                )
167            }
168            AlterTableOperation::DropPrimaryKey => write!(f, "DROP PRIMARY KEY"),
169            AlterTableOperation::DropColumn {
170                column_name,
171                if_exists,
172                cascade,
173            } => write!(
174                f,
175                "DROP COLUMN {}{}{}",
176                if *if_exists { "IF EXISTS " } else { "" },
177                column_name,
178                if *cascade { " CASCADE" } else { "" }
179            ),
180            AlterTableOperation::RenamePartitions {
181                old_partitions,
182                new_partitions,
183            } => write!(
184                f,
185                "PARTITION ({}) RENAME TO PARTITION ({})",
186                display_comma_separated(old_partitions),
187                display_comma_separated(new_partitions)
188            ),
189            AlterTableOperation::RenameColumn {
190                old_column_name,
191                new_column_name,
192            } => write!(f, "RENAME COLUMN {old_column_name} TO {new_column_name}"),
193            AlterTableOperation::RenameTable { table_name } => {
194                write!(f, "RENAME TO {table_name}")
195            }
196            AlterTableOperation::ChangeColumn {
197                old_name,
198                new_name,
199                data_type,
200                options,
201            } => {
202                write!(f, "CHANGE COLUMN {old_name} {new_name} {data_type}")?;
203                if options.is_empty() {
204                    Ok(())
205                } else {
206                    write!(f, " {}", display_separated(options, " "))
207                }
208            }
209            AlterTableOperation::RenameConstraint { old_name, new_name } => {
210                write!(f, "RENAME CONSTRAINT {old_name} TO {new_name}")
211            }
212            AlterTableOperation::SwapWith { table_name } => {
213                write!(f, "SWAP WITH {table_name}")
214            }
215        }
216    }
217}
218
219impl fmt::Display for AlterIndexOperation {
220    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
221        match self {
222            AlterIndexOperation::RenameIndex { index_name } => {
223                write!(f, "RENAME TO {index_name}")
224            }
225        }
226    }
227}
228
229/// An `ALTER COLUMN` (`Statement::AlterTable`) operation
230#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
231#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
232#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
233pub enum AlterColumnOperation {
234    /// `SET NOT NULL`
235    SetNotNull,
236    /// `DROP NOT NULL`
237    DropNotNull,
238    /// `SET DEFAULT <expr>`
239    SetDefault { value: Expr },
240    /// `DROP DEFAULT`
241    DropDefault,
242    /// `[SET DATA] TYPE <data_type> [USING <expr>]`
243    SetDataType {
244        data_type: DataType,
245        /// PostgreSQL specific
246        using: Option<Expr>,
247    },
248}
249
250impl fmt::Display for AlterColumnOperation {
251    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
252        match self {
253            AlterColumnOperation::SetNotNull => write!(f, "SET NOT NULL",),
254            AlterColumnOperation::DropNotNull => write!(f, "DROP NOT NULL",),
255            AlterColumnOperation::SetDefault { value } => {
256                write!(f, "SET DEFAULT {value}")
257            }
258            AlterColumnOperation::DropDefault {} => {
259                write!(f, "DROP DEFAULT")
260            }
261            AlterColumnOperation::SetDataType { data_type, using } => {
262                if let Some(expr) = using {
263                    write!(f, "SET DATA TYPE {data_type} USING {expr}")
264                } else {
265                    write!(f, "SET DATA TYPE {data_type}")
266                }
267            }
268        }
269    }
270}
271
272/// A table-level constraint, specified in a `CREATE TABLE` or an
273/// `ALTER TABLE ADD <constraint>` statement.
274#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
275#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
276#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
277pub enum TableConstraint {
278    /// `[ CONSTRAINT <name> ] { PRIMARY KEY | UNIQUE } (<columns>)`
279    Unique {
280        name: Option<Ident>,
281        columns: Vec<Ident>,
282        /// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint
283        is_primary: bool,
284    },
285    /// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
286    /// REFERENCES <foreign_table> (<referred_columns>)
287    /// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
288    ///   [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
289    /// }`).
290    ForeignKey {
291        name: Option<Ident>,
292        columns: Vec<Ident>,
293        foreign_table: ObjectName,
294        referred_columns: Vec<Ident>,
295        on_delete: Option<ReferentialAction>,
296        on_update: Option<ReferentialAction>,
297    },
298    /// `[ CONSTRAINT <name> ] CHECK (<expr>)`
299    Check {
300        name: Option<Ident>,
301        expr: Box<Expr>,
302    },
303    /// MySQLs [index definition][1] for index creation. Not present on ANSI so, for now, the usage
304    /// is restricted to MySQL, as no other dialects that support this syntax were found.
305    ///
306    /// `{INDEX | KEY} [index_name] [index_type] (key_part,...) [index_option]...`
307    ///
308    /// [1]: https://dev.mysql.com/doc/refman/8.0/en/create-table.html
309    Index {
310        /// Whether this index starts with KEY (true) or INDEX (false), to maintain the same syntax.
311        display_as_key: bool,
312        /// Index name.
313        name: Option<Ident>,
314        /// Optional [index type][1].
315        ///
316        /// [1]: IndexType
317        index_type: Option<IndexType>,
318        /// Referred column identifier list.
319        columns: Vec<Ident>,
320    },
321    /// MySQLs [fulltext][1] definition. Since the [`SPATIAL`][2] definition is exactly the same,
322    /// and MySQL displays both the same way, it is part of this definition as well.
323    ///
324    /// Supported syntax:
325    ///
326    /// ```markdown
327    /// {FULLTEXT | SPATIAL} [INDEX | KEY] [index_name] (key_part,...)
328    ///
329    /// key_part: col_name
330    /// ```
331    ///
332    /// [1]: https://dev.mysql.com/doc/refman/8.0/en/fulltext-natural-language.html
333    /// [2]: https://dev.mysql.com/doc/refman/8.0/en/spatial-types.html
334    FulltextOrSpatial {
335        /// Whether this is a `FULLTEXT` (true) or `SPATIAL` (false) definition.
336        fulltext: bool,
337        /// Whether the type is followed by the keyword `KEY`, `INDEX`, or no keyword at all.
338        index_type_display: KeyOrIndexDisplay,
339        /// Optional index name.
340        opt_index_name: Option<Ident>,
341        /// Referred column identifier list.
342        columns: Vec<Ident>,
343    },
344}
345
346impl fmt::Display for TableConstraint {
347    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
348        match self {
349            TableConstraint::Unique {
350                name,
351                columns,
352                is_primary,
353            } => write!(
354                f,
355                "{}{} ({})",
356                display_constraint_name(name),
357                if *is_primary { "PRIMARY KEY" } else { "UNIQUE" },
358                display_comma_separated(columns)
359            ),
360            TableConstraint::ForeignKey {
361                name,
362                columns,
363                foreign_table,
364                referred_columns,
365                on_delete,
366                on_update,
367            } => {
368                write!(
369                    f,
370                    "{}FOREIGN KEY ({}) REFERENCES {}({})",
371                    display_constraint_name(name),
372                    display_comma_separated(columns),
373                    foreign_table,
374                    display_comma_separated(referred_columns),
375                )?;
376                if let Some(action) = on_delete {
377                    write!(f, " ON DELETE {action}")?;
378                }
379                if let Some(action) = on_update {
380                    write!(f, " ON UPDATE {action}")?;
381                }
382                Ok(())
383            }
384            TableConstraint::Check { name, expr } => {
385                write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
386            }
387            TableConstraint::Index {
388                display_as_key,
389                name,
390                index_type,
391                columns,
392            } => {
393                write!(f, "{}", if *display_as_key { "KEY" } else { "INDEX" })?;
394                if let Some(name) = name {
395                    write!(f, " {name}")?;
396                }
397                if let Some(index_type) = index_type {
398                    write!(f, " USING {index_type}")?;
399                }
400                write!(f, " ({})", display_comma_separated(columns))?;
401
402                Ok(())
403            }
404            Self::FulltextOrSpatial {
405                fulltext,
406                index_type_display,
407                opt_index_name,
408                columns,
409            } => {
410                if *fulltext {
411                    write!(f, "FULLTEXT")?;
412                } else {
413                    write!(f, "SPATIAL")?;
414                }
415
416                if !matches!(index_type_display, KeyOrIndexDisplay::None) {
417                    write!(f, " {index_type_display}")?;
418                }
419
420                if let Some(name) = opt_index_name {
421                    write!(f, " {name}")?;
422                }
423
424                write!(f, " ({})", display_comma_separated(columns))?;
425
426                Ok(())
427            }
428        }
429    }
430}
431
432/// Representation whether a definition can can contains the KEY or INDEX keywords with the same
433/// meaning.
434///
435/// This enum initially is directed to `FULLTEXT`,`SPATIAL`, and `UNIQUE` indexes on create table
436/// statements of `MySQL` [(1)].
437///
438/// [1]: https://dev.mysql.com/doc/refman/8.0/en/create-table.html
439#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
440#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
441#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
442pub enum KeyOrIndexDisplay {
443    /// Nothing to display
444    None,
445    /// Display the KEY keyword
446    Key,
447    /// Display the INDEX keyword
448    Index,
449}
450
451impl fmt::Display for KeyOrIndexDisplay {
452    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
453        match self {
454            KeyOrIndexDisplay::None => {
455                write!(f, "")
456            }
457            KeyOrIndexDisplay::Key => {
458                write!(f, "KEY")
459            }
460            KeyOrIndexDisplay::Index => {
461                write!(f, "INDEX")
462            }
463        }
464    }
465}
466
467/// Indexing method used by that index.
468///
469/// This structure isn't present on ANSI, but is found at least in [`MySQL` CREATE TABLE][1],
470/// [`MySQL` CREATE INDEX][2], and [Postgresql CREATE INDEX][3] statements.
471///
472/// [1]: https://dev.mysql.com/doc/refman/8.0/en/create-table.html
473/// [2]: https://dev.mysql.com/doc/refman/8.0/en/create-index.html
474/// [3]: https://www.postgresql.org/docs/14/sql-createindex.html
475#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
476#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
477#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
478pub enum IndexType {
479    BTree,
480    Hash,
481    // TODO add Postgresql's possible indexes
482}
483
484impl fmt::Display for IndexType {
485    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
486        match self {
487            Self::BTree => write!(f, "BTREE"),
488            Self::Hash => write!(f, "HASH"),
489        }
490    }
491}
492#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
493#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
494#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
495pub struct ProcedureParam {
496    pub name: Ident,
497    pub data_type: DataType,
498}
499
500impl fmt::Display for ProcedureParam {
501    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
502        write!(f, "{} {}", self.name, self.data_type)
503    }
504}
505
506/// SQL column definition
507#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
508#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
509#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
510pub struct ColumnDef {
511    pub name: Ident,
512    pub data_type: DataType,
513    pub collation: Option<ObjectName>,
514    pub options: Vec<ColumnOptionDef>,
515}
516
517impl fmt::Display for ColumnDef {
518    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
519        write!(f, "{} {}", self.name, self.data_type)?;
520        if let Some(collation) = &self.collation {
521            write!(f, " COLLATE {collation}")?;
522        }
523        for option in &self.options {
524            write!(f, " {option}")?;
525        }
526        Ok(())
527    }
528}
529
530/// An optionally-named `ColumnOption`: `[ CONSTRAINT <name> ] <column-option>`.
531///
532/// Note that implementations are substantially more permissive than the ANSI
533/// specification on what order column options can be presented in, and whether
534/// they are allowed to be named. The specification distinguishes between
535/// constraints (NOT NULL, UNIQUE, PRIMARY KEY, and CHECK), which can be named
536/// and can appear in any order, and other options (DEFAULT, GENERATED), which
537/// cannot be named and must appear in a fixed order. `PostgreSQL`, however,
538/// allows preceding any option with `CONSTRAINT <name>`, even those that are
539/// not really constraints, like NULL and DEFAULT. MSSQL is less permissive,
540/// allowing DEFAULT, UNIQUE, PRIMARY KEY and CHECK to be named, but not NULL or
541/// NOT NULL constraints (the last of which is in violation of the spec).
542///
543/// For maximum flexibility, we don't distinguish between constraint and
544/// non-constraint options, lumping them all together under the umbrella of
545/// "column options," and we allow any column option to be named.
546#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
547#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
548#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
549pub struct ColumnOptionDef {
550    pub name: Option<Ident>,
551    pub option: ColumnOption,
552}
553
554impl fmt::Display for ColumnOptionDef {
555    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
556        write!(f, "{}{}", display_constraint_name(&self.name), self.option)
557    }
558}
559
560/// `ColumnOption`s are modifiers that follow a column definition in a `CREATE
561/// TABLE` statement.
562#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
563#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
564#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
565pub enum ColumnOption {
566    /// `NULL`
567    Null,
568    /// `NOT NULL`
569    NotNull,
570    /// `DEFAULT <restricted-expr>`
571    Default(Expr),
572    /// `{ PRIMARY KEY | UNIQUE }`
573    Unique {
574        is_primary: bool,
575    },
576    /// A referential integrity constraint (`[FOREIGN KEY REFERENCES
577    /// <foreign_table> (<referred_columns>)
578    /// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
579    ///   [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
580    /// }`).
581    ForeignKey {
582        foreign_table: ObjectName,
583        referred_columns: Vec<Ident>,
584        on_delete: Option<ReferentialAction>,
585        on_update: Option<ReferentialAction>,
586    },
587    /// `CHECK (<expr>)`
588    Check(Expr),
589    /// Dialect-specific options, such as:
590    /// - MySQL's `AUTO_INCREMENT` or SQLite's `AUTOINCREMENT`
591    /// - ...
592    DialectSpecific(Vec<Token>),
593    CharacterSet(ObjectName),
594    Comment(String),
595    OnUpdate(Expr),
596    /// `Generated`s are modifiers that follow a column definition in a `CREATE
597    /// TABLE` statement.
598    Generated {
599        generated_as: GeneratedAs,
600        sequence_options: Option<Vec<SequenceOptions>>,
601        generation_expr: Option<Expr>,
602    },
603}
604
605impl fmt::Display for ColumnOption {
606    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
607        use ColumnOption::*;
608        match self {
609            Null => write!(f, "NULL"),
610            NotNull => write!(f, "NOT NULL"),
611            Default(expr) => write!(f, "DEFAULT {expr}"),
612            Unique { is_primary } => {
613                write!(f, "{}", if *is_primary { "PRIMARY KEY" } else { "UNIQUE" })
614            }
615            ForeignKey {
616                foreign_table,
617                referred_columns,
618                on_delete,
619                on_update,
620            } => {
621                write!(f, "REFERENCES {foreign_table}")?;
622                if !referred_columns.is_empty() {
623                    write!(f, " ({})", display_comma_separated(referred_columns))?;
624                }
625                if let Some(action) = on_delete {
626                    write!(f, " ON DELETE {action}")?;
627                }
628                if let Some(action) = on_update {
629                    write!(f, " ON UPDATE {action}")?;
630                }
631                Ok(())
632            }
633            Check(expr) => write!(f, "CHECK ({expr})"),
634            DialectSpecific(val) => write!(f, "{}", display_separated(val, " ")),
635            CharacterSet(n) => write!(f, "CHARACTER SET {n}"),
636            Comment(v) => write!(f, "COMMENT '{}'", escape_single_quote_string(v)),
637            OnUpdate(expr) => write!(f, "ON UPDATE {expr}"),
638            Generated {
639                generated_as,
640                sequence_options,
641                generation_expr,
642            } => match generated_as {
643                GeneratedAs::Always => {
644                    write!(f, "GENERATED ALWAYS AS IDENTITY")?;
645                    if sequence_options.is_some() {
646                        let so = sequence_options.as_ref().unwrap();
647                        if !so.is_empty() {
648                            write!(f, " (")?;
649                        }
650                        for sequence_option in so {
651                            write!(f, "{sequence_option}")?;
652                        }
653                        if !so.is_empty() {
654                            write!(f, " )")?;
655                        }
656                    }
657                    Ok(())
658                }
659                GeneratedAs::ByDefault => {
660                    write!(f, "GENERATED BY DEFAULT AS IDENTITY")?;
661                    if sequence_options.is_some() {
662                        let so = sequence_options.as_ref().unwrap();
663                        if !so.is_empty() {
664                            write!(f, " (")?;
665                        }
666                        for sequence_option in so {
667                            write!(f, "{sequence_option}")?;
668                        }
669                        if !so.is_empty() {
670                            write!(f, " )")?;
671                        }
672                    }
673                    Ok(())
674                }
675                GeneratedAs::ExpStored => {
676                    let expr = generation_expr.as_ref().unwrap();
677                    write!(f, "GENERATED ALWAYS AS ({expr}) STORED")
678                }
679            },
680        }
681    }
682}
683
684/// `GeneratedAs`s are modifiers that follow a column option in a `generated`.
685/// 'ExpStored' is PostgreSQL specific
686#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
687#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
688#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
689pub enum GeneratedAs {
690    Always,
691    ByDefault,
692    ExpStored,
693}
694
695fn display_constraint_name(name: &'_ Option<Ident>) -> impl fmt::Display + '_ {
696    struct ConstraintName<'a>(&'a Option<Ident>);
697    impl<'a> fmt::Display for ConstraintName<'a> {
698        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
699            if let Some(name) = self.0 {
700                write!(f, "CONSTRAINT {name} ")?;
701            }
702            Ok(())
703        }
704    }
705    ConstraintName(name)
706}
707
708/// `<referential_action> =
709/// { RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT }`
710///
711/// Used in foreign key constraints in `ON UPDATE` and `ON DELETE` options.
712#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
713#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
714#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
715pub enum ReferentialAction {
716    Restrict,
717    Cascade,
718    SetNull,
719    NoAction,
720    SetDefault,
721}
722
723impl fmt::Display for ReferentialAction {
724    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
725        f.write_str(match self {
726            ReferentialAction::Restrict => "RESTRICT",
727            ReferentialAction::Cascade => "CASCADE",
728            ReferentialAction::SetNull => "SET NULL",
729            ReferentialAction::NoAction => "NO ACTION",
730            ReferentialAction::SetDefault => "SET DEFAULT",
731        })
732    }
733}
734
735/// SQL user defined type definition
736#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
737#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
738#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
739pub enum UserDefinedTypeRepresentation {
740    Composite {
741        attributes: Vec<UserDefinedTypeCompositeAttributeDef>,
742    },
743}
744
745impl fmt::Display for UserDefinedTypeRepresentation {
746    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
747        match self {
748            UserDefinedTypeRepresentation::Composite { attributes } => {
749                write!(f, "({})", display_comma_separated(attributes))
750            }
751        }
752    }
753}
754
755/// SQL user defined type attribute definition
756#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
757#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
758#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
759pub struct UserDefinedTypeCompositeAttributeDef {
760    pub name: Ident,
761    pub data_type: DataType,
762    pub collation: Option<ObjectName>,
763}
764
765impl fmt::Display for UserDefinedTypeCompositeAttributeDef {
766    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
767        write!(f, "{} {}", self.name, self.data_type)?;
768        if let Some(collation) = &self.collation {
769            write!(f, " COLLATE {collation}")?;
770        }
771        Ok(())
772    }
773}
774
775/// PARTITION statement used in ALTER TABLE et al. such as in Hive SQL
776#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
777#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
778#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
779pub struct Partition {
780    pub partitions: Vec<Expr>,
781}
782
783impl fmt::Display for Partition {
784    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
785        write!(
786            f,
787            "PARTITION ({})",
788            display_comma_separated(&self.partitions)
789        )
790    }
791}