GeezerVison is showing github.com — feat: parse the MySQL replication statements by minleejae · Pull Request #2906 · JSQLParser/JSqlParser
Conversation
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.
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]>
This branch has not been deployed
No deployments
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
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.
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.
|