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

github-bot pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new d78dbc97 Added support for `ALTER OPERATOR FAMILY` syntax (#2125)
d78dbc97 is described below

commit d78dbc97a1f65ce7ee353fea181a6c3bb15a5050
Author: Luca Cappelletti <[email protected]>
AuthorDate: Thu Dec 18 05:34:48 2025 +0100

    Added support for `ALTER OPERATOR FAMILY` syntax (#2125)
---
 src/ast/ddl.rs              | 198 +++++++++++++++++++++-
 src/ast/mod.rs              |  30 ++--
 src/ast/spans.rs            |   1 +
 src/parser/mod.rs           | 176 +++++++++++++++++++-
 tests/sqlparser_postgres.rs | 398 +++++++++++++++++++++++++++++++++++++++++++-
 5 files changed, 778 insertions(+), 25 deletions(-)

diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs
index d0aed448..4e042a36 100644
--- a/src/ast/ddl.rs
+++ b/src/ast/ddl.rs
@@ -4198,25 +4198,25 @@ impl fmt::Display for OperatorArgTypes {
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub enum OperatorClassItem {
-    /// OPERATOR clause
+    /// `OPERATOR` clause
     Operator {
-        strategy_number: u32,
+        strategy_number: u64,
         operator_name: ObjectName,
         /// Optional operator argument types
         op_types: Option<OperatorArgTypes>,
-        /// FOR SEARCH or FOR ORDER BY
+        /// `FOR SEARCH` or `FOR ORDER BY`
         purpose: Option<OperatorPurpose>,
     },
-    /// FUNCTION clause
+    /// `FUNCTION` clause
     Function {
-        support_number: u32,
+        support_number: u64,
         /// 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` clause
     Storage { storage_type: DataType },
 }
 
@@ -4413,3 +4413,189 @@ impl Spanned for DropOperatorClass {
         Span::empty()
     }
 }
+
+/// An item in an ALTER OPERATOR FAMILY ADD 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 OperatorFamilyItem {
+    /// `OPERATOR` clause
+    Operator {
+        strategy_number: u64,
+        operator_name: ObjectName,
+        /// Operator argument types
+        op_types: Vec<DataType>,
+        /// `FOR SEARCH` or `FOR ORDER BY`
+        purpose: Option<OperatorPurpose>,
+    },
+    /// `FUNCTION` clause
+    Function {
+        support_number: u64,
+        /// Optional operator argument types for the function
+        op_types: Option<Vec<DataType>>,
+        function_name: ObjectName,
+        /// Function argument types
+        argument_types: Vec<DataType>,
+    },
+}
+
+/// An item in an ALTER OPERATOR FAMILY DROP 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 OperatorFamilyDropItem {
+    /// `OPERATOR` clause
+    Operator {
+        strategy_number: u64,
+        /// Operator argument types
+        op_types: Vec<DataType>,
+    },
+    /// `FUNCTION` clause
+    Function {
+        support_number: u64,
+        /// Operator argument types for the function
+        op_types: Vec<DataType>,
+    },
+}
+
+impl fmt::Display for OperatorFamilyItem {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OperatorFamilyItem::Operator {
+                strategy_number,
+                operator_name,
+                op_types,
+                purpose,
+            } => {
+                write!(
+                    f,
+                    "OPERATOR {strategy_number} {operator_name} ({})",
+                    display_comma_separated(op_types)
+                )?;
+                if let Some(purpose) = purpose {
+                    write!(f, " {purpose}")?;
+                }
+                Ok(())
+            }
+            OperatorFamilyItem::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(())
+            }
+        }
+    }
+}
+
+impl fmt::Display for OperatorFamilyDropItem {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OperatorFamilyDropItem::Operator {
+                strategy_number,
+                op_types,
+            } => {
+                write!(
+                    f,
+                    "OPERATOR {strategy_number} ({})",
+                    display_comma_separated(op_types)
+                )
+            }
+            OperatorFamilyDropItem::Function {
+                support_number,
+                op_types,
+            } => {
+                write!(
+                    f,
+                    "FUNCTION {support_number} ({})",
+                    display_comma_separated(op_types)
+                )
+            }
+        }
+    }
+}
+
+/// `ALTER OPERATOR FAMILY` statement
+/// See <https://www.postgresql.org/docs/current/sql-alteropfamily.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 AlterOperatorFamily {
+    /// Operator family name (can be schema-qualified)
+    pub name: ObjectName,
+    /// Index method (btree, hash, gist, gin, etc.)
+    pub using: Ident,
+    /// The operation to perform
+    pub operation: AlterOperatorFamilyOperation,
+}
+
+/// An [AlterOperatorFamily] operation
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum AlterOperatorFamilyOperation {
+    /// `ADD { OPERATOR ... | FUNCTION ... } [, ...]`
+    Add {
+        /// List of operator family items to add
+        items: Vec<OperatorFamilyItem>,
+    },
+    /// `DROP { OPERATOR ... | FUNCTION ... } [, ...]`
+    Drop {
+        /// List of operator family items to drop
+        items: Vec<OperatorFamilyDropItem>,
+    },
+    /// `RENAME TO new_name`
+    RenameTo { new_name: ObjectName },
+    /// `OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER }`
+    OwnerTo(Owner),
+    /// `SET SCHEMA new_schema`
+    SetSchema { schema_name: ObjectName },
+}
+
+impl fmt::Display for AlterOperatorFamily {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(
+            f,
+            "ALTER OPERATOR FAMILY {} USING {}",
+            self.name, self.using
+        )?;
+        write!(f, " {}", self.operation)
+    }
+}
+
+impl fmt::Display for AlterOperatorFamilyOperation {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            AlterOperatorFamilyOperation::Add { items } => {
+                write!(f, "ADD {}", display_comma_separated(items))
+            }
+            AlterOperatorFamilyOperation::Drop { items } => {
+                write!(f, "DROP {}", display_comma_separated(items))
+            }
+            AlterOperatorFamilyOperation::RenameTo { new_name } => {
+                write!(f, "RENAME TO {new_name}")
+            }
+            AlterOperatorFamilyOperation::OwnerTo(owner) => {
+                write!(f, "OWNER TO {owner}")
+            }
+            AlterOperatorFamilyOperation::SetSchema { schema_name } => {
+                write!(f, "SET SCHEMA {schema_name}")
+            }
+        }
+    }
+}
+
+impl Spanned for AlterOperatorFamily {
+    fn span(&self) -> Span {
+        Span::empty()
+    }
+}
diff --git a/src/ast/mod.rs b/src/ast/mod.rs
index f1e79b0d..46767860 100644
--- a/src/ast/mod.rs
+++ b/src/ast/mod.rs
@@ -60,22 +60,24 @@ pub use self::dcl::{
 };
 pub use self::ddl::{
     Alignment, AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, 
AlterOperator,
-    AlterOperatorOperation, AlterPolicyOperation, AlterSchema, 
AlterSchemaOperation, AlterTable,
-    AlterTableAlgorithm, AlterTableLock, AlterTableOperation, AlterTableType, 
AlterType,
-    AlterTypeAddValue, AlterTypeAddValuePosition, AlterTypeOperation, 
AlterTypeRename,
-    AlterTypeRenameValue, ClusteredBy, ColumnDef, ColumnOption, 
ColumnOptionDef, ColumnOptions,
-    ColumnPolicy, ColumnPolicyProperty, ConstraintCharacteristics, 
CreateConnector, CreateDomain,
+    AlterOperatorFamily, AlterOperatorFamilyOperation, AlterOperatorOperation,
+    AlterPolicyOperation, AlterSchema, AlterSchemaOperation, AlterTable, 
AlterTableAlgorithm,
+    AlterTableLock, AlterTableOperation, AlterTableType, AlterType, 
AlterTypeAddValue,
+    AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, 
AlterTypeRenameValue,
+    ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, 
ColumnPolicy,
+    ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, 
CreateDomain,
     CreateExtension, CreateFunction, CreateIndex, CreateOperator, 
CreateOperatorClass,
     CreateOperatorFamily, CreateTable, CreateTrigger, CreateView, Deduplicate, 
DeferrableInitial,
     DropBehavior, DropExtension, DropFunction, DropOperator, 
DropOperatorClass, DropOperatorFamily,
     DropOperatorSignature, DropTrigger, GeneratedAs, GeneratedExpressionMode, 
IdentityParameters,
     IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, 
IdentityPropertyOrder,
     IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck, 
NullsDistinctOption,
-    OperatorArgTypes, OperatorClassItem, OperatorOption, OperatorPurpose, 
Owner, Partition,
-    ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, 
TagsColumnOption,
-    TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
-    UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, 
UserDefinedTypeRepresentation,
-    UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef,
+    OperatorArgTypes, OperatorClassItem, OperatorFamilyDropItem, 
OperatorFamilyItem,
+    OperatorOption, OperatorPurpose, Owner, Partition, ProcedureParam, 
ReferentialAction,
+    RenameTableNameKind, ReplicaIdentity, TagsColumnOption, TriggerObjectKind, 
Truncate,
+    UserDefinedTypeCompositeAttributeDef, UserDefinedTypeInternalLength,
+    UserDefinedTypeRangeOption, UserDefinedTypeRepresentation, 
UserDefinedTypeSqlDefinitionOption,
+    UserDefinedTypeStorage, ViewColumnDef,
 };
 pub use self::dml::{
     Delete, Insert, Merge, MergeAction, MergeClause, MergeClauseKind, 
MergeInsertExpr,
@@ -3411,6 +3413,11 @@ pub enum Statement {
     /// See 
[PostgreSQL](https://www.postgresql.org/docs/current/sql-alteroperator.html)
     AlterOperator(AlterOperator),
     /// ```sql
+    /// ALTER OPERATOR FAMILY
+    /// ```
+    /// See 
[PostgreSQL](https://www.postgresql.org/docs/current/sql-alteropfamily.html)
+    AlterOperatorFamily(AlterOperatorFamily),
+    /// ```sql
     /// ALTER ROLE
     /// ```
     AlterRole {
@@ -4972,6 +4979,9 @@ impl fmt::Display for Statement {
                 write!(f, "ALTER TYPE {name} {operation}")
             }
             Statement::AlterOperator(alter_operator) => write!(f, 
"{alter_operator}"),
+            Statement::AlterOperatorFamily(alter_operator_family) => {
+                write!(f, "{alter_operator_family}")
+            }
             Statement::AlterRole { name, operation } => {
                 write!(f, "ALTER ROLE {name} {operation}")
             }
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 2ec797db..d4e84315 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -403,6 +403,7 @@ impl Spanned for Statement {
             // These statements need to be implemented
             Statement::AlterType { .. } => Span::empty(),
             Statement::AlterOperator { .. } => Span::empty(),
+            Statement::AlterOperatorFamily { .. } => Span::empty(),
             Statement::AlterRole { .. } => Span::empty(),
             Statement::AlterSession { .. } => Span::empty(),
             Statement::AttachDatabase { .. } => Span::empty(),
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index ade3c250..74b06ec8 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -6701,7 +6701,7 @@ impl<'a> Parser<'a> {
         let mut items = vec![];
         loop {
             if self.parse_keyword(Keyword::OPERATOR) {
-                let strategy_number = self.parse_literal_uint()? as u32;
+                let strategy_number = self.parse_literal_uint()?;
                 let operator_name = self.parse_operator_name()?;
 
                 // Optional operator argument types
@@ -6736,7 +6736,7 @@ impl<'a> Parser<'a> {
                     purpose,
                 });
             } else if self.parse_keyword(Keyword::FUNCTION) {
-                let support_number = self.parse_literal_uint()? as u32;
+                let support_number = self.parse_literal_uint()?;
 
                 // Optional operator types
                 let op_types =
@@ -9898,7 +9898,13 @@ impl<'a> Parser<'a> {
                     operation,
                 })
             }
-            Keyword::OPERATOR => self.parse_alter_operator(),
+            Keyword::OPERATOR => {
+                if self.parse_keyword(Keyword::FAMILY) {
+                    self.parse_alter_operator_family()
+                } else {
+                    self.parse_alter_operator()
+                }
+            }
             Keyword::ROLE => self.parse_alter_role(),
             Keyword::POLICY => self.parse_alter_policy(),
             Keyword::CONNECTOR => self.parse_alter_connector(),
@@ -10130,6 +10136,170 @@ impl<'a> Parser<'a> {
         }))
     }
 
+    /// Parse an operator item for ALTER OPERATOR FAMILY ADD operations
+    fn parse_operator_family_add_operator(&mut self) -> 
Result<OperatorFamilyItem, ParserError> {
+        let strategy_number = self.parse_literal_uint()?;
+        let operator_name = self.parse_operator_name()?;
+
+        // Operator argument types (required for ALTER OPERATOR FAMILY)
+        self.expect_token(&Token::LParen)?;
+        let op_types = self.parse_comma_separated(Parser::parse_data_type)?;
+        self.expect_token(&Token::RParen)?;
+
+        // Optional purpose
+        let purpose = if self.parse_keyword(Keyword::FOR) {
+            if self.parse_keyword(Keyword::SEARCH) {
+                Some(OperatorPurpose::ForSearch)
+            } else if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
+                let sort_family = self.parse_object_name(false)?;
+                Some(OperatorPurpose::ForOrderBy { sort_family })
+            } else {
+                return self.expected("SEARCH or ORDER BY after FOR", 
self.peek_token());
+            }
+        } else {
+            None
+        };
+
+        Ok(OperatorFamilyItem::Operator {
+            strategy_number,
+            operator_name,
+            op_types,
+            purpose,
+        })
+    }
+
+    /// Parse a function item for ALTER OPERATOR FAMILY ADD operations
+    fn parse_operator_family_add_function(&mut self) -> 
Result<OperatorFamilyItem, ParserError> {
+        let support_number = self.parse_literal_uint()?;
+
+        // Optional operator types
+        let op_types = if self.consume_token(&Token::LParen) && 
self.peek_token() != Token::RParen {
+            let types = self.parse_comma_separated(Parser::parse_data_type)?;
+            self.expect_token(&Token::RParen)?;
+            Some(types)
+        } else if self.consume_token(&Token::LParen) {
+            self.expect_token(&Token::RParen)?;
+            Some(vec![])
+        } else {
+            None
+        };
+
+        let function_name = self.parse_object_name(false)?;
+
+        // Function argument types
+        let argument_types = if self.consume_token(&Token::LParen) {
+            if self.peek_token() == Token::RParen {
+                self.expect_token(&Token::RParen)?;
+                vec![]
+            } else {
+                let types = 
self.parse_comma_separated(Parser::parse_data_type)?;
+                self.expect_token(&Token::RParen)?;
+                types
+            }
+        } else {
+            vec![]
+        };
+
+        Ok(OperatorFamilyItem::Function {
+            support_number,
+            op_types,
+            function_name,
+            argument_types,
+        })
+    }
+
+    /// Parse an operator item for ALTER OPERATOR FAMILY DROP operations
+    fn parse_operator_family_drop_operator(
+        &mut self,
+    ) -> Result<OperatorFamilyDropItem, ParserError> {
+        let strategy_number = self.parse_literal_uint()?;
+
+        // Operator argument types (required for DROP)
+        self.expect_token(&Token::LParen)?;
+        let op_types = self.parse_comma_separated(Parser::parse_data_type)?;
+        self.expect_token(&Token::RParen)?;
+
+        Ok(OperatorFamilyDropItem::Operator {
+            strategy_number,
+            op_types,
+        })
+    }
+
+    /// Parse a function item for ALTER OPERATOR FAMILY DROP operations
+    fn parse_operator_family_drop_function(
+        &mut self,
+    ) -> Result<OperatorFamilyDropItem, ParserError> {
+        let support_number = self.parse_literal_uint()?;
+
+        // Operator types (required for DROP)
+        self.expect_token(&Token::LParen)?;
+        let op_types = self.parse_comma_separated(Parser::parse_data_type)?;
+        self.expect_token(&Token::RParen)?;
+
+        Ok(OperatorFamilyDropItem::Function {
+            support_number,
+            op_types,
+        })
+    }
+
+    /// Parse an operator family item for ADD operations (dispatches to 
operator or function parsing)
+    fn parse_operator_family_add_item(&mut self) -> Result<OperatorFamilyItem, 
ParserError> {
+        if self.parse_keyword(Keyword::OPERATOR) {
+            self.parse_operator_family_add_operator()
+        } else if self.parse_keyword(Keyword::FUNCTION) {
+            self.parse_operator_family_add_function()
+        } else {
+            self.expected("OPERATOR or FUNCTION", self.peek_token())
+        }
+    }
+
+    /// Parse an operator family item for DROP operations (dispatches to 
operator or function parsing)
+    fn parse_operator_family_drop_item(&mut self) -> 
Result<OperatorFamilyDropItem, ParserError> {
+        if self.parse_keyword(Keyword::OPERATOR) {
+            self.parse_operator_family_drop_operator()
+        } else if self.parse_keyword(Keyword::FUNCTION) {
+            self.parse_operator_family_drop_function()
+        } else {
+            self.expected("OPERATOR or FUNCTION", self.peek_token())
+        }
+    }
+
+    /// Parse a [Statement::AlterOperatorFamily]
+    /// See <https://www.postgresql.org/docs/current/sql-alteropfamily.html>
+    pub fn parse_alter_operator_family(&mut self) -> Result<Statement, 
ParserError> {
+        let name = self.parse_object_name(false)?;
+        self.expect_keyword(Keyword::USING)?;
+        let using = self.parse_identifier()?;
+
+        let operation = if self.parse_keyword(Keyword::ADD) {
+            let items = 
self.parse_comma_separated(Parser::parse_operator_family_add_item)?;
+            AlterOperatorFamilyOperation::Add { items }
+        } else if self.parse_keyword(Keyword::DROP) {
+            let items = 
self.parse_comma_separated(Parser::parse_operator_family_drop_item)?;
+            AlterOperatorFamilyOperation::Drop { items }
+        } else if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
+            let new_name = self.parse_object_name(false)?;
+            AlterOperatorFamilyOperation::RenameTo { new_name }
+        } else if self.parse_keywords(&[Keyword::OWNER, Keyword::TO]) {
+            let owner = self.parse_owner()?;
+            AlterOperatorFamilyOperation::OwnerTo(owner)
+        } else if self.parse_keywords(&[Keyword::SET, Keyword::SCHEMA]) {
+            let schema_name = self.parse_object_name(false)?;
+            AlterOperatorFamilyOperation::SetSchema { schema_name }
+        } else {
+            return self.expected_ref(
+                "ADD, DROP, RENAME TO, OWNER TO, or SET SCHEMA after ALTER 
OPERATOR FAMILY",
+                self.peek_token_ref(),
+            );
+        };
+
+        Ok(Statement::AlterOperatorFamily(AlterOperatorFamily {
+            name,
+            using,
+            operation,
+        }))
+    }
+
     // Parse a [Statement::AlterSchema]
     // ALTER SCHEMA [ IF EXISTS ] schema_name
     pub fn parse_alter_schema(&mut self) -> Result<Statement, ParserError> {
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index d595a0a2..9f4564ef 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -23,15 +23,11 @@
 mod test_utils;
 
 use helpers::attached_token::AttachedToken;
-use sqlparser::ast::{
-    DataType, DropBehavior, DropOperator, DropOperatorClass, 
DropOperatorSignature,
-};
-use sqlparser::tokenizer::Span;
-use test_utils::*;
-
 use sqlparser::ast::*;
 use sqlparser::dialect::{GenericDialect, PostgreSqlDialect};
 use sqlparser::parser::ParserError;
+use sqlparser::tokenizer::Span;
+use test_utils::*;
 
 #[test]
 fn parse_create_table_generated_always_as_identity() {
@@ -7145,6 +7141,396 @@ fn parse_alter_operator() {
     );
 }
 
+#[test]
+fn parse_alter_operator_family() {
+    // Test ALTER OPERATOR FAMILY ... ADD OPERATOR
+    let sql = "ALTER OPERATOR FAMILY integer_ops USING btree ADD OPERATOR 1 < 
(INT4, INT2)";
+    assert_eq!(
+        pg_and_generic().verified_stmt(sql),
+        Statement::AlterOperatorFamily(AlterOperatorFamily {
+            name: ObjectName::from(vec![Ident::new("integer_ops")]),
+            using: Ident::new("btree"),
+            operation: AlterOperatorFamilyOperation::Add {
+                items: vec![OperatorFamilyItem::Operator {
+                    strategy_number: 1,
+                    operator_name: ObjectName::from(vec![Ident::new("<")]),
+                    op_types: vec![DataType::Int4(None), DataType::Int2(None)],
+                    purpose: None,
+                }],
+            },
+        })
+    );
+
+    // Test ALTER OPERATOR FAMILY ... ADD OPERATOR with FOR SEARCH
+    let sql =
+        "ALTER OPERATOR FAMILY text_ops USING btree ADD OPERATOR 1 @@ (TEXT, 
TEXT) FOR SEARCH";
+    assert_eq!(
+        pg_and_generic().verified_stmt(sql),
+        Statement::AlterOperatorFamily(AlterOperatorFamily {
+            name: ObjectName::from(vec![Ident::new("text_ops")]),
+            using: Ident::new("btree"),
+            operation: AlterOperatorFamilyOperation::Add {
+                items: vec![OperatorFamilyItem::Operator {
+                    strategy_number: 1,
+                    operator_name: ObjectName::from(vec![Ident::new("@@")]),
+                    op_types: vec![DataType::Text, DataType::Text],
+                    purpose: Some(OperatorPurpose::ForSearch),
+                }],
+            },
+        })
+    );
+
+    // Test ALTER OPERATOR FAMILY ... ADD FUNCTION
+    let sql = "ALTER OPERATOR FAMILY integer_ops USING btree ADD FUNCTION 1 
btint42cmp(INT4, INT2)";
+    assert_eq!(
+        pg_and_generic().verified_stmt(sql),
+        Statement::AlterOperatorFamily(AlterOperatorFamily {
+            name: ObjectName::from(vec![Ident::new("integer_ops")]),
+            using: Ident::new("btree"),
+            operation: AlterOperatorFamilyOperation::Add {
+                items: vec![OperatorFamilyItem::Function {
+                    support_number: 1,
+                    op_types: None,
+                    function_name: 
ObjectName::from(vec![Ident::new("btint42cmp")]),
+                    argument_types: vec![DataType::Int4(None), 
DataType::Int2(None)],
+                }],
+            },
+        })
+    );
+
+    // Test ALTER OPERATOR FAMILY ... DROP OPERATOR
+    let sql = "ALTER OPERATOR FAMILY integer_ops USING btree DROP OPERATOR 1 
(INT4, INT2)";
+    assert_eq!(
+        pg_and_generic().verified_stmt(sql),
+        Statement::AlterOperatorFamily(AlterOperatorFamily {
+            name: ObjectName::from(vec![Ident::new("integer_ops")]),
+            using: Ident::new("btree"),
+            operation: AlterOperatorFamilyOperation::Drop {
+                items: vec![OperatorFamilyDropItem::Operator {
+                    strategy_number: 1,
+                    op_types: vec![DataType::Int4(None), DataType::Int2(None)],
+                }],
+            },
+        })
+    );
+
+    // Test ALTER OPERATOR FAMILY ... DROP FUNCTION
+    let sql = "ALTER OPERATOR FAMILY integer_ops USING btree DROP FUNCTION 1 
(INT4, INT2)";
+    assert_eq!(
+        pg_and_generic().verified_stmt(sql),
+        Statement::AlterOperatorFamily(AlterOperatorFamily {
+            name: ObjectName::from(vec![Ident::new("integer_ops")]),
+            using: Ident::new("btree"),
+            operation: AlterOperatorFamilyOperation::Drop {
+                items: vec![OperatorFamilyDropItem::Function {
+                    support_number: 1,
+                    op_types: vec![DataType::Int4(None), DataType::Int2(None)],
+                }],
+            },
+        })
+    );
+
+    // Test ALTER OPERATOR FAMILY ... RENAME TO
+    let sql = "ALTER OPERATOR FAMILY old_ops USING btree RENAME TO new_ops";
+    assert_eq!(
+        pg_and_generic().verified_stmt(sql),
+        Statement::AlterOperatorFamily(AlterOperatorFamily {
+            name: ObjectName::from(vec![Ident::new("old_ops")]),
+            using: Ident::new("btree"),
+            operation: AlterOperatorFamilyOperation::RenameTo {
+                new_name: ObjectName::from(vec![Ident::new("new_ops")]),
+            },
+        })
+    );
+
+    // Test ALTER OPERATOR FAMILY ... OWNER TO
+    let sql = "ALTER OPERATOR FAMILY my_ops USING btree OWNER TO joe";
+    assert_eq!(
+        pg_and_generic().verified_stmt(sql),
+        Statement::AlterOperatorFamily(AlterOperatorFamily {
+            name: ObjectName::from(vec![Ident::new("my_ops")]),
+            using: Ident::new("btree"),
+            operation: 
AlterOperatorFamilyOperation::OwnerTo(Owner::Ident(Ident::new("joe"))),
+        })
+    );
+
+    // Test ALTER OPERATOR FAMILY ... SET SCHEMA
+    let sql = "ALTER OPERATOR FAMILY my_ops USING btree SET SCHEMA new_schema";
+    assert_eq!(
+        pg_and_generic().verified_stmt(sql),
+        Statement::AlterOperatorFamily(AlterOperatorFamily {
+            name: ObjectName::from(vec![Ident::new("my_ops")]),
+            using: Ident::new("btree"),
+            operation: AlterOperatorFamilyOperation::SetSchema {
+                schema_name: ObjectName::from(vec![Ident::new("new_schema")]),
+            },
+        })
+    );
+
+    // Test error cases
+    // Missing USING clause
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops ADD OPERATOR 1 < 
(INT4, INT2)")
+        .is_err());
+
+    // Invalid operation
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree 
INVALID_OPERATION")
+        .is_err());
+
+    // Missing operator name in ADD OPERATOR
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1 (INT4, 
INT2)"
+        )
+        .is_err());
+
+    // Missing function name in ADD FUNCTION
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD FUNCTION 1 (INT4, 
INT2)"
+        )
+        .is_err());
+
+    // Missing parentheses in DROP OPERATOR
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree DROP 
OPERATOR 1 INT4, INT2")
+        .is_err());
+
+    // Invalid operator name (empty)
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1  (INT4, 
INT2)"
+        )
+        .is_err());
+
+    // Invalid operator name (special characters)
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1 @#$ 
(INT4, INT2)"
+        )
+        .is_err());
+
+    // Negative strategy number
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR -1 < (INT4, 
INT2)"
+        )
+        .is_err());
+
+    // Non-integer strategy number
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1.5 < 
(INT4, INT2)"
+        )
+        .is_err());
+
+    // Missing closing parenthesis in operator types
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1 < (INT4, 
INT2"
+        )
+        .is_err());
+
+    // Missing opening parenthesis in operator types
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1 < INT4, 
INT2)"
+        )
+        .is_err());
+
+    // Empty operator types
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree ADD 
OPERATOR 1 < ()")
+        .is_err());
+
+    // Invalid data type (using punctuation)
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1 < (@#$%, 
INT2)"
+        )
+        .is_err());
+
+    // Incomplete FOR clause
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1 < (INT4, 
INT2) FOR"
+        )
+        .is_err());
+
+    // Invalid FOR clause keyword
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1 < (INT4, 
INT2) FOR INVALID"
+        )
+        .is_err());
+
+    // FOR ORDER BY without sort family
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1 < (INT4, 
INT2) FOR ORDER BY"
+        )
+        .is_err());
+
+    // Missing function name in ADD FUNCTION
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD FUNCTION 1 (INT4, 
INT2)"
+        )
+        .is_err());
+
+    // Invalid function name
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD FUNCTION 1 
123invalid(INT4, INT2)"
+        )
+        .is_err());
+
+    // Negative support number
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD FUNCTION -1 
func(INT4, INT2)"
+        )
+        .is_err());
+
+    // Non-integer support number
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD FUNCTION 1.5 
func(INT4, INT2)"
+        )
+        .is_err());
+
+    // Missing closing parenthesis in function operator types
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD FUNCTION 1 (INT4, 
INT2 func()"
+        )
+        .is_err());
+
+    // Missing closing parenthesis in function arguments
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD FUNCTION 1 
func(INT4, INT2"
+        )
+        .is_err());
+
+    // Invalid data type in function arguments
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD FUNCTION 1 
func(@#$%, INT2)"
+        )
+        .is_err());
+
+    // DROP OPERATOR with FOR clause (not allowed)
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree DROP OPERATOR 1 (INT4, 
INT2) FOR SEARCH"
+        )
+        .is_err());
+
+    // DROP FUNCTION with function arguments (not allowed)
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree DROP FUNCTION 1 (INT4, 
INT2) func(INT4)"
+        )
+        .is_err());
+
+    // Multiple ADD items with error in middle
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1 < (INT4, 
INT2), INVALID_ITEM"
+        )
+        .is_err());
+
+    // Multiple DROP items with error in middle
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree DROP OPERATOR 1 (INT4, 
INT2), INVALID_ITEM"
+        )
+        .is_err());
+
+    // RENAME TO with invalid new name
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree RENAME 
TO 123invalid")
+        .is_err());
+
+    // OWNER TO with invalid owner
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree OWNER 
TO 123invalid")
+        .is_err());
+
+    // SET SCHEMA with invalid schema name
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree SET 
SCHEMA 123invalid")
+        .is_err());
+
+    // Schema-qualified operator family name with invalid schema
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY 123invalid.my_ops USING btree ADD OPERATOR 
1 < (INT4, INT2)"
+        )
+        .is_err());
+
+    // Missing operator family name
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY USING btree ADD OPERATOR 
1 < (INT4, INT2)")
+        .is_err());
+
+    // Extra tokens at end
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD OPERATOR 1 < (INT4, 
INT2) EXTRA"
+        )
+        .is_err());
+
+    // Incomplete statement
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree ADD")
+        .is_err());
+
+    // Very long numbers
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree ADD 
OPERATOR 999999999999999999999 < (INT4, INT2)")
+        .is_err());
+
+    // Multiple FOR clauses
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree ADD 
OPERATOR 1 < (INT4, INT2) FOR SEARCH FOR ORDER BY sort_family")
+        .is_err());
+
+    // FOR SEARCH with extra tokens
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree ADD 
OPERATOR 1 < (INT4, INT2) FOR SEARCH EXTRA")
+        .is_err());
+
+    // FOR ORDER BY with invalid sort family
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree ADD 
OPERATOR 1 < (INT4, INT2) FOR ORDER BY 123invalid")
+        .is_err());
+
+    // Function with empty operator types but missing function args parens
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree ADD 
FUNCTION 1 () func")
+        .is_err());
+
+    // Function with mismatched parentheses
+    assert!(pg()
+        .parse_sql_statements(
+            "ALTER OPERATOR FAMILY my_ops USING btree ADD FUNCTION 1 (INT4 
func(INT2"
+        )
+        .is_err());
+
+    // DROP with empty types
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree DROP 
OPERATOR 1 ()")
+        .is_err());
+
+    // DROP FUNCTION with empty types
+    assert!(pg()
+        .parse_sql_statements("ALTER OPERATOR FAMILY my_ops USING btree DROP 
FUNCTION 1 ()")
+        .is_err());
+}
+
 #[test]
 fn parse_drop_operator_family() {
     for if_exists in [true, false] {


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


Reply via email to