| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| //! AST types specific to CREATE/ALTER variants of [Statement] |
| //! (commonly referred to as Data Definition Language, or DDL) |
| use super::{display_comma_separated, DataType, Expr, Ident, ObjectName}; |
| use std::fmt; |
| |
| /// An `ALTER TABLE` (`Statement::AlterTable`) operation |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub enum AlterTableOperation { |
| /// `ADD <table_constraint>` |
| AddConstraint(TableConstraint), |
| /// TODO: implement `DROP CONSTRAINT <name>` |
| DropConstraint { name: Ident }, |
| } |
| |
| impl fmt::Display for AlterTableOperation { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| AlterTableOperation::AddConstraint(c) => write!(f, "ADD {}", c), |
| AlterTableOperation::DropConstraint { name } => write!(f, "DROP CONSTRAINT {}", name), |
| } |
| } |
| } |
| |
| /// A table-level constraint, specified in a `CREATE TABLE` or an |
| /// `ALTER TABLE ADD <constraint>` statement. |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub enum TableConstraint { |
| /// `[ CONSTRAINT <name> ] { PRIMARY KEY | UNIQUE } (<columns>)` |
| Unique { |
| name: Option<Ident>, |
| columns: Vec<Ident>, |
| /// Whether this is a `PRIMARY KEY` or just a `UNIQUE` constraint |
| is_primary: bool, |
| }, |
| /// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>) |
| /// REFERENCES <foreign_table> (<referred_columns>)`) |
| ForeignKey { |
| name: Option<Ident>, |
| columns: Vec<Ident>, |
| foreign_table: ObjectName, |
| referred_columns: Vec<Ident>, |
| }, |
| /// `[ CONSTRAINT <name> ] CHECK (<expr>)` |
| Check { |
| name: Option<Ident>, |
| expr: Box<Expr>, |
| }, |
| } |
| |
| impl fmt::Display for TableConstraint { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| match self { |
| TableConstraint::Unique { |
| name, |
| columns, |
| is_primary, |
| } => write!( |
| f, |
| "{}{} ({})", |
| display_constraint_name(name), |
| if *is_primary { "PRIMARY KEY" } else { "UNIQUE" }, |
| display_comma_separated(columns) |
| ), |
| TableConstraint::ForeignKey { |
| name, |
| columns, |
| foreign_table, |
| referred_columns, |
| } => write!( |
| f, |
| "{}FOREIGN KEY ({}) REFERENCES {}({})", |
| display_constraint_name(name), |
| display_comma_separated(columns), |
| foreign_table, |
| display_comma_separated(referred_columns) |
| ), |
| TableConstraint::Check { name, expr } => { |
| write!(f, "{}CHECK ({})", display_constraint_name(name), expr) |
| } |
| } |
| } |
| } |
| |
| /// SQL column definition |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub struct ColumnDef { |
| pub name: Ident, |
| pub data_type: DataType, |
| pub collation: Option<ObjectName>, |
| pub options: Vec<ColumnOptionDef>, |
| } |
| |
| impl fmt::Display for ColumnDef { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| write!(f, "{} {}", self.name, self.data_type)?; |
| for option in &self.options { |
| write!(f, " {}", option)?; |
| } |
| Ok(()) |
| } |
| } |
| |
| /// An optionally-named `ColumnOption`: `[ CONSTRAINT <name> ] <column-option>`. |
| /// |
| /// Note that implementations are substantially more permissive than the ANSI |
| /// specification on what order column options can be presented in, and whether |
| /// they are allowed to be named. The specification distinguishes between |
| /// constraints (NOT NULL, UNIQUE, PRIMARY KEY, and CHECK), which can be named |
| /// and can appear in any order, and other options (DEFAULT, GENERATED), which |
| /// cannot be named and must appear in a fixed order. PostgreSQL, however, |
| /// allows preceding any option with `CONSTRAINT <name>`, even those that are |
| /// not really constraints, like NULL and DEFAULT. MSSQL is less permissive, |
| /// allowing DEFAULT, UNIQUE, PRIMARY KEY and CHECK to be named, but not NULL or |
| /// NOT NULL constraints (the last of which is in violation of the spec). |
| /// |
| /// For maximum flexibility, we don't distinguish between constraint and |
| /// non-constraint options, lumping them all together under the umbrella of |
| /// "column options," and we allow any column option to be named. |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub struct ColumnOptionDef { |
| pub name: Option<Ident>, |
| pub option: ColumnOption, |
| } |
| |
| impl fmt::Display for ColumnOptionDef { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| write!(f, "{}{}", display_constraint_name(&self.name), self.option) |
| } |
| } |
| |
| /// `ColumnOption`s are modifiers that follow a column definition in a `CREATE |
| /// TABLE` statement. |
| #[derive(Debug, Clone, PartialEq, Eq, Hash)] |
| pub enum ColumnOption { |
| /// `NULL` |
| Null, |
| /// `NOT NULL` |
| NotNull, |
| /// `DEFAULT <restricted-expr>` |
| Default(Expr), |
| /// `{ PRIMARY KEY | UNIQUE }` |
| Unique { |
| is_primary: bool, |
| }, |
| /// A referential integrity constraint (`[FOREIGN KEY REFERENCES |
| /// <foreign_table> (<referred_columns>)`). |
| ForeignKey { |
| foreign_table: ObjectName, |
| referred_columns: Vec<Ident>, |
| }, |
| // `CHECK (<expr>)` |
| Check(Expr), |
| } |
| |
| impl fmt::Display for ColumnOption { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| use ColumnOption::*; |
| match self { |
| Null => write!(f, "NULL"), |
| NotNull => write!(f, "NOT NULL"), |
| Default(expr) => write!(f, "DEFAULT {}", expr), |
| Unique { is_primary } => { |
| write!(f, "{}", if *is_primary { "PRIMARY KEY" } else { "UNIQUE" }) |
| } |
| ForeignKey { |
| foreign_table, |
| referred_columns, |
| } => write!( |
| f, |
| "REFERENCES {} ({})", |
| foreign_table, |
| display_comma_separated(referred_columns) |
| ), |
| Check(expr) => write!(f, "CHECK ({})", expr), |
| } |
| } |
| } |
| |
| fn display_constraint_name<'a>(name: &'a Option<Ident>) -> impl fmt::Display + 'a { |
| struct ConstraintName<'a>(&'a Option<Ident>); |
| impl<'a> fmt::Display for ConstraintName<'a> { |
| fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| if let Some(name) = self.0 { |
| write!(f, "CONSTRAINT {} ", name)?; |
| } |
| Ok(()) |
| } |
| } |
| ConstraintName(name) |
| } |