Added support for DROP OPERATOR syntax (#2102)

Co-authored-by: Ifeanyi Ubah <ify1992@yahoo.com>
This commit is contained in:
Luca Cappelletti 2025-11-26 10:51:11 +01:00 committed by GitHub
parent 2ceae006a4
commit 2a2abc8dad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 206 additions and 9 deletions

View file

@ -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()
}
}

View file

@ -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};
@ -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
@ -4836,6 +4843,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,

View file

@ -375,6 +375,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(),

View file

@ -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(),
);
};
@ -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]) {

View file

@ -23,6 +23,7 @@
mod test_utils;
use helpers::attached_token::AttachedToken;
use sqlparser::ast::{DataType, DropBehavior, DropOperator, DropOperatorSignature};
use sqlparser::tokenizer::Span;
use test_utils::*;
@ -6763,6 +6764,92 @@ fn parse_create_operator() {
assert!(pg().parse_sql_statements("CREATE OPERATOR > ())").is_err());
}
#[test]
fn parse_drop_operator() {
// Test DROP OPERATOR with NONE for prefix operator
let sql = "DROP OPERATOR ~ (NONE, BIT)";
assert_eq!(
pg_and_generic().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_and_generic().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_and_generic().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_and_generic().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"] {