sql_ast/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)
15use super::{display_comma_separated, DataType, Expr, Ident, ObjectName};
16use std::fmt;
17
18/// An `ALTER TABLE` (`Statement::AlterTable`) operation
19#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub enum AlterTableOperation {
21    AddColumn(ColumnDef),
22    /// `ADD <table_constraint>`
23    AddConstraint(TableConstraint),
24    DropColumn {
25        column: Ident,
26        if_exists: bool,
27        cascade: bool,
28    },
29    /// TODO: implement `DROP CONSTRAINT <name>`
30    DropConstraint {
31        name: Ident,
32    },
33}
34
35impl fmt::Display for AlterTableOperation {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        match self {
38            AlterTableOperation::AddColumn(column_def) => write!(f, "ADD COLUMN {}", column_def),
39            AlterTableOperation::AddConstraint(c) => write!(f, "ADD {}", c),
40            AlterTableOperation::DropColumn {
41                column,
42                if_exists,
43                cascade,
44            } => write!(
45                f,
46                "DROP COLUMN {}{} {}",
47                if *if_exists { "IF EXISTS " } else { " " },
48                column,
49                if *cascade { "CASCADE" } else { "" }
50            ),
51            AlterTableOperation::DropConstraint { name } => write!(f, "DROP CONSTRAINT {}", name),
52        }
53    }
54}
55
56/// A table-level constraint, specified in a `CREATE TABLE` or an
57/// `ALTER TABLE ADD <constraint>` statement.
58#[derive(Debug, Clone, PartialEq, Eq, Hash)]
59pub enum TableConstraint {
60    /// `[ CONSTRAINT <name> ] { PRIMARY KEY | UNIQUE } (<columns>)`
61    Unique {
62        name: Option<Ident>,
63        columns: Vec<Ident>,
64        /// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint
65        is_primary: bool,
66    },
67    /// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
68    /// REFERENCES <foreign_table> (<referred_columns>)`)
69    ForeignKey {
70        name: Option<Ident>,
71        columns: Vec<Ident>,
72        foreign_table: ObjectName,
73        referred_columns: Vec<Ident>,
74    },
75    /// `[ CONSTRAINT <name> ] CHECK (<expr>)`
76    Check {
77        name: Option<Ident>,
78        expr: Box<Expr>,
79    },
80}
81
82impl fmt::Display for TableConstraint {
83    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
84        match self {
85            TableConstraint::Unique {
86                name,
87                columns,
88                is_primary,
89            } => write!(
90                f,
91                "{}{} ({})",
92                display_constraint_name(name),
93                if *is_primary { "PRIMARY KEY" } else { "UNIQUE" },
94                display_comma_separated(columns)
95            ),
96            TableConstraint::ForeignKey {
97                name,
98                columns,
99                foreign_table,
100                referred_columns,
101            } => write!(
102                f,
103                "{}FOREIGN KEY ({}) REFERENCES {}({})",
104                display_constraint_name(name),
105                display_comma_separated(columns),
106                foreign_table,
107                display_comma_separated(referred_columns)
108            ),
109            TableConstraint::Check { name, expr } => {
110                write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
111            }
112        }
113    }
114}
115
116/// SQL column definition
117#[derive(Debug, Clone, PartialEq, Eq, Hash)]
118pub struct ColumnDef {
119    pub name: Ident,
120    pub data_type: DataType,
121    pub collation: Option<ObjectName>,
122    pub options: Vec<ColumnOptionDef>,
123}
124
125impl fmt::Display for ColumnDef {
126    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
127        //TODO: change the data type to serial if it is integer type, but is a primary and
128        //autogenerated
129        write!(f, "{} {}", self.name, self.data_type)?;
130        for option in &self.options {
131            write!(f, " {}", option)?;
132        }
133        Ok(())
134    }
135}
136
137/// An optionally-named `ColumnOption`: `[ CONSTRAINT <name> ] <column-option>`.
138///
139/// Note that implementations are substantially more permissive than the ANSI
140/// specification on what order column options can be presented in, and whether
141/// they are allowed to be named. The specification distinguishes between
142/// constraints (NOT NULL, UNIQUE, PRIMARY KEY, and CHECK), which can be named
143/// and can appear in any order, and other options (DEFAULT, GENERATED), which
144/// cannot be named and must appear in a fixed order. PostgreSQL, however,
145/// allows preceding any option with `CONSTRAINT <name>`, even those that are
146/// not really constraints, like NULL and DEFAULT. MSSQL is less permissive,
147/// allowing DEFAULT, UNIQUE, PRIMARY KEY and CHECK to be named, but not NULL or
148/// NOT NULL constraints (the last of which is in violation of the spec).
149///
150/// For maximum flexibility, we don't distinguish between constraint and
151/// non-constraint options, lumping them all together under the umbrella of
152/// "column options," and we allow any column option to be named.
153#[derive(Debug, Clone, PartialEq, Eq, Hash)]
154pub struct ColumnOptionDef {
155    pub name: Option<Ident>,
156    pub option: ColumnOption,
157}
158
159impl fmt::Display for ColumnOptionDef {
160    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
161        write!(f, "{}{}", display_constraint_name(&self.name), self.option)
162    }
163}
164
165/// `ColumnOption`s are modifiers that follow a column definition in a `CREATE
166/// TABLE` statement.
167#[derive(Debug, Clone, PartialEq, Eq, Hash)]
168pub enum ColumnOption {
169    /// `NULL`
170    Null,
171    /// `NOT NULL`
172    NotNull,
173    /// `DEFAULT <restricted-expr>`
174    Default(Expr),
175    /// `{ PRIMARY KEY | UNIQUE }`
176    Unique {
177        is_primary: bool,
178    },
179    /// A referential integrity constraint (`[FOREIGN KEY REFERENCES
180    /// <foreign_table> (<referred_columns>)`).
181    ForeignKey {
182        foreign_table: ObjectName,
183        referred_columns: Vec<Ident>,
184    },
185    // `CHECK (<expr>)`
186    Check(Expr),
187}
188
189impl fmt::Display for ColumnOption {
190    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
191        use ColumnOption::*;
192        match self {
193            Null => write!(f, "NULL"),
194            NotNull => write!(f, "NOT NULL"),
195            Default(expr) => write!(f, "DEFAULT {}", expr),
196            Unique { is_primary } => {
197                write!(f, "{}", if *is_primary { "PRIMARY KEY" } else { "UNIQUE" })
198            }
199            ForeignKey {
200                foreign_table,
201                referred_columns,
202            } => write!(
203                f,
204                "REFERENCES {} ({})",
205                foreign_table,
206                display_comma_separated(referred_columns)
207            ),
208            Check(expr) => write!(f, "CHECK ({})", expr),
209        }
210    }
211}
212
213fn display_constraint_name(name: &Option<Ident>) -> impl fmt::Display + '_ {
214    struct ConstraintName<'a>(&'a Option<Ident>);
215    impl<'a> fmt::Display for ConstraintName<'a> {
216        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
217            if let Some(name) = self.0 {
218                write!(f, "CONSTRAINT {} ", name)?;
219            }
220            Ok(())
221        }
222    }
223    ConstraintName(name)
224}