#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, string::String, vec::Vec};
use core::fmt::{self, Display};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};
pub use super::ddl::{ColumnDef, TableConstraint};
use super::{
display_comma_separated, display_separated, ClusteredBy, CommentDef, Expr, FileFormat,
FromTable, HiveDistributionStyle, HiveFormat, HiveIOFormat, HiveRowFormat, Ident,
InsertAliases, MysqlInsertPriority, ObjectName, OnCommit, OnInsert, OneOrManyWithParens,
OrderByExpr, Query, RowAccessPolicy, SelectItem, SqlOption, SqliteOnConflict, TableEngine,
TableWithJoins, Tag, WrappedCollection,
};
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateIndex {
pub name: Option<ObjectName>,
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub table_name: ObjectName,
pub using: Option<Ident>,
pub columns: Vec<OrderByExpr>,
pub unique: bool,
pub concurrently: bool,
pub if_not_exists: bool,
pub include: Vec<Ident>,
pub nulls_distinct: Option<bool>,
pub with: Vec<Expr>,
pub predicate: Option<Expr>,
}
impl Display for CreateIndex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"CREATE {unique}INDEX {concurrently}{if_not_exists}",
unique = if self.unique { "UNIQUE " } else { "" },
concurrently = if self.concurrently {
"CONCURRENTLY "
} else {
""
},
if_not_exists = if self.if_not_exists {
"IF NOT EXISTS "
} else {
""
},
)?;
if let Some(value) = &self.name {
write!(f, "{value} ")?;
}
write!(f, "ON {}", self.table_name)?;
if let Some(value) = &self.using {
write!(f, " USING {value} ")?;
}
write!(f, "({})", display_separated(&self.columns, ","))?;
if !self.include.is_empty() {
write!(f, " INCLUDE ({})", display_separated(&self.include, ","))?;
}
if let Some(value) = self.nulls_distinct {
if value {
write!(f, " NULLS DISTINCT")?;
} else {
write!(f, " NULLS NOT DISTINCT")?;
}
}
if !self.with.is_empty() {
write!(f, " WITH ({})", display_comma_separated(&self.with))?;
}
if let Some(predicate) = &self.predicate {
write!(f, " WHERE {predicate}")?;
}
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 CreateTable {
pub or_replace: bool,
pub temporary: bool,
pub external: bool,
pub global: Option<bool>,
pub if_not_exists: bool,
pub transient: bool,
pub volatile: bool,
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub name: ObjectName,
pub columns: Vec<ColumnDef>,
pub constraints: Vec<TableConstraint>,
pub hive_distribution: HiveDistributionStyle,
pub hive_formats: Option<HiveFormat>,
pub table_properties: Vec<SqlOption>,
pub with_options: Vec<SqlOption>,
pub file_format: Option<FileFormat>,
pub location: Option<String>,
pub query: Option<Box<Query>>,
pub without_rowid: bool,
pub like: Option<ObjectName>,
pub clone: Option<ObjectName>,
pub engine: Option<TableEngine>,
pub comment: Option<CommentDef>,
pub auto_increment_offset: Option<u32>,
pub default_charset: Option<String>,
pub collation: Option<String>,
pub on_commit: Option<OnCommit>,
pub on_cluster: Option<Ident>,
pub primary_key: Option<Box<Expr>>,
pub order_by: Option<OneOrManyWithParens<Expr>>,
pub partition_by: Option<Box<Expr>>,
pub cluster_by: Option<WrappedCollection<Vec<Ident>>>,
pub clustered_by: Option<ClusteredBy>,
pub options: Option<Vec<SqlOption>>,
pub strict: bool,
pub copy_grants: bool,
pub enable_schema_evolution: Option<bool>,
pub change_tracking: Option<bool>,
pub data_retention_time_in_days: Option<u64>,
pub max_data_extension_time_in_days: Option<u64>,
pub default_ddl_collation: Option<String>,
pub with_aggregation_policy: Option<ObjectName>,
pub with_row_access_policy: Option<RowAccessPolicy>,
pub with_tags: Option<Vec<Tag>>,
}
impl Display for CreateTable {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"CREATE {or_replace}{external}{global}{temporary}{transient}{volatile}TABLE {if_not_exists}{name}",
or_replace = if self.or_replace { "OR REPLACE " } else { "" },
external = if self.external { "EXTERNAL " } else { "" },
global = self.global
.map(|global| {
if global {
"GLOBAL "
} else {
"LOCAL "
}
})
.unwrap_or(""),
if_not_exists = if self.if_not_exists { "IF NOT EXISTS " } else { "" },
temporary = if self.temporary { "TEMPORARY " } else { "" },
transient = if self.transient { "TRANSIENT " } else { "" },
volatile = if self.volatile { "VOLATILE " } else { "" },
name = self.name,
)?;
if let Some(on_cluster) = &self.on_cluster {
write!(f, " ON CLUSTER {}", on_cluster)?;
}
if !self.columns.is_empty() || !self.constraints.is_empty() {
write!(f, " ({}", display_comma_separated(&self.columns))?;
if !self.columns.is_empty() && !self.constraints.is_empty() {
write!(f, ", ")?;
}
write!(f, "{})", display_comma_separated(&self.constraints))?;
} else if self.query.is_none() && self.like.is_none() && self.clone.is_none() {
write!(f, " ()")?;
}
if let Some(CommentDef::AfterColumnDefsWithoutEq(comment)) = &self.comment {
write!(f, " COMMENT '{comment}'")?;
}
if self.without_rowid {
write!(f, " WITHOUT ROWID")?;
}
if let Some(l) = &self.like {
write!(f, " LIKE {l}")?;
}
if let Some(c) = &self.clone {
write!(f, " CLONE {c}")?;
}
match &self.hive_distribution {
HiveDistributionStyle::PARTITIONED { columns } => {
write!(f, " PARTITIONED BY ({})", display_comma_separated(columns))?;
}
HiveDistributionStyle::SKEWED {
columns,
on,
stored_as_directories,
} => {
write!(
f,
" SKEWED BY ({})) ON ({})",
display_comma_separated(columns),
display_comma_separated(on)
)?;
if *stored_as_directories {
write!(f, " STORED AS DIRECTORIES")?;
}
}
_ => (),
}
if let Some(clustered_by) = &self.clustered_by {
write!(f, " {clustered_by}")?;
}
if let Some(HiveFormat {
row_format,
serde_properties,
storage,
location,
}) = &self.hive_formats
{
match row_format {
Some(HiveRowFormat::SERDE { class }) => write!(f, " ROW FORMAT SERDE '{class}'")?,
Some(HiveRowFormat::DELIMITED { delimiters }) => {
write!(f, " ROW FORMAT DELIMITED")?;
if !delimiters.is_empty() {
write!(f, " {}", display_separated(delimiters, " "))?;
}
}
None => (),
}
match storage {
Some(HiveIOFormat::IOF {
input_format,
output_format,
}) => write!(
f,
" STORED AS INPUTFORMAT {input_format} OUTPUTFORMAT {output_format}"
)?,
Some(HiveIOFormat::FileFormat { format }) if !self.external => {
write!(f, " STORED AS {format}")?
}
_ => (),
}
if let Some(serde_properties) = serde_properties.as_ref() {
write!(
f,
" WITH SERDEPROPERTIES ({})",
display_comma_separated(serde_properties)
)?;
}
if !self.external {
if let Some(loc) = location {
write!(f, " LOCATION '{loc}'")?;
}
}
}
if self.external {
if let Some(file_format) = self.file_format {
write!(f, " STORED AS {file_format}")?;
}
write!(f, " LOCATION '{}'", self.location.as_ref().unwrap())?;
}
if !self.table_properties.is_empty() {
write!(
f,
" TBLPROPERTIES ({})",
display_comma_separated(&self.table_properties)
)?;
}
if !self.with_options.is_empty() {
write!(f, " WITH ({})", display_comma_separated(&self.with_options))?;
}
if let Some(engine) = &self.engine {
write!(f, " ENGINE={engine}")?;
}
if let Some(comment_def) = &self.comment {
match comment_def {
CommentDef::WithEq(comment) => {
write!(f, " COMMENT = '{comment}'")?;
}
CommentDef::WithoutEq(comment) => {
write!(f, " COMMENT '{comment}'")?;
}
CommentDef::AfterColumnDefsWithoutEq(_) => (),
}
}
if let Some(auto_increment_offset) = self.auto_increment_offset {
write!(f, " AUTO_INCREMENT {auto_increment_offset}")?;
}
if let Some(primary_key) = &self.primary_key {
write!(f, " PRIMARY KEY {}", primary_key)?;
}
if let Some(order_by) = &self.order_by {
write!(f, " ORDER BY {}", order_by)?;
}
if let Some(partition_by) = self.partition_by.as_ref() {
write!(f, " PARTITION BY {partition_by}")?;
}
if let Some(cluster_by) = self.cluster_by.as_ref() {
write!(f, " CLUSTER BY {cluster_by}")?;
}
if let Some(options) = self.options.as_ref() {
write!(
f,
" OPTIONS({})",
display_comma_separated(options.as_slice())
)?;
}
if self.copy_grants {
write!(f, " COPY GRANTS")?;
}
if let Some(is_enabled) = self.enable_schema_evolution {
write!(
f,
" ENABLE_SCHEMA_EVOLUTION={}",
if is_enabled { "TRUE" } else { "FALSE" }
)?;
}
if let Some(is_enabled) = self.change_tracking {
write!(
f,
" CHANGE_TRACKING={}",
if is_enabled { "TRUE" } else { "FALSE" }
)?;
}
if let Some(data_retention_time_in_days) = self.data_retention_time_in_days {
write!(
f,
" DATA_RETENTION_TIME_IN_DAYS={data_retention_time_in_days}",
)?;
}
if let Some(max_data_extension_time_in_days) = self.max_data_extension_time_in_days {
write!(
f,
" MAX_DATA_EXTENSION_TIME_IN_DAYS={max_data_extension_time_in_days}",
)?;
}
if let Some(default_ddl_collation) = &self.default_ddl_collation {
write!(f, " DEFAULT_DDL_COLLATION='{default_ddl_collation}'",)?;
}
if let Some(with_aggregation_policy) = &self.with_aggregation_policy {
write!(f, " WITH AGGREGATION POLICY {with_aggregation_policy}",)?;
}
if let Some(row_access_policy) = &self.with_row_access_policy {
write!(f, " {row_access_policy}",)?;
}
if let Some(tag) = &self.with_tags {
write!(f, " WITH TAG ({})", display_comma_separated(tag.as_slice()))?;
}
if let Some(default_charset) = &self.default_charset {
write!(f, " DEFAULT CHARSET={default_charset}")?;
}
if let Some(collation) = &self.collation {
write!(f, " COLLATE={collation}")?;
}
if self.on_commit.is_some() {
let on_commit = match self.on_commit {
Some(OnCommit::DeleteRows) => "ON COMMIT DELETE ROWS",
Some(OnCommit::PreserveRows) => "ON COMMIT PRESERVE ROWS",
Some(OnCommit::Drop) => "ON COMMIT DROP",
None => "",
};
write!(f, " {on_commit}")?;
}
if self.strict {
write!(f, " STRICT")?;
}
if let Some(query) = &self.query {
write!(f, " AS {query}")?;
}
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 Insert {
pub or: Option<SqliteOnConflict>,
pub ignore: bool,
pub into: bool,
#[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
pub table_name: ObjectName,
pub table_alias: Option<Ident>,
pub columns: Vec<Ident>,
pub overwrite: bool,
pub source: Option<Box<Query>>,
pub partitioned: Option<Vec<Expr>>,
pub after_columns: Vec<Ident>,
pub table: bool,
pub on: Option<OnInsert>,
pub returning: Option<Vec<SelectItem>>,
pub replace_into: bool,
pub priority: Option<MysqlInsertPriority>,
pub insert_alias: Option<InsertAliases>,
}
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct Delete {
pub tables: Vec<ObjectName>,
pub from: FromTable,
pub using: Option<Vec<TableWithJoins>>,
pub selection: Option<Expr>,
pub returning: Option<Vec<SelectItem>>,
pub order_by: Vec<OrderByExpr>,
pub limit: Option<Expr>,
}