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