Skip to content
Open
59 changes: 59 additions & 0 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4188,3 +4188,62 @@ impl fmt::Display for OperatorPurpose {
}
}
}

/// `DROP OPERATOR` statement
/// See <https://www.postgresql.org/docs/current/sql-dropoperator.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 DropOperator {
/// `IF EXISTS` clause
pub if_exists: bool,
/// One or more operators to drop with their signatures
pub operators: Vec<DropOperatorSignature>,
/// `CASCADE or RESTRICT`
pub drop_behavior: Option<DropBehavior>,
}

/// Operator signature for a `DROP OPERATOR` statement
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct DropOperatorSignature {
/// Operator name
pub name: ObjectName,
/// Left operand type
pub left_type: Option<DataType>,
/// Right operand type
pub right_type: DataType,
}

impl fmt::Display for DropOperatorSignature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} (", self.name)?;
if let Some(left_type) = &self.left_type {
write!(f, "{}", left_type)?;
} else {
write!(f, "NONE")?;
}
write!(f, ", {})", self.right_type)
}
}

impl fmt::Display for DropOperator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "DROP OPERATOR")?;
if self.if_exists {
write!(f, " IF EXISTS")?;
}
write!(f, " {}", display_comma_separated(&self.operators))?;
if let Some(drop_behavior) = &self.drop_behavior {
write!(f, " {}", drop_behavior)?;
}
Ok(())
}
}

impl Spanned for DropOperator {
fn span(&self) -> Span {
Span::empty()
}
}
24 changes: 16 additions & 8 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,15 @@ pub use self::ddl::{
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
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,
UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef,
DropBehavior, DropExtension, DropFunction, DropOperator, DropOperatorSignature, 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, UserDefinedTypeSqlDefinitionOption,
UserDefinedTypeStorage, ViewColumnDef,
};
pub use self::dml::{Delete, Insert, Update};
pub use self::operator::{BinaryOperator, UnaryOperator};
Expand Down Expand Up @@ -3573,6 +3574,12 @@ pub enum Statement {
/// <https://www.postgresql.org/docs/current/sql-dropextension.html>
DropExtension(DropExtension),
/// ```sql
/// DROP OPERATOR [ IF EXISTS ] name ( { left_type | NONE } , right_type ) [, ...] [ CASCADE | RESTRICT ]
/// ```
/// Note: this is a PostgreSQL-specific statement.
/// <https://www.postgresql.org/docs/current/sql-dropoperator.html>
DropOperator(DropOperator),
/// ```sql
/// FETCH
/// ```
/// Retrieve rows from a query using a cursor
Expand Down Expand Up @@ -4835,6 +4842,7 @@ impl fmt::Display for Statement {
Statement::CreateIndex(create_index) => create_index.fmt(f),
Statement::CreateExtension(create_extension) => write!(f, "{create_extension}"),
Statement::DropExtension(drop_extension) => write!(f, "{drop_extension}"),
Statement::DropOperator(drop_operator) => write!(f, "{drop_operator}"),
Statement::CreateRole(create_role) => write!(f, "{create_role}"),
Statement::CreateSecret {
or_replace,
Expand Down
1 change: 1 addition & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@ impl Spanned for Statement {
Statement::CreateRole(create_role) => create_role.span(),
Statement::CreateExtension(create_extension) => create_extension.span(),
Statement::DropExtension(drop_extension) => drop_extension.span(),
Statement::DropOperator(drop_operator) => drop_operator.span(),
Statement::CreateSecret { .. } => Span::empty(),
Statement::CreateServer { .. } => Span::empty(),
Statement::CreateConnector { .. } => Span::empty(),
Expand Down
44 changes: 43 additions & 1 deletion src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6767,9 +6767,11 @@ impl<'a> Parser<'a> {
return self.parse_drop_trigger();
} else if self.parse_keyword(Keyword::EXTENSION) {
return self.parse_drop_extension();
} else if self.parse_keyword(Keyword::OPERATOR) {
return self.parse_drop_operator();
} else {
return self.expected(
"CONNECTOR, DATABASE, EXTENSION, FUNCTION, INDEX, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW or USER after DROP",
"CONNECTOR, DATABASE, EXTENSION, FUNCTION, INDEX, OPERATOR, POLICY, PROCEDURE, ROLE, SCHEMA, SECRET, SEQUENCE, STAGE, TABLE, TRIGGER, TYPE, VIEW, MATERIALIZED VIEW or USER after DROP",
self.peek_token(),
);
};
Expand Down Expand Up @@ -7525,6 +7527,46 @@ impl<'a> Parser<'a> {
}))
}

/// Parse a[Statement::DropOperator] statement.
///
pub fn parse_drop_operator(&mut self) -> Result<Statement, ParserError> {
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
let operators = self.parse_comma_separated(|p| p.parse_drop_operator_signature())?;
let drop_behavior = self.parse_optional_drop_behavior();
Ok(Statement::DropOperator(DropOperator {
if_exists,
operators,
drop_behavior,
}))
}

/// Parse an operator signature for a [Statement::DropOperator]
/// Format: `name ( { left_type | NONE } , right_type )`
fn parse_drop_operator_signature(&mut self) -> Result<DropOperatorSignature, ParserError> {
let name = self.parse_operator_name()?;
self.expect_token(&Token::LParen)?;

// Parse left operand type (or NONE for prefix operators)
let left_type = if self.parse_keyword(Keyword::NONE) {
None
} else {
Some(self.parse_data_type()?)
};

self.expect_token(&Token::Comma)?;

// Parse right operand type (always required)
let right_type = self.parse_data_type()?;

self.expect_token(&Token::RParen)?;

Ok(DropOperatorSignature {
name,
left_type,
right_type,
})
}

//TODO: Implement parsing for Skewed
pub fn parse_hive_distribution(&mut self) -> Result<HiveDistributionStyle, ParserError> {
if self.parse_keywords(&[Keyword::PARTITIONED, Keyword::BY]) {
Expand Down
88 changes: 88 additions & 0 deletions tests/sqlparser_postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6763,6 +6763,94 @@ fn parse_create_operator() {
assert!(pg().parse_sql_statements("CREATE OPERATOR > ())").is_err());
}

#[test]
fn parse_drop_operator() {
use sqlparser::ast::{DataType, DropBehavior, DropOperator, DropOperatorSignature};

// Test DROP OPERATOR with NONE for prefix operator
let sql = "DROP OPERATOR ~ (NONE, BIT)";
assert_eq!(
pg().verified_stmt(sql),
Statement::DropOperator(DropOperator {
if_exists: false,
operators: vec![DropOperatorSignature {
name: ObjectName::from(vec![Ident::new("~")]),
left_type: None,
right_type: DataType::Bit(None),
}],
drop_behavior: None,
})
);

for if_exist in [true, false] {
for cascading in [
None,
Some(DropBehavior::Cascade),
Some(DropBehavior::Restrict),
] {
for op in &["<", ">", "<=", ">=", "<>", "||", "&&", "<<", ">>"] {
let sql = format!(
"DROP OPERATOR{} {op} (INTEGER, INTEGER){}",
if if_exist { " IF EXISTS" } else { "" },
match cascading {
Some(cascading) => format!(" {cascading}"),
None => String::new(),
}
);
assert_eq!(
pg().verified_stmt(&sql),
Statement::DropOperator(DropOperator {
if_exists: if_exist,
operators: vec![DropOperatorSignature {
name: ObjectName::from(vec![Ident::new(*op)]),
left_type: Some(DataType::Integer(None)),
right_type: DataType::Integer(None),
}],
drop_behavior: cascading,
})
);
}
}
}

// Test DROP OPERATOR with schema-qualified operator name
let sql = "DROP OPERATOR myschema.@@ (TEXT, TEXT)";
assert_eq!(
pg().verified_stmt(sql),
Statement::DropOperator(DropOperator {
if_exists: false,
operators: vec![DropOperatorSignature {
name: ObjectName::from(vec![Ident::new("myschema"), Ident::new("@@")]),
left_type: Some(DataType::Text),
right_type: DataType::Text,
}],
drop_behavior: None,
})
);

// Test DROP OPERATOR with multiple operators, IF EXISTS and CASCADE
let sql = "DROP OPERATOR IF EXISTS + (INTEGER, INTEGER), - (INTEGER, INTEGER) CASCADE";
assert_eq!(
pg().verified_stmt(sql),
Statement::DropOperator(DropOperator {
if_exists: true,
operators: vec![
DropOperatorSignature {
name: ObjectName::from(vec![Ident::new("+")]),
left_type: Some(DataType::Integer(None)),
right_type: DataType::Integer(None),
},
DropOperatorSignature {
name: ObjectName::from(vec![Ident::new("-")]),
left_type: Some(DataType::Integer(None)),
right_type: DataType::Integer(None),
}
],
drop_behavior: Some(DropBehavior::Cascade),
})
);
}

#[test]
fn parse_create_operator_family() {
for index_method in &["btree", "hash", "gist", "gin", "spgist", "brin"] {
Expand Down