Skip to content

fix(parser): support MySQL index options and prefix-length key parts in index DDL - #2505

Merged
manticore-projects merged 3 commits into
JSQLParser:masterfrom
minleejae:fix/issue-2490-index-key-part-prefix-direction
Aug 24, 2026
Merged

fix(parser): support MySQL index options and prefix-length key parts in index DDL#2505
manticore-projects merged 3 commits into
JSQLParser:masterfrom
minleejae:fix/issue-2490-index-key-part-prefix-direction

Conversation

@minleejae

Copy link
Copy Markdown
Contributor

Refs #2490

Description

Fixes five of the six groups of MySQL index DDL reported in #2490. These now parse:

-- 1. key part with a prefix length *and* a direction (CREATE and ALTER)
CREATE INDEX i03 ON t (c1(20) DESC);
ALTER TABLE t ADD INDEX i05 (c1(20) DESC);

-- 2/3. CREATE INDEX index_option / algorithm_option / lock_option
CREATE INDEX i08 ON t (c1) KEY_BLOCK_SIZE = 8;
CREATE INDEX i10 ON t (c1) ALGORITHM = INPLACE LOCK = NONE;

-- 4. DROP INDEX algorithm_option / lock_option
DROP INDEX i15 ON t ALGORITHM = INPLACE LOCK = NONE;

-- 5. FULLTEXT / SPATIAL (previously fell back to UnsupportedStatement)
CREATE FULLTEXT INDEX i18 ON t (body) WITH PARSER ngram;
CREATE SPATIAL INDEX i19 ON t (g);

Two causes:

  • IndexColumnWithParams() allowed only one CreateParameter() per key part, so (20) consumed it and the trailing ASC/DESC could not be matched. It now collects parameters in a loop. IndexColumnsWithParamsList() is shared by the CREATE and ALTER paths, so one change fixes both.
  • The option keywords (KEY_BLOCK_SIZE, ALGORITHM, LOCK, NONE, PARSER, FULLTEXT, SPATIAL) were simply not reachable from the option lists that CREATE INDEX and DROP INDEX use.

No new grammar ambiguity

Those keywords all live in NonReservedWord(), so adding them as new alternatives to the trailing (...)* loops pushed JavaCC from 13 to 15 choice-conflict warnings, and explicit syntactic LOOKAHEAD did not help. They are instead added to the existing flat token lists of CreateParameter() and Drop(), which adds no new choice point and leaves the warning count unchanged.

LOCK is the one real ambiguity, since it also starts a LOCK TABLE statement. It is guarded so it is only taken as a DROP argument when it cannot begin the next statement:

LOOKAHEAD({ getToken(1).kind == K_LOCK && getToken(2).kind != K_TABLE })
tk=<K_LOCK> { dropArgs.add(tk.image); }

Scope

Group 6 (CAST(... AS UNSIGNED ARRAY) for multi-valued indexes) is not included: it needs an ARRAY marker on the public ColDataType model, and K_ARRAY_LITERAL is already used in six other places. Better as its own PR, so #2490 stays open.

Testing

  • ./gradlew check passes (checkstyle, PMD, spotless, spotbugs, JaCoCo).
  • 4923 tests, 0 failures, 0 errors.
  • JavaCC reports 0 errors and 13 warnings, identical to master — verified by building both states and diffing the warning list.
  • Seven new tests in CreateIndexTest, AlterTest and DropTest, including one that pins DROP TABLE t1; LOCK TABLE t2 IN SHARE MODE; still parsing as two statements.

Deparsing follows the existing conventions: c1 (20) DESC (matching the mycol2 (75) output already asserted in CreateIndexTest) and KEY_BLOCK_SIZE = 8. All statements above were verified against MySQL 8.4.11 while writing #2490.

PR Checklist

  • I have read the contribution guidelines and the governance document on PR expectations.
  • Minimal changes to code not directly related to your change
  • One feature/change per PR unless tightly coupled
  • Do a rebase on upstream master

…tion

MySQL allows a key part to carry both a prefix length and a sort direction,
e.g. "CREATE INDEX i ON t (c1(20) DESC)". IndexColumnWithParams() accepted at
most one optional CreateParameter() per key part, so the prefix length consumed
it and the following ASC/DESC could not be matched.

Collect the parameters in a loop instead. IndexColumnsWithParamsList() is shared
by the CREATE INDEX and the ALTER TABLE ADD INDEX paths, so both are fixed.

Refs JSQLParser#2490
Several valid MySQL index DDL statements were rejected because their option
keywords are tokens of their own and were therefore not reachable from the
option lists that CREATE INDEX and DROP INDEX use:

  CREATE INDEX i ON t (c1) KEY_BLOCK_SIZE = 8
  CREATE INDEX i ON t (c1) ALGORITHM = INPLACE LOCK = NONE
  CREATE FULLTEXT INDEX i ON t (body) WITH PARSER ngram
  CREATE SPATIAL INDEX i ON t (g)
  DROP INDEX i ON t ALGORITHM = INPLACE LOCK = NONE

The keywords are added to the existing flat token lists of CreateParameter()
and Drop() rather than as new grammar alternatives, so no new choice is
introduced and the JavaCC warning count is unchanged.

LOCK is the one exception: it also starts a LOCK TABLE statement, so taking it
unconditionally as a DROP argument would be ambiguous with the next statement.
It is guarded by a semantic lookahead that only accepts it when it is not
followed by TABLE.

Refs JSQLParser#2490
Comment thread src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt Outdated
…mnWithParams

Rename the accumulator to columnParams and keep parameter for the result of a
single CreateParameter(), matching how the other CreateParameter() loops in the
grammar name them. Collect eagerly and pass null only when no option was parsed,
because ColumnParams renders a separating space for a non-null list.
@manticore-projects

Copy link
Copy Markdown
Contributor

Much nicer now, thank you for work and effort!

@manticore-projects
manticore-projects merged commit 9fc38bd into JSQLParser:master Aug 24, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants