1
0
mirror of https://github.com/MariaDB/server.git synced 2025-12-24 11:21:21 +03:00

Backport of:

------------------------------------------------------------
revno: 2617.23.18
committer: Davi Arnaut <Davi.Arnaut@Sun.COM>
branch nick: 4284-6.0
timestamp: Mon 2009-03-02 18:18:26 -0300
message:
Bug#989: If DROP TABLE while there's an active transaction, wrong binlog order
WL#4284: Transactional DDL locking

This is a prerequisite patch:

These changes are intended to split lock requests from granted
locks and to allow the memory and lifetime of granted locks to
be managed within the MDL subsystem. Furthermore, tickets can
now be shared and therefore are used to satisfy multiple lock
requests, but only shared locks can be recursive.

The problem is that the MDL subsystem morphs lock requests into
granted locks locks but does not manage the memory and lifetime
of lock requests, and hence, does not manage the memory of
granted locks either. This can be problematic because it puts the
burden of tracking references on the users of the subsystem and
it can't be easily done in transactional contexts where the locks
have to be kept around for the duration of a transaction.

Another issue is that recursive locks (when the context trying to
acquire a lock already holds a lock on the same object) requires
that each time the lock is granted, a unique lock request/granted
lock structure structure must be kept around until the lock is
released. This can lead to memory leaks in transactional contexts
as locks taken during the transaction should only be released at
the end of the transaction. This also leads to unnecessary wake
ups (broadcasts) in the MDL subsystem if the context still holds
a equivalent of the lock being released.

These issues are exacerbated due to the fact that WL#4284 low-level
design says that the implementation should "2) Store metadata locks
in transaction memory root, rather than statement memory root" but
this is not possible because a memory root, as implemented in mysys,
requires all objects allocated from it to be freed all at once.

This patch combines review input and significant code contributions
from Konstantin Osipov (kostja) and Dmitri Lenev (dlenev).


mysql-test/r/mdl_sync.result:
  Add test case result.
mysql-test/t/mdl_sync.test:
  Add test case for shared lock upgrade case.
sql/event_db_repository.cc:
  Rename mdl_alloc_lock to mdl_request_alloc.
sql/ha_ndbcluster_binlog.cc:
  Use new function names to initialize MDL lock requests.
sql/lock.cc:
  Rename MDL functions.
sql/log_event.cc:
  The MDL request now holds the table and database name data (MDL_KEY).
sql/mdl.cc:
  Move the MDL key to the MDL_LOCK structure in order to make the
  object suitable for allocation from a fixed-size allocator. This
  allows the simplification of the lists in the MDL_LOCK object,
  which now are just two, one for granted tickets and other for
  waiting (upgraders) tickets.
  
  Recursive requests for a shared lock on the same object can now
  be granted using the same lock ticket. This schema is only used
  for shared locks because that the only case that matters. This
  is used to avoid waste of resources in case a context (connection)
  already holds a shared lock on a object.
sql/mdl.h:
  Introduce a metadata lock object key which is used  to uniquely
  identify lock objects.
  
  Separate the structure used to represent pending lock requests
  from the structure used to represent granted metadata locks.
  
  Rename functions used to manipulate locks requests in order to
  have a more consistent function naming schema.
sql/sp_head.cc:
  Rename mdl_alloc_lock to mdl_request_alloc.
sql/sql_acl.cc:
  Rename alloc_mdl_locks to alloc_mdl_requests.
sql/sql_base.cc:
  Various changes to accommodate that lock requests are separated
  from lock tickets (granted locks).
sql/sql_class.h:
  Last acquired lock before the savepoint was set.
sql/sql_delete.cc:
  Various changes to accommodate that lock requests are separated
  from lock tickets (granted locks).
sql/sql_handler.cc:
  Various changes to accommodate that lock requests are separated
  from lock tickets (granted locks).
sql/sql_insert.cc:
  Rename alloc_mdl_locks to alloc_mdl_requests.
sql/sql_parse.cc:
  Rename alloc_mdl_locks to alloc_mdl_requests.
sql/sql_plist.h:
  Typedef for iterator type.
sql/sql_plugin.cc:
  Rename alloc_mdl_locks to alloc_mdl_requests.
sql/sql_servers.cc:
  Rename alloc_mdl_locks to alloc_mdl_requests.
sql/sql_show.cc:
  Various changes to accommodate that lock requests are separated
  from lock tickets (granted locks).
sql/sql_table.cc:
  Various changes to accommodate that lock requests are separated
  from lock tickets (granted locks).
sql/sql_trigger.cc:
  Save reference to the lock ticket so it can be downgraded later.
sql/sql_udf.cc:
  Rename alloc_mdl_locks to alloc_mdl_requests.
sql/table.cc:
  Rename mdl_alloc_lock to mdl_request_alloc.
sql/table.h:
  Separate MDL lock requests from lock tickets (granted locks).
storage/myisammrg/ha_myisammrg.cc:
  Rename alloc_mdl_locks to alloc_mdl_requests.
This commit is contained in:
Konstantin Osipov
2009-12-04 02:29:40 +03:00
parent 62ed4904c7
commit f477e66ec5
26 changed files with 1046 additions and 822 deletions

View File

@@ -1910,7 +1910,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
Since we don't acquire metadata lock if we have found temporary
table, we should do something to avoid releasing it at the end.
*/
table->mdl_lock_data= 0;
table->mdl_lock_request= NULL;
}
else
{
@@ -1923,7 +1923,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
table->table_name);
if (!table->table)
DBUG_RETURN(1);
table->mdl_lock_data= table->table->mdl_lock_data;
table->mdl_lock_request->ticket= table->table->mdl_lock_ticket;
}
}
}
@@ -2202,15 +2202,15 @@ err:
}
for (table= tables; table; table= table->next_local)
{
if (table->mdl_lock_data)
if (table->mdl_lock_request)
{
/*
Under LOCK TABLES we may have several instances of table open
and locked and therefore have to remove several metadata lock
requests associated with them.
*/
mdl_release_and_remove_all_locks_for_name(&thd->mdl_context,
table->mdl_lock_data);
mdl_ticket_release_all_for_name(&thd->mdl_context,
table->mdl_lock_request->ticket);
}
}
}
@@ -4108,29 +4108,28 @@ warn:
static bool lock_table_name_if_not_cached(THD *thd, const char *db,
const char *table_name,
MDL_LOCK_DATA **lock_data)
MDL_LOCK_REQUEST **lock_req)
{
bool conflict;
if (!(*lock_data= mdl_alloc_lock(0, db, table_name, thd->mem_root)))
if (!(*lock_req= mdl_request_alloc(0, db, table_name, thd->mem_root)))
return TRUE;
mdl_set_lock_type(*lock_data, MDL_EXCLUSIVE);
mdl_add_lock(&thd->mdl_context, *lock_data);
if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock_data,
&conflict))
mdl_request_set_type(*lock_req, MDL_EXCLUSIVE);
mdl_request_add(&thd->mdl_context, *lock_req);
if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock_req, &conflict))
{
/*
To simplify our life under LOCK TABLES we remove unsatisfied
lock request from the context.
*/
mdl_remove_lock(&thd->mdl_context, *lock_data);
mdl_request_remove(&thd->mdl_context, *lock_req);
if (!conflict)
{
/* Probably OOM. */
return TRUE;
}
else
*lock_data= 0;
*lock_req= NULL;
}
return FALSE;
}
@@ -4146,7 +4145,7 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
bool internal_tmp_table,
uint select_field_count)
{
MDL_LOCK_DATA *target_lock_data= 0;
MDL_LOCK_REQUEST *target_lock_req= NULL;
bool result;
DBUG_ENTER("mysql_create_table");
@@ -4169,12 +4168,12 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_data))
if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_req))
{
result= TRUE;
goto unlock;
}
if (!target_lock_data)
if (!target_lock_req)
{
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
@@ -4200,10 +4199,10 @@ bool mysql_create_table(THD *thd, const char *db, const char *table_name,
select_field_count);
unlock:
if (target_lock_data)
if (target_lock_req)
{
mdl_release_lock(&thd->mdl_context, target_lock_data);
mdl_remove_lock(&thd->mdl_context, target_lock_data);
mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket);
mdl_request_remove(&thd->mdl_context, target_lock_req);
}
pthread_mutex_lock(&LOCK_lock_db);
if (!--creating_table && creating_database)
@@ -4368,7 +4367,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
char from[FN_REFLEN],tmp[FN_REFLEN+32];
const char **ext;
MY_STAT stat_info;
MDL_LOCK_DATA *mdl_lock_data;
MDL_LOCK_REQUEST *mdl_lock_request= NULL;
enum enum_open_table_action ot_action_unused;
DBUG_ENTER("prepare_for_repair");
uint reopen_for_repair_flags= (MYSQL_LOCK_IGNORE_FLUSH |
@@ -4387,13 +4386,13 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
uint key_length;
key_length= create_table_def_key(thd, key, table_list, 0);
mdl_lock_data= mdl_alloc_lock(0, table_list->db, table_list->table_name,
thd->mem_root);
mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE);
mdl_add_lock(&thd->mdl_context, mdl_lock_data);
mdl_lock_request= mdl_request_alloc(0, table_list->db,
table_list->table_name, thd->mem_root);
mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE);
mdl_request_add(&thd->mdl_context, mdl_lock_request);
if (mdl_acquire_exclusive_locks(&thd->mdl_context))
{
mdl_remove_lock(&thd->mdl_context, mdl_lock_data);
mdl_request_remove(&thd->mdl_context, mdl_lock_request);
DBUG_RETURN(0);
}
@@ -4413,11 +4412,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
}
pthread_mutex_unlock(&LOCK_open);
table= &tmp_table;
table_list->mdl_lock_data= mdl_lock_data;
}
else
{
mdl_lock_data= table->mdl_lock_data;
table_list->mdl_lock_request= mdl_lock_request;
}
/* A MERGE table must not come here. */
@@ -4528,10 +4523,10 @@ end:
pthread_mutex_unlock(&LOCK_open);
}
/* In case of a temporary table there will be no metadata lock. */
if (error && mdl_lock_data)
if (error && mdl_lock_request)
{
mdl_release_lock(&thd->mdl_context, mdl_lock_data);
mdl_remove_lock(&thd->mdl_context, mdl_lock_data);
mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket);
mdl_request_remove(&thd->mdl_context, mdl_lock_request);
}
DBUG_RETURN(error);
}
@@ -5234,7 +5229,7 @@ bool mysql_create_like_schema_frm(THD* thd, TABLE_LIST* schema_table,
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
HA_CREATE_INFO *create_info)
{
MDL_LOCK_DATA *target_lock_data= 0;
MDL_LOCK_REQUEST *target_lock_req= NULL;
char src_path[FN_REFLEN], dst_path[FN_REFLEN + 1];
uint dst_path_length;
char *db= table->db;
@@ -5291,9 +5286,9 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
}
else
{
if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_data))
if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock_req))
goto err;
if (!target_lock_data)
if (!target_lock_req)
goto table_exists;
dst_path_length= build_table_filename(dst_path, sizeof(dst_path) - 1,
db, table_name, reg_ext, 0);
@@ -5303,7 +5298,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
Make the metadata lock available to open_table() called to
reopen the table down the road.
*/
table->mdl_lock_data= target_lock_data;
table->mdl_lock_request= target_lock_req;
}
DBUG_EXECUTE_IF("sleep_create_like_before_copy", my_sleep(6000000););
@@ -5474,10 +5469,10 @@ binlog:
res= FALSE;
err:
if (target_lock_data)
if (target_lock_req)
{
mdl_release_lock(&thd->mdl_context, target_lock_data);
mdl_remove_lock(&thd->mdl_context, target_lock_data);
mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket);
mdl_request_remove(&thd->mdl_context, target_lock_req);
}
DBUG_RETURN(res);
}
@@ -6416,7 +6411,8 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
uint order_num, ORDER *order, bool ignore)
{
TABLE *table, *new_table= 0;
MDL_LOCK_DATA *mdl_lock_data, *target_lock_data= 0;
MDL_LOCK_TICKET *mdl_lock_ticket;
MDL_LOCK_REQUEST *target_lock_req= NULL;
int error= 0;
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1];
char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
@@ -6587,7 +6583,7 @@ view_err:
MYSQL_OPEN_TAKE_UPGRADABLE_MDL)))
DBUG_RETURN(TRUE);
table->use_all_columns();
mdl_lock_data= table->mdl_lock_data;
mdl_lock_ticket= table->mdl_lock_ticket;
/*
Prohibit changing of the UNION list of a non-temporary MERGE table
@@ -6640,9 +6636,9 @@ view_err:
else
{
if (lock_table_name_if_not_cached(thd, new_db, new_name,
&target_lock_data))
&target_lock_req))
DBUG_RETURN(TRUE);
if (!target_lock_data)
if (!target_lock_req)
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
DBUG_RETURN(TRUE);
@@ -6835,13 +6831,12 @@ view_err:
*/
if (new_name != table_name || new_db != db)
{
mdl_release_lock(&thd->mdl_context, target_lock_data);
mdl_remove_lock(&thd->mdl_context, target_lock_data);
mdl_release_and_remove_all_locks_for_name(&thd->mdl_context,
mdl_lock_data);
mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket);
mdl_request_remove(&thd->mdl_context, target_lock_req);
mdl_ticket_release_all_for_name(&thd->mdl_context, mdl_lock_ticket);
}
else
mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_data);
mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket);
}
DBUG_RETURN(error);
}
@@ -7074,7 +7069,7 @@ view_err:
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (fast_alter_partition)
{
DBUG_ASSERT(!target_lock_data);
DBUG_ASSERT(!target_lock_req);
DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
create_info, table_list,
db, table_name,
@@ -7441,16 +7436,16 @@ view_err:
table_list->table_name_length= strlen(new_name);
table_list->db= new_db;
table_list->db_length= strlen(new_db);
table_list->mdl_lock_data= target_lock_data;
table_list->mdl_lock_request= target_lock_req;
}
else
{
/*
Under LOCK TABLES, we have a different mdl_lock_data
Under LOCK TABLES, we have a different mdl_lock_ticket
points to a different instance than the one set initially
to request the lock.
*/
table_list->mdl_lock_data= mdl_lock_data;
table_list->mdl_lock_request->ticket= mdl_lock_ticket;
}
if (open_table(thd, table_list, thd->mem_root,
&ot_action_unused, MYSQL_OPEN_REOPEN))
@@ -7516,13 +7511,12 @@ view_err:
{
if ((new_name != table_name || new_db != db))
{
mdl_release_lock(&thd->mdl_context, target_lock_data);
mdl_remove_lock(&thd->mdl_context, target_lock_data);
mdl_release_and_remove_all_locks_for_name(&thd->mdl_context,
mdl_lock_data);
mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket);
mdl_request_remove(&thd->mdl_context, target_lock_req);
mdl_ticket_release_all_for_name(&thd->mdl_context, mdl_lock_ticket);
}
else
mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_data);
mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket);
}
end_temporary:
@@ -7577,10 +7571,10 @@ err:
alter_info->datetime_field->field_name);
thd->abort_on_warning= save_abort_on_warning;
}
if (target_lock_data)
if (target_lock_req)
{
mdl_release_lock(&thd->mdl_context, target_lock_data);
mdl_remove_lock(&thd->mdl_context, target_lock_data);
mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket);
mdl_request_remove(&thd->mdl_context, target_lock_req);
}
DBUG_RETURN(TRUE);
@@ -7592,12 +7586,12 @@ err_with_mdl:
tables and release the exclusive metadata lock.
*/
thd->locked_tables_list.unlink_all_closed_tables();
if (target_lock_data)
if (target_lock_req)
{
mdl_release_lock(&thd->mdl_context, target_lock_data);
mdl_remove_lock(&thd->mdl_context, target_lock_data);
mdl_ticket_release(&thd->mdl_context, target_lock_req->ticket);
mdl_request_remove(&thd->mdl_context, target_lock_req);
}
mdl_release_and_remove_all_locks_for_name(&thd->mdl_context, mdl_lock_data);
mdl_ticket_release_all_for_name(&thd->mdl_context, mdl_lock_ticket);
DBUG_RETURN(TRUE);
}
/* mysql_alter_table */