Skip to content
/ server Public
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions mysql-test/main/backup_lock.result
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,27 @@
InnoDB 0 transactions not purged
BACKUP STAGE START;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats' ORDER BY TABLE_NAME;
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_START Backup lock
BACKUP STAGE FLUSH;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats' ORDER BY TABLE_NAME;
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_FLUSH Backup lock
BACKUP STAGE BLOCK_DDL;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats' ORDER BY TABLE_NAME;
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_WAIT_DDL Backup lock
BACKUP STAGE BLOCK_COMMIT;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats' ORDER BY TABLE_NAME;
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_WAIT_COMMIT Backup lock
BACKUP STAGE END;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats' ORDER BY TABLE_NAME;
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
#
# testing BACKUP STAGE LOCK's
Expand All @@ -47,13 +47,14 @@ alter table t1 add column (j int), algorithm copy, lock shared;
connection con2;
backup stage flush;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats' ORDER BY TABLE_NAME;
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_INTENTION_EXCLUSIVE Schema metadata lock test
MDL_BACKUP_DDL Backup lock
MDL_BACKUP_FLUSH Backup lock
MDL_SHARED_WRITE Table metadata lock test t1
MDL_EXCLUSIVE Table metadata lock test #sql-alter
MDL_SHARED_UPGRADABLE Table metadata lock test t1
MDL_INTENTION_EXCLUSIVE Schema metadata lock test
MDL_SHARED_WRITE Table metadata lock test t1
SET STATEMENT max_statement_time=1 FOR backup stage block_ddl;
ERROR 70100: Query execution was interrupted (max_statement_time exceeded)
backup stage block_ddl;
Expand Down Expand Up @@ -84,13 +85,14 @@ connection con2;
backup stage start;
backup stage flush;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats' ORDER BY TABLE_NAME;
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_ALTER_COPY Backup lock
MDL_INTENTION_EXCLUSIVE Schema metadata lock test
MDL_BACKUP_FLUSH Backup lock
MDL_SHARED_WRITE Table metadata lock test t1
MDL_EXCLUSIVE Table metadata lock test #sql-alter
MDL_SHARED_UPGRADABLE Table metadata lock test t1
MDL_INTENTION_EXCLUSIVE Schema metadata lock test
MDL_SHARED_WRITE Table metadata lock test t1
backup stage block_ddl;
backup stage block_commit;
connection default;
Expand Down Expand Up @@ -121,11 +123,11 @@ SET STATEMENT lock_wait_timeout=0 FOR SELECT * FROM t1;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction
backup stage block_ddl;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats' ORDER BY TABLE_NAME;
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_WAIT_DDL Backup lock
MDL_SHARED_WRITE Table metadata lock test t1
MDL_INTENTION_EXCLUSIVE Schema metadata lock test
MDL_SHARED_WRITE Table metadata lock test t1
backup stage end;
connection default;
commit;
Expand All @@ -143,7 +145,7 @@ DROP TABLE t1;
connection con2;
connection con2;
SELECT LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats' ORDER BY TABLE_NAME;
LOCK_MODE LOCK_TYPE TABLE_SCHEMA TABLE_NAME
MDL_BACKUP_WAIT_DDL Backup lock
SELECT * FROM t1;
Expand Down
4 changes: 3 additions & 1 deletion mysql-test/main/backup_lock.test
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
--echo #

let $mdl= LOCK_MODE, LOCK_TYPE, TABLE_SCHEMA, TABLE_NAME FROM information_schema.metadata_lock_info
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats';
WHERE TABLE_NAME NOT LIKE 'innodb_%_stats' ORDER BY TABLE_NAME;

--source ../suite/innodb/include/wait_all_purged.inc

Expand Down Expand Up @@ -62,6 +62,7 @@ let $wait_condition=
where state = "Waiting for table metadata lock";
--source include/wait_condition.inc
backup stage flush;
--replace_regex /#sql-alter-[0-9a-f_\-]*/#sql-alter/
eval SELECT $mdl;
#
# Do first test with max_statement_time, other tests later are done with
Expand Down Expand Up @@ -111,6 +112,7 @@ let $wait_condition=
--source include/wait_condition.inc
backup stage start;
backup stage flush;
--replace_regex /#sql-alter-[0-9a-f_\-]*/#sql-alter/
eval SELECT $mdl;
backup stage block_ddl;
backup stage block_commit;
Expand Down
135 changes: 134 additions & 1 deletion sql/ddl_log.cc
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ uchar ddl_log_file_magic[]=
{ (uchar) 254, (uchar) 254, (uchar) 11, (uchar) 2 };

/* Action names for ddl_log_action_code */

const char *ddl_log_action_name[DDL_LOG_LAST_ACTION]=
{
"Unknown", "partitioning delete", "partitioning rename",
Expand All @@ -96,6 +95,136 @@ const char *ddl_log_action_name[DDL_LOG_LAST_ACTION]=
"delete tmp file", "create trigger", "alter table", "store query"
};

/**
Acquire MDL exclusive lock on a single table name.
@param thd MySQL thread handle
@param db_name Database name
@param tbl_name Table name
*/
static void ddl_log_acquire_table_mdl(
THD *thd, const char *db_name, const char *tbl_name)
{
if (!db_name || !tbl_name || !*db_name || !*tbl_name)
return;
MDL_request request;
MDL_REQUEST_INIT(&request, MDL_key::TABLE, db_name, tbl_name,
MDL_EXCLUSIVE, MDL_TRANSACTION);

thd->mdl_context.acquire_lock(&request, 0);
}

/**
Conditionally acquire MDL based on database name availability.
If db_name is empty, treat table_name as full path and parse it.
Otherwise, use db_name and table_name directly.
@param thd MySQL thread handle
@param db_name Database name (may be empty)
@param table_name Table name or full path
*/
static void ddl_log_acquire_table_mdl(
THD *thd, const LEX_CSTRING &db_name, const LEX_CSTRING &table_name)
{
if (db_name.length == 0)
{
/* If db is empty, treat table_name as full path and parse it */
const char *full_name= table_name.str;
if (!full_name || !*full_name)
return;

/* Skip leading ./ if present */
if (full_name[0] == '.' && full_name[1] == '/')
full_name+= 2;

/* Find database/table separator (either '/' or '.') */
const char *separator= strchr(full_name, '/');
if (!separator)
separator= strchr(full_name, '.');

if (!separator)
return;

const char *db_start= full_name;
const char *tbl_start= separator + 1;
size_t db_len= separator - db_start;
size_t tbl_len= strlen(tbl_start);

if (db_len == 0 || tbl_len == 0 || db_len > NAME_LEN || tbl_len > NAME_LEN)
return;

/* Handle partition table names - look for partition markers like #P# */
const char *part_marker= strstr(tbl_start, "#P#");
if (part_marker)
tbl_len= part_marker - tbl_start;

/* Copy and null-terminate names */
char parsed_db_name[NAME_LEN + 1];
char parsed_tbl_name[NAME_LEN + 1];
memcpy(parsed_db_name, db_start, db_len);
parsed_db_name[db_len]= '\0';
memcpy(parsed_tbl_name, tbl_start, tbl_len);
parsed_tbl_name[tbl_len]= '\0';

ddl_log_acquire_table_mdl(thd, parsed_db_name, parsed_tbl_name);
}
else
{
/* Use db and table names directly */
ddl_log_acquire_table_mdl(thd, db_name.str, table_name.str);
}
}

/**
Acquire MDL exclusive locks for DDL log operations based on action type.
@param thd MySQL thread handle
@param ddl_log_entry DDL log entry containing action type and table names
*/
static void ddl_log_acquire_mdl(THD *thd, DDL_LOG_ENTRY *ddl_log_entry)
{
if (memcmp(ddl_log_entry->handler_name.str, "InnoDB",
ddl_log_entry->handler_name.length))
return;

switch (ddl_log_entry->action_type) {
case DDL_LOG_DELETE_ACTION:
case DDL_LOG_REPLACE_ACTION:
/* Acquire MDL on table being deleted/replaced */
ddl_log_acquire_table_mdl(thd, ddl_log_entry->db, ddl_log_entry->name);
break;

case DDL_LOG_RENAME_ACTION:
case DDL_LOG_RENAME_TABLE_ACTION:
/* Acquire MDL on both source and destination tables for rename */
ddl_log_acquire_table_mdl(thd, ddl_log_entry->from_db,
ddl_log_entry->from_name);
ddl_log_acquire_table_mdl(thd, ddl_log_entry->db, ddl_log_entry->name);
break;

case DDL_LOG_ALTER_TABLE_ACTION:
/* Acquire MDL on all tables involved in alter table operations */
ddl_log_acquire_table_mdl(thd, ddl_log_entry->db, ddl_log_entry->name);
ddl_log_acquire_table_mdl(thd, ddl_log_entry->from_db,
ddl_log_entry->from_name);
ddl_log_acquire_table_mdl(thd, ddl_log_entry->db, ddl_log_entry->extra_name);
break;

case DDL_LOG_EXCHANGE_ACTION:
{
/* Acquire MDL on primary table */
ddl_log_acquire_table_mdl(thd, ddl_log_entry->db, ddl_log_entry->name);

/* Acquire MDL on source table for exchange */
ddl_log_acquire_table_mdl(thd, ddl_log_entry->from_db,
ddl_log_entry->from_name);

ddl_log_acquire_table_mdl(thd, ddl_log_entry->db, ddl_log_entry->tmp_name);
break;
}
default:
/* No MDL acquisition needed for other action types */
break;
}
}

/* Number of phases per entry */
const uchar ddl_log_entry_phases[DDL_LOG_LAST_ACTION]=
{
Expand Down Expand Up @@ -1446,6 +1575,10 @@ static int ddl_log_execute_action(THD *thd, MEM_ROOT *mem_root,
invers_fn_flags|= FN_FROM_IS_TMP;
}

if (!frm_action)
ddl_log_acquire_mdl(thd, ddl_log_entry);

/* Acquire MDL for table operations */
switch (ddl_log_entry->action_type) {
case DDL_LOG_REPLACE_ACTION:
case DDL_LOG_DELETE_ACTION:
Expand Down
8 changes: 8 additions & 0 deletions sql/sql_partition_admin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,14 @@ bool Sql_cmd_alter_table_exchange_partition::
table_list->next_local->db.str,
temp_name, "", FN_IS_TMP);

{
MDL_request mdl_request;
MDL_REQUEST_INIT(&mdl_request, MDL_key::TABLE,
table_list->next_local->db.str,
temp_name, MDL_EXCLUSIVE, MDL_TRANSACTION);
thd->mdl_context.acquire_lock(&mdl_request, 0);
}

if (unlikely(!(part_elem=
part_table->part_info->get_part_elem(partition_name,
part_file_name +
Expand Down
23 changes: 23 additions & 0 deletions sql/sql_table.cc
Original file line number Diff line number Diff line change
Expand Up @@ -11929,6 +11929,19 @@ do_continue:;
*/
alter_info->original_table= table;

{
/*
Create an exclusive lock on the temporary table name.
Needed by InnoDB storage engine to avoid MDL on source
table during purge.
*/
MDL_request mdl_request;
MDL_REQUEST_INIT(&mdl_request, MDL_key::TABLE,
alter_ctx.new_db.str, alter_ctx.tmp_name.str,
MDL_EXCLUSIVE, MDL_TRANSACTION);
thd->mdl_context.acquire_lock(&mdl_request, 0);
}

/*
Create the .frm file for the new table. Storage engine table will not be
created at this stage.
Expand Down Expand Up @@ -12501,6 +12514,16 @@ do_continue:;
If we are changing to use another table handler, we don't
have to do the rename as the table names will not interfer.
*/
/*
Create an exclusive lock on the backup table name.
Needed by InnoDB storage engine to avoid MDL conflicts
during backup table operations.
*/
MDL_request mdl_request;
MDL_REQUEST_INIT(&mdl_request, MDL_key::TABLE,
alter_ctx.db.str, backup_name.str,
MDL_EXCLUSIVE, MDL_TRANSACTION);
thd->mdl_context.acquire_lock(&mdl_request, 0);
if (mysql_rename_table(old_db_type, &alter_ctx.db, &alter_ctx.table_name,
&alter_ctx.db, &backup_name, &alter_ctx.id,
FN_TO_IS_TMP |
Expand Down
Loading
Loading