mirror of
https://github.com/MariaDB/server.git
synced 2025-07-27 18:02:13 +03:00
A 5.5 version of the fix for Bug #54360 "Deadlock DROP/ALTER/CREATE
DATABASE with open HANDLER" Remove LOCK_create_db, database name locks, and use metadata locks instead. This exposes CREATE/DROP/ALTER DATABASE statements to the graph-based deadlock detector in MDL, and paves the way for a safe, deadlock-free implementation of RENAME DATABASE. Database DDL statements will now take exclusive metadata locks on the database name, while table/view/routine DDL statements take intention exclusive locks on the database name. This prevents race conditions between database DDL and table/view/routine DDL. (e.g. DROP DATABASE with concurrent CREATE/ALTER/DROP TABLE) By adding database name locks, this patch implements WL#4450 "DDL locking: CREATE/DROP DATABASE must use database locks" and WL#4985 "DDL locking: namespace/hierarchical locks". The patch also changes code to use init_one_table() where appropriate. The new lock_table_names() function requires TABLE_LIST::db_length to be set correctly, and this is taken care of by init_one_table(). This patch also adds a simple template to help work with the mysys HASH data structure. Most of the patch was written by Konstantin Osipov.
This commit is contained in:
@ -242,9 +242,10 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
MDL_ticket **ticket_downgrade)
|
||||
{
|
||||
TABLE *table= NULL;
|
||||
MDL_ticket *mdl_ticket= NULL;
|
||||
DBUG_ENTER("open_and_lock_table_for_truncate");
|
||||
|
||||
DBUG_ASSERT(table_ref->lock_type == TL_WRITE);
|
||||
DBUG_ASSERT(table_ref->mdl_request.type == MDL_SHARED_NO_READ_WRITE);
|
||||
/*
|
||||
Before doing anything else, acquire a metadata lock on the table,
|
||||
or ensure we have one. We don't use open_and_lock_tables()
|
||||
@ -266,6 +267,7 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
|
||||
*hton_can_recreate= ha_check_storage_engine_flag(table->s->db_type(),
|
||||
HTON_CAN_RECREATE);
|
||||
table_ref->mdl_request.ticket= table->mdl_ticket;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -273,21 +275,12 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
Even though we could use the previous execution branch here just as
|
||||
well, we must not try to open the table:
|
||||
*/
|
||||
MDL_request mdl_global_request, mdl_request;
|
||||
MDL_request_list mdl_requests;
|
||||
|
||||
mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
|
||||
mdl_request.init(MDL_key::TABLE, table_ref->db, table_ref->table_name,
|
||||
MDL_SHARED_NO_READ_WRITE);
|
||||
mdl_requests.push_front(&mdl_request);
|
||||
mdl_requests.push_front(&mdl_global_request);
|
||||
|
||||
if (thd->mdl_context.acquire_locks(&mdl_requests,
|
||||
thd->variables.lock_wait_timeout))
|
||||
DBUG_ASSERT(table_ref->next_global == NULL);
|
||||
if (lock_table_names(thd, table_ref, NULL,
|
||||
thd->variables.lock_wait_timeout,
|
||||
MYSQL_OPEN_SKIP_TEMPORARY))
|
||||
DBUG_RETURN(TRUE);
|
||||
|
||||
mdl_ticket= mdl_request.ticket;
|
||||
|
||||
if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name,
|
||||
HTON_CAN_RECREATE, hton_can_recreate))
|
||||
DBUG_RETURN(TRUE);
|
||||
@ -313,7 +306,9 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
else
|
||||
{
|
||||
ulong timeout= thd->variables.lock_wait_timeout;
|
||||
if (thd->mdl_context.upgrade_shared_lock_to_exclusive(mdl_ticket, timeout))
|
||||
if (thd->mdl_context.
|
||||
upgrade_shared_lock_to_exclusive(table_ref->mdl_request.ticket,
|
||||
timeout))
|
||||
DBUG_RETURN(TRUE);
|
||||
mysql_mutex_lock(&LOCK_open);
|
||||
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db,
|
||||
@ -335,15 +330,14 @@ static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
|
||||
table_ref->required_type= FRMTYPE_TABLE;
|
||||
/* We don't need to load triggers. */
|
||||
DBUG_ASSERT(table_ref->trg_event_map == 0);
|
||||
/* Work around partition parser rules using alter table's. */
|
||||
if (thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
|
||||
{
|
||||
table_ref->lock_type= TL_WRITE;
|
||||
table_ref->mdl_request.set_type(MDL_SHARED_WRITE);
|
||||
}
|
||||
/* Ensure proper lock types (e.g. from the parser). */
|
||||
DBUG_ASSERT(table_ref->lock_type == TL_WRITE);
|
||||
DBUG_ASSERT(table_ref->mdl_request.type == MDL_SHARED_WRITE);
|
||||
/*
|
||||
Even though we have an MDL lock on the table here, we don't
|
||||
pass MYSQL_OPEN_HAS_MDL_LOCK to open_and_lock_tables
|
||||
since to truncate a MERGE table, we must open and lock
|
||||
merge children, and on those we don't have an MDL lock.
|
||||
Thus clear the ticket to satisfy MDL asserts.
|
||||
*/
|
||||
table_ref->mdl_request.ticket= NULL;
|
||||
|
||||
/*
|
||||
Open the table as it will handle some required preparations.
|
||||
|
Reference in New Issue
Block a user