Repository navigation
feat: parse MySQL loadable aggregate functions and the ALTER TABLE validation modifier - #2917
Merged
manticore-projects merged 2 commits intoNov 14, 2026
Conversation
…lidation modifier CREATE [AGGREGATE] FUNCTION [IF NOT EXISTS] name RETURNS type SONAME 'library' is a CreateFunction with a LoadableFunction definition; the AGGREGATE form was an UnsupportedStatement. WITH | WITHOUT VALIDATION is a typed ALTER TABLE action (AlterOperation.VALIDATION) wherever MySQL accepts the modifier in the action list, instead of raw text that swallowed the following actions. With the MySQL dialect both follow the server grammar. MariaDB and no dialect read them only where the statement has exactly that shape; other dialects are unchanged. Signed-off-by: minleejae <mmj9808@gmail.com>
Preserve MySQL loadable functions alongside PostgreSQL aggregate support. Update overlapping unsupported-statement expectations to the combined feature set while keeping both implementations unchanged. Validation: mvn --batch-mode --no-transfer-progress test 15,888 tests; zero failures/errors; 25 existing skips. JavaCC reports no choice conflicts.
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
Two MySQL 8.4 forms did not get a usable AST:
CREATE AGGREGATE FUNCTION [IF NOT EXISTS] name RETURNS {STRING | INTEGER | REAL | DECIMAL} SONAME 'library'(CREATE FUNCTION Statement for Loadable Functions) came back as anUnsupportedStatement, while the same statement withoutAGGREGATEis aCreateFunction.{WITH | WITHOUT} VALIDATIONas a modifier of theALTER TABLEaction list (ALTER TABLE Statement,alter_commands_modifierinsql_yacc.yy) was read as raw text that swallowed the rest of the statement.Current design (As-is)
Create()has no branch forAGGREGATE, so the statement reaches the unsupported fallback. The form withoutAGGREGATEgoes through the routine token capture:CreateFunctiononly hasgetFunctionDeclarationParts(), and the capture also accepts what MySQL rejects (RETURNS DOUBLE, a trailingCOMMENT, …).AlterExpression()knowsWITH | WITHOUT VALIDATIONonly as the tail ofEXCHANGE PARTITION … WITH TABLE t. Anywhere else it falls into thecaptureRest()alternative, which takes every remaining token, so the actions after it are lost (TablesNamesFinderdid not seet2above) andtoString()prints,.Target design (To-be)
Loadable functions
CreateFunctiongetsisAggregate()andgetLoadableFunction(). The newLoadableFunctionholdsisIfNotExists(), the name, theReturnType(STRING,INTEGER,REAL,DECIMAL; the synonymsINT,INT4andDECkeep their spelling ingetReturnTypeKeyword()) and the library as aStringValue.getFunctionDeclarationParts()is derived from it, andsetFunctionDeclarationParts(…)removes it.toString()andStatementDeParsershareCreateFunctionalStatement.appendTo, which now asks the subclass for the kind keywords (AGGREGATE FUNCTION);getKind()staysFUNCTION.CreateLoadableFunction().SONAMEis not a token.TablesNamesFinderreturns no tables for a loadable function (it still refuses otherCreateFunctionstatements). Validation is unchanged:functionalStatementandcreateFunction.Validation modifier
AlterOperation.VALIDATION, carried byAlterExpressionTableOptionwithgetValidationMode(), next to theALGORITHMandLOCKmodifiers. It reusesAlterExpressionPartition.ExchangeValidationMode, and one production (AlterValidationMode()) reads the keywords for both the modifier and theEXCHANGE PARTITIONtail, which keeps its owngetExchangeValidationMode().Dialects
AGGREGATE, or a function name directly followed byRETURNS, must continue as a loadable function, as insql_yacc.yy(udf_tail); noOR REPLACEWITH/WITHOUTat the start of an action must be the modifier; rejected after a comma that follows a command MySQL allows only at the end of the list (standalone_alter_commands,PARTITION BY,REMOVE PARTITIONING)OR REPLACEincluded); everything else keeps its readingBehaviour changes
masteraccepted as tokens or raw text are now parse errors, for exampleCREATE FUNCTION f RETURNS DOUBLE SONAME 'udf.so',CREATE OR REPLACE FUNCTION f RETURNS INTEGER SONAME 'udf.so',CREATE AGGREGATE FUNCTION f (a INT) RETURNS INTEGER RETURN 1,ALTER TABLE t1 WITH NO VALIDATION,ALTER TABLE t1 EXCHANGE PARTITION p0 WITH TABLE t2, WITHOUT VALIDATION.VALIDATIONinstead ofUNSPECIFIC), andtoString()prints,instead of,after it.TablesNamesFinderreturns an empty result for a loadable function instead of throwing.Validation
New tests:
MySqlLoadableFunctionTest(45) andMySqlAlterValidationModifierTest(36): parse per dialect,toString,StatementDeParser, reparse, AST fields and node spans, rejected forms, other dialects, statement boundaries and scripts, visitors,TablesNamesFinder, validation features, model mutation../gradlew spotlessApply cleanTest check jar: 14,533 tests, 0 failures, 25 skipped (includingcheckGrammarAmbiguity, Checkstyle, PMD, SpotBugs, JaCoCo).Compared with
master(cf057dd33) on the MySQL and PostgreSQL DDL/DCL and non-DDL statement corpora, in the dialect and without one: 1 statement fixed in each MySQL run (ALTER TABLE t1 WITHOUT VALIDATION, EXCHANGE PARTITION p0 WITH TABLE t2, now two actions), 0 regressions, 0 other changes.MySQL 8.4.11: of 128 statements, the 55 whose syntax the server accepts all parse with
Dialect.MYSQL(some stop only at execution, for example with a missing shared library), and 63 of the 73 it rejects are parse errors. The other 10 are accepted as onmaster, by code this change does not touch: the stored-routine token capture (CREATE DEFINER = … FUNCTION f RETURNS INTEGER SONAME …,CREATE FUNCTION f() RETURNS INTEGER SONAME …), the unsupported-statement fallbacks (CREATE DEFINER = … AGGREGATE FUNCTION …,ALTER AGGREGATE FUNCTION …), the raw-text action (ALTER TABLE t , WITH VALIDATION,ALTER TABLE t VALIDATION), and the end-of-list position rule for other items (… DISCARD TABLESPACE, LOCK = NONE,… EXCHANGE PARTITION p0 WITH TABLE t2, ALGORITHM = INPLACE,… REMOVE PARTITIONING, ALGORITHM = COPY,… WITH VALIDATION, ADD COLUMN c INT, DISCARD TABLESPACE).AI assistance: prepared with Claude Code.