#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, string::String, vec::Vec};
use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};
use crate::ast::value::escape_single_quote_string;
use crate::ast::{
display_comma_separated, display_separated, DataType, Expr, Ident, ObjectName, SequenceOptions,
};
use crate::tokenizer::Token;
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AlterTableOperation {
AddConstraint(TableConstraint),
AddColumn {
column_keyword: bool,
if_not_exists: bool,
column_def: ColumnDef,
},
DropConstraint {
if_exists: bool,
name: Ident,
cascade: bool,
},
DropColumn {
column_name: Ident,
if_exists: bool,
cascade: bool,
},
DropPrimaryKey,
RenamePartitions {
old_partitions: Vec<Expr>,
new_partitions: Vec<Expr>,
},
AddPartitions {
if_not_exists: bool,
new_partitions: Vec<Partition>,
},
DropPartitions {
partitions: Vec<Expr>,
if_exists: bool,
},
RenameColumn {
old_column_name: Ident,
new_column_name: Ident,
},
RenameTable { table_name: ObjectName },
ChangeColumn {
old_name: Ident,
new_name: Ident,
data_type: DataType,
options: Vec<ColumnOption>,
},
RenameConstraint { old_name: Ident, new_name: Ident },
AlterColumn {
column_name: Ident,
op: AlterColumnOperation,
},
SwapWith { table_name: ObjectName },
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AlterIndexOperation {
RenameIndex { index_name: ObjectName },
}
impl fmt::Display for AlterTableOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AlterTableOperation::AddPartitions {
if_not_exists,
new_partitions,
} => write!(
f,
"ADD{ine} {}",
display_separated(new_partitions, " "),
ine = if *if_not_exists { " IF NOT EXISTS" } else { "" }
),
AlterTableOperation::AddConstraint(c) => write!(f, "ADD {c}"),
AlterTableOperation::AddColumn {
column_keyword,
if_not_exists,
column_def,
} => {
write!(f, "ADD")?;
if *column_keyword {
write!(f, " COLUMN")?;
}
if *if_not_exists {
write!(f, " IF NOT EXISTS")?;
}
write!(f, " {column_def}")?;
Ok(())
}
AlterTableOperation::AlterColumn { column_name, op } => {
write!(f, "ALTER COLUMN {column_name} {op}")
}
AlterTableOperation::DropPartitions {
partitions,
if_exists,
} => write!(
f,
"DROP{ie} PARTITION ({})",
display_comma_separated(partitions),
ie = if *if_exists { " IF EXISTS" } else { "" }
),
AlterTableOperation::DropConstraint {
if_exists,
name,
cascade,
} => {
write!(
f,
"DROP CONSTRAINT {}{}{}",
if *if_exists { "IF EXISTS " } else { "" },
name,
if *cascade { " CASCADE" } else { "" },
)
}
AlterTableOperation::DropPrimaryKey => write!(f, "DROP PRIMARY KEY"),
AlterTableOperation::DropColumn {
column_name,
if_exists,
cascade,
} => write!(
f,
"DROP COLUMN {}{}{}",
if *if_exists { "IF EXISTS " } else { "" },
column_name,
if *cascade { " CASCADE" } else { "" }
),
AlterTableOperation::RenamePartitions {
old_partitions,
new_partitions,
} => write!(
f,
"PARTITION ({}) RENAME TO PARTITION ({})",
display_comma_separated(old_partitions),
display_comma_separated(new_partitions)
),
AlterTableOperation::RenameColumn {
old_column_name,
new_column_name,
} => write!(f, "RENAME COLUMN {old_column_name} TO {new_column_name}"),
AlterTableOperation::RenameTable { table_name } => {
write!(f, "RENAME TO {table_name}")
}
AlterTableOperation::ChangeColumn {
old_name,
new_name,
data_type,
options,
} => {
write!(f, "CHANGE COLUMN {old_name} {new_name} {data_type}")?;
if options.is_empty() {
Ok(())
} else {
write!(f, " {}", display_separated(options, " "))
}
}
AlterTableOperation::RenameConstraint { old_name, new_name } => {
write!(f, "RENAME CONSTRAINT {old_name} TO {new_name}")
}
AlterTableOperation::SwapWith { table_name } => {
write!(f, "SWAP WITH {table_name}")
}
}
}
}
impl fmt::Display for AlterIndexOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AlterIndexOperation::RenameIndex { index_name } => {
write!(f, "RENAME TO {index_name}")
}
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AlterColumnOperation {
SetNotNull,
DropNotNull,
SetDefault { value: Expr },
DropDefault,
SetDataType {
data_type: DataType,
using: Option<Expr>,
},
}
impl fmt::Display for AlterColumnOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AlterColumnOperation::SetNotNull => write!(f, "SET NOT NULL",),
AlterColumnOperation::DropNotNull => write!(f, "DROP NOT NULL",),
AlterColumnOperation::SetDefault { value } => {
write!(f, "SET DEFAULT {value}")
}
AlterColumnOperation::DropDefault {} => {
write!(f, "DROP DEFAULT")
}
AlterColumnOperation::SetDataType { data_type, using } => {
if let Some(expr) = using {
write!(f, "SET DATA TYPE {data_type} USING {expr}")
} else {
write!(f, "SET DATA TYPE {data_type}")
}
}
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum TableConstraint {
Unique {
name: Option<Ident>,
columns: Vec<Ident>,
is_primary: bool,
},
ForeignKey {
name: Option<Ident>,
columns: Vec<Ident>,
foreign_table: ObjectName,
referred_columns: Vec<Ident>,
on_delete: Option<ReferentialAction>,
on_update: Option<ReferentialAction>,
},
Check {
name: Option<Ident>,
expr: Box<Expr>,
},
Index {
display_as_key: bool,
name: Option<Ident>,
index_type: Option<IndexType>,
columns: Vec<Ident>,
},
FulltextOrSpatial {
fulltext: bool,
index_type_display: KeyOrIndexDisplay,
opt_index_name: Option<Ident>,
columns: Vec<Ident>,
},
}
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,
on_delete,
on_update,
} => {
write!(
f,
"{}FOREIGN KEY ({}) REFERENCES {}({})",
display_constraint_name(name),
display_comma_separated(columns),
foreign_table,
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}")?;
}
Ok(())
}
TableConstraint::Check { name, expr } => {
write!(f, "{}CHECK ({})", display_constraint_name(name), expr)
}
TableConstraint::Index {
display_as_key,
name,
index_type,
columns,
} => {
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))?;
Ok(())
}
Self::FulltextOrSpatial {
fulltext,
index_type_display,
opt_index_name,
columns,
} => {
if *fulltext {
write!(f, "FULLTEXT")?;
} else {
write!(f, "SPATIAL")?;
}
if !matches!(index_type_display, KeyOrIndexDisplay::None) {
write!(f, " {index_type_display}")?;
}
if let Some(name) = opt_index_name {
write!(f, " {name}")?;
}
write!(f, " ({})", display_comma_separated(columns))?;
Ok(())
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum KeyOrIndexDisplay {
None,
Key,
Index,
}
impl fmt::Display for KeyOrIndexDisplay {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
KeyOrIndexDisplay::None => {
write!(f, "")
}
KeyOrIndexDisplay::Key => {
write!(f, "KEY")
}
KeyOrIndexDisplay::Index => {
write!(f, "INDEX")
}
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum IndexType {
BTree,
Hash,
}
impl fmt::Display for IndexType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::BTree => write!(f, "BTREE"),
Self::Hash => write!(f, "HASH"),
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct ProcedureParam {
pub name: Ident,
pub data_type: DataType,
}
impl fmt::Display for ProcedureParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.name, self.data_type)
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
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)?;
if let Some(collation) = &self.collation {
write!(f, " COLLATE {collation}")?;
}
for option in &self.options {
write!(f, " {option}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
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)
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ColumnOption {
Null,
NotNull,
Default(Expr),
Unique {
is_primary: bool,
},
ForeignKey {
foreign_table: ObjectName,
referred_columns: Vec<Ident>,
on_delete: Option<ReferentialAction>,
on_update: Option<ReferentialAction>,
},
Check(Expr),
DialectSpecific(Vec<Token>),
CharacterSet(ObjectName),
Comment(String),
OnUpdate(Expr),
Generated {
generated_as: GeneratedAs,
sequence_options: Option<Vec<SequenceOptions>>,
generation_expr: Option<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,
on_delete,
on_update,
} => {
write!(f, "REFERENCES {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}")?;
}
Ok(())
}
Check(expr) => write!(f, "CHECK ({expr})"),
DialectSpecific(val) => write!(f, "{}", display_separated(val, " ")),
CharacterSet(n) => write!(f, "CHARACTER SET {n}"),
Comment(v) => write!(f, "COMMENT '{}'", escape_single_quote_string(v)),
OnUpdate(expr) => write!(f, "ON UPDATE {expr}"),
Generated {
generated_as,
sequence_options,
generation_expr,
} => match generated_as {
GeneratedAs::Always => {
write!(f, "GENERATED ALWAYS AS IDENTITY")?;
if sequence_options.is_some() {
let so = sequence_options.as_ref().unwrap();
if !so.is_empty() {
write!(f, " (")?;
}
for sequence_option in so {
write!(f, "{sequence_option}")?;
}
if !so.is_empty() {
write!(f, " )")?;
}
}
Ok(())
}
GeneratedAs::ByDefault => {
write!(f, "GENERATED BY DEFAULT AS IDENTITY")?;
if sequence_options.is_some() {
let so = sequence_options.as_ref().unwrap();
if !so.is_empty() {
write!(f, " (")?;
}
for sequence_option in so {
write!(f, "{sequence_option}")?;
}
if !so.is_empty() {
write!(f, " )")?;
}
}
Ok(())
}
GeneratedAs::ExpStored => {
let expr = generation_expr.as_ref().unwrap();
write!(f, "GENERATED ALWAYS AS ({expr}) STORED")
}
},
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum GeneratedAs {
Always,
ByDefault,
ExpStored,
}
fn display_constraint_name(name: &'_ Option<Ident>) -> impl fmt::Display + '_ {
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)
}
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum ReferentialAction {
Restrict,
Cascade,
SetNull,
NoAction,
SetDefault,
}
impl fmt::Display for ReferentialAction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(match self {
ReferentialAction::Restrict => "RESTRICT",
ReferentialAction::Cascade => "CASCADE",
ReferentialAction::SetNull => "SET NULL",
ReferentialAction::NoAction => "NO ACTION",
ReferentialAction::SetDefault => "SET DEFAULT",
})
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum UserDefinedTypeRepresentation {
Composite {
attributes: Vec<UserDefinedTypeCompositeAttributeDef>,
},
}
impl fmt::Display for UserDefinedTypeRepresentation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
UserDefinedTypeRepresentation::Composite { attributes } => {
write!(f, "({})", display_comma_separated(attributes))
}
}
}
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct UserDefinedTypeCompositeAttributeDef {
pub name: Ident,
pub data_type: DataType,
pub collation: Option<ObjectName>,
}
impl fmt::Display for UserDefinedTypeCompositeAttributeDef {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} {}", self.name, self.data_type)?;
if let Some(collation) = &self.collation {
write!(f, " COLLATE {collation}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Partition {
pub partitions: Vec<Expr>,
}
impl fmt::Display for Partition {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"PARTITION ({})",
display_comma_separated(&self.partitions)
)
}
}