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
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",
+ );
+}