STET

agent_patch

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

23902 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/ddl.rs b/app/src/ast/ddl.rs
index 286b16a..a02ea06 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/ddl.rs
+++ b/app/src/ast/ddl.rs
@@ -3952,3 +3952,209 @@ impl Spanned for DropFunction {
         Span::empty()
     }
 }
+
+/// Operator name container used in PostgreSQL operator DDL statements.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub struct PgOperator {
+    pub names: Vec<String>,
+}
+
+impl fmt::Display for PgOperator {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "OPERATOR({})", display_separated(&self.names, "."))
+    }
+}
+
+/// CREATE OPERATOR statement
+///
+/// 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 {
+    pub name: ObjectName,
+    pub function_name: ObjectName,
+    pub procedure_keyword: bool,
+    pub left_arg: Option<DataType>,
+    pub right_arg: Option<DataType>,
+    pub commutator: Option<PgOperator>,
+    pub negator: Option<PgOperator>,
+    pub restrict: Option<ObjectName>,
+    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();
+        let fn_keyword = if self.procedure_keyword { "PROCEDURE" } else { "FUNCTION" };
+        clauses.push(format!("{fn_keyword} = {function_name}", function_name = self.function_name));
+        if let Some(left) = &self.left_arg {
+            clauses.push(format!("LEFTARG = {left}"));
+        }
+        if let Some(right) = &self.right_arg {
+            clauses.push(format!("RIGHTARG = {right}"));
+        }
+        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 = clauses.join(", "))
+    }
+}
+
+/// CREATE OPERATOR FAMILY statement
+///
+/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createopfamily.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 {
+    pub name: ObjectName,
+    pub using: Ident,
+}
+
+impl fmt::Display for CreateOperatorFamily {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "CREATE OPERATOR FAMILY {name} USING {method}", name = self.name, method = self.using)
+    }
+}
+
+/// CREATE OPERATOR CLASS statement
+///
+/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createopclass.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 name: ObjectName,
+    pub is_default: bool,
+    pub data_type: DataType,
+    pub using: Ident,
+    pub family: Option<ObjectName>,
+    pub items: Vec<OperatorClassItem>,
+}
+
+impl fmt::Display for CreateOperatorClass {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, "CREATE OPERATOR CLASS {name}", name = self.name)?;
+        if self.is_default {
+            write!(f, " DEFAULT")?;
+        }
+        write!(f, " FOR TYPE {data_type} USING {using}", data_type = self.data_type, using = self.using)?;
+        if let Some(family) = &self.family {
+            write!(f, " FAMILY {family}")?;
+        }
+        write!(f, " AS {}", display_comma_separated(&self.items))
+    }
+}
+
+/// PostgreSQL operator class sort modifiers.
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum OperatorClassSort {
+    Search,
+    OrderBy(ObjectName),
+}
+
+impl fmt::Display for OperatorClassSort {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OperatorClassSort::Search => f.write_str("FOR SEARCH"),
+            OperatorClassSort::OrderBy(name) => write!(f, "FOR ORDER BY {name}"),
+        }
+    }
+}
+
+/// Operand type descriptor used inside operator class definitions.
+#[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 {
+    DataType(DataType),
+    None,
+}
+
+impl fmt::Display for OperatorClassOpType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OperatorClassOpType::DataType(dt) => fmt::Display::fmt(dt, f),
+            OperatorClassOpType::None => f.write_str("NONE"),
+        }
+    }
+}
+
+/// Items that can appear inside the `AS` block of CREATE OPERATOR CLASS.
+#[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 {
+        strategy_number: u64,
+        operator_name: PgOperator,
+        op_types: Option<Vec<OperatorClassOpType>>,
+        sort: Option<OperatorClassSort>,
+    },
+    Function {
+        support_number: u64,
+        op_types: Option<Vec<OperatorClassOpType>>,
+        function_name: ObjectName,
+        arguments: Vec<DataType>,
+    },
+    Storage {
+        storage_type: DataType,
+    },
+}
+
+impl fmt::Display for OperatorClassItem {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            OperatorClassItem::Operator {
+                strategy_number,
+                operator_name,
+                op_types,
+                sort,
+            } => {
+                write!(f, "OPERATOR {strategy_number} {operator_name}")?;
+                if let Some(op_types) = op_types {
+                    write!(f, " ({})", display_comma_separated(op_types))?;
+                }
+                if let Some(sort) = sort {
+                    write!(f, " {sort}")?;
+                }
+                Ok(())
+            }
+            OperatorClassItem::Function {
+                support_number,
+                op_types,
+                function_name,
+                arguments,
+            } => {
+                write!(f, "FUNCTION {support_number}")?;
+                if let Some(op_types) = op_types {
+                    write!(f, " ({})", display_comma_separated(op_types))?;
+                }
+                write!(f, " {function_name} ({})", display_comma_separated(arguments))
+            }
+            OperatorClassItem::Storage { storage_type } => write!(f, "STORAGE {storage_type}"),
+        }
+    }
+}
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/mod.rs b/app/src/ast/mod.rs
index aa3fb08..5bf8c53 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -65,11 +65,13 @@ pub use self::ddl::{
     AlterTypeAddValuePosition, AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue,
     ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnOptions, ColumnPolicy,
     ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateDomain,
-    CreateExtension, CreateFunction, CreateIndex, CreateTable, CreateTrigger, CreateView,
+    CreateExtension, CreateFunction, CreateIndex, CreateOperator, CreateOperatorClass,
+    CreateOperatorFamily, CreateTable, CreateTrigger, CreateView,
     Deduplicate, DeferrableInitial, DropBehavior, DropExtension, DropFunction, DropTrigger,
     GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
     IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexColumn,
-    IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption, Owner, Partition,
+    IndexOption, IndexType, KeyOrIndexDisplay, Msck, NullsDistinctOption, Owner,
+    OperatorClassItem, OperatorClassOpType, OperatorClassSort, PgOperator, Partition,
     ProcedureParam, ReferentialAction, RenameTableNameKind, ReplicaIdentity, TagsColumnOption,
     TriggerObjectKind, Truncate, UserDefinedTypeCompositeAttributeDef,
     UserDefinedTypeInternalLength, UserDefinedTypeRangeOption, UserDefinedTypeRepresentation,
@@ -3308,6 +3310,21 @@ pub enum Statement {
     /// ```
     CreateIndex(CreateIndex),
     /// ```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-createopfamily.html)
+    CreateOperatorFamily(CreateOperatorFamily),
+    /// ```sql
+    /// CREATE OPERATOR CLASS
+    /// ```
+    /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createopclass.html)
+    CreateOperatorClass(CreateOperatorClass),
+    /// ```sql
     /// CREATE ROLE
     /// ```
     /// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createrole.html)
@@ -4817,6 +4834,11 @@ impl fmt::Display for Statement {
                 Ok(())
             }
             Statement::CreateIndex(create_index) => create_index.fmt(f),
+            Statement::CreateOperator(create_operator) => create_operator.fmt(f),
+            Statement::CreateOperatorFamily(create_operator_family) =>
+                create_operator_family.fmt(f),
+            Statement::CreateOperatorClass(create_operator_class) =>
+                create_operator_class.fmt(f),
             Statement::CreateExtension(create_extension) => write!(f, "{create_extension}"),
             Statement::DropExtension(drop_extension) => write!(f, "{drop_extension}"),
             Statement::CreateRole(create_role) => write!(f, "{create_role}"),
diff --git a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 1ab4626..32f46a6 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -4776,6 +4776,14 @@ impl<'a> Parser<'a> {
             self.parse_create_index(false)
         } else if self.parse_keywords(&[Keyword::UNIQUE, Keyword::INDEX]) {
             self.parse_create_index(true)
+        } else if self.parse_keyword(Keyword::OPERATOR) {
+            if self.parse_keyword(Keyword::FAMILY) {
+                self.parse_create_operator_family()
+            } else if self.parse_keyword(Keyword::CLASS) {
+                self.parse_create_operator_class()
+            } else {
+                self.parse_create_operator()
+            }
         } else if self.parse_keyword(Keyword::VIRTUAL) {
             self.parse_create_virtual_table()
         } else if self.parse_keyword(Keyword::SCHEMA) {
@@ -6436,6 +6444,227 @@ 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_name: Option<ObjectName> = None;
+        let mut procedure_keyword = false;
+        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;
+
+        loop {
+            if self.consume_token(&Token::RParen) {
+                break;
+            }
+            if self.parse_keyword(Keyword::FUNCTION) {
+                self.expect_token(&Token::Eq)?;
+                function_name = Some(self.parse_object_name(false)?);
+                procedure_keyword = false;
+            } else if self.parse_keyword(Keyword::PROCEDURE) {
+                self.expect_token(&Token::Eq)?;
+                function_name = Some(self.parse_object_name(false)?);
+                procedure_keyword = true;
+            } 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_postgres_operator_name()?);
+            } else if self.parse_keyword(Keyword::NEGATOR) {
+                self.expect_token(&Token::Eq)?;
+                negator = Some(self.parse_postgres_operator_name()?);
+            } 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("operator option", self.peek_token());
+            }
+
+            if self.consume_token(&Token::Comma) {
+                continue;
+            }
+            self.expect_token(&Token::RParen)?;
+            break;
+        }
+
+        if function_name.is_none() {
+            return self.expected("FUNCTION or PROCEDURE clause", self.peek_token());
+        }
+
+        Ok(Statement::CreateOperator(CreateOperator {
+            name,
+            function_name: function_name.unwrap(),
+            procedure_keyword,
+            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_identifier()?;
+        Ok(Statement::CreateOperatorFamily(CreateOperatorFamily { name, using }))
+    }
+
+    pub 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_keywords(&[Keyword::FOR, Keyword::TYPE])?;
+        let data_type = self.parse_data_type()?;
+        self.expect_keyword(Keyword::USING)?;
+        let using = self.parse_identifier()?;
+        let family = if self.parse_keyword(Keyword::FAMILY) {
+            Some(self.parse_object_name(false)?)
+        } else {
+            None
+        };
+        self.expect_keyword(Keyword::AS)?;
+        let mut items = Vec::new();
+        loop {
+            items.push(self.parse_operator_class_item()?);
+            if !self.consume_token(&Token::Comma) {
+                break;
+            }
+        }
+        if items.is_empty() {
+            return self.expected("operator class item", self.peek_token());
+        }
+        Ok(Statement::CreateOperatorClass(CreateOperatorClass {
+            name,
+            is_default,
+            data_type,
+            using,
+            family,
+            items,
+        }))
+    }
+
+    fn parse_operator_class_item(&mut self) -> Result<OperatorClassItem, ParserError> {
+        if self.parse_keyword(Keyword::OPERATOR) {
+            let strategy_number = self.parse_literal_uint()?;
+            let operator_name = self.parse_postgres_operator_name()?;
+            let op_types = if self.consume_token(&Token::LParen) {
+                let types = self.parse_operator_class_op_types()?;
+                self.expect_token(&Token::RParen)?;
+                Some(types)
+            } else {
+                None
+            };
+            let sort = self.parse_operator_class_sort()?;
+            Ok(OperatorClassItem::Operator {
+                strategy_number,
+                operator_name,
+                op_types,
+                sort,
+            })
+        } else if self.parse_keyword(Keyword::FUNCTION) {
+            let support_number = self.parse_literal_uint()?;
+            let op_types = if self.consume_token(&Token::LParen) {
+                let types = self.parse_operator_class_op_types()?;
+                self.expect_token(&Token::RParen)?;
+                Some(types)
+            } else {
+                None
+            };
+            let function_name = self.parse_object_name(false)?;
+            self.expect_token(&Token::LParen)?;
+            let arguments = self.parse_comma_separated(Parser::parse_data_type)?;
+            self.expect_token(&Token::RParen)?;
+            Ok(OperatorClassItem::Function {
+                support_number,
+                op_types,
+                function_name,
+                arguments,
+            })
+        } else if self.parse_keyword(Keyword::STORAGE) {
+            let storage_type = self.parse_data_type()?;
+            Ok(OperatorClassItem::Storage { storage_type })
+        } else {
+            self.expected("OPERATOR, FUNCTION, or STORAGE", self.peek_token())
+        }
+    }
+
+    fn parse_operator_class_sort(&mut self) -> Result<Option<OperatorClassSort>, ParserError> {
+        if self.parse_keyword(Keyword::FOR) {
+            if self.parse_keyword(Keyword::SEARCH) {
+                Ok(Some(OperatorClassSort::Search))
+            } else if self.parse_keywords(&[Keyword::ORDER, Keyword::BY]) {
+                let sort_family = self.parse_object_name(false)?;
+                Ok(Some(OperatorClassSort::OrderBy(sort_family)))
+            } else {
+                self.expected("SEARCH or ORDER BY after FOR", self.peek_token())
+            }
+        } else {
+            Ok(None)
+        }
+    }
+
+    fn parse_operator_class_op_types(&mut self) -> Result<Vec<OperatorClassOpType>, ParserError> {
+        let mut types = Vec::new();
+        loop {
+            if self.parse_keyword(Keyword::NONE) {
+                types.push(OperatorClassOpType::None);
+            } else {
+                types.push(OperatorClassOpType::DataType(self.parse_data_type()?));
+            }
+            if !self.consume_token(&Token::Comma) {
+                break;
+            }
+        }
+        Ok(types)
+    }
+
+    fn parse_postgres_operator_name(&mut self) -> Result<PgOperator, ParserError> {
+        if self.parse_keyword(Keyword::OPERATOR) {
+            self.expect_token(&Token::LParen)?;
+            let names = self.parse_postgres_operator_parts()?;
+            self.expect_token(&Token::RParen)?;
+            return Ok(PgOperator { names });
+        }
+        Ok(PgOperator {
+            names: self.parse_postgres_operator_parts()?,
+        })
+    }
+
+    fn parse_postgres_operator_parts(&mut self) -> Result<Vec<String>, ParserError> {
+        let mut names = Vec::new();
+        loop {
+            self.advance_token();
+            names.push(self.get_current_token().to_string());
+            if !self.consume_token(&Token::Period) {
+                break;
+            }
+        }
+        if names.is_empty() {
+            self.expected("operator name", self.peek_token())
+        } else {
+            Ok(names)
+        }
+    }
+
     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-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index 3bdf6d1..6dac727 100644
--- a/tmp/agent-patch-flux-pr-2096.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -3563,6 +3563,47 @@ fn parse_on_commit() {
     pg_and_generic().verified_stmt("CREATE TEMPORARY TABLE table (COL INT) ON COMMIT DROP");
 }
 
+#[test]
+fn parse_create_operator_statements() {
+    let sql = r#"CREATE OPERATOR === (
+        FUNCTION = area_equal_function,
+        LEFTARG = box,
+        RIGHTARG = box,
+        COMMUTATOR = OPERATOR(pg_catalog.===),
+        NEGATOR = OPERATOR(pg_catalog.!==),
+        RESTRICT = area_restriction_function,
+        JOIN = area_join_function,
+        HASHES,
+        MERGES
+    )"#;
+    let canonical = "CREATE OPERATOR === (FUNCTION = area_equal_function, LEFTARG = box, RIGHTARG = box, COMMUTATOR = OPERATOR(pg_catalog.===), NEGATOR = OPERATOR(pg_catalog.!==), RESTRICT = area_restriction_function, JOIN = area_join_function, HASHES, MERGES)";
+    pg().one_statement_parses_to(sql, canonical);
+}
+
+#[test]
+fn parse_create_operator_family_and_class() {
+    pg().one_statement_parses_to(
+        "CREATE OPERATOR FAMILY gist_ops USING gist",
+        "CREATE OPERATOR FAMILY gist_ops USING gist",
+    );
+    let sql = r#"CREATE OPERATOR CLASS gist__int_ops DEFAULT FOR TYPE _int4 USING gist FAMILY foo AS
+        OPERATOR 3 &&,
+        OPERATOR 6 = (anyarray, anyarray),
+        OPERATOR 7 @>,
+        OPERATOR 8 <@,
+        OPERATOR 20 @@ (_int4, query_int),
+        FUNCTION 1 g_int_consistent (internal, _int4, smallint, oid, internal),
+        FUNCTION 2 g_int_union (internal, internal),
+        FUNCTION 3 g_int_compress (internal),
+        FUNCTION 4 g_int_decompress (internal),
+        FUNCTION 5 g_int_penalty (internal, internal, internal),
+        FUNCTION 6 g_int_picksplit (internal, internal),
+        FUNCTION 7 g_int_same (_int4, _int4, internal),
+        STORAGE internal"#;
+    let canonical = "CREATE OPERATOR CLASS gist__int_ops DEFAULT FOR TYPE _int4 USING gist FAMILY foo AS OPERATOR 3 OPERATOR(&&), OPERATOR 6 OPERATOR(=) (anyarray, anyarray), OPERATOR 7 OPERATOR(@>), OPERATOR 8 OPERATOR(<@), OPERATOR 20 OPERATOR(@@) (_int4, query_int), FUNCTION 1 g_int_consistent (internal, _int4, smallint, oid, internal), FUNCTION 2 g_int_union (internal, internal), FUNCTION 3 g_int_compress (internal), FUNCTION 4 g_int_decompress (internal), FUNCTION 5 g_int_penalty (internal, internal, internal), FUNCTION 6 g_int_picksplit (internal, internal), FUNCTION 7 g_int_same (_int4, _int4, internal), STORAGE internal";
+    pg().one_statement_parses_to(sql, canonical);
+}
+
 fn pg() -> TestedDialects {
     TestedDialects::new(vec![Box::new(PostgreSqlDialect {})])
 }