STET

agent_patch

runs/2026-02-28__18-27-22__gpt-5-3-codex/flux-pr-2096/flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/agent-logs/agent.patch

27150 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/ddl.rs b/app/src/ast/ddl.rs
index 286b16a..51e2846 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/ddl.rs
+++ b/app/src/ast/ddl.rs
@@ -3858,6 +3858,202 @@ impl Spanned for DropExtension {
     }
 }
 
+/// PostgreSQL operator name, such as `+`, `===`, or `schema.===`.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct PostgresOperatorName(pub Vec<String>);
+
+impl fmt::Display for PostgresOperatorName {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "{}", display_separated(&self.0, "."))
+    }
+}
+
+/// PostgreSQL operator reference.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum PostgresOperatorReference {
+    /// Unqualified/reference form, for example `=`.
+    Name(PostgresOperatorName),
+    /// `OPERATOR(<operator>)` reference form.
+    Qualified(PostgresOperatorName),
+}
+
+impl fmt::Display for PostgresOperatorReference {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            PostgresOperatorReference::Name(name) => write!(f, "{name}"),
+            PostgresOperatorReference::Qualified(name) => write!(f, "OPERATOR({name})"),
+        }
+    }
+}
+
+/// PostgreSQL `CREATE OPERATOR` option.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum CreateOperatorOption {
+    Function(ObjectName),
+    Procedure(ObjectName),
+    LeftArg(DataType),
+    RightArg(DataType),
+    Commutator(PostgresOperatorReference),
+    Negator(PostgresOperatorReference),
+    Restrict(ObjectName),
+    Join(ObjectName),
+    Hashes,
+    Merges,
+}
+
+impl fmt::Display for CreateOperatorOption {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            CreateOperatorOption::Function(name) => write!(f, "FUNCTION = {name}"),
+            CreateOperatorOption::Procedure(name) => write!(f, "PROCEDURE = {name}"),
+            CreateOperatorOption::LeftArg(data_type) => write!(f, "LEFTARG = {data_type}"),
+            CreateOperatorOption::RightArg(data_type) => write!(f, "RIGHTARG = {data_type}"),
+            CreateOperatorOption::Commutator(operator) => write!(f, "COMMUTATOR = {operator}"),
+            CreateOperatorOption::Negator(operator) => write!(f, "NEGATOR = {operator}"),
+            CreateOperatorOption::Restrict(name) => write!(f, "RESTRICT = {name}"),
+            CreateOperatorOption::Join(name) => write!(f, "JOIN = {name}"),
+            CreateOperatorOption::Hashes => write!(f, "HASHES"),
+            CreateOperatorOption::Merges => write!(f, "MERGES"),
+        }
+    }
+}
+
+/// PostgreSQL `CREATE 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 CreateOperator {
+    pub name: PostgresOperatorName,
+    pub options: Vec<CreateOperatorOption>,
+}
+
+impl fmt::Display for CreateOperator {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(
+            f,
+            "CREATE OPERATOR {} ({})",
+            self.name,
+            display_comma_separated(&self.options)
+        )
+    }
+}
+
+/// PostgreSQL `CREATE OPERATOR FAMILY` 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 CreateOperatorFamily {
+    pub name: ObjectName,
+    pub index_method: Ident,
+}
+
+impl fmt::Display for CreateOperatorFamily {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(
+            f,
+            "CREATE OPERATOR FAMILY {} USING {}",
+            self.name, self.index_method
+        )
+    }
+}
+
+/// PostgreSQL `CREATE OPERATOR CLASS ... AS` item.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum CreateOperatorClassItem {
+    Operator {
+        strategy: u64,
+        operator: PostgresOperatorReference,
+        argument_types: Option<Vec<DataType>>,
+        is_for_search: bool,
+        order_by_family: Option<ObjectName>,
+    },
+    Function {
+        support: u64,
+        argument_types: Option<Vec<DataType>>,
+        function: FunctionDesc,
+    },
+    Storage(DataType),
+}
+
+impl fmt::Display for CreateOperatorClassItem {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            CreateOperatorClassItem::Operator {
+                strategy,
+                operator,
+                argument_types,
+                is_for_search,
+                order_by_family,
+            } => {
+                write!(f, "OPERATOR {strategy} {operator}")?;
+                if let Some(argument_types) = argument_types {
+                    write!(f, " ({})", display_comma_separated(argument_types))?;
+                }
+                if *is_for_search {
+                    write!(f, " FOR SEARCH")?;
+                } else if let Some(order_by_family) = order_by_family {
+                    write!(f, " FOR ORDER BY {order_by_family}")?;
+                }
+                Ok(())
+            }
+            CreateOperatorClassItem::Function {
+                support,
+                argument_types,
+                function,
+            } => {
+                write!(f, "FUNCTION {support}")?;
+                if let Some(argument_types) = argument_types {
+                    write!(f, " ({})", display_comma_separated(argument_types))?;
+                }
+                write!(f, " {function}")
+            }
+            CreateOperatorClassItem::Storage(data_type) => write!(f, "STORAGE {data_type}"),
+        }
+    }
+}
+
+/// PostgreSQL `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 struct CreateOperatorClass {
+    pub name: ObjectName,
+    pub is_default: bool,
+    pub for_type: DataType,
+    pub index_method: Ident,
+    pub family: Option<ObjectName>,
+    pub items: Vec<CreateOperatorClassItem>,
+}
+
+impl fmt::Display for CreateOperatorClass {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "CREATE OPERATOR CLASS {}", self.name)?;
+        if self.is_default {
+            write!(f, " DEFAULT")?;
+        }
+        write!(
+            f,
+            " FOR TYPE {} USING {}",
+            self.for_type, self.index_method
+        )?;
+        if let Some(family) = &self.family {
+            write!(f, " FAMILY {family}")?;
+        }
+        if !self.items.is_empty() {
+            write!(f, " AS {}", display_comma_separated(&self.items))?;
+        }
+        Ok(())
+    }
+}
+
 /// Table type for ALTER TABLE statements.
 /// Used to distinguish between regular tables, Iceberg tables, and Dynamic tables.
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/mod.rs b/app/src/ast/mod.rs
index aa3fb08..f1ef929 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -65,15 +65,17 @@ 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,
-    ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption,
-    TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
-    UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
-    UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef,
+    CreateExtension, CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass,
+    CreateOperatorClassItem, CreateOperatorFamily, CreateOperatorOption, CreateTable,
+    CreateTrigger, CreateView, Deduplicate, DeferrableInitial, DropBehavior, DropExtension,
+    DropFunction, DropTrigger, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
+    IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
+    IndexColumn, IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption, Owner,
+    Partition, PostgresOperatorName, PostgresOperatorReference, 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};
@@ -4104,6 +4106,12 @@ pub enum Statement {
     },
     /// A `CREATE DOMAIN` statement.
     CreateDomain(CreateDomain),
+    /// A PostgreSQL `CREATE OPERATOR` statement.
+    CreateOperator(CreateOperator),
+    /// A PostgreSQL `CREATE OPERATOR FAMILY` statement.
+    CreateOperatorFamily(CreateOperatorFamily),
+    /// A PostgreSQL `CREATE OPERATOR CLASS` statement.
+    CreateOperatorClass(CreateOperatorClass),
     /// ```sql
     /// CREATE TYPE <name>
     /// ```
@@ -4716,6 +4724,13 @@ impl fmt::Display for Statement {
             }
             Statement::CreateFunction(create_function) => create_function.fmt(f),
             Statement::CreateDomain(create_domain) => create_domain.fmt(f),
+            Statement::CreateOperator(create_operator) => write!(f, "{create_operator}"),
+            Statement::CreateOperatorFamily(create_operator_family) => {
+                write!(f, "{create_operator_family}")
+            }
+            Statement::CreateOperatorClass(create_operator_class) => {
+                write!(f, "{create_operator_class}")
+            }
             Statement::CreateTrigger(create_trigger) => create_trigger.fmt(f),
             Statement::DropTrigger(drop_trigger) => drop_trigger.fmt(f),
             Statement::CreateProcedure {
@@ -10727,6 +10742,24 @@ impl From<CreateIndex> for Statement {
     }
 }
 
+impl From<CreateOperator> for Statement {
+    fn from(c: CreateOperator) -> Self {
+        Self::CreateOperator(c)
+    }
+}
+
+impl From<CreateOperatorFamily> for Statement {
+    fn from(c: CreateOperatorFamily) -> Self {
+        Self::CreateOperatorFamily(c)
+    }
+}
+
+impl From<CreateOperatorClass> for Statement {
+    fn from(c: CreateOperatorClass) -> Self {
+        Self::CreateOperatorClass(c)
+    }
+}
+
 impl From<CreateServerStatement> for Statement {
     fn from(c: CreateServerStatement) -> Self {
         Self::CreateServer(c)
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/spans.rs b/app/src/ast/spans.rs
index 3a4f1d0..33e5483 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/spans.rs
+++ b/app/src/ast/spans.rs
@@ -416,6 +416,9 @@ impl Spanned for Statement {
             Statement::CreateDatabase { .. } => Span::empty(),
             Statement::CreateFunction { .. } => Span::empty(),
             Statement::CreateDomain { .. } => Span::empty(),
+            Statement::CreateOperator { .. } => Span::empty(),
+            Statement::CreateOperatorFamily { .. } => Span::empty(),
+            Statement::CreateOperatorClass { .. } => Span::empty(),
             Statement::CreateTrigger { .. } => Span::empty(),
             Statement::DropTrigger { .. } => Span::empty(),
             Statement::CreateProcedure { .. } => Span::empty(),
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 1ab4626..213b099 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -4770,6 +4770,8 @@ impl<'a> Parser<'a> {
                 "[EXTERNAL] TABLE or [MATERIALIZED] VIEW or FUNCTION after CREATE OR REPLACE",
                 self.peek_token(),
             )
+        } else if self.parse_keyword(Keyword::OPERATOR) {
+            self.parse_create_operator_statement()
         } else if self.parse_keyword(Keyword::EXTENSION) {
             self.parse_create_extension()
         } else if self.parse_keyword(Keyword::INDEX) {
@@ -17352,6 +17354,212 @@ impl<'a> Parser<'a> {
         })
     }
 
+    fn parse_create_operator_statement(&mut self) -> Result<Statement, ParserError> {
+        if let Token::Word(w) = &self.peek_token_ref().token {
+            if w.value.eq_ignore_ascii_case("FAMILY") {
+                self.next_token();
+                return self.parse_create_operator_family();
+            }
+            if w.value.eq_ignore_ascii_case("CLASS") {
+                self.next_token();
+                return self.parse_create_operator_class();
+            }
+        }
+        self.parse_create_operator()
+    }
+
+    fn parse_create_operator(&mut self) -> Result<Statement, ParserError> {
+        let name = self.parse_postgres_operator_name()?;
+        self.expect_token(&Token::LParen)?;
+        let options =
+            self.parse_comma_separated0(Parser::parse_create_operator_option, Token::RParen)?;
+        self.expect_token(&Token::RParen)?;
+        Ok(Statement::CreateOperator(CreateOperator { name, options }))
+    }
+
+    fn parse_create_operator_option(&mut self) -> Result<CreateOperatorOption, ParserError> {
+        if self.parse_keyword(Keyword::FUNCTION) {
+            self.expect_token(&Token::Eq)?;
+            return Ok(CreateOperatorOption::Function(self.parse_object_name(false)?));
+        }
+        if self.parse_keyword(Keyword::PROCEDURE) {
+            self.expect_token(&Token::Eq)?;
+            return Ok(CreateOperatorOption::Procedure(self.parse_object_name(false)?));
+        }
+        if self.parse_keyword(Keyword::RESTRICT) {
+            self.expect_token(&Token::Eq)?;
+            return Ok(CreateOperatorOption::Restrict(self.parse_object_name(false)?));
+        }
+        if self.parse_keyword(Keyword::JOIN) {
+            self.expect_token(&Token::Eq)?;
+            return Ok(CreateOperatorOption::Join(self.parse_object_name(false)?));
+        }
+
+        let option = self.parse_identifier()?;
+        match option.value.to_ascii_uppercase().as_str() {
+            "LEFTARG" => {
+                self.expect_token(&Token::Eq)?;
+                Ok(CreateOperatorOption::LeftArg(self.parse_data_type()?))
+            }
+            "RIGHTARG" => {
+                self.expect_token(&Token::Eq)?;
+                Ok(CreateOperatorOption::RightArg(self.parse_data_type()?))
+            }
+            "COMMUTATOR" => {
+                self.expect_token(&Token::Eq)?;
+                Ok(CreateOperatorOption::Commutator(
+                    self.parse_postgres_operator_reference()?,
+                ))
+            }
+            "NEGATOR" => {
+                self.expect_token(&Token::Eq)?;
+                Ok(CreateOperatorOption::Negator(
+                    self.parse_postgres_operator_reference()?,
+                ))
+            }
+            "HASHES" => Ok(CreateOperatorOption::Hashes),
+            "MERGES" => Ok(CreateOperatorOption::Merges),
+            _ => parser_err!(
+                format!("Expected CREATE OPERATOR option, found: {}", option.value),
+                option.span.start
+            ),
+        }
+    }
+
+    fn parse_create_operator_family(&mut self) -> Result<Statement, ParserError> {
+        let name = self.parse_object_name(false)?;
+        self.expect_keyword_is(Keyword::USING)?;
+        let index_method = self.parse_identifier()?;
+        Ok(Statement::CreateOperatorFamily(CreateOperatorFamily {
+            name,
+            index_method,
+        }))
+    }
+
+    fn parse_create_operator_class(&mut self) -> Result<Statement, ParserError> {
+        let name = self.parse_object_name(false)?;
+        let is_default = self.parse_keyword(Keyword::DEFAULT);
+        self.expect_keyword_is(Keyword::FOR)?;
+        self.expect_keyword_is(Keyword::TYPE)?;
+        let for_type = self.parse_data_type()?;
+        self.expect_keyword_is(Keyword::USING)?;
+        let index_method = self.parse_identifier()?;
+        let family = if let Token::Word(w) = &self.peek_token_ref().token {
+            if w.value.eq_ignore_ascii_case("FAMILY") {
+                self.next_token();
+                Some(self.parse_object_name(false)?)
+            } else {
+                None
+            }
+        } else {
+            None
+        };
+        let items = if self.parse_keyword(Keyword::AS) {
+            self.parse_comma_separated(Parser::parse_create_operator_class_item)?
+        } else {
+            vec![]
+        };
+        Ok(Statement::CreateOperatorClass(CreateOperatorClass {
+            name,
+            is_default,
+            for_type,
+            index_method,
+            family,
+            items,
+        }))
+    }
+
+    fn parse_create_operator_class_item(&mut self) -> Result<CreateOperatorClassItem, ParserError> {
+        if self.parse_keyword(Keyword::OPERATOR) {
+            let strategy = self.parse_literal_uint()?;
+            let operator = self.parse_postgres_operator_reference()?;
+            let argument_types = if self.consume_token(&Token::LParen) {
+                let argument_types =
+                    self.parse_comma_separated0(Parser::parse_data_type, Token::RParen)?;
+                self.expect_token(&Token::RParen)?;
+                Some(argument_types)
+            } else {
+                None
+            };
+            let (is_for_search, order_by_family) = if self.parse_keyword(Keyword::FOR) {
+                if self.parse_keyword(Keyword::SEARCH) {
+                    (true, None)
+                } else {
+                    self.expect_keyword_is(Keyword::ORDER)?;
+                    self.expect_keyword_is(Keyword::BY)?;
+                    (false, Some(self.parse_object_name(false)?))
+                }
+            } else {
+                (false, None)
+            };
+            return Ok(CreateOperatorClassItem::Operator {
+                strategy,
+                operator,
+                argument_types,
+                is_for_search,
+                order_by_family,
+            });
+        }
+
+        if self.parse_keyword(Keyword::FUNCTION) {
+            let support = self.parse_literal_uint()?;
+            let argument_types = if self.consume_token(&Token::LParen) {
+                let argument_types =
+                    self.parse_comma_separated0(Parser::parse_data_type, Token::RParen)?;
+                self.expect_token(&Token::RParen)?;
+                Some(argument_types)
+            } else {
+                None
+            };
+            let function = self.parse_function_desc()?;
+            return Ok(CreateOperatorClassItem::Function {
+                support,
+                argument_types,
+                function,
+            });
+        }
+
+        if self.parse_keyword(Keyword::STORAGE) {
+            return Ok(CreateOperatorClassItem::Storage(self.parse_data_type()?));
+        }
+
+        self.expected("OPERATOR, FUNCTION, or STORAGE", self.peek_token())
+    }
+
+    fn parse_postgres_operator_reference(
+        &mut self,
+    ) -> Result<PostgresOperatorReference, ParserError> {
+        if self.parse_keyword(Keyword::OPERATOR) {
+            self.expect_token(&Token::LParen)?;
+            let name = self.parse_postgres_operator_name()?;
+            self.expect_token(&Token::RParen)?;
+            Ok(PostgresOperatorReference::Qualified(name))
+        } else {
+            Ok(PostgresOperatorReference::Name(
+                self.parse_postgres_operator_name()?,
+            ))
+        }
+    }
+
+    fn parse_postgres_operator_name(&mut self) -> Result<PostgresOperatorName, ParserError> {
+        let mut parts = vec![];
+        loop {
+            let token = self.next_token();
+            match token.token {
+                Token::EOF | Token::LParen | Token::RParen | Token::Comma => {
+                    return self.expected("operator name", token);
+                }
+                _ => parts.push(token.to_string()),
+            }
+
+            if !self.consume_token(&Token::Period) {
+                break;
+            }
+        }
+
+        Ok(PostgresOperatorName(parts))
+    }
+
     pub fn parse_window_spec(&mut self) -> Result<WindowSpec, ParserError> {
         let window_name = match self.peek_token().token {
             Token::Word(word) if word.keyword == Keyword::NoKeyword => {
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index 3bdf6d1..971f50c 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -670,6 +670,109 @@ fn parse_create_extension() {
         .verified_stmt("CREATE EXTENSION extension_name WITH SCHEMA schema_name VERSION version");
 }
 
+#[test]
+fn parse_create_operator() {
+    let sql = "CREATE OPERATOR === (FUNCTION = public.eq_fn, LEFTARG = INT4, RIGHTARG = INT4, COMMUTATOR = OPERATOR(pg_catalog.===), NEGATOR = !==, RESTRICT = eqsel, JOIN = eqjoinsel, HASHES, MERGES)";
+    let stmt = pg_and_generic().verified_stmt(sql);
+    assert_eq!(
+        stmt.to_string(),
+        "CREATE OPERATOR === (FUNCTION = public.eq_fn, LEFTARG = INT4, RIGHTARG = INT4, COMMUTATOR = OPERATOR(pg_catalog.===), NEGATOR = !==, RESTRICT = eqsel, JOIN = eqjoinsel, HASHES, MERGES)"
+    );
+
+    match stmt {
+        Statement::CreateOperator(CreateOperator { name, options }) => {
+            assert_eq!(name, PostgresOperatorName(vec!["===".to_string()]));
+            assert_eq!(
+                options,
+                vec![
+                    CreateOperatorOption::Function(ObjectName::from(vec!["public", "eq_fn"])),
+                    CreateOperatorOption::LeftArg(DataType::Int4),
+                    CreateOperatorOption::RightArg(DataType::Int4),
+                    CreateOperatorOption::Commutator(PostgresOperatorReference::Qualified(
+                        PostgresOperatorName(vec!["pg_catalog".to_string(), "===".to_string()])
+                    )),
+                    CreateOperatorOption::Negator(PostgresOperatorReference::Name(
+                        PostgresOperatorName(vec!["!==".to_string()])
+                    )),
+                    CreateOperatorOption::Restrict(ObjectName::from(vec!["eqsel"])),
+                    CreateOperatorOption::Join(ObjectName::from(vec!["eqjoinsel"])),
+                    CreateOperatorOption::Hashes,
+                    CreateOperatorOption::Merges,
+                ]
+            );
+        }
+        _ => unreachable!(),
+    }
+}
+
+#[test]
+fn parse_create_operator_family() {
+    let sql = "CREATE OPERATOR FAMILY int_family USING btree";
+    assert_eq!(
+        pg_and_generic().verified_stmt(sql),
+        Statement::CreateOperatorFamily(CreateOperatorFamily {
+            name: ObjectName::from(vec!["int_family"]),
+            index_method: Ident::new("btree"),
+        })
+    );
+}
+
+#[test]
+fn parse_create_operator_class() {
+    let sql = "CREATE OPERATOR CLASS int4_ops DEFAULT FOR TYPE int4 USING btree FAMILY integer_ops AS OPERATOR 1 < FOR ORDER BY pg_catalog.integer_ops, FUNCTION 1 btint4cmp(int4, int4), STORAGE int4";
+    let stmt = pg_and_generic().verified_stmt(sql);
+    assert_eq!(
+        stmt.to_string(),
+        "CREATE OPERATOR CLASS int4_ops DEFAULT FOR TYPE INT4 USING btree FAMILY integer_ops AS OPERATOR 1 < FOR ORDER BY pg_catalog.integer_ops, FUNCTION 1 btint4cmp(INT4, INT4), STORAGE INT4"
+    );
+    match stmt {
+        Statement::CreateOperatorClass(CreateOperatorClass {
+            name,
+            is_default,
+            for_type,
+            index_method,
+            family,
+            items,
+        }) => {
+            assert_eq!(name, ObjectName::from(vec!["int4_ops"]));
+            assert!(is_default);
+            assert_eq!(for_type, DataType::Int4);
+            assert_eq!(index_method, Ident::new("btree"));
+            assert_eq!(family, Some(ObjectName::from(vec!["integer_ops"])));
+            assert_eq!(
+                items,
+                vec![
+                    CreateOperatorClassItem::Operator {
+                        strategy: 1,
+                        operator: PostgresOperatorReference::Name(PostgresOperatorName(vec![
+                            "<".to_string()
+                        ])),
+                        argument_types: None,
+                        is_for_search: false,
+                        order_by_family: Some(ObjectName::from(vec![
+                            "pg_catalog",
+                            "integer_ops"
+                        ])),
+                    },
+                    CreateOperatorClassItem::Function {
+                        support: 1,
+                        argument_types: None,
+                        function: FunctionDesc {
+                            name: ObjectName::from(vec!["btint4cmp"]),
+                            args: Some(vec![
+                                OperateFunctionArg::unnamed(DataType::Int4),
+                                OperateFunctionArg::unnamed(DataType::Int4),
+                            ]),
+                        },
+                    },
+                    CreateOperatorClassItem::Storage(DataType::Int4),
+                ]
+            );
+        }
+        _ => unreachable!(),
+    }
+}
+
 #[test]
 fn parse_drop_extension() {
     assert_eq!(