wing_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]
14//! (commonly referred to as Data Definition Language, or DDL)
15
16#[cfg(not(feature = "std"))]
17use alloc::{boxed::Box, string::ToString, vec::Vec};
18use core::fmt;
19
20#[cfg(feature = "serde")]
21use serde::{Deserialize, Serialize};
22
23use crate::ast::{display_comma_separated, display_separated, DataType, Expr, Ident, ObjectName};
24use crate::tokenizer::Token;
25
26/// An `ALTER TABLE` (`Statement::AlterTable`) operation
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29pub enum AlterTableOperation {
30    /// `ADD <table_constraint>`
31    AddConstraint(TableConstraint),
32    /// `ADD [ COLUMN ] <column_def>`
33    AddColumn { column_def: ColumnDef },
34    /// TODO: implement `DROP CONSTRAINT <name>`
35    DropConstraint { name: Ident },
36    /// `DROP [ COLUMN ] [ IF EXISTS ] <column_name> [ CASCADE ]`
37    DropColumn {
38        column_name: Ident,
39        if_exists: bool,
40        cascade: bool,
41    },
42    /// `RENAME TO PARTITION (partition=val)`
43    RenamePartitions {
44        old_partitions: Vec<Expr>,
45        new_partitions: Vec<Expr>,
46    },
47    /// Add Partitions
48    AddPartitions {
49        if_not_exists: bool,
50        new_partitions: Vec<Expr>,
51    },
52    DropPartitions {
53        partitions: Vec<Expr>,
54        if_exists: bool,
55    },
56    /// `RENAME [ COLUMN ] <old_column_name> TO <new_column_name>`
57    RenameColumn {
58        old_column_name: Ident,
59        new_column_name: Ident,
60    },
61    /// `RENAME TO <table_name>`
62    RenameTable { table_name: ObjectName },
63    // CHANGE [ COLUMN ] <old_name> <new_name> <data_type> [ <options> ]
64    ChangeColumn {
65        old_name: Ident,
66        new_name: Ident,
67        data_type: DataType,
68        options: Vec<ColumnOption>,
69    },
70    /// `RENAME CONSTRAINT <old_constraint_name> TO <new_constraint_name>`
71    ///
72    /// Note: this is a PostgreSQL-specific operation.
73    RenameConstraint { old_name: Ident, new_name: Ident },
74    /// `ALTER [ COLUMN ]`
75    AlterColumn {
76        column_name: Ident,
77        op: AlterColumnOperation,
78    },
79}
80
81impl fmt::Display for AlterTableOperation {
82    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83        match self {
84            AlterTableOperation::AddPartitions {
85                if_not_exists,
86                new_partitions,
87            } => write!(
88                f,
89                "ADD{ine} PARTITION ({})",
90                display_comma_separated(new_partitions),
91                ine = if *if_not_exists { " IF NOT EXISTS" } else { "" }
92            ),
93            AlterTableOperation::AddConstraint(c) => write!(f, "ADD {}", c),
94            AlterTableOperation::AddColumn { column_def } => {
95                write!(f, "ADD COLUMN {}", column_def.to_string())
96            }
97            AlterTableOperation::AlterColumn { column_name, op } => {
98                write!(f, "ALTER COLUMN {} {}", column_name, op)
99            }
100            AlterTableOperation::DropPartitions {
101                partitions,
102                if_exists,
103            } => write!(
104                f,
105                "DROP{ie} PARTITION ({})",
106                display_comma_separated(partitions),
107                ie = if *if_exists { " IF EXISTS" } else { "" }
108            ),
109            AlterTableOperation::DropConstraint { name } => write!(f, "DROP CONSTRAINT {}", name),
110            AlterTableOperation::DropColumn {
111                column_name,
112                if_exists,
113                cascade,
114            } => write!(
115                f,
116                "DROP COLUMN {}{}{}",
117                if *if_exists { "IF EXISTS " } else { "" },
118                column_name,
119                if *cascade { " CASCADE" } else { "" }
120            ),
121            AlterTableOperation::RenamePartitions {
122                old_partitions,
123                new_partitions,
124            } => write!(
125                f,
126                "PARTITION ({}) RENAME TO PARTITION ({})",
127                display_comma_separated(old_partitions),
128                display_comma_separated(new_partitions)
129            ),
130            AlterTableOperation::RenameColumn {
131                old_column_name,
132                new_column_name,
133            } => write!(
134                f,
135                "RENAME COLUMN {} TO {}",
136                old_column_name, new_column_name
137            ),
138            AlterTableOperation::RenameTable { table_name } => {
139                write!(f, "RENAME TO {}", table_name)
140            }
141            AlterTableOperation::ChangeColumn {
142                old_name,
143                new_name,
144                data_type,
145                options,
146            } => {
147                write!(f, "CHANGE COLUMN {} {} {}", old_name, new_name, data_type)?;
148                if options.is_empty() {
149                    Ok(())
150                } else {
151                    write!(f, " {}", display_separated(options, " "))
152                }
153            }
154            AlterTableOperation::RenameConstraint { old_name, new_name } => {
155                write!(f, "RENAME CONSTRAINT {} TO {}", old_name, new_name)
156            }
157        }
158    }
159}
160
161/// An `ALTER COLUMN` (`Statement::AlterTable`) operation
162#[derive(Debug, Clone, PartialEq, Eq, Hash)]
163#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
164pub enum AlterColumnOperation {
165    /// `SET NOT NULL`
166    SetNotNull,
167    /// `DROP NOT NULL`
168    DropNotNull,
169    /// `SET DEFAULT <expr>`
170    SetDefault { value: Expr },
171    /// `DROP DEFAULT`
172    DropDefault,
173    /// `[SET DATA] TYPE <data_type> [USING <expr>]`
174    SetDataType {
175        data_type: DataType,
176        /// PostgreSQL specific
177        using: Option<Expr>,
178    },
179}
180
181impl fmt::Display for AlterColumnOperation {
182    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183        match self {
184            AlterColumnOperation::SetNotNull => write!(f, "SET NOT NULL",),
185            AlterColumnOperation::DropNotNull => write!(f, "DROP NOT NULL",),
186            AlterColumnOperation::SetDefault { value } => {
187                write!(f, "SET DEFAULT {}", value)
188            }
189            AlterColumnOperation::DropDefault {} => {
190                write!(f, "DROP DEFAULT")
191            }
192            AlterColumnOperation::SetDataType { data_type, using } => {
193                if let Some(expr) = using {
194                    write!(f, "SET DATA TYPE {} USING {}", data_type, expr)
195                } else {
196                    write!(f, "SET DATA TYPE {}", data_type)
197                }
198            }
199        }
200    }
201}
202
203/// A table-level constraint, specified in a `CREATE TABLE` or an
204/// `ALTER TABLE ADD <constraint>` statement.
205#[derive(Debug, Clone, PartialEq, Eq, Hash)]
206#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
207pub enum TableConstraint {
208    /// `[ CONSTRAINT <name> ] { PRIMARY KEY | UNIQUE } (<columns>)`
209    Unique {
210        name: Option<Ident>,
211        columns: Vec<Ident>,
212        /// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint
213        is_primary: bool,
214    },
215    /// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
216    /// REFERENCES <foreign_table> (<referred_columns>)
217    /// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
218    ///   [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
219    /// }`).
220    ForeignKey {
221        name: Option<Ident>,
222        columns: Vec<Ident>,
223        foreign_table: ObjectName,
224        referred_columns: Vec<Ident>,
225        on_delete: Option<ReferentialAction>,
226        on_update: Option<ReferentialAction>,
227    },
228    /// `[ CONSTRAINT <name> ] CHECK (<expr>)`
229    Check {
230        name: Option<Ident>,
231        expr: Box<Expr>,
232    },
233}
234
235impl fmt::Display for TableConstraint {
236    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
237        match self {
238            TableConstraint::Unique {
239                name,
240                columns,
241                is_primary,
242            } => write!(
243                f,
244                "{}{} ({})",
245                display_constraint_name(name),
246                if *is_primary { "PRIMARY KEY" } else { "UNIQUE" },
247                display_comma_separated(columns)
248            ),
249            TableConstraint::ForeignKey {
250                name,
251                columns,
252                foreign_table,
253                referred_columns,
254                on_delete,
255                on_update,
256            } => {
257                write!(
258                    f,
259                    "{}FOREIGN KEY ({}) REFERENCES {}({})",
260                    display_constraint_name(name),
261                    display_comma_separated(columns),
262                    foreign_table,
263                    display_comma_separated(referred_columns),
264                )?;
265                if let Some(action) = on_delete {
266                    write!(f, " ON DELETE {}", action)?;
267                }
268                if let Some(action) = on_update {
269                    write!(f, " ON UPDATE {}", action)?;
270                }
271                Ok(())
272            }
273            TableConstraint::Check { name, expr } => {
274                write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
275            }
276        }
277    }
278}
279
280/// SQL column definition
281#[derive(Debug, Clone, PartialEq, Eq, Hash)]
282#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
283pub struct ColumnDef {
284    pub name: Ident,
285    pub data_type: DataType,
286    pub collation: Option<ObjectName>,
287    pub options: Vec<ColumnOptionDef>,
288}
289
290impl fmt::Display for ColumnDef {
291    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
292        write!(f, "{} {}", self.name, self.data_type)?;
293        for option in &self.options {
294            write!(f, " {}", option)?;
295        }
296        Ok(())
297    }
298}
299
300/// An optionally-named `ColumnOption`: `[ CONSTRAINT <name> ] <column-option>`.
301///
302/// Note that implementations are substantially more permissive than the ANSI
303/// specification on what order column options can be presented in, and whether
304/// they are allowed to be named. The specification distinguishes between
305/// constraints (NOT NULL, UNIQUE, PRIMARY KEY, and CHECK), which can be named
306/// and can appear in any order, and other options (DEFAULT, GENERATED), which
307/// cannot be named and must appear in a fixed order. PostgreSQL, however,
308/// allows preceding any option with `CONSTRAINT <name>`, even those that are
309/// not really constraints, like NULL and DEFAULT. MSSQL is less permissive,
310/// allowing DEFAULT, UNIQUE, PRIMARY KEY and CHECK to be named, but not NULL or
311/// NOT NULL constraints (the last of which is in violation of the spec).
312///
313/// For maximum flexibility, we don't distinguish between constraint and
314/// non-constraint options, lumping them all together under the umbrella of
315/// "column options," and we allow any column option to be named.
316#[derive(Debug, Clone, PartialEq, Eq, Hash)]
317#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
318pub struct ColumnOptionDef {
319    pub name: Option<Ident>,
320    pub option: ColumnOption,
321}
322
323impl fmt::Display for ColumnOptionDef {
324    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
325        write!(f, "{}{}", display_constraint_name(&self.name), self.option)
326    }
327}
328
329/// `ColumnOption`s are modifiers that follow a column definition in a `CREATE
330/// TABLE` statement.
331#[derive(Debug, Clone, PartialEq, Eq, Hash)]
332#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
333pub enum ColumnOption {
334    /// `NULL`
335    Null,
336    /// `NOT NULL`
337    NotNull,
338    /// `DEFAULT <restricted-expr>`
339    Default(Expr),
340    /// `{ PRIMARY KEY | UNIQUE }`
341    Unique { is_primary: bool },
342    /// A referential integrity constraint (`[FOREIGN KEY REFERENCES
343    /// <foreign_table> (<referred_columns>)
344    /// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
345    ///   [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
346    /// }`).
347    ForeignKey {
348        foreign_table: ObjectName,
349        referred_columns: Vec<Ident>,
350        on_delete: Option<ReferentialAction>,
351        on_update: Option<ReferentialAction>,
352    },
353    /// `CHECK (<expr>)`
354    Check(Expr),
355    /// Dialect-specific options, such as:
356    /// - MySQL's `AUTO_INCREMENT` or SQLite's `AUTOINCREMENT`
357    /// - ...
358    DialectSpecific(Vec<Token>),
359}
360
361impl fmt::Display for ColumnOption {
362    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
363        use ColumnOption::*;
364        match self {
365            Null => write!(f, "NULL"),
366            NotNull => write!(f, "NOT NULL"),
367            Default(expr) => write!(f, "DEFAULT {}", expr),
368            Unique { is_primary } => {
369                write!(f, "{}", if *is_primary { "PRIMARY KEY" } else { "UNIQUE" })
370            }
371            ForeignKey {
372                foreign_table,
373                referred_columns,
374                on_delete,
375                on_update,
376            } => {
377                write!(f, "REFERENCES {}", foreign_table)?;
378                if !referred_columns.is_empty() {
379                    write!(f, " ({})", display_comma_separated(referred_columns))?;
380                }
381                if let Some(action) = on_delete {
382                    write!(f, " ON DELETE {}", action)?;
383                }
384                if let Some(action) = on_update {
385                    write!(f, " ON UPDATE {}", action)?;
386                }
387                Ok(())
388            }
389            Check(expr) => write!(f, "CHECK ({})", expr),
390            DialectSpecific(val) => write!(f, "{}", display_separated(val, " ")),
391        }
392    }
393}
394
395fn display_constraint_name(name: &'_ Option<Ident>) -> impl fmt::Display + '_ {
396    struct ConstraintName<'a>(&'a Option<Ident>);
397    impl<'a> fmt::Display for ConstraintName<'a> {
398        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
399            if let Some(name) = self.0 {
400                write!(f, "CONSTRAINT {} ", name)?;
401            }
402            Ok(())
403        }
404    }
405    ConstraintName(name)
406}
407
408/// `<referential_action> =
409/// { RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT }`
410///
411/// Used in foreign key constraints in `ON UPDATE` and `ON DELETE` options.
412#[derive(Debug, Clone, PartialEq, Eq, Hash)]
413#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
414pub enum ReferentialAction {
415    Restrict,
416    Cascade,
417    SetNull,
418    NoAction,
419    SetDefault,
420}
421
422impl fmt::Display for ReferentialAction {
423    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
424        f.write_str(match self {
425            ReferentialAction::Restrict => "RESTRICT",
426            ReferentialAction::Cascade => "CASCADE",
427            ReferentialAction::SetNull => "SET NULL",
428            ReferentialAction::NoAction => "NO ACTION",
429            ReferentialAction::SetDefault => "SET DEFAULT",
430        })
431    }
432}