Repository navigation
feat: parse PostgreSQL CREATE/ALTER OPERATOR, OPERATOR CLASS/FAMILY and AGGREGATE - #2919
Merged
manticore-projects merged 1 commit intoNov 13, 2026
Conversation
…nd AGGREGATE CREATE and ALTER OPERATOR, OPERATOR CLASS, OPERATOR FAMILY and AGGREGATE were unsupported statements, whose text broke an operator such as === into "== =". They parse to CreateOperator, AlterOperator, CreateOperatorClass, AlterOperatorClass, CreateOperatorFamily, AlterOperatorFamily, CreateAggregate and AlterAggregate. These reuse the models DROP OPERATOR, DROP AGGREGATE and CREATE TYPE have: ExclusionOperator and OperatorSignature for an operator, RoutineReference for an aggregate signature and DefinitionElement for a definition list. OperatorClassItem is a member of an operator class or family. The statements are wired into the deparser, visitors, table finder, validation (eight features, enabled for PostgreSQL) and feature analysis. An operator name follows PostgreSQL's lexer wherever DDL names an operator: <==> and @#@ are one name, a name such as +- or => alone is rejected, and a database may precede the schema. An aggregate signature needs * or an argument, so DROP AGGREGATE agg() is now a parse error. Without the PostgreSQL dialect, CREATE and ALTER OPERATOR are read this way when an operator symbol follows, so Oracle's CREATE OPERATOR name BINDING stays unsupported, as do MySQL's CREATE AGGREGATE FUNCTION and SQL Server's CREATE AGGREGATE name (@parameter type). Signed-off-by: minleejae <mmj9808@gmail.com>
Contributor
|
Thank you! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Purpose
PostgreSQL 18 operator and aggregate definitions are returned as
UnsupportedStatementtoday, and the captured text breaks an operator name:CREATE OPERATOR === (...)renders asCREATE OPERATOR == = (...), which PostgreSQL does not read as the same operator. This PR parses them into typed statements.References: CREATE OPERATOR, ALTER OPERATOR, CREATE OPERATOR CLASS, ALTER OPERATOR CLASS, CREATE OPERATOR FAMILY, ALTER OPERATOR FAMILY, CREATE AGGREGATE, ALTER AGGREGATE, operator names in the lexical structure.
Current design (As-is)
CREATE/ALTER OPERATOR,OPERATOR CLASS,OPERATOR FAMILYandAGGREGATEfall through toUnsupportedStatement, in the PostgreSQL dialect too. The statement is a token list, so nothing rejects a malformed statement and an operator the lexer splits (===,<==>) is rendered with a space inside.DROP OPERATOR,DROP AGGREGATE,DROP OPERATOR CLASS|FAMILY,COMMENT ON OPERATOR|AGGREGATEandCREATE TYPE name (definition)already have models:ExclusionOperatorandOperatorSignaturefor an operator,RoutineReferencefor an aggregate signature,DefinitionElement/DefinitionArgumentfor a definition list.+-,=-), fails on<==>and@#@, and reads one schema only.DROP AGGREGATE agg()parses, which PostgreSQL rejects.Target design (To-be)
Eight statements, built from the existing models:
CREATE OPERATOR [schema.]op (definition)CreateOperator:getOperator()(ExclusionOperator),getDefinition()(DefinitionElementlist),getDefinitionElement(name)ALTER OPERATOR op (left, right) {OWNER TO role | SET SCHEMA schema | SET (...)}AlterOperator:getOperator()(OperatorSignature),getAction(),getNewName(),getProperties()CREATE OPERATOR CLASS name [DEFAULT] FOR TYPE type USING method [FAMILY family] AS member [, ...]CreateOperatorClass:getName(),isDefaultClass(),getDataType(),getAccessMethod(),getFamily(),getItems()ALTER OPERATOR CLASS name USING method {RENAME TO | OWNER TO | SET SCHEMA}AlterOperatorClass:getName(),getAccessMethod(),getAction(),getNewName()CREATE OPERATOR FAMILY name USING methodCreateOperatorFamily:getName(),getAccessMethod()ALTER OPERATOR FAMILY name USING method {ADD member [, ...] | DROP member [, ...] | RENAME TO | OWNER TO | SET SCHEMA}AlterOperatorFamily:getName(),getAccessMethod(),getAction(),getItems(),getNewName()CREATE [OR REPLACE] AGGREGATE name (arguments) (definition),CREATE AGGREGATE name (BASETYPE = type, ...)CreateAggregate:isOrReplace(),getAggregate()(RoutineReference),isOldStyle(),getDefinition(),getDefinitionElement(name)ALTER AGGREGATE name (arguments) {RENAME TO | OWNER TO | SET SCHEMA}AlterAggregate:getAggregate(),getAction(),getNewName()OperatorClassItemis one member of an operator class or family:KindOPERATOR,FUNCTIONorSTORAGE, the strategy or support number, the operator with optional operand types (nullforNONE), thePurpose(FOR SEARCH, orFOR ORDER BYwithgetSortFamily()), the function as aRoutineReference, or the storage type. A member thatDROPremoves has a kind, a number and operand types.gram.yof PostgreSQL 18 (DefineStmt,AlterOperatorStmt,CreateOpClassStmt,CreateOpFamilyStmt,AlterOpFamilyStmtand theRENAME/OWNER/SET SCHEMAalternatives). The definition lists use thePostgreSqlDefinition()rule ofCREATE TYPE, operators the reference and signature rules ofDROP OPERATOR, aggregates the signature rule ofDROP AGGREGATE. The names and values of a definition are not checked, as PostgreSQL checks them when it runs the statement. The old aggregate form takes identifiers with a value only (old_aggr_elem), so a keyword such asparallelis rejected there unless it is quoted.DROP OPERATOR,COMMENT ON OPERATOR,ALTER EXTENSION ... OPERATOR, exclusion constraints and definition values, which share the rule. It now follows PostgreSQL's lexer:<==>(the lexer's<=and=>) and@#@(lexed as a variable name) are operators;+or-only if it contains one of~ ! @ # % ^ & | ?, so+-,=-and*-are rejected, as PostgreSQL reads them as two tokens;=>alone is not an operator, and a name has at most 63 characters;db.schema.===);ExclusionOperator.getSchemaName()then returnsdb.schema.*or has at least one argument (aggr_args), wherever an aggregate is named.OPERATOR,CLASS,FAMILYandOWNERstay identifiers.StatementDeParser(sharingappendTowithtoString),StatementVisitor,TablesNamesFinder(they name no table),StatementValidator(createOperator,alterOperator,createOperatorClass,alterOperatorClass,createOperatorFamily,alterOperatorFamily,createAggregate,alterAggregate, enabled for PostgreSQL) andStatementFeatureVisitor(schema change).Dialects:
Dialect.POSTGRESQL,CREATE/ALTER OPERATORandCREATE/ALTER AGGREGATEare always read this way, so a malformed statement is a parse error instead of anUnsupportedStatement.CREATE/ALTER OPERATORis read this way when an operator symbol orCLASS/FAMILYfollows. Oracle'sCREATE OPERATOR name BINDING ...andALTER OPERATOR name COMPILEstayUnsupportedStatement, and so do MySQL'sCREATE AGGREGATE FUNCTIONand SQL Server'sCREATE AGGREGATE name (@parameter type) ....Behaviour changes for SQL that parsed before:
DROP OPERATOR +- (int, int),COMMENT ON OPERATOR =- (...)and similar names PostgreSQL does not take as one operator are parse errors.DROP AGGREGATE agg()andCOMMENT ON AGGREGATE agg() IS ...are parse errors.DROP OPERATOR <==> (...),DROP OPERATOR @#@ (...)andDROP OPERATOR db.schema.+ (...)now parse.Not covered:
<`>: the lexer reads a backtick as the start of a quoted identifier.Dialect.POSTGRESQL, the lexer reads#before#,@or a letter as a name, soCREATE OPERATOR ## (...)stays anUnsupportedStatementthere; the dialect reads it.gram.ydoes:ALTER OPERATOR FAMILY ... ADDwith an operator without operand types or withSTORAGE, a member with more than two operand types, and a right-unary operator signature(type, NONE)in an operator class.The change is additive for the Java API:
DefinitionElement.find(elements, name)is new, andBaseTypeDefinition.getElement(name)uses it.Validation
New
PostgreSqlOperatorDdlTest(277 tests) andPostgreSqlAggregateDdlTest(73 tests): AST fields,toString/StatementDeParser/reparse round trips with and without the dialect, 88 operator spellings throughCREATE OPERATOR,ALTER OPERATOR,ALTER OPERATOR FAMILYandDROP OPERATOR, statement boundaries in a script, visitors,TablesNamesFinder, features, validation, rejected forms, and the Oracle, MySQL and SQL Server forms that stay unsupported.ASTNodeCoverageTestandStatementFeatureVisitorMetadataDdlTestcover the new statements;UnsupportedStatementTestno longer lists the three statements that are typed now../gradlew spotlessApply cleanTest check jaron Java 17: 14,845 tests, 0 failures, 25 skipped.Compared with
master(cf057dd33) on the DDL/DCL and other-statement survey corpora for MySQL and PostgreSQL (dialect and default mode, keyword and script corpora): 0 regressions and no changed result. The corpora contain none of these statements; a separate list of 138 accepted and 143 rejected forms covers them.PostgreSQL 18.6: of 281 statements, the 129 whose syntax the server accepts are all typed with
Dialect.POSTGRESQL(they stop only at execution, for example with a missing function, role or access method), and 147 of the 152 it rejects are parse errors. The other five are accepted bygram.yand rejected by a check after parsing that reports a syntax error (SQLSTATE 42601); the same text against a missing object reaches the catalog lookup instead: an unrecognized attribute inALTER OPERATOR … SET (x = …), a right-unary signatureOPERATOR 1 ! (int, NONE)in an operator class, and inALTER OPERATOR FAMILYanADD OPERATOR 1 <without operand types,ADD STORAGE int, andDROP FUNCTION 2 (int4, int2, int8)with three operand types.Operator names: 88 operator spellings in four statements each and 18 texts that are not operator names in seven statements each (478 statements) all agree with the server.
AI assistance: prepared with Claude Code.