Description:
When a database name contains characters that require filename encoding (e.g. # → @0023, $ → @0024), XA recovery after server restart produces a corrupted MDL lock key. The OBJECT_SCHEMA in performance_schema.metadata_locks shows kly??1 instead of the correct kly#$1, because # and $ are replaced with ?.
This breaks MDL lock protection: DDL operations (DROP TABLE, ALTER TABLE) on the affected table are not blocked by the XA-prepared transaction, which violates the XA contract.
How to repeat:
-- Create database with special characters
CREATE DATABASE `kly#$1`;
CREATE TABLE `kly#$1`.t1 (a INT);
-- Start XA transaction, prepare it
XA START 'xatest';
INSERT INTO `kly#$1`.t1 VALUES (10);
XA END 'xatest';
XA PREPARE 'xatest';
-- Disconnect the session, then restart the server
-- After restart, check MDL locks:
SELECT object_type, object_schema, object_name, lock_type, lock_duration, lock_status
FROM performance_schema.metadata_locks
WHERE object_name = 't1' AND lock_status = 'GRANTED';
-- Expected: OBJECT_SCHEMA = 'kly#$1'
-- Actual: OBJECT_SCHEMA = 'kly??1'
Error log output during recovery:
[ERROR] Invalid (old?) table or database name 'kly#$1'
Suggested fix:
Remove the redundant filename_to_tablename() calls in xarecover_create_mdl_backup(), since InnoDB already provides decoded names through dict_name::get_table(convert=true):
// Before (buggy):
char db_buff[NAME_CHAR_LEN * FILENAME_CHARSET_MBMAXLEN + 1];
int len = filename_to_tablename(tbl_name->db, db_buff, sizeof(db_buff));
db_buff[len] = '\0';
// ...
MDL_REQUEST_INIT(table_mdl_request, MDL_key::TABLE, db_buff, name_buff, ...)
// After (fixed):
// tbl_name->db and tbl_name->tablename are already decoded by InnoDB,
// use them directly as MDL key arguments.
MDL_REQUEST_INIT(table_mdl_request, MDL_key::TABLE,
tbl_name->db, tbl_name->tablename, ...)
Caveat: This change assumes all recover_t handler implementations decode names before returning them. If any storage engine returns filename-encoded names (without going through dict_name::get_table(convert=true)), removing filename_to_tablename() would break that path. Verification is needed across all recover_t implementations.
Description: When a database name contains characters that require filename encoding (e.g. # → @0023, $ → @0024), XA recovery after server restart produces a corrupted MDL lock key. The OBJECT_SCHEMA in performance_schema.metadata_locks shows kly??1 instead of the correct kly#$1, because # and $ are replaced with ?. This breaks MDL lock protection: DDL operations (DROP TABLE, ALTER TABLE) on the affected table are not blocked by the XA-prepared transaction, which violates the XA contract. How to repeat: -- Create database with special characters CREATE DATABASE `kly#$1`; CREATE TABLE `kly#$1`.t1 (a INT); -- Start XA transaction, prepare it XA START 'xatest'; INSERT INTO `kly#$1`.t1 VALUES (10); XA END 'xatest'; XA PREPARE 'xatest'; -- Disconnect the session, then restart the server -- After restart, check MDL locks: SELECT object_type, object_schema, object_name, lock_type, lock_duration, lock_status FROM performance_schema.metadata_locks WHERE object_name = 't1' AND lock_status = 'GRANTED'; -- Expected: OBJECT_SCHEMA = 'kly#$1' -- Actual: OBJECT_SCHEMA = 'kly??1' Error log output during recovery: [ERROR] Invalid (old?) table or database name 'kly#$1' Suggested fix: Remove the redundant filename_to_tablename() calls in xarecover_create_mdl_backup(), since InnoDB already provides decoded names through dict_name::get_table(convert=true): // Before (buggy): char db_buff[NAME_CHAR_LEN * FILENAME_CHARSET_MBMAXLEN + 1]; int len = filename_to_tablename(tbl_name->db, db_buff, sizeof(db_buff)); db_buff[len] = '\0'; // ... MDL_REQUEST_INIT(table_mdl_request, MDL_key::TABLE, db_buff, name_buff, ...) // After (fixed): // tbl_name->db and tbl_name->tablename are already decoded by InnoDB, // use them directly as MDL key arguments. MDL_REQUEST_INIT(table_mdl_request, MDL_key::TABLE, tbl_name->db, tbl_name->tablename, ...) Caveat: This change assumes all recover_t handler implementations decode names before returning them. If any storage engine returns filename-encoded names (without going through dict_name::get_table(convert=true)), removing filename_to_tablename() would break that path. Verification is needed across all recover_t implementations.