This is an automated email from the ASF dual-hosted git repository.

github-bot pushed a commit to branch 
gh-readonly-queue/main/pr-2102-2ceae006a45b090a76b982f04a413603ab87ab61
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit 2a2abc8dad933068e2b024eb1ed986f2c141e05a
Author: Luca Cappelletti <[email protected]>
AuthorDate: Wed Nov 26 10:51:11 2025 +0100

    Added support for `DROP OPERATOR` syntax (#2102)
    
    Co-authored-by: Ifeanyi Ubah <[email protected]>
---
 src/ast/ddl.rs              | 59 ++++++++++++++++++++++++++++++
 src/ast/mod.rs              | 24 ++++++++-----
 src/ast/spans.rs            |  1 +
 src/parser/mod.rs           | 44 ++++++++++++++++++++++-
 tests/sqlparser_postgres.rs | 87 +++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 206 insertions(+), 9 deletions(-)

diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs
index 3a38fbaa..6f5010f0 100644
--- a/src/ast/ddl.rs
+++ b/src/ast/ddl.rs
@@ -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()
+    }
+}
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index 2c452a69..8b50452c 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -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,
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index b50cc56d..66799fe5 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -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(),
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 57d94bee..759e2eef 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -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]) {
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index a14ff5ec..858e2e80 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -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"] {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to