Skip to content
238 changes: 237 additions & 1 deletion src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@
//! (commonly referred to as Data Definition Language, or DDL)

#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, format, string::String, vec, vec::Vec};
use alloc::{
boxed::Box,
format,
string::{String, ToString},
vec,
vec::Vec,
};
use core::fmt::{self, Display, Write};

#[cfg(feature = "serde")]
Expand Down Expand Up @@ -3952,3 +3958,233 @@ impl Spanned for DropFunction {
Span::empty()
}
}

/// CREATE OPERATOR statement
/// See <https://www.postgresql.org/docs/current/sql-createoperator.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateOperator {
/// Operator name (can be schema-qualified)
pub name: ObjectName,
/// FUNCTION or PROCEDURE parameter (function name)
pub function: ObjectName,
/// Whether PROCEDURE keyword was used (vs FUNCTION)
pub is_procedure: bool,
/// LEFTARG parameter (left operand type)
pub left_arg: Option<DataType>,
/// RIGHTARG parameter (right operand type)
pub right_arg: Option<DataType>,
/// COMMUTATOR parameter (commutator operator)
pub commutator: Option<ObjectName>,
/// NEGATOR parameter (negator operator)
pub negator: Option<ObjectName>,
/// RESTRICT parameter (restriction selectivity function)
pub restrict: Option<ObjectName>,
/// JOIN parameter (join selectivity function)
pub join: Option<ObjectName>,
/// HASHES flag
pub hashes: bool,
/// MERGES flag
pub merges: bool,
}

/// CREATE OPERATOR FAMILY statement
/// See <https://www.postgresql.org/docs/current/sql-createopfamily.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateOperatorFamily {
/// Operator family name (can be schema-qualified)
pub name: ObjectName,
/// Index method (btree, hash, gist, gin, etc.)
pub using: Ident,
}

/// CREATE OPERATOR CLASS statement
/// See <https://www.postgresql.org/docs/current/sql-createopclass.html>
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CreateOperatorClass {
/// Operator class name (can be schema-qualified)
pub name: ObjectName,
/// Whether this is the default operator class for the type
pub default: bool,
/// The data type
pub for_type: DataType,
/// Index method (btree, hash, gist, gin, etc.)
pub using: Ident,
/// Optional operator family name
pub family: Option<ObjectName>,
/// List of operator class items (operators, functions, storage)
pub items: Vec<OperatorClassItem>,
}

impl fmt::Display for CreateOperator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "CREATE OPERATOR {} (", self.name)?;

let function_keyword = if self.is_procedure {
"PROCEDURE"
} else {
"FUNCTION"
};
let mut params = vec![format!("{} = {}", function_keyword, self.function)];

if let Some(left_arg) = &self.left_arg {
params.push(format!("LEFTARG = {}", left_arg));
}
if let Some(right_arg) = &self.right_arg {
params.push(format!("RIGHTARG = {}", right_arg));
}
if let Some(commutator) = &self.commutator {
params.push(format!("COMMUTATOR = {}", commutator));
}
if let Some(negator) = &self.negator {
params.push(format!("NEGATOR = {}", negator));
}
if let Some(restrict) = &self.restrict {
params.push(format!("RESTRICT = {}", restrict));
}
if let Some(join) = &self.join {
params.push(format!("JOIN = {}", join));
}
if self.hashes {
params.push("HASHES".to_string());
}
if self.merges {
params.push("MERGES".to_string());
}

write!(f, "{}", params.join(", "))?;
write!(f, ")")
}
}

impl fmt::Display for CreateOperatorFamily {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"CREATE OPERATOR FAMILY {} USING {}",
self.name, self.using
)
}
}

impl fmt::Display for CreateOperatorClass {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "CREATE OPERATOR CLASS {}", self.name)?;
if self.default {
write!(f, " DEFAULT")?;
}
write!(f, " FOR TYPE {} USING {}", self.for_type, self.using)?;
if let Some(family) = &self.family {
write!(f, " FAMILY {}", family)?;
}
write!(f, " AS {}", display_comma_separated(&self.items))
}
}

/// Operator argument types for CREATE OPERATOR CLASS
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct OperatorArgTypes {
pub left: DataType,
pub right: DataType,
}

impl fmt::Display for OperatorArgTypes {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}, {}", self.left, self.right)
}
}

/// An item in a CREATE OPERATOR CLASS statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum OperatorClassItem {
/// OPERATOR clause
Operator {
strategy_number: u32,
operator_name: ObjectName,
/// Optional operator argument types
op_types: Option<OperatorArgTypes>,
/// FOR SEARCH or FOR ORDER BY
purpose: Option<OperatorPurpose>,
},
/// FUNCTION clause
Function {
support_number: u32,
/// Optional function argument types for the operator class
op_types: Option<Vec<DataType>>,
function_name: ObjectName,
/// Function argument types
argument_types: Vec<DataType>,
},
/// STORAGE clause
Storage { storage_type: DataType },
}

/// Purpose of an operator in an operator class
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum OperatorPurpose {
ForSearch,
ForOrderBy { sort_family: ObjectName },
}

impl fmt::Display for OperatorClassItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
OperatorClassItem::Operator {
strategy_number,
operator_name,
op_types,
purpose,
} => {
write!(f, "OPERATOR {strategy_number} {operator_name}")?;
if let Some(types) = op_types {
write!(f, " ({types})")?;
}
if let Some(purpose) = purpose {
write!(f, " {purpose}")?;
}
Ok(())
}
OperatorClassItem::Function {
support_number,
op_types,
function_name,
argument_types,
} => {
write!(f, "FUNCTION {support_number}")?;
if let Some(types) = op_types {
write!(f, " ({})", display_comma_separated(types))?;
}
write!(f, " {function_name}")?;
if !argument_types.is_empty() {
write!(f, "({})", display_comma_separated(argument_types))?;
}
Ok(())
}
OperatorClassItem::Storage { storage_type } => {
write!(f, "STORAGE {storage_type}")
}
}
}
}

impl fmt::Display for OperatorPurpose {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
OperatorPurpose::ForSearch => write!(f, "FOR SEARCH"),
OperatorPurpose::ForOrderBy { sort_family } => {
write!(f, "FOR ORDER BY {sort_family}")
}
}
}
}
31 changes: 26 additions & 5 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,12 @@ pub use self::ddl::{
AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
CreateExtension, CreateFunction, CreateIndex, CreateTable, CreateTrigger, CreateView,
Deduplicate, DeferrableInitial, DropBehavior, DropExtension, DropFunction, DropTrigger,
GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexColumn,
IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption, Owner, Partition,
CreateExtension, CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass,
CreateOperatorFamily, CreateTable, CreateTrigger, CreateView, Deduplicate, DeferrableInitial,
DropBehavior, DropExtension, DropFunction, DropTrigger, GeneratedAs, GeneratedExpressionMode,
IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind,
IdentityPropertyOrder, IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck,
NullsDistinctOption, OperatorArgTypes, OperatorClassItem, OperatorPurpose, Owner, Partition,
ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption,
TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
Expand Down Expand Up @@ -3347,6 +3348,21 @@ pub enum Statement {
/// See [Hive](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-CreateDataConnectorCreateConnector)
CreateConnector(CreateConnector),
/// ```sql
/// CREATE OPERATOR
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createoperator.html)
CreateOperator(CreateOperator),
/// ```sql
/// CREATE OPERATOR FAMILY
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createopfamily.html)
CreateOperatorFamily(CreateOperatorFamily),
/// ```sql
/// CREATE OPERATOR CLASS
/// ```
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createopclass.html)
CreateOperatorClass(CreateOperatorClass),
/// ```sql
/// ALTER TABLE
/// ```
AlterTable(AlterTable),
Expand Down Expand Up @@ -4901,6 +4917,11 @@ impl fmt::Display for Statement {
Ok(())
}
Statement::CreateConnector(create_connector) => create_connector.fmt(f),
Statement::CreateOperator(create_operator) => create_operator.fmt(f),
Statement::CreateOperatorFamily(create_operator_family) => {
create_operator_family.fmt(f)
}
Statement::CreateOperatorClass(create_operator_class) => create_operator_class.fmt(f),
Statement::AlterTable(alter_table) => write!(f, "{alter_table}"),
Statement::AlterIndex { name, operation } => {
write!(f, "ALTER INDEX {name} {operation}")
Expand Down
26 changes: 25 additions & 1 deletion src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@

use crate::ast::{
ddl::AlterSchema, query::SelectItemQualifiedWildcardKind, AlterSchemaOperation, AlterTable,
ColumnOptions, CreateView, ExportData, Owner, TypedString,
ColumnOptions, CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreateView,
ExportData, Owner, TypedString,
};
use core::iter;

Expand Down Expand Up @@ -368,6 +369,11 @@ impl Spanned for Statement {
Statement::CreateSecret { .. } => Span::empty(),
Statement::CreateServer { .. } => Span::empty(),
Statement::CreateConnector { .. } => Span::empty(),
Statement::CreateOperator(create_operator) => create_operator.span(),
Statement::CreateOperatorFamily(create_operator_family) => {
create_operator_family.span()
}
Statement::CreateOperatorClass(create_operator_class) => create_operator_class.span(),
Statement::AlterTable(alter_table) => alter_table.span(),
Statement::AlterIndex { name, operation } => name.span().union(&operation.span()),
Statement::AlterView {
Expand Down Expand Up @@ -2357,6 +2363,24 @@ impl Spanned for AlterTable {
}
}

impl Spanned for CreateOperator {
fn span(&self) -> Span {
Span::empty()
}
}

impl Spanned for CreateOperatorFamily {
fn span(&self) -> Span {
Span::empty()
}
}

impl Spanned for CreateOperatorClass {
fn span(&self) -> Span {
Span::empty()
}
}

#[cfg(test)]
pub mod tests {
use crate::dialect::{Dialect, GenericDialect, SnowflakeDialect};
Expand Down
Loading