GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — feat: parse the MySQL replication statements by minleejae · Pull Request #2906 · JSQLParser/JSqlParser

Conversation

@minleejae

@minleejae minleejae commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

Purpose

The MySQL statements that set up and run replication failed to parse, so a replica setup script
failed at its first statement:

CHANGE REPLICATION SOURCE TO SOURCE_HOST = 'h', SOURCE_PORT = 3306, SOURCE_AUTO_POSITION = 1
    FOR CHANNEL 'c1';
CHANGE REPLICATION FILTER REPLICATE_DO_DB = (d1, d2), REPLICATE_REWRITE_DB = ((a, b));
START REPLICA SQL_THREAD UNTIL SQL_BEFORE_GTIDS = '3E11FA47-71CA-11E1-9E33-C80AA9429562:11-56';
STOP REPLICA IO_THREAD;
RESET REPLICA ALL FOR CHANNEL 'c1';
RESET BINARY LOGS AND GTIDS TO 1234;
PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;

CHANGE and STOP were not statement starts, START only began START TRANSACTION,
RESET only took a parameter name (RESET REPLICA ALL failed at ALL), and PURGE only knew
Oracle's objects.

Current design (As-is)

  • ResetStatement is the PostgreSQL RESET name | ALL | TIME ZONE. RESET REPLICA happens to
    parse as the reset of a parameter named REPLICA.
  • PurgeStatement models Oracle's PURGE TABLE | INDEX | RECYCLEBIN | DBA_RECYCLEBIN | TABLESPACE, keyed by PurgeObjectType.
  • ReplicationOption<K> is the base of the PostgreSQL publication and subscription options: a
    spelled name, a typed kind and an Expression value.
  • There is no model for the replication source options, the replication filters, the replica
    threads or FOR CHANNEL.

Target design (To-be)

  • CHANGE REPLICATION SOURCE TO. ChangeReplicationSourceStatement holds
    ReplicationSourceOptions, which extend ReplicationOption<Kind>. Kind lists the 37
    options of MySQL 8.4, and Kind.getValueType() says what each takes, following
    sql_yacc.yy:

    • a string;
    • a string or NULL;
    • an unsigned number;
    • 0 or 1 (GTID_ONLY and SOURCE_CONNECTION_AUTO_FAILOVER, where MySQL reports any other
      value as a syntax error);
    • the possibly empty server ID list of IGNORE_SERVER_IDS = (1, 2);
    • an account or NULL;
    • STREAM | ON | OFF | GENERATE;
    • OFF | LOCAL | 'uuid'.

    A value of another type is a parse error, as it is in MySQL. Keyword values are kept as a
    Column, like ON in the PostgreSQL options. PRIVILEGE_CHECKS_USER keeps its account in
    getAccount() (the UserAccount of CREATE USER). getPrimaryKeyCheck() and
    getGtidAssignment() read the keyword options as enums.

  • CHANGE REPLICATION FILTER. ChangeReplicationFilterStatement holds ReplicationFilter
    rules. Each rule's list may be empty and holds what its kind takes:

    • database names (REPLICATE_DO_DB, REPLICATE_IGNORE_DB);
    • db.table names as Table (REPLICATE_DO_TABLE, REPLICATE_IGNORE_TABLE);
    • quoted patterns (REPLICATE_WILD_DO_TABLE, REPLICATE_WILD_IGNORE_TABLE);
    • (from, to) pairs (REPLICATE_REWRITE_DB = ((a, b), (c, d))).
  • START / STOP REPLICA. StartReplicaStatement and StopReplicaStatement keep their threads
    (IO_THREAD, SQL_THREAD). START REPLICA also keeps:

    • its UNTIL conditions as ReplicaUntilOptions. The first may be any condition, and later
      ones only log coordinates, as in the server grammar;
    • the USER, PASSWORD, DEFAULT_AUTH and PLUGIN_DIR strings, in the server's order.
  • RESET. ResetReplicationStatement holds the comma list of MySQL's RESET statement:
    BINARY LOGS AND GTIDS [TO n] and REPLICA [ALL] [FOR CHANNEL 'c'], as in
    RESET REPLICA, BINARY LOGS AND GTIDS. ResetStatement is not changed.

  • PURGE. PurgeStatement gains the additive PurgeObjectType.BINARY_LOGS, a LogBound
    (TO, BEFORE) and getLogExpression(), which is the log name literal or the BEFORE
    expression. getObject() returns the same expression. MySQL parses BEFORE with subqueries
    disallowed, so a subquery anywhere in it is a parse error:
    PURGE BINARY LOGS BEFORE NOW() + (SELECT 1) is rejected, while BEFORE (NOW()) and
    BEFORE @cutoff are accepted. The Oracle forms are unchanged.

  • FOR CHANNEL. One ReplicationChannel() production serves every statement. Following
    opt_channel, the channel is a string, kept as a StringValue in getChannel().

  • Dialects.

    • Dialect.MYSQL and the parser without a dialect read these forms.
    • Without a dialect, a bare RESET REPLICA stays the ResetStatement it is today. ALL,
      FOR CHANNEL or a further reset option after it makes it MySQL's.
    • Dialect.MARIADB reads PURGE BINARY LOGS and MariaDB's PURGE MASTER LOGS
      (PurgeObjectType.MASTER_LOGS). MariaDB writes the other replication statements
      differently (CHANGE MASTER, connection names, MASTER_ options), so they keep today's
      readings there.
    • MySQL 8.4 removed CHANGE MASTER TO, START SLAVE, RESET MASTER and the MASTER_
      options, so they stay unsupported.
    • PostgreSQL, Oracle and MariaDB keep their readings of RESET, PURGE and START.
    • STOP becomes a non-reserved keyword and stays usable as a name. REPLICA, REPLICATION,
      SOURCE, CHANNEL and LOGS remain identifiers.
    • Oracle's PURGE TABLESPACE name [USER name] read both names as plain identifiers. It now
      reads them as object names, so PURGE TABLESPACE stop keeps parsing; quoted names are
      accepted as well.
  • Wiring. The following all handle the new statements:

    • statement visitors and StatementDeParser, through each model's appendTo, with values
      passing through the expression deparser;
    • TablesNamesFinder, which reports the tables of REPLICATE_DO_TABLE and
      REPLICATE_IGNORE_TABLE;
    • feature analysis, which reports MODIFIES_SESSION, as for ALTER SYSTEM;
    • validation, through the new features changeReplicationSource, changeReplicationFilter,
      startReplica, stopReplica, resetReplication and purgeBinaryLogs. The MySQL
      capabilities enable all six, and MariaDB enables purgeBinaryLogs.

References:
CHANGE REPLICATION SOURCE TO,
CHANGE REPLICATION FILTER,
START REPLICA,
STOP REPLICA,
RESET,
RESET BINARY LOGS AND GTIDS,
RESET REPLICA,
PURGE BINARY LOGS,
MariaDB PURGE BINARY LOGS.

Not included, all spellings the server accepts that are rarely written:

  • the undocumented RELAY_THREAD synonym of IO_THREAD;
  • hexadecimal numbers (0x…) as option values;
  • double-quoted strings, which the server reads as strings outside ANSI_QUOTES mode;
  • the leading comma IGNORE_SERVER_IDS = (, 1) that the server's grammar lets through.

Validation

  • 129 new tests: 126 in MySqlReplicationStatementsTest and 3 in
    ReplicationStatementValidatorTest. They cover each form with and without the MySQL dialect,
    toString, StatementDeParser, reparsing, the AST fields, 60 rejected forms (among them
    subqueries in PURGE BINARY LOGS BEFORE), the readings
    kept in PostgreSQL, Oracle and MariaDB, statement boundaries and unsupported-statement
    scripts, STOP as a name, TablesNamesFinder, feature analysis, node ranges and building in
    code. ASTNodeCoverageTest checks seven new statements. The new tests fail on master, where
    every new form is a parse error.

  • spotlessApply cleanTest check jar: 13,067 tests, 25 skipped, 0 failures. Grammar ambiguity
    check, Checkstyle, PMD, SpotBugs and JaCoCo pass.

  • Compared with master (a30a67920) on the DDL/DCL survey corpora: 0 regressions.

  • Compared with master (a30a67920) on the non-DDL MySQL corpus:

    • with the MySQL dialect, 7 replication cases fixed, 0 regressed, 0 changed;
    • without a dialect, the same 7 fixed, 0 regressed, 0 changed.
  • Compared with master (a30a67920) on the PostgreSQL corpora: no changes.

  • MySQL 8.4.11 accepts all 55 statements the tests accept for the MySQL dialect, 53 of which master rejects. Some fail only when run, for example Target log not found in binlog, Multiple channels exist on the replica or Replica channel 'c1' does not exist. This includes the comma lists of RESET, every CHANGE REPLICATION SOURCE TO option with its value type, empty filter lists and the START REPLICA connection options. It reports a syntax error (1064) for all 60 statements the tests reject, including GTID_ONLY = 2, SOURCE_HOST = NULL, an unquoted channel name, a subquery in PURGE BINARY LOGS BEFORE, and START SLAVE / CHANGE MASTER TO, which MySQL 8.4 removed.

  • PostgreSQL 18.6 still reads RESET REPLICA as resetting a configuration parameter (it fails only with unrecognized configuration parameter "replica"), and reports a syntax error for the MySQL replication statements.

AI assistance: prepared with Claude Code.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@minleejae
feat: parse the MySQL replication statements
0c9e4c7
CHANGE REPLICATION SOURCE TO, CHANGE REPLICATION FILTER, START REPLICA,
STOP REPLICA, RESET REPLICA, RESET BINARY LOGS AND GTIDS and PURGE BINARY
LOGS failed to parse, so a replica setup script failed at its first
statement.

The new statements of net.sf.jsqlparser.statement.replication follow the
MySQL 8.4 grammar. ReplicationSourceOption extends ReplicationOption, and
each option kind says which value it takes: a string, a number, NULL,
0 or 1, a keyword, the server ID list or an account. ReplicationFilter
keeps database names, tables, patterns or rewrite pairs, and the
statements share one FOR CHANNEL production. RESET reads MySQL's comma
list into ResetReplicationStatement, apart from ResetStatement.
PurgeStatement gains BINARY LOGS TO and BEFORE, which MariaDB also reads,
with its MASTER LOGS; as in MySQL, BEFORE takes no subquery. Without a
dialect a bare RESET REPLICA stays a ResetStatement, and other dialects
keep their readings. STOP becomes a non-reserved keyword and remains
usable as a name, also in Oracle's PURGE TABLESPACE.

Signed-off-by: minleejae <[email protected]>
@minleejae
minleejae force-pushed the feat/mysql-replication-20261107 branch from 6362141 to 0c9e4c7 Compare November 10, 2026 06:42

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

Reviewers

No reviews

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@minleejae
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.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.