Repository navigation
Conversation
…ements mysqldump wraps the rows of each table in LOCK TABLES ... WRITE and UNLOCK TABLES, so a dump script failed at its first table. LOCK INSTANCE FOR BACKUP and UNLOCK INSTANCE failed as well. LOCK TABLES extends LockStatement: each target keeps its lock type (TableLockType) and its table keeps the alias, and isTablesKeyword() records the TABLES spelling. PostgreSQL and Oracle LOCK keep their reading, since neither follows a table name with an alias or a lock type. MariaDB's WRITE CONCURRENT is a lock type, and its WAIT and NOWAIT reuse the wait clause of the statement. UNLOCK TABLES and the backup lock name no table, so they are the new UnlockStatement and BackupLockStatement. UNLOCK becomes a non-reserved keyword and remains usable as a name. Signed-off-by: minleejae <mmj9808@gmail.com>
This branch has not been deployed
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
mysqldump wraps the rows of every table in
LOCK TABLESandUNLOCK TABLESby default (--add-locks, part of--opt):CCJSqlParserUtil.parseStatementstherefore fails at the first table of a dump. Onmasterthese statements fail in every dialect:LOCK TABLES `t` WRITEandLOCK TABLES t AS a READ LOCAL, u WRITEfail at the table name,LOCK TABLE t READatREAD.UNLOCK TABLESfails atUNLOCK; with unsupported statements enabled it becomes anUnsupportedStatement.LOCK INSTANCE FOR BACKUPfails atFOR, andUNLOCK INSTANCEatUNLOCK.Syntax: MySQL 8.4 LOCK TABLES and UNLOCK TABLES, MySQL 8.4 LOCK INSTANCE FOR BACKUP and UNLOCK INSTANCE, MariaDB LOCK TABLES.
MariaDB adds the lock type
WRITE CONCURRENTand a trailingWAIT norNOWAIT.LOW_PRIORITY WRITEis also accepted, because MySQL 8.0 (where it is deprecated and has no effect) and MariaDB accept it; MySQL 8.4 rejects it.Current design (As-is)
LockStatementmodels PostgreSQL's and Oracle'sLOCK [TABLE] [ONLY] name [*], … [IN mode MODE] [NOWAIT | WAIT n]. Its targets (LockStatement.Target) come fromRelationTarget(), whichTRUNCATEshares. A target has no alias and no lock type, so the parser stops atREAD, atWRITEor at an alias.LOCK TABLES t …readsTABLESas the name of a table and stops att.UNLOCKis a plain identifier. No statement starts with it, andisStatementStartAhead()does not know it.ALTER USER … ACCOUNT UNLOCKreads it as a contextual word.LOCK INSTANCE FOR BACKUPreadsinstanceas a table name.StatementValidator.visit(LockStatement)is a TODO, so noLOCKstatement is validated.Target design (To-be)
MySQL table locks extend
LockStatement. A MySQLLOCK TABLESis the same statement as PostgreSQL'sLOCK TABLE: a list of tables to lock. Only each target carries more, so the existing model, its target list and its consumers (TablesNamesFinder,StatementDeParser, the validator) serve every dialect, and a consumer handles one class. A separate class would have duplicated the target list, theWAIT/NOWAITclause and every visitor.LockStatement.Target#getLockType()returns the new enumTableLockType:READ,READ_LOCAL,WRITE,LOW_PRIORITY_WRITE(MySQL 8.0 and MariaDB) or MariaDB'sWRITE_CONCURRENT. It is null for a PostgreSQL or Oracle target.Table#getAlias(), as for the tables ofFROM,UPDATEandDELETE; the target renders it before the lock type.isTablesKeyword()records theTABLESspelling next toisUseTableKeyword(). SettingTABLESsets theTABLEflag, and clearing theTABLEflag clearsTABLES, asTableReferencekeepsONLYand*consistent.WAIT nandNOWAITare the clause Oracle already has, so they usegetWaitSeconds()andisNoWait().LockStatement,LockStatement.Target,LockModeandTableReferenceis unchanged, and so are the PostgreSQL and Oracle readings.UNLOCK TABLESand the backup lock are new statements, because they name no table. Parsed asLockStatement, they would add aLockStatementwithout targets, whosegetTable()is null. The split follows the two pages of the MySQL manual.UnlockStatement:UNLOCK TABLEorUNLOCK TABLES(isTablesKeyword()).BackupLockStatement:getAction()isLOCK(LOCK INSTANCE FOR BACKUP) orUNLOCK(UNLOCK INSTANCE).Grammar:
LockStatement()takes the MySQL branch whenisMySqlLockTablesAhead()seesTABLEorTABLES, a table name, and thenAS, a lock type, or an alias followed by a lock type. PostgreSQL and Oracle never follow a table name with any of these, soLOCK tables,LOCK tables NOWAIT,LOCK tables.x IN SHARE MODE,LOCK TABLE tables, instance,LOCK instanceandLOCK TABLE ONLY t …keep their reading.ONLYcounts as a name in this check.tbl_name [[AS] alias] lock_type: the branch reads the name with the sharedTable()and the alias withAlias(), not with PostgreSQL'sRelationTarget(). MySQL has noONLYor*there, so theONLYtoken is taken as the table name, as inSELECT * FROM only:LOCK TABLES only WRITE,LOCK TABLES only t WRITEandLOCK TABLES t READ, only WRITElock a table namedonly, while PostgreSQL'sLOCK TABLE ONLY t IN SHARE MODEkeeps its reading. The branch rejectsIN … MODEand a target without a lock type (LOCK TABLES t READ, u).UNLOCKbecomes a non-reserved keyword token, likeCHECKSUMin feat: parse MySQL CHECKSUM TABLE and histogram update options #2883, so that a statement can start with it without a bare identifier alternative. It remains usable as a name (KeywordsTest, keyword sweeps), andACCOUNT UNLOCKreads the token.INSTANCE,BACKUP,WRITEandCONCURRENTremain contextual words.isStatementStartAhead()recognizesUNLOCK TABLE(S)andUNLOCK INSTANCEwith the same dialect gates as the statement, so they end a preceding statement and are not captured as unsupported statements.Dialects:
MYSQLMARIADBLOCK TABLE(S) … lock_type,UNLOCK TABLE(S)masterWRITE CONCURRENT, trailingWAIT n/NOWAITafter a MySQL listLOCK INSTANCE FOR BACKUP,UNLOCK INSTANCEmasterLOCK [TABLE] …Wiring:
StatementVisitordefault methods andStatementVisitorAdapterfor the two new statements;StatementDeParseruses theappendTothattoString()uses.TablesNamesFinderreports the locked tables, not their aliases;UNLOCKand the backup lock report none.lockTables,unlockTablesandbackupLock.MySqlVersionenables all three,MariaDbVersionthe first two, andPostgresqlVersionandOracleVersionlockTables.LOCKstatements are now validated: a capability withoutlockTables, such as SQL Server or H2, reports them.MODIFIES_TRANSACTIONfor all three statements, as forLOCK.usage.rst, the README and the documentation index list the statements.Validation
master(7123d25a1) every MySQL form they parse is aParseException:MySqlLockTablesTest(75): every form with and withoutDialect.MYSQL(parse,toString(),StatementDeParser, reparse), MariaDB's forms, a table namedonly(with and without an alias, after a comma, qualified and quoted) in the MySQL and MariaDB dialects and without a dialect, the AST fields (lock types, aliases,TABLE/TABLES, wait clause), rejected forms, the MySQL forms rejected withDialect.POSTGRESQLandDialect.ORACLE, the unchanged PostgreSQL readings (LOCK tables NOWAIT,LOCK TABLE tables, instance,LOCK TABLE ONLY t IN SHARE MODE, …) in every dialect, the words as names andACCOUNT UNLOCK, a mysqldump script, statement boundaries with and without unsupported-statement capture,TablesNamesFinder, feature analysis, AST node ranges and statements built in code.LockStatementValidatorTest(5): the capabilities of MySQL, MariaDB, PostgreSQL and Oracle, and the three features.KeywordsTestcoversUNLOCKas a schema, table, column and alias name.LockTest,PostgreSqlLockTestandPostgreSqlTruncateTestpass unchanged.spotlessApply cleanTest check jar: 12,513 tests, 12,488 passed, 25 skipped, zero failures/errors.ASTNodeCoverageTestpasses; a one-off run of its coverage walk over a script of the new statements reported no problem. The grammar choice conflict check, Checkstyle, PMD, SpotBugs and JaCoCo pass.LOW_PRIORITY WRITE, which MySQL 8.4 rejects and which is accepted for MySQL 8.0 and MariaDB as above, andLOCK INSTANCEwithoutFOR BACKUP, which keeps the PostgreSQL/Oracle reading as a lock of a table namedinstance, as onmaster. The server also accepts an alias withoutAS(LOCK TABLES t a READ) and a table namedonly(LOCK TABLES only t WRITE), and so does the parser.only(LOCK TABLES t only WRITE) stays rejected, asSELECT * FROM t onlyis onmaster;LOCK TABLES t AS only WRITEparses.master(7123d25a1) on a corpus of 188 MySQL 8.4 and 166 PostgreSQL 18 statements other than DDL/DCL, each accepted by the server, with the MySQL or PostgreSQL dialect and without a dialect:Dialect.MYSQLand without a dialect:LOCK TABLES t READ, s AS x WRITE,LOCK TABLES t READ LOCAL,UNLOCK TABLESandLOCK INSTANCE FOR BACKUP. Each renders identically to the source.LOCK TABLE t IN SHARE ROW EXCLUSIVE MODE NOWAITandLOCK t, sare unchanged.master(7123d25a1) on a corpus of 635 MySQL 8.4 and PostgreSQL 18 DDL/DCL statements, each accepted by the server, plus variants, scripts and the keyword sweeps (which useinstance,backup,concurrentandwriteas table and column names): 0 regressed, 0 changed.ALTER USER … ACCOUNT UNLOCKandGRANT LOCK TABLES …are among them.AI assistance: prepared with Claude Code.