STET

agent_patch

runs/2026-02-21__03-36-16__gpt-5-1-codex-mini/flux-pr-2096/flux-pr-2096.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/agent-logs/agent.patch

29615 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/ddl.rs b/app/src/ast/ddl.rs
index 286b16a..ecfaa66 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/ddl.rs
+++ b/app/src/ast/ddl.rs
@@ -84,6 +84,284 @@ impl fmt::Display for IndexColumn {
     }
 }
 
+/// PostgreSQL operator identifier, optionally preserved behind `OPERATOR(...)` syntax.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct OperatorName {
+    pub parts: Vec<String>,
+    pub use_operator_keyword: bool,
+}
+
+impl OperatorName {
+    pub fn new(parts: Vec<String>, use_operator_keyword: bool) -> Self {
+        Self {
+            parts,
+            use_operator_keyword,
+        }
+    }
+}
+
+impl fmt::Display for OperatorName {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        if self.use_operator_keyword {
+            write!(f, "OPERATOR({})", display_separated(self.parts.as_slice(), "."))
+        } else {
+            write!(f, "{}", display_separated(self.parts.as_slice(), "."))
+        }
+    }
+}
+
+/// The keyword used to specify the operator implementation function.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum CreateOperatorFunctionKind {
+    /// `FUNCTION`
+    Function,
+    /// `PROCEDURE`
+    Procedure,
+}
+
+impl fmt::Display for CreateOperatorFunctionKind {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        f.write_str(match self {
+            CreateOperatorFunctionKind::Function => "FUNCTION",
+            CreateOperatorFunctionKind::Procedure => "PROCEDURE",
+        })
+    }
+}
+
+/// ```sql
+/// CREATE OPERATOR name (
+///   FUNCTION|PROCEDURE = function_name,
+///   LEFTARG = data_type,
+///   RIGHTARG = data_type,
+///   COMMUTATOR = operator,
+///   NEGATOR = operator,
+///   RESTRICT = function_name,
+///   JOIN = function_name,
+///   HASHES,
+///   MERGES
+/// )
+/// ```
+///
+/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createoperator.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 CreateOperator {
+    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+    pub name: ObjectName,
+    pub function_kind: CreateOperatorFunctionKind,
+    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+    pub function: ObjectName,
+    pub left_arg: Option<DataType>,
+    pub right_arg: Option<DataType>,
+    pub commutator: Option<OperatorName>,
+    pub negator: Option<OperatorName>,
+    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+    pub restrict: Option<ObjectName>,
+    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+    pub join: Option<ObjectName>,
+    pub hashes: bool,
+    pub merges: bool,
+}
+
+impl fmt::Display for CreateOperator {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let mut clauses = Vec::new();
+        clauses.push(format!("{} = {}", self.function_kind, self.function));
+        if let Some(left_arg) = &self.left_arg {
+            clauses.push(format!("LEFTARG = {left_arg}"));
+        }
+        if let Some(right_arg) = &self.right_arg {
+            clauses.push(format!("RIGHTARG = {right_arg}"));
+        }
+        if let Some(commutator) = &self.commutator {
+            clauses.push(format!("COMMUTATOR = {commutator}"));
+        }
+        if let Some(negator) = &self.negator {
+            clauses.push(format!("NEGATOR = {negator}"));
+        }
+        if let Some(restrict) = &self.restrict {
+            clauses.push(format!("RESTRICT = {restrict}"));
+        }
+        if let Some(join) = &self.join {
+            clauses.push(format!("JOIN = {join}"));
+        }
+        if self.hashes {
+            clauses.push("HASHES".to_string());
+        }
+        if self.merges {
+            clauses.push("MERGES".to_string());
+        }
+
+        write!(f, "CREATE OPERATOR {name} ({clauses})", name = self.name,
+            clauses = display_comma_separated(clauses.as_slice()))
+    }
+}
+
+/// ```sql
+/// CREATE OPERATOR FAMILY name USING index_method
+/// ```
+/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createoperatorfamily.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 CreateOperatorFamily {
+    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+    pub name: ObjectName,
+    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+    pub using: ObjectName,
+}
+
+impl fmt::Display for CreateOperatorFamily {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "CREATE OPERATOR FAMILY {name} USING {using}", name = self.name, using = self.using)
+    }
+}
+
+/// ```sql
+/// CREATE [ DEFAULT ] OPERATOR CLASS name FOR TYPE type USING index_method
+/// [ FAMILY family ]
+/// AS {
+///   OPERATOR strategy operator [ ( op_type [, ...] ) ] [ FOR SEARCH | FOR ORDER BY sort_family ]
+/// | FUNCTION support ( op_type [, ...] ) function_name ( argument_type [, ...] )
+/// | STORAGE type
+/// }
+/// ```
+/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createoperatorclass.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 CreateOperatorClass {
+    pub default: bool,
+    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+    pub name: ObjectName,
+    pub for_type: DataType,
+    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+    pub using: ObjectName,
+    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+    pub family: Option<ObjectName>,
+    pub members: Vec<OperatorClassItem>,
+}
+
+impl fmt::Display for CreateOperatorClass {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "CREATE {default}OPERATOR CLASS {name} FOR TYPE {for_type} USING {using}",
+            default = if self.default { "DEFAULT " } else { "" },
+            name = self.name,
+            for_type = self.for_type,
+            using = self.using
+        )?;
+        if let Some(family) = &self.family {
+            write!(f, " FAMILY {family}")?;
+        }
+        write!(f, " AS {}", display_comma_separated(self.members.as_slice()))
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum OperatorClassItem {
+    Operator(OperatorClassOperator),
+    Function(OperatorClassFunction),
+    Storage(DataType),
+}
+
+impl fmt::Display for OperatorClassItem {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OperatorClassItem::Operator(op) => write!(f, "{op}"),
+            OperatorClassItem::Function(func) => write!(f, "{func}"),
+            OperatorClassItem::Storage(ty) => write!(f, "STORAGE {ty}"),
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct OperatorClassOperator {
+    pub strategy_number: u64,
+    pub operator: OperatorName,
+    pub op_types: Vec<OperatorClassOpType>,
+    pub for_clause: Option<OperatorClassOperatorFor>,
+}
+
+impl fmt::Display for OperatorClassOperator {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "OPERATOR {} {}", self.strategy_number, self.operator)?;
+        if !self.op_types.is_empty() {
+            write!(f, " ({})", display_comma_separated(self.op_types.as_slice()))?;
+        }
+        if let Some(for_clause) = &self.for_clause {
+            write!(f, " {for_clause}")?;
+        }
+        Ok(())
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum OperatorClassOperatorFor {
+    Search,
+    OrderBy(
+        #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+        ObjectName,
+    ),
+}
+
+impl fmt::Display for OperatorClassOperatorFor {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OperatorClassOperatorFor::Search => f.write_str("FOR SEARCH"),
+            OperatorClassOperatorFor::OrderBy(name) => write!(f, "FOR ORDER BY {name}"),
+        }
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct OperatorClassFunction {
+    pub support_number: u64,
+    pub op_types: Vec<OperatorClassOpType>,
+    #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))]
+    pub function_name: ObjectName,
+    pub argument_types: Vec<DataType>,
+}
+
+impl fmt::Display for OperatorClassFunction {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "FUNCTION {}", self.support_number)?;
+        if !self.op_types.is_empty() {
+            write!(f, " ({})", display_comma_separated(self.op_types.as_slice()))?;
+        }
+        write!(f, " {}({})", self.function_name, display_comma_separated(self.argument_types.as_slice()))
+    }
+}
+
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum OperatorClassOpType {
+    None,
+    Type(DataType),
+}
+
+impl fmt::Display for OperatorClassOpType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OperatorClassOpType::None => f.write_str("NONE"),
+            OperatorClassOpType::Type(ty) => write!(f, "{ty}"),
+        }
+    }
+}
+
 /// ALTER TABLE operation REPLICA IDENTITY values
 /// See [Postgres ALTER TABLE docs](https://www.postgresql.org/docs/current/sql-altertable.html)
 #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/mod.rs b/app/src/ast/mod.rs
index aa3fb08..faf74e5 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -66,6 +66,7 @@ pub use self::ddl::{
     ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
     ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
     CreateExtension, CreateFunction, CreateIndex, CreateTable, CreateTrigger, CreateView,
+    CreateOperator, CreateOperatorClass, CreateOperatorFamily, CreateOperatorFunctionKind,
     Deduplicate, DeferrableInitial, DropBehavior, DropExtension, DropFunction, DropTrigger,
     GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
     IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexColumn,
@@ -74,6 +75,8 @@ pub use self::ddl::{
     TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
     UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
     UserDefinedTypeSqlDefinitionOption, UserDefinedTypeStorage, ViewColumnDef,
+    OperatorClassFunction, OperatorClassItem, OperatorClassOpType, OperatorClassOperator,
+    OperatorClassOperatorFor, OperatorName,
 };
 pub use self::dml::{Delete, Insert, Update};
 pub use self::operator::{BinaryOperator, UnaryOperator};
@@ -3347,6 +3350,21 @@ pub enum Statement {
     /// See [Hive](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=27362034#LanguageManualDDL-CreateDataConnectorCreateConnector)
     CreateConnector(CreateConnector),
     /// ```sql
+    /// CREATE OPERATOR
+    /// ```
+    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createoperator.html)
+    CreateOperator(CreateOperator),
+    /// ```sql
+    /// CREATE OPERATOR FAMILY
+    /// ```
+    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createoperatorfamily.html)
+    CreateOperatorFamily(CreateOperatorFamily),
+    /// ```sql
+    /// CREATE OPERATOR CLASS
+    /// ```
+    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createoperatorclass.html)
+    CreateOperatorClass(CreateOperatorClass),
+    /// ```sql
     /// ALTER TABLE
     /// ```
     AlterTable(AlterTable),
@@ -4901,6 +4919,9 @@ impl fmt::Display for Statement {
                 Ok(())
             }
             Statement::CreateConnector(create_connector) => create_connector.fmt(f),
+            Statement::CreateOperator(create_operator) => create_operator.fmt(f),
+            Statement::CreateOperatorFamily(create_family) => create_family.fmt(f),
+            Statement::CreateOperatorClass(create_class) => create_class.fmt(f),
             Statement::AlterTable(alter_table) => write!(f, "{alter_table}"),
             Statement::AlterIndex { name, operation } => {
                 write!(f, "ALTER INDEX {name} {operation}")
@@ -10739,6 +10760,24 @@ impl From<CreateConnector> 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<AlterSchema> for Statement {
     fn from(a: AlterSchema) -> Self {
         Self::AlterSchema(a)
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 1ab4626..1cf266b 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -4749,6 +4749,14 @@ impl<'a> Parser<'a> {
             self.parse_create_view(or_alter, or_replace, temporary, create_view_params)
         } else if self.parse_keyword(Keyword::POLICY) {
             self.parse_create_policy()
+        } else if self.parse_keyword(Keyword::OPERATOR) {
+            if self.parse_keyword(Keyword::CLASS) {
+                self.parse_create_operator_class()
+            } else if self.parse_keyword(Keyword::FAMILY) {
+                self.parse_create_operator_family()
+            } else {
+                self.parse_create_operator()
+            }
         } else if self.parse_keyword(Keyword::EXTERNAL) {
             self.parse_create_external_table(or_replace)
         } else if self.parse_keyword(Keyword::FUNCTION) {
@@ -6436,6 +6444,322 @@ impl<'a> Parser<'a> {
         }))
     }
 
+    pub fn parse_create_operator(&mut self) -> Result<Statement, ParserError> {
+        let name = self.parse_object_name(false)?;
+        self.expect_token(&Token::LParen)?;
+        let mut function_kind = None;
+        let mut function = None;
+        let mut left_arg = None;
+        let mut right_arg = None;
+        let mut commutator = None;
+        let mut negator = None;
+        let mut restrict = None;
+        let mut join = None;
+        let mut hashes = false;
+        let mut merges = false;
+        let mut first = true;
+
+        loop {
+            if self.consume_token(&Token::RParen) {
+                break;
+            }
+            if !first {
+                self.expect_token(&Token::Comma)?;
+            }
+            first = false;
+
+            if self.parse_keyword(Keyword::FUNCTION) {
+                if function_kind.is_some() {
+                    return parser_err!(
+                        "FUNCTION or PROCEDURE already provided in CREATE OPERATOR",
+                        self.peek_token().span.start,
+                    );
+                }
+                self.expect_token(&Token::Eq)?;
+                function_kind = Some(CreateOperatorFunctionKind::Function);
+                function = Some(self.parse_object_name(false)?);
+            } else if self.parse_keyword(Keyword::PROCEDURE) {
+                if function_kind.is_some() {
+                    return parser_err!(
+                        "FUNCTION or PROCEDURE already provided in CREATE OPERATOR",
+                        self.peek_token().span.start,
+                    );
+                }
+                self.expect_token(&Token::Eq)?;
+                function_kind = Some(CreateOperatorFunctionKind::Procedure);
+                function = Some(self.parse_object_name(false)?);
+            } else if self.parse_keyword(Keyword::LEFTARG) {
+                self.expect_token(&Token::Eq)?;
+                left_arg = Some(self.parse_data_type()?);
+            } else if self.parse_keyword(Keyword::RIGHTARG) {
+                self.expect_token(&Token::Eq)?;
+                right_arg = Some(self.parse_data_type()?);
+            } else if self.parse_keyword(Keyword::COMMUTATOR) {
+                self.expect_token(&Token::Eq)?;
+                commutator = Some(self.parse_operator_name_optional_keyword()?);
+            } else if self.parse_keyword(Keyword::NEGATOR) {
+                self.expect_token(&Token::Eq)?;
+                negator = Some(self.parse_operator_name_optional_keyword()?);
+            } else if self.parse_keyword(Keyword::RESTRICT) {
+                self.expect_token(&Token::Eq)?;
+                restrict = Some(self.parse_object_name(false)?);
+            } else if self.parse_keyword(Keyword::JOIN) {
+                self.expect_token(&Token::Eq)?;
+                join = Some(self.parse_object_name(false)?);
+            } else if self.parse_keyword(Keyword::HASHES) {
+                hashes = true;
+            } else if self.parse_keyword(Keyword::MERGES) {
+                merges = true;
+            } else {
+                return self.expected(
+                    "FUNCTION, PROCEDURE, LEFTARG, RIGHTARG, COMMUTATOR, NEGATOR, RESTRICT, JOIN, HASHES, or MERGES",
+                    self.peek_token(),
+                );
+            }
+        }
+
+        let missing_function_span = self.peek_token().span.start;
+        let function_kind = function_kind.ok_or_else(|| {
+            parser_err!(
+                "CREATE OPERATOR requires FUNCTION or PROCEDURE clause",
+                missing_function_span
+            )
+        })?;
+        let function = function.ok_or_else(|| {
+            parser_err!(
+                "CREATE OPERATOR requires FUNCTION or PROCEDURE clause",
+                missing_function_span
+            )
+        })?;
+
+        Ok(Statement::CreateOperator(CreateOperator {
+            name,
+            function_kind,
+            function,
+            left_arg,
+            right_arg,
+            commutator,
+            negator,
+            restrict,
+            join,
+            hashes,
+            merges,
+        }))
+    }
+
+    pub fn parse_create_operator_family(&mut self) -> Result<Statement, ParserError> {
+        let name = self.parse_object_name(false)?;
+        self.expect_keyword(Keyword::USING)?;
+        let using = self.parse_object_name(false)?;
+        Ok(Statement::CreateOperatorFamily(CreateOperatorFamily { name, using }))
+    }
+
+    pub fn parse_create_operator_class(&mut self) -> Result<Statement, ParserError> {
+        let default = self.parse_keyword(Keyword::DEFAULT);
+        let name = self.parse_object_name(false)?;
+        self.expect_keywords(&[Keyword::FOR, Keyword::TYPE])?;
+        let for_type = self.parse_data_type()?;
+        self.expect_keyword(Keyword::USING)?;
+        let using = self.parse_object_name(false)?;
+        let family = if self.parse_keyword(Keyword::FAMILY) {
+            Some(self.parse_object_name(false)?)
+        } else {
+            None
+        };
+        self.expect_keyword(Keyword::AS)?;
+
+        let mut members = Vec::new();
+        loop {
+            members.push(self.parse_operator_class_item()?);
+            if !self.consume_token(&Token::Comma) {
+                break;
+            }
+        }
+
+        if members.is_empty() {
+            return parser_err!(
+                "CREATE OPERATOR CLASS requires at least one OPERATOR, FUNCTION, or STORAGE clause",
+                self.peek_token().span.start,
+            );
+        }
+
+        Ok(Statement::CreateOperatorClass(CreateOperatorClass {
+            default,
+            name,
+            for_type,
+            using,
+            family,
+            members,
+        }))
+    }
+
+    fn parse_operator_name_optional_keyword(&mut self) -> Result<OperatorName, ParserError> {
+        if self.parse_keyword(Keyword::OPERATOR) {
+            self.expect_token(&Token::LParen)?;
+            let parts = self.parse_operator_name_parts()?;
+            self.expect_token(&Token::RParen)?;
+            return Ok(OperatorName::new(parts, true));
+        }
+        Ok(OperatorName::new(self.parse_operator_name_parts()?, false))
+    }
+
+    fn parse_operator_name_parts(&mut self) -> Result<Vec<String>, ParserError> {
+        let mut parts = Vec::new();
+        parts.push(self.parse_operator_name_part()?);
+        while self.consume_token(&Token::Period) {
+            parts.push(self.parse_operator_name_part()?);
+        }
+        Ok(parts)
+    }
+
+    fn parse_operator_name_part(&mut self) -> Result<String, ParserError> {
+        let token_with_span = self.next_token();
+        match token_with_span.token {
+            Token::Comma | Token::RParen | Token::LParen | Token::EOF => {
+                return parser_err!(
+                    "Unexpected token in operator name",
+                    token_with_span.span.start
+                );
+            }
+            Token::Period => {
+                return parser_err!(
+                    "Unexpected period in operator name",
+                    token_with_span.span.start
+                );
+            }
+            _ => Ok(token_with_span.token.to_string()),
+        }
+    }
+
+    fn parse_operator_class_item(&mut self) -> Result<OperatorClassItem, ParserError> {
+        if self.parse_keyword(Keyword::OPERATOR) {
+            return Ok(OperatorClassItem::Operator(
+                self.parse_operator_class_operator()?,
+            ));
+        }
+        if self.parse_keyword(Keyword::FUNCTION) {
+            return Ok(OperatorClassItem::Function(
+                self.parse_operator_class_function()?,
+            ));
+        }
+        if self.parse_keyword(Keyword::STORAGE) {
+            return Ok(OperatorClassItem::Storage(self.parse_data_type()?));
+        }
+
+        parser_err!(
+            "Expected OPERATOR, FUNCTION, or STORAGE in CREATE OPERATOR CLASS AS clause",
+            self.peek_token().span.start
+        )
+    }
+
+    fn parse_operator_class_operator(&mut self) -> Result<OperatorClassOperator, ParserError> {
+        let strategy_number = self.parse_operator_class_number()?;
+        let operator = OperatorName::new(self.parse_operator_name_parts()?, false);
+        let op_types = self.parse_operator_class_op_types()?;
+        let for_clause = self.parse_operator_class_for_clause()?;
+        Ok(OperatorClassOperator {
+            strategy_number,
+            operator,
+            op_types,
+            for_clause,
+        })
+    }
+
+    fn parse_operator_class_function(&mut self) -> Result<OperatorClassFunction, ParserError> {
+        let support_number = self.parse_operator_class_number()?;
+        let op_types = self.parse_operator_class_op_types()?;
+        let function_name = self.parse_object_name(false)?;
+        let argument_types = self.parse_parenthesized_data_types()?;
+        Ok(OperatorClassFunction {
+            support_number,
+            op_types,
+            function_name,
+            argument_types,
+        })
+    }
+
+    fn parse_operator_class_op_types(&mut self) -> Result<Vec<OperatorClassOpType>, ParserError> {
+        if !self.consume_token(&Token::LParen) {
+            return Ok(Vec::new());
+        }
+        if self.consume_token(&Token::RParen) {
+            return Ok(Vec::new());
+        }
+
+        let mut op_types = Vec::new();
+        loop {
+            op_types.push(self.parse_operator_class_op_type()?);
+            if self.consume_token(&Token::Comma) {
+                continue;
+            }
+            self.expect_token(&Token::RParen)?;
+            break;
+        }
+
+        Ok(op_types)
+    }
+
+    fn parse_operator_class_op_type(&mut self) -> Result<OperatorClassOpType, ParserError> {
+        if self.parse_keyword(Keyword::NONE) {
+            Ok(OperatorClassOpType::None)
+        } else {
+            Ok(OperatorClassOpType::Type(self.parse_data_type()?))
+        }
+    }
+
+    fn parse_parenthesized_data_types(&mut self) -> Result<Vec<DataType>, ParserError> {
+        self.expect_token(&Token::LParen)?;
+        if self.consume_token(&Token::RParen) {
+            return Ok(Vec::new());
+        }
+
+        let mut types = Vec::new();
+        loop {
+            types.push(self.parse_data_type()?);
+            if self.consume_token(&Token::Comma) {
+                continue;
+            }
+            self.expect_token(&Token::RParen)?;
+            break;
+        }
+
+        Ok(types)
+    }
+
+    fn parse_operator_class_for_clause(
+        &mut self,
+    ) -> Result<Option<OperatorClassOperatorFor>, ParserError> {
+        if !self.parse_keyword(Keyword::FOR) {
+            return Ok(None);
+        }
+        if self.parse_keyword(Keyword::SEARCH) {
+            return Ok(Some(OperatorClassOperatorFor::Search));
+        }
+        self.expect_keyword(Keyword::ORDER)?;
+        self.expect_keyword(Keyword::BY)?;
+        let sort_family = self.parse_object_name(false)?;
+        Ok(Some(OperatorClassOperatorFor::OrderBy(sort_family)))
+    }
+
+    fn parse_operator_class_number(&mut self) -> Result<u64, ParserError> {
+        let number = self.parse_number_value()?;
+        let span = number.span.start;
+        if let Value::Number(text, _) = number.value {
+            match text.parse::<u64>() {
+                Ok(value) => Ok(value),
+                Err(_) => parser_err!(
+                    format!("Invalid operator class number: {text}"),
+                    span
+                ),
+            }
+        } else {
+            parser_err!(
+                "Numeric literal expected for operator class number",
+                span
+            )
+        }
+    }
+
     pub fn parse_drop(&mut self) -> Result<Statement, ParserError> {
         // MySQL dialect supports `TEMPORARY`
         let temporary = dialect_of!(self is MySqlDialect | GenericDialect | DuckDbDialect)
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index 3bdf6d1..cc120b4 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-03-05__17-48-34__gpt-5-1-codex-mini/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -6661,3 +6661,48 @@ fn parse_foreign_key_match_with_actions() {
 
     pg_and_generic().verified_stmt(sql);
 }
+
+#[test]
+fn parse_create_operator_statements() {
+    let sql = "\
+        CREATE OPERATOR public.<> (\
+            PROCEDURE = public.operator_neq,\
+            LEFTARG = bigint,\
+            RIGHTARG = bigint,\
+            COMMUTATOR = OPERATOR(pg_catalog.=),\
+            NEGATOR = <>,\
+            RESTRICT = public.operator_eq,\
+            JOIN = public.operator_join,\
+            HASHES,\
+            MERGES\
+        );";
+    pg().one_statement_parses_to(
+        sql,
+        "CREATE OPERATOR public.<> (\
+        PROCEDURE = public.operator_neq, \
+        LEFTARG = BIGINT, \
+        RIGHTARG = BIGINT, \
+        COMMUTATOR = OPERATOR(pg_catalog.=), \
+        NEGATOR = <>, \
+        RESTRICT = public.operator_eq, \
+        JOIN = public.operator_join, \
+        HASHES, \
+        MERGES)",
+    );
+
+    let sql = "CREATE OPERATOR FAMILY public.text_ops USING btree;";
+    pg().one_statement_parses_to(sql, "CREATE OPERATOR FAMILY public.text_ops USING btree");
+
+    let sql = "\
+        CREATE DEFAULT OPERATOR CLASS public.text_ops \
+        FOR TYPE text USING btree \
+        FAMILY public.text_family \
+        AS \
+        OPERATOR 1 public.< (text, text) FOR ORDER BY public.text_order, \
+        FUNCTION 1 (text) public.text_compare(text), \
+        STORAGE text;";
+    pg().one_statement_parses_to(
+        sql,
+        "CREATE DEFAULT OPERATOR CLASS public.text_ops FOR TYPE TEXT USING btree FAMILY public.text_family AS OPERATOR 1 public.< (TEXT, TEXT) FOR ORDER BY public.text_order, FUNCTION 1 (TEXT) public.text_compare(TEXT), STORAGE TEXT",
+    );
+}