Skip to content

Expose MySQL table and partition comments as string values - #2821

Open
minleejae wants to merge 2 commits into
JSQLParser:masterfrom
minleejae:feat/mysql-comment-literals-20261014
Open

minleejae wants to merge 2 commits into
JSQLParser:masterfrom
minleejae:feat/mysql-comment-literals-20261014

Conversation

@minleejae

@minleejae minleejae commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Builds on #2814, which adds the ON COMMIT state to TableOption; this PR adds the COMMENT literal to the same state model, so please review that first.

Purpose

Let callers read MySQL table and partition comments as decoded text. Column and event comments are already StringValue literals whose getNotExcapedValue() matches what MySQL stores. Table and partition comments are only raw SQL text, so a caller that converts COMMENT='it\'s' to another database has to strip the quotes and undo the MySQL escapes itself.

Current design (As-is)

  • The table COMMENT of CREATE TABLE, CREATE TABLE … AS SELECT and ALTER TABLE is read by MySqlTableOptionValue() and stored as a String in TableOption.getValue(), for example 'it\'s'.
  • Partition and subpartition COMMENT in CREATE TABLE, ALTER TABLE ADD PARTITION and REORGANIZE PARTITION is the token image in PartitionDefinition.getComment().
  • Neither records the escaping the literal was read with, so \', '', \\, \n and double-quoted MySQL strings cannot be decoded reliably.

Target design (To-be)

  • Both comments are read with the grammar's stringValue(token) helper, like column comments. The StringValue records the dialect's escaping, so getNotExcapedValue() returns the stored text: it's for 'it''s' and, with Dialect.MYSQL, for 'it\'s'; a\b for 'a\\b'; a line break for 'line1\nline2'; double "quoted" text for "double \"quoted\" text".
  • Table comment: TableOption.getComment(), setComment(StringValue), withComment(...) and a TableOption.comment(StringValue) factory. The state rules follow Make table option state transitions explicit #2765:
    • setComment replaces the option's contents with the literal and selects COMMENT. It keeps the equals spelling, and an existing COMMENT also keeps its name (comment = …). A null literal omits the value.
    • setValue stores a plain value and setTokens opaque tokens. Neither is parsed, so getComment() then returns null. Selecting another kind keeps the literal's SQL text as a plain value.
    • A COMMENT value that is not a string literal stays a plain value, for example COMMENT "x" without the MySQL dialect, where "x" is a quoted identifier.
    • The literal is rendered through the expression printer and visited by TableDefinitionTraversal, like a column COMMENT literal.
  • Partition comment: PartitionDefinition.getCommentValue() / setCommentValue(StringValue). The existing getComment() / setComment(String) remain as String accessors: the getter returns the literal's SQL text, and the setter reads SQL text into a StringValue with standard escaping.
  • Compatibility

Validation

  • MySqlCommentLiteralTest adds 27 cases:
    • parse, deparse and reparse of table comments in CREATE TABLE, CREATE TABLE … AS SELECT and ALTER TABLE, with Dialect.MYSQL and without a dialect;
    • getNotExcapedValue() compared with the values MySQL 8.4 stored: 'it''s', 'it\'s', "dq", "double \"quoted\" text", '', 'line1\nline2', 'a\\b', 'tab\there', 'percent\% underscore\_', Korean text, an emoji and a literal line break;
    • partition and subpartition comments in CREATE TABLE, ADD PARTITION and REORGANIZE PARTITION;
    • legacy accessors, ALTER operations, setters, the factory, raw/plain/typed state transitions, custom expression printing and traversal.
  • Java 17 offline Gradle spotlessApply check on top of Model PostgreSQL ON COMMIT, access method and DROP behavior #2814: 10,420 tests, 10,395 passed, 25 skipped, zero failures/errors. The grammar choice conflict check, Checkstyle, PMD, SpotBugs and coverage checks pass.
  • Compared with master (bdf8ac31) on a corpus of 635 MySQL 8.4 and PostgreSQL 18 DDL/DCL statements, each accepted by the server, plus variants, with and without a dialect:
    • parse results, statement classes, toString(), deparser output and reparse are identical, with zero regressions;
    • the AST changes only for the five MySQL statements with a table or partition comment, where the literal becomes a StringValue.
  • On a MySQL comment corpus (CREATE TABLE, CTAS and ALTER TABLE comments; partition and subpartition comments in CREATE TABLE, ADD PARTITION and REORGANIZE PARTITION), every table and partition comment is now a StringValue. Its decoded value matches what MySQL 8.4 stored for the same literal forms. COMMENT 'a' 'b' and COMMENT = _utf8mb4'x', which MySQL rejects, parse as before.
  • MySQL 8.4 and PostgreSQL 18.3: every corpus statement this branch parses ran as written on one server and as rendered by toString() on a second, identical server, in the same order. All 205 MySQL and 328 PostgreSQL statements give the same result (success, or the same error).
  • The same check on the comment corpora (statements that create, alter and read MySQL and PostgreSQL comments): all 49 MySQL and 65 PostgreSQL statements this branch parses give the same results. Afterwards, every stored comment (MySQL table, column, index, partition, routine and event comments; PostgreSQL pg_description) is byte-identical on both servers.

Integration validation

Applied this PR together with the other comment PRs (#2820, #2822, #2824, #2823), the DDL/DCL PRs #2806–#2819 and #2778 on master (bdf8ac31), with the stacked PRs on top of their bases (#2814 → #2821, #2816 → #2822, #2806 → #2824 and #2811 → #2812 → #2823).

  • Each branch merges cleanly into master. Between the branches, conflicts are limited to adjacent additions (README/index rows, usage sections, imports, test methods, visitor and feature lists, an enum line and an alias guard).
  • The integrated build passes Java 17 offline Gradle spotlessApply check: 11,877 tests, 11,852 passed, 25 skipped, zero failures/errors. The grammar choice conflict check passes.
  • On the corpus of 635 MySQL 8.4 and PostgreSQL 18 DDL/DCL statements, the integrated build parses 265 of 271 MySQL statements (master: 206) and 363 of 364 PostgreSQL statements (master: 328), the same as Share MySQL account names across CREATE USER and DEFINER #2806–Render single spaces in ALTER visibility, FORCE and row level security actions #2819 together. There are no regressions.
  • On the comment corpora and an extra set of comment edge cases, the integrated build returns a typed statement for all 80 MySQL statements (master: 67) and for 118 of 119 PostgreSQL statements (master: 76). The exception is a CREATE AGGREGATE setup statement. Every table, column, index, partition, routine, event, user and COMMENT ON comment literal is a StringValue, except COMMENT = _utf8mb4'x', which MySQL rejects.
  • On paired MySQL 8.4 and PostgreSQL 18.3 servers, all 263 MySQL and 362 PostgreSQL DDL/DCL statements and all 56 MySQL and 90 PostgreSQL comment statements rendered by the integrated build give the same results as the originals. The stored comments are identical on both servers.

CREATE TABLE ON COMMIT and USING clauses were split into one raw
TableOption per word. Parse them as Kind.ON_COMMIT with a typed
OnCommitAction and as Kind.ACCESS_METHOD; the legacy token lists and
SQL output are unchanged.

ALTER TABLE DROP [COLUMN] c RESTRICT was rejected. AlterExpressionDrop
now keeps CASCADE/RESTRICT as a CascadeBehavior for column and
constraint drops, and getParameters() still lists the keyword.

Signed-off-by: minleejae <mmj9808@gmail.com>
Read the table COMMENT of CREATE TABLE, CREATE TABLE ... AS SELECT and
ALTER TABLE, and the partition and subpartition COMMENT, with the
stringValue() helper so the literal records the dialect's escaping and
getNotExcapedValue() returns the stored text.

TableOption gains getComment(), setComment(), withComment() and a
comment() factory, following the table option state rules: the literal
replaces the option's contents, while setValue() and setTokens() store
unparsed text. PartitionDefinition gains getCommentValue() and
setCommentValue(); getComment() and setComment(String) remain String
accessors. getValue(), the token snapshots and the rendered SQL keep the
literal as written.

Signed-off-by: minleejae <mmj9808@gmail.com>

This branch has not been deployed

No deployments
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.

1 participant