Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
285 changes: 61 additions & 224 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,19 @@ use sqlparser_derive::{Visit, VisitMut};

use crate::ast::value::escape_single_quote_string;
use crate::ast::{
display_comma_separated, display_separated, ArgMode, CommentDef, ConditionalStatements,
CreateFunctionBody, CreateFunctionUsing, CreateTableLikeKind, CreateTableOptions, DataType,
Expr, FileFormat, FunctionBehavior, FunctionCalledOnNull, FunctionDeterminismSpecifier,
FunctionParallel, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident,
InitializeKind, MySQLColumnPosition, ObjectName, OnCommit, OneOrManyWithParens,
OperateFunctionArg, OrderByExpr, ProjectionSelect, Query, RefreshModeKind, RowAccessPolicy,
SequenceOptions, Spanned, SqlOption, StorageSerializationPolicy, TableVersion, Tag,
TriggerEvent, TriggerExecBody, TriggerObject, TriggerPeriod, TriggerReferencing, Value,
ValueWithSpan, WrappedCollection,
display_comma_separated, display_separated,
table_constraints::{
CheckConstraint, ForeignKeyConstraint, FullTextOrSpatialConstraint, IndexConstraint,
PrimaryKeyConstraint, UniqueConstraint,
},
ArgMode, CommentDef, ConditionalStatements, CreateFunctionBody, CreateFunctionUsing,
CreateTableLikeKind, CreateTableOptions, DataType, Expr, FileFormat, FunctionBehavior,
FunctionCalledOnNull, FunctionDeterminismSpecifier, FunctionParallel, HiveDistributionStyle,
HiveFormat, HiveIOFormat, HiveRowFormat, Ident, InitializeKind, MySQLColumnPosition,
ObjectName, OnCommit, OneOrManyWithParens, OperateFunctionArg, OrderByExpr, ProjectionSelect,
Query, RefreshModeKind, RowAccessPolicy, SequenceOptions, Spanned, SqlOption,
StorageSerializationPolicy, TableVersion, Tag, TriggerEvent, TriggerExecBody, TriggerObject,
TriggerPeriod, TriggerReferencing, Value, ValueWithSpan, WrappedCollection,
};
use crate::display_utils::{DisplayCommaSeparated, Indent, NewLine, SpaceOrNewline};
use crate::keywords::Keyword;
Expand Down Expand Up @@ -1047,26 +1051,7 @@ pub enum TableConstraint {
/// [2]: IndexType
/// [3]: IndexOption
/// [4]: KeyOrIndexDisplay
Unique {
/// Constraint name.
///
/// Can be not the same as `index_name`
name: Option<Ident>,
/// Index name
index_name: Option<Ident>,
/// Whether the type is followed by the keyword `KEY`, `INDEX`, or no keyword at all.
index_type_display: KeyOrIndexDisplay,
/// Optional `USING` of [index type][1] statement before columns.
///
/// [1]: IndexType
index_type: Option<IndexType>,
/// Identifiers of the columns that are unique.
columns: Vec<IndexColumn>,
index_options: Vec<IndexOption>,
characteristics: Option<ConstraintCharacteristics>,
/// Optional Postgres nulls handling: `[ NULLS [ NOT ] DISTINCT ]`
nulls_distinct: NullsDistinctOption,
},
Unique(UniqueConstraint),
/// MySQL [definition][1] for `PRIMARY KEY` constraints statements:\
/// * `[CONSTRAINT [<name>]] PRIMARY KEY [index_name] [index_type] (<columns>) <index_options>`
///
Expand All @@ -1085,67 +1070,22 @@ pub enum TableConstraint {
/// [1]: https://dev.mysql.com/doc/refman/8.3/en/create-table.html
/// [2]: IndexType
/// [3]: IndexOption
PrimaryKey {
/// Constraint name.
///
/// Can be not the same as `index_name`
name: Option<Ident>,
/// Index name
index_name: Option<Ident>,
/// Optional `USING` of [index type][1] statement before columns.
///
/// [1]: IndexType
index_type: Option<IndexType>,
/// Identifiers of the columns that form the primary key.
columns: Vec<IndexColumn>,
index_options: Vec<IndexOption>,
characteristics: Option<ConstraintCharacteristics>,
},
PrimaryKey(PrimaryKeyConstraint),
/// A referential integrity constraint (`[ CONSTRAINT <name> ] FOREIGN KEY (<columns>)
/// REFERENCES <foreign_table> (<referred_columns>)
/// { [ON DELETE <referential_action>] [ON UPDATE <referential_action>] |
/// [ON UPDATE <referential_action>] [ON DELETE <referential_action>]
/// }`).
ForeignKey {
name: Option<Ident>,
/// MySQL-specific field
/// <https://dev.mysql.com/doc/refman/8.4/en/create-table-foreign-keys.html>
index_name: Option<Ident>,
columns: Vec<Ident>,
foreign_table: ObjectName,
referred_columns: Vec<Ident>,
on_delete: Option<ReferentialAction>,
on_update: Option<ReferentialAction>,
characteristics: Option<ConstraintCharacteristics>,
},
ForeignKey(ForeignKeyConstraint),
/// `[ CONSTRAINT <name> ] CHECK (<expr>) [[NOT] ENFORCED]`
Check {
name: Option<Ident>,
expr: Box<Expr>,
/// MySQL-specific syntax
/// <https://dev.mysql.com/doc/refman/8.4/en/create-table.html>
enforced: Option<bool>,
},
Check(CheckConstraint),
/// MySQLs [index definition][1] for index creation. Not present on ANSI so, for now, the usage
/// is restricted to MySQL, as no other dialects that support this syntax were found.
///
/// `{INDEX | KEY} [index_name] [index_type] (key_part,...) [index_option]...`
///
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/create-table.html
Index {
/// Whether this index starts with KEY (true) or INDEX (false), to maintain the same syntax.
display_as_key: bool,
/// Index name.
name: Option<Ident>,
/// Optional [index type][1].
///
/// [1]: IndexType
index_type: Option<IndexType>,
/// Referred column identifier list.
columns: Vec<IndexColumn>,
/// Optional index options such as `USING`; see [`IndexOption`].
index_options: Vec<IndexOption>,
},
Index(IndexConstraint),
/// MySQLs [fulltext][1] definition. Since the [`SPATIAL`][2] definition is exactly the same,
/// and MySQL displays both the same way, it is part of this definition as well.
///
Expand All @@ -1159,157 +1099,54 @@ pub enum TableConstraint {
///
/// [1]: https://dev.mysql.com/doc/refman/8.0/en/fulltext-natural-language.html
/// [2]: https://dev.mysql.com/doc/refman/8.0/en/spatial-types.html
FulltextOrSpatial {
/// Whether this is a `FULLTEXT` (true) or `SPATIAL` (false) definition.
fulltext: bool,
/// Whether the type is followed by the keyword `KEY`, `INDEX`, or no keyword at all.
index_type_display: KeyOrIndexDisplay,
/// Optional index name.
opt_index_name: Option<Ident>,
/// Referred column identifier list.
columns: Vec<IndexColumn>,
},
FulltextOrSpatial(FullTextOrSpatialConstraint),
}

impl fmt::Display for TableConstraint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TableConstraint::Unique {
name,
index_name,
index_type_display,
index_type,
columns,
index_options,
characteristics,
nulls_distinct,
} => {
write!(
f,
"{}UNIQUE{nulls_distinct}{index_type_display:>}{}{} ({})",
display_constraint_name(name),
display_option_spaced(index_name),
display_option(" USING ", "", index_type),
display_comma_separated(columns),
)?;

if !index_options.is_empty() {
write!(f, " {}", display_separated(index_options, " "))?;
}

write!(f, "{}", display_option_spaced(characteristics))?;
Ok(())
}
TableConstraint::PrimaryKey {
name,
index_name,
index_type,
columns,
index_options,
characteristics,
} => {
write!(
f,
"{}PRIMARY KEY{}{} ({})",
display_constraint_name(name),
display_option_spaced(index_name),
display_option(" USING ", "", index_type),
display_comma_separated(columns),
)?;
impl From<UniqueConstraint> for TableConstraint {
fn from(constraint: UniqueConstraint) -> Self {
TableConstraint::Unique(constraint)
}
}

if !index_options.is_empty() {
write!(f, " {}", display_separated(index_options, " "))?;
}
impl From<PrimaryKeyConstraint> for TableConstraint {
fn from(constraint: PrimaryKeyConstraint) -> Self {
TableConstraint::PrimaryKey(constraint)
}
}

write!(f, "{}", display_option_spaced(characteristics))?;
Ok(())
}
TableConstraint::ForeignKey {
name,
index_name,
columns,
foreign_table,
referred_columns,
on_delete,
on_update,
characteristics,
} => {
write!(
f,
"{}FOREIGN KEY{} ({}) REFERENCES {}",
display_constraint_name(name),
display_option_spaced(index_name),
display_comma_separated(columns),
foreign_table,
)?;
if !referred_columns.is_empty() {
write!(f, "({})", display_comma_separated(referred_columns))?;
}
if let Some(action) = on_delete {
write!(f, " ON DELETE {action}")?;
}
if let Some(action) = on_update {
write!(f, " ON UPDATE {action}")?;
}
if let Some(characteristics) = characteristics {
write!(f, " {characteristics}")?;
}
Ok(())
}
TableConstraint::Check {
name,
expr,
enforced,
} => {
write!(f, "{}CHECK ({})", display_constraint_name(name), expr)?;
if let Some(b) = enforced {
write!(f, " {}", if *b { "ENFORCED" } else { "NOT ENFORCED" })
} else {
Ok(())
}
}
TableConstraint::Index {
display_as_key,
name,
index_type,
columns,
index_options,
} => {
write!(f, "{}", if *display_as_key { "KEY" } else { "INDEX" })?;
if let Some(name) = name {
write!(f, " {name}")?;
}
if let Some(index_type) = index_type {
write!(f, " USING {index_type}")?;
}
write!(f, " ({})", display_comma_separated(columns))?;
if !index_options.is_empty() {
write!(f, " {}", display_comma_separated(index_options))?;
}
Ok(())
}
Self::FulltextOrSpatial {
fulltext,
index_type_display,
opt_index_name,
columns,
} => {
if *fulltext {
write!(f, "FULLTEXT")?;
} else {
write!(f, "SPATIAL")?;
}
impl From<ForeignKeyConstraint> for TableConstraint {
fn from(constraint: ForeignKeyConstraint) -> Self {
TableConstraint::ForeignKey(constraint)
}
}

write!(f, "{index_type_display:>}")?;
impl From<CheckConstraint> for TableConstraint {
fn from(constraint: CheckConstraint) -> Self {
TableConstraint::Check(constraint)
}
}

if let Some(name) = opt_index_name {
write!(f, " {name}")?;
}
impl From<IndexConstraint> for TableConstraint {
fn from(constraint: IndexConstraint) -> Self {
TableConstraint::Index(constraint)
}
}

write!(f, " ({})", display_comma_separated(columns))?;
impl From<FullTextOrSpatialConstraint> for TableConstraint {
fn from(constraint: FullTextOrSpatialConstraint) -> Self {
TableConstraint::FulltextOrSpatial(constraint)
}
}

Ok(())
}
impl fmt::Display for TableConstraint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TableConstraint::Unique(constraint) => constraint.fmt(f),
TableConstraint::PrimaryKey(constraint) => constraint.fmt(f),
TableConstraint::ForeignKey(constraint) => constraint.fmt(f),
TableConstraint::Check(constraint) => constraint.fmt(f),
TableConstraint::Index(constraint) => constraint.fmt(f),
TableConstraint::FulltextOrSpatial(constraint) => constraint.fmt(f),
}
}
}
Expand Down Expand Up @@ -2065,7 +1902,7 @@ pub enum GeneratedExpressionMode {
}

#[must_use]
fn display_constraint_name(name: &'_ Option<Ident>) -> impl fmt::Display + '_ {
pub fn display_constraint_name(name: &'_ Option<Ident>) -> impl fmt::Display + '_ {
struct ConstraintName<'a>(&'a Option<Ident>);
impl fmt::Display for ConstraintName<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Expand All @@ -2082,7 +1919,7 @@ fn display_constraint_name(name: &'_ Option<Ident>) -> impl fmt::Display + '_ {
/// * `Some(inner)` => create display struct for `"{prefix}{inner}{postfix}"`
/// * `_` => do nothing
#[must_use]
fn display_option<'a, T: fmt::Display>(
pub fn display_option<'a, T: fmt::Display>(
prefix: &'a str,
postfix: &'a str,
option: &'a Option<T>,
Expand All @@ -2104,7 +1941,7 @@ fn display_option<'a, T: fmt::Display>(
/// * `Some(inner)` => create display struct for `" {inner}"`
/// * `_` => do nothing
#[must_use]
fn display_option_spaced<T: fmt::Display>(option: &Option<T>) -> impl fmt::Display + '_ {
pub fn display_option_spaced<T: fmt::Display>(option: &Option<T>) -> impl fmt::Display + '_ {
display_option(" ", "", option)
}

Expand Down
5 changes: 5 additions & 0 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ mod dcl;
mod ddl;
mod dml;
pub mod helpers;
pub mod table_constraints;
pub use table_constraints::{
CheckConstraint, ForeignKeyConstraint, FullTextOrSpatialConstraint, IndexConstraint,
PrimaryKeyConstraint, UniqueConstraint,
};
mod operator;
mod query;
mod spans;
Expand Down
Loading
Loading