From: Date: March 2 2009 10:18pm Subject: bzr commit into mysql-6.0 branch (davi:2736) Bug#989 WL#4284 List-Archive: http://lists.mysql.com/commits/68037 X-Bug: 989 Message-Id: <20090302211845.55CD2EC0FF@skynet.ctb.virtua.com.br> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============3602214253238818122==" --===============3602214253238818122== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline # At a local mysql-6.0 repository of davi 2736 Davi Arnaut 2009-03-02 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/backup/backup_aux.h Rename mdl_alloc_lock to mdl_request_alloc. @ sql/event_db_repository.cc Rename alloc_mdl_locks to alloc_mdl_requests. @ sql/ha_ndbcluster_binlog.cc Use new function names to initialize MDL lock requests. @ sql/lock.cc Rename MDL functions. @ sql/log.cc Rename alloc_mdl_locks to alloc_mdl_requests. @ 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/si_objects.cc Use the MDL API to construct and hold the key. @ 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. added: mysql-test/r/mdl_sync.result mysql-test/t/mdl_sync.test modified: sql/backup/backup_aux.h sql/event_db_repository.cc sql/ha_ndbcluster_binlog.cc sql/lock.cc sql/log.cc sql/log_event.cc sql/mdl.cc sql/mdl.h sql/si_objects.cc sql/sp_head.cc sql/sql_acl.cc sql/sql_base.cc sql/sql_class.h sql/sql_delete.cc sql/sql_handler.cc sql/sql_insert.cc sql/sql_parse.cc sql/sql_plist.h sql/sql_plugin.cc sql/sql_servers.cc sql/sql_show.cc sql/sql_table.cc sql/sql_trigger.cc sql/sql_udf.cc sql/table.cc sql/table.h storage/myisammrg/ha_myisammrg.cc === added file 'mysql-test/r/mdl_sync.result' --- a/mysql-test/r/mdl_sync.result 1970-01-01 00:00:00 +0000 +++ b/mysql-test/r/mdl_sync.result 2009-03-02 21:18:26 +0000 @@ -0,0 +1,21 @@ +SET DEBUG_SYNC= 'RESET'; +drop table if exists t1,t2,t3; +create table t1 (i int); +create table t2 (i int); +connection: default +lock tables t2 read; +connection: con1 +set debug_sync='mdl_upgrade_shared_lock_to_exclusive SIGNAL parked WAIT_FOR go'; +alter table t1 rename t3; +connection: default +set debug_sync= 'now WAIT_FOR parked'; +connection: con2 +set debug_sync='mdl_acquire_exclusive_locks_wait SIGNAL go'; +drop table t1,t2; +connection: con1 +connection: default +unlock tables; +connection: con2 +ERROR 42S02: Unknown table 't1' +drop table t3; +SET DEBUG_SYNC= 'RESET'; === added file 'mysql-test/t/mdl_sync.test' --- a/mysql-test/t/mdl_sync.test 1970-01-01 00:00:00 +0000 +++ b/mysql-test/t/mdl_sync.test 2009-03-02 21:18:26 +0000 @@ -0,0 +1,69 @@ +# +# We need the Debug Sync Facility. +# +--source include/have_debug_sync.inc + +# Clean up resources used in this test case. +--disable_warnings +SET DEBUG_SYNC= 'RESET'; +--enable_warnings + +# +# Test the case of when a exclusive lock request waits for a +# shared lock being upgraded to a exclusive lock. +# + +connect (con1,localhost,root,,test,,); +connect (con2,localhost,root,,test,,); +connect (con3,localhost,root,,test,,); + +connection default; + +--disable_warnings +drop table if exists t1,t2,t3; +--enable_warnings + +create table t1 (i int); +create table t2 (i int); + +--echo connection: default +lock tables t2 read; + +connection con1; +--echo connection: con1 +set debug_sync='mdl_upgrade_shared_lock_to_exclusive SIGNAL parked WAIT_FOR go'; +--send alter table t1 rename t3 + +connection default; +--echo connection: default +set debug_sync= 'now WAIT_FOR parked'; + +connection con2; +--echo connection: con2 +set debug_sync='mdl_acquire_exclusive_locks_wait SIGNAL go'; +--send drop table t1,t2 + +connection con1; +--echo connection: con1 +--reap + +connection default; +--echo connection: default +unlock tables; + +connection con2; +--echo connection: con2 +--error ER_BAD_TABLE_ERROR +--reap + +connection default; +drop table t3; + +disconnect con1; +disconnect con2; +disconnect con3; + +# Clean up resources used in this test case. +--disable_warnings +SET DEBUG_SYNC= 'RESET'; +--enable_warnings === modified file 'sql/backup/backup_aux.h' --- a/sql/backup/backup_aux.h 2009-02-04 10:49:16 +0000 +++ b/sql/backup/backup_aux.h 2009-03-02 21:18:26 +0000 @@ -207,8 +207,8 @@ int set_table_list(TABLE_LIST &tl, const tl.db= const_cast(tbl.db().name().ptr()); tl.lock_type= lock_type; - tl.mdl_lock_data= mdl_alloc_lock(0, tl.db, tl.table_name, mem); - if (!tl.mdl_lock_data) // Failed to allocate lock + tl.mdl_lock_request= mdl_request_alloc(0, tl.db, tl.table_name, mem); + if (!tl.mdl_lock_request) { return 1; } === modified file 'sql/event_db_repository.cc' --- a/sql/event_db_repository.cc 2008-12-17 18:40:14 +0000 +++ b/sql/event_db_repository.cc 2009-03-02 21:18:26 +0000 @@ -555,7 +555,7 @@ Event_db_repository::open_event_table(TH DBUG_ENTER("Event_db_repository::open_event_table"); tables.init_one_table("mysql", 5, "event", 5, "event", lock_type); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1110,7 +1110,7 @@ Event_db_repository::check_system_tables /* Check mysql.db */ tables.init_one_table("mysql", 5, "db", 2, "db", TL_READ); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1128,7 +1128,7 @@ Event_db_repository::check_system_tables } /* Check mysql.user */ tables.init_one_table("mysql", 5, "user", 4, "user", TL_READ); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { @@ -1149,7 +1149,7 @@ Event_db_repository::check_system_tables } /* Check mysql.event */ tables.init_one_table("mysql", 5, "event", 5, "event", TL_READ); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { === modified file 'sql/ha_ndbcluster_binlog.cc' --- a/sql/ha_ndbcluster_binlog.cc 2009-02-13 16:30:54 +0000 +++ b/sql/ha_ndbcluster_binlog.cc 2009-03-02 21:18:26 +0000 @@ -147,8 +147,7 @@ static NDB_SCHEMA_OBJECT *ndb_get_schema static void ndb_free_schema_object(NDB_SCHEMA_OBJECT **ndb_schema_object, bool have_lock); -static MDL_LOCK_DATA binlog_mdl_lock_data; -static char binlog_mdlkey[MAX_MDLKEY_LENGTH]; +static MDL_LOCK_REQUEST binlog_mdl_lock_request; /* Helper functions @@ -3019,9 +3018,8 @@ static int open_and_lock_ndb_binlog_inde THD_SET_PROC_INFO(thd, "Opening " NDB_REP_DB "." NDB_REP_TABLE); tables->required_type= FRMTYPE_TABLE; thd->clear_error(); - mdl_init_lock(&binlog_mdl_lock_data, binlog_mdlkey, 0, tables->db, - tables->table_name); - tables->mdl_lock_data= &binlog_mdl_lock_data; + mdl_request_init(&binlog_mdl_lock_request, 0, tables->db, tables->table_name); + tables->mdl_lock_request= &binlog_mdl_lock_request; if (simple_open_n_lock_tables(thd, tables)) { if (thd->killed) === modified file 'sql/lock.cc' --- a/sql/lock.cc 2009-02-16 21:18:45 +0000 +++ b/sql/lock.cc 2009-03-02 21:18:26 +0000 @@ -963,25 +963,25 @@ static MYSQL_LOCK *get_lock_data(THD *th bool lock_table_names(THD *thd, TABLE_LIST *table_list) { TABLE_LIST *lock_table; - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_REQUEST *mdl_lock_req; DEBUG_SYNC(thd, "before_wait_locked_tname"); for (lock_table= table_list; lock_table; lock_table= lock_table->next_local) { - if (!(mdl_lock_data= mdl_alloc_lock(0, lock_table->db, - lock_table->table_name, - thd->mem_root))) + mdl_lock_req= mdl_request_alloc(0, lock_table->db, lock_table->table_name, + thd->mem_root); + if (!mdl_lock_req) goto end; - mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, mdl_lock_data); - lock_table->mdl_lock_data= mdl_lock_data; + mdl_request_set_type(mdl_lock_req, MDL_EXCLUSIVE); + mdl_request_add(&thd->mdl_context, mdl_lock_req); + lock_table->mdl_lock_request= mdl_lock_req; } if (mdl_acquire_exclusive_locks(&thd->mdl_context)) goto end; return 0; end: - mdl_remove_all_locks(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); return 1; } @@ -997,8 +997,8 @@ end: void unlock_table_names(THD *thd) { DBUG_ENTER("unlock_table_names"); - mdl_release_locks(&thd->mdl_context); - mdl_remove_all_locks(&thd->mdl_context); + mdl_ticket_release_all(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); DBUG_VOID_RETURN; } === modified file 'sql/log.cc' --- a/sql/log.cc 2009-02-13 16:30:54 +0000 +++ b/sql/log.cc 2009-03-02 21:18:26 +0000 @@ -1347,7 +1347,7 @@ bool Log_to_csv_event_handler::purge_bac tables.init_one_table("mysql", strlen("mysql"), "backup_history", strlen("backup_history"), "backup_history", TL_READ); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); res= mysql_truncate(thd, &tables, 1); close_thread_tables(thd); if (res) @@ -1359,7 +1359,7 @@ bool Log_to_csv_event_handler::purge_bac tables.init_one_table("mysql", strlen("mysql"), "backup_progress", strlen("backup_progress"), "backup_progress", TL_READ); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); res= mysql_truncate(thd, &tables, 1); close_thread_tables(thd); } @@ -3906,7 +3906,7 @@ my_bool MYSQL_BACKUP_LOG::check_backup_l tables.init_one_table("mysql", strlen("mysql"), "backup_history", strlen("backup_history"), "backup_history", TL_READ); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { /* @@ -3928,7 +3928,7 @@ my_bool MYSQL_BACKUP_LOG::check_backup_l tables.init_one_table("mysql", strlen("mysql"), "backup_progress", strlen("backup_progress"), "backup_progress", TL_READ); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (simple_open_n_lock_tables(thd, &tables)) { /* === modified file 'sql/log_event.cc' --- a/sql/log_event.cc 2009-02-16 21:18:45 +0000 +++ b/sql/log_event.cc 2009-03-02 21:18:26 +0000 @@ -7982,8 +7982,8 @@ Table_map_log_event::~Table_map_log_even int Table_map_log_event::do_apply_event(Relay_log_info const *rli) { RPL_TABLE_LIST *table_list; - char *db_mem, *tname_mem, *mdlkey; - MDL_LOCK_DATA *mdl_lock_data; + char *db_mem, *tname_mem; + MDL_LOCK_REQUEST *mdl_lock_request; size_t dummy_len; void *memory; DBUG_ENTER("Table_map_log_event::do_apply_event(Relay_log_info*)"); @@ -7998,8 +7998,7 @@ int Table_map_log_event::do_apply_event( &table_list, (uint) sizeof(RPL_TABLE_LIST), &db_mem, (uint) NAME_LEN + 1, &tname_mem, (uint) NAME_LEN + 1, - &mdl_lock_data, sizeof(MDL_LOCK_DATA), - &mdlkey, MAX_MDLKEY_LENGTH, + &mdl_lock_request, sizeof(MDL_LOCK_REQUEST), NullS))) DBUG_RETURN(HA_ERR_OUT_OF_MEM); @@ -8012,9 +8011,9 @@ int Table_map_log_event::do_apply_event( table_list->updating= 1; strmov(table_list->db, rpl_filter->get_rewrite_db(m_dbnam, &dummy_len)); strmov(table_list->table_name, m_tblnam); - mdl_init_lock(mdl_lock_data, mdlkey, 0, table_list->db, - table_list->table_name); - table_list->mdl_lock_data= mdl_lock_data; + mdl_request_init(mdl_lock_request, 0, table_list->db, + table_list->table_name); + table_list->mdl_lock_request= mdl_lock_request; int error= 0; === modified file 'sql/mdl.cc' --- a/sql/mdl.cc 2009-01-27 13:41:58 +0000 +++ b/sql/mdl.cc 2009-03-02 21:18:26 +0000 @@ -30,39 +30,43 @@ static bool mdl_initialized= 0; struct MDL_LOCK { - I_P_List active_shared; + typedef I_P_List > + Ticket_list; + + typedef Ticket_list::Iterator Ticket_iterator; + + /** The type of lock (shared or exclusive). */ + enum + { + SHARED, + EXCLUSIVE, + } type; + /** The key of the object (data) being protected. */ + MDL_KEY key; + /** List of granted tickets for this lock. */ + Ticket_list granted; /* There can be several upgraders and active exclusive belonging to the same context. */ - I_P_List active_shared_waiting_upgrade; - I_P_List active_exclusive; - I_P_List waiting_exclusive; - /** - Number of MDL_LOCK_DATA objects associated with this MDL_LOCK instance - and therefore present in one of above lists. Note that this number - doesn't account for pending requests for shared lock since we don't - associate them with MDL_LOCK and don't keep them in any list. - */ - uint lock_data_count; + Ticket_list waiting; void *cached_object; mdl_cached_object_release_hook cached_object_release_hook; - MDL_LOCK() : cached_object(0), cached_object_release_hook(0) {} - - MDL_LOCK_DATA *get_key_owner() + MDL_LOCK(const MDL_KEY *mdl_key) + : type(SHARED), cached_object(0), cached_object_release_hook(0) { - return !active_shared.is_empty() ? - active_shared.head() : - (!active_shared_waiting_upgrade.is_empty() ? - active_shared_waiting_upgrade.head() : - (!active_exclusive.is_empty() ? - active_exclusive.head() : waiting_exclusive.head())); + key.mdl_key_init(mdl_key); + granted.empty(); + waiting.empty(); } - bool has_one_lock_data() + bool is_empty() { - return (lock_data_count == 1); + return (granted.is_empty() && waiting.is_empty()); } }; @@ -87,12 +91,13 @@ struct MDL_GLOBAL_LOCK } global_lock; -extern "C" uchar *mdl_locks_key(const uchar *record, size_t *length, - my_bool not_used __attribute__((unused))) +extern "C" uchar * +mdl_locks_key(const uchar *record, size_t *length, + my_bool not_used __attribute__((unused))) { MDL_LOCK *entry=(MDL_LOCK*) record; - *length= entry->get_key_owner()->key_length; - return (uchar*) entry->get_key_owner()->key; + *length= entry->key.length(); + return (uchar*) entry->key.ptr(); } @@ -154,7 +159,8 @@ void mdl_destroy() void mdl_context_init(MDL_CONTEXT *context, THD *thd) { - context->locks.empty(); + context->requests.empty(); + context->tickets.empty(); context->thd= thd; context->has_global_shared_lock= FALSE; } @@ -174,7 +180,8 @@ void mdl_context_init(MDL_CONTEXT *conte void mdl_context_destroy(MDL_CONTEXT *context) { - DBUG_ASSERT(context->locks.is_empty()); + DBUG_ASSERT(context->requests.is_empty()); + DBUG_ASSERT(context->tickets.is_empty()); DBUG_ASSERT(!context->has_global_shared_lock); } @@ -191,8 +198,10 @@ void mdl_context_destroy(MDL_CONTEXT *co void mdl_context_backup_and_reset(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) { - backup->locks.empty(); - ctx->locks.swap(backup->locks); + backup->requests.empty(); + backup->tickets.empty(); + ctx->requests.swap(backup->requests); + ctx->tickets.swap(backup->tickets); } @@ -202,8 +211,10 @@ void mdl_context_backup_and_reset(MDL_CO void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup) { - DBUG_ASSERT(ctx->locks.is_empty()); - ctx->locks.swap(backup->locks); + DBUG_ASSERT(ctx->requests.is_empty()); + DBUG_ASSERT(ctx->tickets.is_empty()); + ctx->requests.swap(backup->requests); + ctx->tickets.swap(backup->tickets); } @@ -213,20 +224,29 @@ void mdl_context_restore(MDL_CONTEXT *ct void mdl_context_merge(MDL_CONTEXT *dst, MDL_CONTEXT *src) { - MDL_LOCK_DATA *lock_data; + MDL_LOCK_TICKET *ticket; + MDL_LOCK_REQUEST *lock_req; DBUG_ASSERT(dst->thd == src->thd); - if (!src->locks.is_empty()) + if (!src->requests.is_empty()) + { + MDL_CONTEXT::Request_iterator it(src->requests); + while ((lock_req= it++)) + dst->requests.push_front(lock_req); + src->requests.empty(); + } + + if (!src->tickets.is_empty()) { - I_P_List_iterator it(src->locks); - while ((lock_data= it++)) + MDL_CONTEXT::Ticket_iterator it(src->tickets); + while ((ticket= it++)) { - DBUG_ASSERT(lock_data->ctx); - lock_data->ctx= dst; - dst->locks.push_front(lock_data); + DBUG_ASSERT(ticket->ctx); + ticket->ctx= dst; + dst->tickets.push_front(ticket); } - src->locks.empty(); + src->tickets.empty(); } } @@ -236,53 +256,36 @@ void mdl_context_merge(MDL_CONTEXT *dst, This is to be used for every lock request. - Note that initialization and allocation are split - into two calls. This is to allow flexible memory management - of lock requests. Normally a lock request is stored - in statement memory (e.g. is a member of struct TABLE_LIST), - but we would also like to allow allocation of lock - requests in other memory roots, for example in the grant - subsystem, to lock privilege tables. - - The MDL subsystem does not own or manage memory of lock - requests. Instead it assumes that the life time of every lock - request encloses calls to mdl_acquire_shared_lock() and - mdl_release_locks(). - - @param lock_data Pointer to an MDL_LOCK_DATA object to initialize - @param key_buff Pointer to the buffer for key for the lock request - (should be at least 4+ strlen(db) + 1 + strlen(name) - + 1 bytes, or, if the lengths are not known, - MAX_MDLKEY_LENGTH) + Note that initialization and allocation are split into two + calls. This is to allow flexible memory management of lock + requests. Normally a lock request is stored in statement memory + (e.g. is a member of struct TABLE_LIST), but we would also like + to allow allocation of lock requests in other memory roots, + for example in the grant subsystem, to lock privilege tables. + + The MDL subsystem does not own or manage memory of lock requests. + Instead it assumes that the life time of every lock request (including + encompassed members db/name) encloses calls to mdl_request_add() + and mdl_request_remove() or mdl_request_remove_all(). + + @param lock_req Pointer to an MDL_LOCK_REQUEST object to initialize @param type Id of type of object to be locked @param db Name of database to which the object belongs @param name Name of of the object - Stores the database name, object name and the type in the key - buffer. Initializes mdl_el to point to the key. - We can't simply initialize MDL_LOCK_DATA with type, db and name - by-pointer because of the underlying HASH implementation - requires the key to be a contiguous buffer. - The initialized lock request will have MDL_SHARED type. Suggested lock types: TABLE - 0 PROCEDURE - 1 FUNCTION - 2 - Note that tables and views have the same lock type, since + Note that tables and views must have the same lock type, since they share the same name space in the SQL standard. */ -void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, - const char *db, const char *name) +void mdl_request_init(MDL_LOCK_REQUEST *lock_req, unsigned char type, + const char *db, const char *name) { - int4store(key, type); - lock_data->key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; - lock_data->key= key; - lock_data->type= MDL_SHARED; - lock_data->state= MDL_INITIALIZED; -#ifndef DBUG_OFF - lock_data->ctx= 0; - lock_data->lock= 0; -#endif + lock_req->key.mdl_key_init(type, db, name); + lock_req->type= MDL_SHARED; + lock_req->ticket= NULL; } @@ -304,19 +307,19 @@ void mdl_init_lock(MDL_LOCK_DATA *lock_d @retval non-0 Pointer to an object representing a lock request */ -MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, - MEM_ROOT *root) +MDL_LOCK_REQUEST * +mdl_request_alloc(unsigned char type, const char *db, + const char *name, MEM_ROOT *root) { - MDL_LOCK_DATA *lock_data; - char *key; + MDL_LOCK_REQUEST *lock_req; - if (!multi_alloc_root(root, &lock_data, sizeof(MDL_LOCK_DATA), &key, - MAX_MDLKEY_LENGTH, NULL)) + if (!(lock_req= (MDL_LOCK_REQUEST*) alloc_root(root, + sizeof(MDL_LOCK_REQUEST)))) return NULL; - mdl_init_lock(lock_data, key, type, db, name); + mdl_request_init(lock_req, type, db, name); - return lock_data; + return lock_req; } @@ -324,121 +327,122 @@ MDL_LOCK_DATA *mdl_alloc_lock(int type, Add a lock request to the list of lock requests of the context. The procedure to acquire metadata locks is: - - allocate and initialize lock requests (mdl_alloc_lock()) - - associate them with a context (mdl_add_lock()) - - call mdl_acquire_shared_lock()/mdl_release_lock() (maybe repeatedly). + - allocate and initialize lock requests (mdl_request_alloc()) + - associate them with a context (mdl_request_add()) + - call mdl_acquire_shared_lock()/mdl_ticket_release() (maybe repeatedly). Associates a lock request with the given context. @param context The MDL context to associate the lock with. There should be no more than one context per connection, to avoid deadlocks. - @param lock_data The lock request to be added. + @param lock_req The lock request to be added. */ -void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) +void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) { - DBUG_ENTER("mdl_add_lock"); - DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); - DBUG_ASSERT(!lock_data->ctx); - lock_data->ctx= context; - context->locks.push_front(lock_data); + DBUG_ENTER("mdl_request_add"); + DBUG_ASSERT(lock_req->ticket == NULL); + context->requests.push_front(lock_req); DBUG_VOID_RETURN; } /** - Remove a lock request from the list of lock requests of the context. + Remove a lock request from the list of lock requests. Disassociates a lock request from the given context. @param context The MDL context to remove the lock from. - @param lock_data The lock request to be removed. + @param lock_req The lock request to be removed. - @pre The lock request being removed should correspond to lock which + @pre The lock request being removed should correspond to a ticket that was released or was not acquired. - @note Resets lock request for lock released back to its initial state + @note Resets lock request back to its initial state (i.e. sets type to MDL_SHARED). */ -void mdl_remove_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) +void mdl_request_remove(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req) { - DBUG_ENTER("mdl_remove_lock"); - DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); - DBUG_ASSERT(context == lock_data->ctx); + DBUG_ENTER("mdl_request_remove"); /* Reset lock request back to its initial state. */ - lock_data->type= MDL_SHARED; -#ifndef DBUG_OFF - lock_data->ctx= 0; -#endif - context->locks.remove(lock_data); + lock_req->type= MDL_SHARED; + lock_req->ticket= NULL; + context->requests.remove(lock_req); DBUG_VOID_RETURN; } /** - Clear all lock requests in the context (clear the context). - + Clear all lock requests in the context. Disassociates lock requests from the context. - All granted locks must be released prior to calling this - function. - - In other words, the expected procedure to release locks is: - - mdl_release_locks(); - - mdl_remove_all_locks(); - - We could possibly merge mdl_remove_all_locks() and mdl_release_locks(), - but this function comes in handy when we need to back off: in that case - we release all the locks acquired so-far but do not free them, since - we know that the respective lock requests will be used again. Also resets lock requests back to their initial state (i.e. MDL_SHARED). @param context Context to be cleared. */ -void mdl_remove_all_locks(MDL_CONTEXT *context) +void mdl_request_remove_all(MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); - while ((lock_data= it++)) + MDL_LOCK_REQUEST *lock_req; + MDL_CONTEXT::Request_iterator it(context->requests); + while ((lock_req= it++)) { /* Reset lock request back to its initial state. */ - lock_data->type= MDL_SHARED; -#ifndef DBUG_OFF - lock_data->ctx= 0; -#endif + lock_req->type= MDL_SHARED; + lock_req->ticket= NULL; } - context->locks.empty(); + context->requests.empty(); } /** - Auxiliary functions needed for creation/destruction of MDL_LOCK - objects. + Auxiliary functions needed for creation/destruction of MDL_LOCK objects. @todo This naive implementation should be replaced with one that saves on memory allocation by reusing released objects. */ -static MDL_LOCK* get_lock_object(void) +static MDL_LOCK* alloc_lock_object(const MDL_KEY *mdl_key) { - return new MDL_LOCK(); + return new MDL_LOCK(mdl_key); } -static void release_lock_object(MDL_LOCK *lock) +static void free_lock_object(MDL_LOCK *lock) { delete lock; } /** + Auxiliary functions needed for creation/destruction of MDL_LOCK_TICKET + objects. + + @todo This naive implementation should be replaced with one that saves + on memory allocation by reusing released objects. +*/ + +static MDL_LOCK_TICKET* alloc_ticket_object(MDL_CONTEXT *context) +{ + MDL_LOCK_TICKET *ticket= new MDL_LOCK_TICKET; + return ticket; +} + + +static void free_ticket_object(MDL_LOCK_TICKET *ticket) +{ + delete ticket; +} + + +/** Helper functions which simplifies writing various checks and asserts. */ -static bool is_shared(MDL_LOCK_DATA *lock_data) +template +static inline bool is_shared(T *lock_data) { return (lock_data->type < MDL_EXCLUSIVE); } @@ -508,7 +512,7 @@ static inline void mdl_exit_cond(MDL_CON request, implying a form of lock on the global metadata, is compatible with the current state of the global metadata lock. - @param lock_data Request for lock on an individual object, implying a + @param lock_req Request for lock on an individual object, implying a certain kind of global metadata lock. @retval TRUE - Lock request can be satisfied @@ -538,9 +542,9 @@ static inline void mdl_exit_cond(MDL_CON type of locks we don't even have any accounting for them. */ -static bool can_grant_global_lock(MDL_LOCK_DATA *lock_data) +static bool can_grant_global_lock(enum_mdl_type type, bool is_upgrade) { - switch (lock_data->type) + switch (type) { case MDL_SHARED: case MDL_SHARED_HIGH_PRIO: @@ -559,7 +563,7 @@ static bool can_grant_global_lock(MDL_LO return TRUE; break; case MDL_EXCLUSIVE: - if (lock_data->state == MDL_PENDING_UPGRADE) + if (is_upgrade) { /* We are upgrading MDL_SHARED to MDL_EXCLUSIVE. @@ -596,7 +600,7 @@ static bool can_grant_global_lock(MDL_LO Check if request for the lock can be satisfied given current state of lock. @param lock Lock. - @param lock_data Request for lock. + @param lock_req Request for lock. @retval TRUE Lock request can be satisfied @retval FALSE There is some conflicting lock. @@ -622,68 +626,96 @@ static bool can_grant_global_lock(MDL_LO being upgraded. */ -static bool can_grant_lock(MDL_LOCK *lock, MDL_LOCK_DATA *lock_data) +static bool can_grant_lock(MDL_CONTEXT *ctx, MDL_LOCK *lock, + enum_mdl_type type, bool is_upgrade) { - switch (lock_data->type) - { + bool can_grant= FALSE; + + switch (type) { case MDL_SHARED: case MDL_SHARED_UPGRADABLE: case MDL_SHARED_HIGH_PRIO: - if ((lock->active_exclusive.is_empty() && - (lock_data->type == MDL_SHARED_HIGH_PRIO || - lock->waiting_exclusive.is_empty() && - lock->active_shared_waiting_upgrade.is_empty())) || - (!lock->active_exclusive.is_empty() && - lock->active_exclusive.head()->ctx == lock_data->ctx)) + if (lock->type == MDL_LOCK::SHARED) + { + /* Pending exclusive locks have higher priority over shared locks. */ + if (lock->waiting.is_empty() || type == MDL_SHARED_HIGH_PRIO) + can_grant= TRUE; + } + else if (lock->granted.head()->ctx == ctx) { /* When exclusive lock comes from the same context we can satisfy our shared lock. This is required for CREATE TABLE ... SELECT ... and ALTER VIEW ... AS .... */ - return TRUE; + can_grant= TRUE; } - else - return FALSE; break; case MDL_EXCLUSIVE: - if (lock_data->state == MDL_PENDING_UPGRADE) + if (is_upgrade) { /* We are upgrading MDL_SHARED to MDL_EXCLUSIVE. */ - MDL_LOCK_DATA *conf_lock_data; - I_P_List_iterator it(lock->active_shared); + MDL_LOCK_TICKET *conf_lock_ticket; + MDL_LOCK::Ticket_iterator it(lock->granted); /* There should be no active exclusive locks since we own shared lock on the object. */ - DBUG_ASSERT(lock->active_exclusive.is_empty() && - lock->active_shared_waiting_upgrade.head() == lock_data); + DBUG_ASSERT(lock->type == MDL_LOCK::SHARED); - while ((conf_lock_data= it++)) + while ((conf_lock_ticket= it++)) { /* When upgrading shared lock to exclusive one we can have other shared locks for the same object in the same context, e.g. in case when several instances of TABLE are open. */ - if (conf_lock_data->ctx != lock_data->ctx) - return FALSE; + if (conf_lock_ticket->ctx != ctx) + break; } - return TRUE; + /* Grant lock if there are no conflicting shared locks. */ + if (conf_lock_ticket == NULL) + can_grant= TRUE; + break; } - else + else if (lock->type == MDL_LOCK::SHARED) { - return (lock->active_exclusive.is_empty() && - lock->active_shared_waiting_upgrade.is_empty() && - lock->active_shared.is_empty()); + can_grant= lock->granted.is_empty(); } break; default: DBUG_ASSERT(0); } - return FALSE; + return can_grant; +} + + +/** + Check whether the context already holds a compatible lock ticket + on a object. Only shared locks can be recursive. + + @param lock_req Lock request object for lock to be acquired + + @return A pointer to the lock ticket for the object or NULL otherwise. +*/ + +static MDL_LOCK_TICKET * +mdl_context_find_ticket(MDL_CONTEXT *ctx, MDL_LOCK_REQUEST *lock_req) +{ + MDL_LOCK_TICKET *ticket; + MDL_CONTEXT::Ticket_iterator it(ctx->tickets); + + DBUG_ASSERT(is_shared(lock_req)); + + while ((ticket= it++)) + { + if (lock_req->type == ticket->type && + lock_req->key.is_equal(&ticket->lock->key)) + break; + } + + return ticket; } @@ -701,7 +733,7 @@ static bool can_grant_lock(MDL_LOCK *loc This function must be called after the lock is added to a context. @param context [in] Context containing request for lock - @param lock_data [in] Lock request object for lock to be acquired + @param lock_req [in] Lock request object for lock to be acquired @param retry [out] Indicates that conflicting lock exists and another attempt should be made after releasing all current locks and waiting for conflicting lock go away @@ -712,81 +744,111 @@ static bool can_grant_lock(MDL_LOCK *loc In the latter case "retry" parameter is set to TRUE. */ -bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data, +bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, bool *retry) { MDL_LOCK *lock; + MDL_KEY *key= &lock_req->key; + MDL_LOCK_TICKET *ticket; *retry= FALSE; - DBUG_ASSERT(is_shared(lock_data) && lock_data->state == MDL_INITIALIZED); - - DBUG_ASSERT(lock_data->ctx == context); + DBUG_ASSERT(is_shared(lock_req) && lock_req->ticket == NULL); safe_mutex_assert_not_owner(&LOCK_open); if (context->has_global_shared_lock && - lock_data->type == MDL_SHARED_UPGRADABLE) + lock_req->type == MDL_SHARED_UPGRADABLE) { my_error(ER_CANT_UPDATE_WITH_READLOCK, MYF(0)); return TRUE; } + /* + Check whether the context already holds a shared lock on the object, + and if so, grant the request. + */ + if ((ticket= mdl_context_find_ticket(context, lock_req))) + { + DBUG_ASSERT(ticket->state == MDL_ACQUIRED); + lock_req->ticket= ticket; + return FALSE; + } + pthread_mutex_lock(&LOCK_mdl); - if (!can_grant_global_lock(lock_data)) + if (!can_grant_global_lock(lock_req->type, FALSE)) { pthread_mutex_unlock(&LOCK_mdl); *retry= TRUE; return TRUE; } - if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, (uchar*)lock_data->key, - lock_data->key_length))) + if (!(ticket= alloc_ticket_object(context))) { - if (!(lock= get_lock_object())) - { - pthread_mutex_unlock(&LOCK_mdl); - return TRUE; - } - /* - Before inserting MDL_LOCK object into hash we should add at least one - MDL_LOCK_DATA to its lists in order to provide key for this element. - Thus we can't merge two branches of the above if-statement. - */ - lock->active_shared.push_front(lock_data); - lock->lock_data_count= 1; - if (my_hash_insert(&mdl_locks, (uchar*)lock)) + pthread_mutex_unlock(&LOCK_mdl); + return TRUE; + } + + if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, + key->ptr(), key->length()))) + { + /* Default lock type is MDL_LOCK::SHARED */ + lock= alloc_lock_object(key); + if (!lock || my_hash_insert(&mdl_locks, (uchar*)lock)) { - release_lock_object(lock); + free_lock_object(lock); + free_ticket_object(ticket); pthread_mutex_unlock(&LOCK_mdl); return TRUE; } - lock_data->state= MDL_ACQUIRED; - lock_data->lock= lock; - if (lock_data->type == MDL_SHARED_UPGRADABLE) + } + + if (can_grant_lock(context, lock, lock_req->type, FALSE)) + { + lock->granted.push_front(ticket); + context->tickets.push_front(ticket); + ticket->state= MDL_ACQUIRED; + lock_req->ticket= ticket; + ticket->lock= lock; + ticket->type= lock_req->type; + ticket->ctx= context; + if (lock_req->type == MDL_SHARED_UPGRADABLE) global_lock.active_intention_exclusive++; } else { - if (can_grant_lock(lock, lock_data)) - { - lock->active_shared.push_front(lock_data); - lock->lock_data_count++; - lock_data->state= MDL_ACQUIRED; - lock_data->lock= lock; - if (lock_data->type == MDL_SHARED_UPGRADABLE) - global_lock.active_intention_exclusive++; - } - else - *retry= TRUE; + /* We can't get here if we allocated a new lock. */ + DBUG_ASSERT(! lock->is_empty()); + *retry= TRUE; + free_ticket_object(ticket); } + pthread_mutex_unlock(&LOCK_mdl); return *retry; } -static void release_lock(MDL_LOCK_DATA *lock_data); +static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket); + + +/** + Notify a thread holding a shared metadata lock of a pending exclusive lock. + + @param thd Current thread context + @param conf_lock_ticket Conflicting metadata lock + + @retval TRUE A thread was woken up + @retval FALSE Lock is not a shared one or no thread was woken up +*/ + +static bool notify_shared_lock(THD *thd, MDL_LOCK_TICKET *conf_lock_ticket) +{ + bool woke= FALSE; + if (conf_lock_ticket->type != MDL_EXCLUSIVE) + woke= mysql_notify_thread_having_shared_lock(thd, conf_lock_ticket->ctx->thd); + return woke; +} /** @@ -806,12 +868,13 @@ static void release_lock(MDL_LOCK_DATA * bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data; MDL_LOCK *lock; bool signalled= FALSE; const char *old_msg; - I_P_List_iterator it(context->locks); + MDL_LOCK_REQUEST *lock_req; + MDL_LOCK_TICKET *ticket; st_my_thread_var *mysys_var= my_thread_var; + MDL_CONTEXT::Request_iterator it(context->requests); safe_mutex_assert_not_owner(&LOCK_open); @@ -825,46 +888,44 @@ bool mdl_acquire_exclusive_locks(MDL_CON old_msg= MDL_ENTER_COND(context, mysys_var); - while ((lock_data= it++)) + while ((lock_req= it++)) { - DBUG_ASSERT(lock_data->type == MDL_EXCLUSIVE && - lock_data->state == MDL_INITIALIZED); - if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, (uchar*)lock_data->key, - lock_data->key_length))) + MDL_KEY *key= &lock_req->key; + DBUG_ASSERT(lock_req->type == MDL_EXCLUSIVE && + lock_req->ticket == NULL); + + /* Early allocation: ticket is used as a shortcut to the lock. */ + if (!(ticket= alloc_ticket_object(context))) + goto err; + + if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, + key->ptr(), key->length()))) { - if (!(lock= get_lock_object())) - goto err; - /* - Again before inserting MDL_LOCK into hash provide key for - it by adding MDL_LOCK_DATA to one of its lists. - */ - lock->waiting_exclusive.push_front(lock_data); - lock->lock_data_count= 1; - if (my_hash_insert(&mdl_locks, (uchar*)lock)) + lock= alloc_lock_object(key); + if (!lock || my_hash_insert(&mdl_locks, (uchar*)lock)) { - release_lock_object(lock); + free_ticket_object(ticket); + free_lock_object(lock); goto err; } - lock_data->lock= lock; - lock_data->state= MDL_PENDING; - } - else - { - lock->waiting_exclusive.push_front(lock_data); - lock->lock_data_count++; - lock_data->lock= lock; - lock_data->state= MDL_PENDING; } + + lock_req->ticket= ticket; + ticket->state= MDL_PENDING; + ticket->ctx= context; + ticket->lock= lock; + ticket->type= lock_req->type; + lock->waiting.push_front(ticket); } while (1) { it.rewind(); - while ((lock_data= it++)) + while ((lock_req= it++)) { - lock= lock_data->lock; + lock= lock_req->ticket->lock; - if (!can_grant_global_lock(lock_data)) + if (!can_grant_global_lock(lock_req->type, FALSE)) { /* There is an active or pending global shared lock so we have @@ -873,27 +934,25 @@ bool mdl_acquire_exclusive_locks(MDL_CON signalled= TRUE; break; } - else if (!can_grant_lock(lock, lock_data)) + else if (!can_grant_lock(context, lock, lock_req->type, FALSE)) { - MDL_LOCK_DATA *conf_lock_data; - I_P_List_iterator it(lock->active_shared); - - signalled= !lock->active_exclusive.is_empty() || - !lock->active_shared_waiting_upgrade.is_empty(); - - while ((conf_lock_data= it++)) - { - signalled|= - mysql_notify_thread_having_shared_lock(context->thd, - conf_lock_data->ctx->thd); - } + MDL_LOCK_TICKET *conf_lock_ticket; + MDL_LOCK::Ticket_iterator it(lock->granted); + + signalled= (lock->type == MDL_LOCK::EXCLUSIVE); + + while ((conf_lock_ticket= it++)) + signalled|= notify_shared_lock(context->thd, conf_lock_ticket); break; } } - if (!lock_data) + if (!lock_req) break; + + /* There is a shared or exclusive lock on the object. */ + DEBUG_SYNC(context->thd, "mdl_acquire_exclusive_locks_wait"); + if (signalled) pthread_cond_wait(&COND_mdl, &LOCK_mdl); else @@ -912,13 +971,16 @@ bool mdl_acquire_exclusive_locks(MDL_CON goto err; } it.rewind(); - while ((lock_data= it++)) + while ((lock_req= it++)) { global_lock.active_intention_exclusive++; - lock= lock_data->lock; - lock->waiting_exclusive.remove(lock_data); - lock->active_exclusive.push_front(lock_data); - lock_data->state= MDL_ACQUIRED; + ticket= lock_req->ticket; + lock= ticket->lock; + lock->type= MDL_LOCK::EXCLUSIVE; + lock->waiting.remove(ticket); + lock->granted.push_front(ticket); + context->tickets.push_front(ticket); + ticket->state= MDL_ACQUIRED; if (lock->cached_object) (*lock->cached_object_release_hook)(lock->cached_object); lock->cached_object= NULL; @@ -933,10 +995,20 @@ err: Ignore those lock requests which were not made MDL_PENDING. */ it.rewind(); - while ((lock_data= it++) && lock_data->state == MDL_PENDING) + while ((lock_req= it++) && lock_req->ticket) { - release_lock(lock_data); - lock_data->state= MDL_INITIALIZED; + ticket= lock_req->ticket; + DBUG_ASSERT(ticket->state == MDL_PENDING); + lock= ticket->lock; + free_ticket_object(ticket); + lock->waiting.remove(ticket); + /* Reset lock request back to its initial state. */ + lock_req->ticket= NULL; + if (lock->is_empty()) + { + my_hash_delete(&mdl_locks, (uchar *)lock); + free_lock_object(lock); + } } /* May be some pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); @@ -952,77 +1024,61 @@ err: new definition has been constructed. @param context Context to which shared lock belongs - @param lock_data Satisfied request for shared lock to be upgraded + @param ticket Ticket for shared lock to be upgraded @note In case of failure to upgrade lock (e.g. because upgrader was killed) leaves lock in its original state (locked in shared mode). + @note There can be only one upgrader for a lock or we will have deadlock. + This invariant is ensured by code outside of metadata subsystem usually + by obtaining some sort of exclusive table-level lock (e.g. TL_WRITE, + TL_WRITE_ALLOW_READ) before performing upgrade of metadata lock. + @retval FALSE Success @retval TRUE Failure (thread was killed) */ bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data) + MDL_LOCK_TICKET *ticket) { - MDL_LOCK *lock; + MDL_LOCK *lock= ticket->lock; const char *old_msg; st_my_thread_var *mysys_var= my_thread_var; DBUG_ENTER("mdl_upgrade_shared_lock_to_exclusive"); + DEBUG_SYNC(context->thd, "mdl_upgrade_shared_lock_to_exclusive"); safe_mutex_assert_not_owner(&LOCK_open); - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); - /* Allow this function to be called twice for the same lock request. */ - if (lock_data->type == MDL_EXCLUSIVE) + if (ticket->type == MDL_EXCLUSIVE) DBUG_RETURN(FALSE); - DBUG_ASSERT(lock_data->type == MDL_SHARED_UPGRADABLE); - - lock= lock_data->lock; - pthread_mutex_lock(&LOCK_mdl); old_msg= MDL_ENTER_COND(context, mysys_var); - lock_data->state= MDL_PENDING_UPGRADE; - /* Set type of lock request to the type at which we are aiming. */ - lock_data->type= MDL_EXCLUSIVE; - lock->active_shared.remove(lock_data); - /* - There can be only one upgrader for this lock or we will have deadlock. - This invariant is ensured by code outside of metadata subsystem usually - by obtaining some sort of exclusive table-level lock (e.g. TL_WRITE, - TL_WRITE_ALLOW_READ) before performing upgrade of metadata lock. - */ - DBUG_ASSERT(lock->active_shared_waiting_upgrade.is_empty()); - lock->active_shared_waiting_upgrade.push_front(lock_data); /* - Since we should have been already acquired intention exclusive global lock - this call is only enforcing asserts. + Since we should have already acquired an intention exclusive + global lock this call is only enforcing asserts. */ - DBUG_ASSERT(can_grant_global_lock(lock_data)); + DBUG_ASSERT(can_grant_global_lock(MDL_EXCLUSIVE, TRUE)); while (1) { - if (can_grant_lock(lock, lock_data)) + if (can_grant_lock(context, lock, MDL_EXCLUSIVE, TRUE)) break; bool signalled= FALSE; - MDL_LOCK_DATA *conf_lock_data; - I_P_List_iterator it(lock->active_shared); + MDL_LOCK_TICKET *conf_lock_ticket; + MDL_LOCK::Ticket_iterator it(lock->granted); - while ((conf_lock_data= it++)) + while ((conf_lock_ticket= it++)) { - if (conf_lock_data->ctx != context) - { - signalled|= - mysql_notify_thread_having_shared_lock(context->thd, - conf_lock_data->ctx->thd); - } + if (conf_lock_ticket->ctx != context) + signalled|= notify_shared_lock(context->thd, conf_lock_ticket); } if (signalled) @@ -1042,10 +1098,6 @@ bool mdl_upgrade_shared_lock_to_exclusiv } if (mysys_var->abort) { - lock_data->state= MDL_ACQUIRED; - lock_data->type= MDL_SHARED_UPGRADABLE; - lock->active_shared_waiting_upgrade.remove(lock_data); - lock->active_shared.push_front(lock_data); /* Pending requests for shared locks can be satisfied now. */ pthread_cond_broadcast(&COND_mdl); MDL_EXIT_COND(context, mysys_var, old_msg); @@ -1053,9 +1105,9 @@ bool mdl_upgrade_shared_lock_to_exclusiv } } - lock->active_shared_waiting_upgrade.remove(lock_data); - lock->active_exclusive.push_front(lock_data); - lock_data->state= MDL_ACQUIRED; + lock->type= MDL_LOCK::EXCLUSIVE; + /* Set the new type of lock in the ticket. */ + ticket->type= MDL_EXCLUSIVE; if (lock->cached_object) (*lock->cached_object_release_hook)(lock->cached_object); lock->cached_object= 0; @@ -1079,7 +1131,7 @@ bool mdl_upgrade_shared_lock_to_exclusiv block and wait for the lock if the table already exists. @param context [in] The context containing the lock request - @param lock [in] The lock request + @param lock_req [in] The lock request @param conflict [out] Indicates that conflicting lock exists @retval TRUE Failure either conflicting lock exists or some error @@ -1091,13 +1143,15 @@ bool mdl_upgrade_shared_lock_to_exclusiv */ bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data, + MDL_LOCK_REQUEST *lock_req, bool *conflict) { MDL_LOCK *lock; + MDL_LOCK_TICKET *ticket; + MDL_KEY *key= &lock_req->key; - DBUG_ASSERT(lock_data->type == MDL_EXCLUSIVE && - lock_data->state == MDL_INITIALIZED); + DBUG_ASSERT(lock_req->type == MDL_EXCLUSIVE && + lock_req->ticket == NULL); safe_mutex_assert_not_owner(&LOCK_open); @@ -1105,20 +1159,25 @@ bool mdl_try_acquire_exclusive_lock(MDL_ pthread_mutex_lock(&LOCK_mdl); - if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, (uchar*)lock_data->key, - lock_data->key_length))) + if (!(lock= (MDL_LOCK*) my_hash_search(&mdl_locks, + key->ptr(), key->length()))) { - if (!(lock= get_lock_object())) - goto err; - lock->active_exclusive.push_front(lock_data); - lock->lock_data_count= 1; - if (my_hash_insert(&mdl_locks, (uchar*)lock)) + ticket= alloc_ticket_object(context); + lock= alloc_lock_object(key); + if (!ticket || !lock || my_hash_insert(&mdl_locks, (uchar*)lock)) { - release_lock_object(lock); + free_ticket_object(ticket); + free_lock_object(lock); goto err; } - lock_data->state= MDL_ACQUIRED; - lock_data->lock= lock; + lock->type= MDL_LOCK::EXCLUSIVE; + lock->granted.push_front(ticket); + context->tickets.push_front(ticket); + ticket->state= MDL_ACQUIRED; + lock_req->ticket= ticket; + ticket->ctx= context; + ticket->lock= lock; + ticket->type= lock_req->type; global_lock.active_intention_exclusive++; pthread_mutex_unlock(&LOCK_mdl); return FALSE; @@ -1193,9 +1252,9 @@ bool mdl_acquire_global_shared_lock(MDL_ bool mdl_wait_for_locks(MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data; MDL_LOCK *lock; - I_P_List_iterator it(context->locks); + MDL_LOCK_REQUEST *lock_req; + MDL_CONTEXT::Request_iterator it(context->requests); const char *old_msg; st_my_thread_var *mysys_var= my_thread_var; @@ -1217,22 +1276,23 @@ bool mdl_wait_for_locks(MDL_CONTEXT *con pthread_mutex_lock(&LOCK_mdl); old_msg= MDL_ENTER_COND(context, mysys_var); it.rewind(); - while ((lock_data= it++)) + while ((lock_req= it++)) { - DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); - if (!can_grant_global_lock(lock_data)) + MDL_KEY *key= &lock_req->key; + DBUG_ASSERT(lock_req->ticket == NULL); + if (!can_grant_global_lock(lock_req->type, FALSE)) break; /* To avoid starvation we don't wait if we have a conflict against request for MDL_EXCLUSIVE lock. */ - if (is_shared(lock_data) && - (lock= (MDL_LOCK*) my_hash_search(&mdl_locks, (uchar*)lock_data->key, - lock_data->key_length)) && - !can_grant_lock(lock, lock_data)) + if (is_shared(lock_req) && + (lock= (MDL_LOCK*) my_hash_search(&mdl_locks, key->ptr(), + key->length())) && + !can_grant_lock(context, lock, lock_req->type, FALSE)) break; } - if (!lock_data) + if (!lock_req) { pthread_mutex_unlock(&LOCK_mdl); break; @@ -1247,58 +1307,48 @@ bool mdl_wait_for_locks(MDL_CONTEXT *con /** Auxiliary function which allows to release particular lock - ownership of which is represented by lock request object. + ownership of which is represented by a lock ticket object. */ -static void release_lock(MDL_LOCK_DATA *lock_data) +static void release_ticket(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) { - MDL_LOCK *lock; + MDL_LOCK *lock= ticket->lock; + DBUG_ENTER("release_ticket"); + DBUG_PRINT("enter", ("db=%s name=%s", lock->key.db_name(), + lock->key.table_name())); + + safe_mutex_assert_owner(&LOCK_mdl); + + context->tickets.remove(ticket); - DBUG_ENTER("release_lock"); - DBUG_PRINT("enter", ("db=%s name=%s", lock_data->key + 4, - lock_data->key + 4 + strlen(lock_data->key + 4) + 1)); + switch (ticket->type) + { + case MDL_SHARED_UPGRADABLE: + global_lock.active_intention_exclusive--; + /* Fallthrough. */ + case MDL_SHARED: + case MDL_SHARED_HIGH_PRIO: + lock->granted.remove(ticket); + break; + case MDL_EXCLUSIVE: + lock->type= MDL_LOCK::SHARED; + lock->granted.remove(ticket); + global_lock.active_intention_exclusive--; + break; + default: + DBUG_ASSERT(0); + } - DBUG_ASSERT(lock_data->state == MDL_PENDING || - lock_data->state == MDL_ACQUIRED); + free_ticket_object(ticket); - lock= lock_data->lock; - if (lock->has_one_lock_data()) + if (lock->is_empty()) { my_hash_delete(&mdl_locks, (uchar *)lock); DBUG_PRINT("info", ("releasing cached_object cached_object=%p", lock->cached_object)); if (lock->cached_object) (*lock->cached_object_release_hook)(lock->cached_object); - release_lock_object(lock); - if (lock_data->state == MDL_ACQUIRED && - (lock_data->type == MDL_EXCLUSIVE || - lock_data->type == MDL_SHARED_UPGRADABLE)) - global_lock.active_intention_exclusive--; - } - else - { - switch (lock_data->type) - { - case MDL_SHARED_UPGRADABLE: - global_lock.active_intention_exclusive--; - /* Fallthrough. */ - case MDL_SHARED: - case MDL_SHARED_HIGH_PRIO: - lock->active_shared.remove(lock_data); - break; - case MDL_EXCLUSIVE: - if (lock_data->state == MDL_PENDING) - lock->waiting_exclusive.remove(lock_data); - else - { - lock->active_exclusive.remove(lock_data); - global_lock.active_intention_exclusive--; - } - break; - default: - DBUG_ASSERT(0); - } - lock->lock_data_count--; + free_lock_object(lock); } DBUG_VOID_RETURN; @@ -1317,42 +1367,38 @@ static void release_lock(MDL_LOCK_DATA * are associated. */ -void mdl_release_locks(MDL_CONTEXT *context) +void mdl_ticket_release_all(MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); - DBUG_ENTER("mdl_release_locks"); + MDL_LOCK_TICKET *ticket; + MDL_CONTEXT::Ticket_iterator it(context->tickets); + DBUG_ENTER("mdl_ticket_release_all"); safe_mutex_assert_not_owner(&LOCK_open); + /* Detach lock tickets from the requests for back off. */ + { + MDL_LOCK_REQUEST *lock_req; + MDL_CONTEXT::Request_iterator it(context->requests); + + while ((lock_req= it++)) + lock_req->ticket= NULL; + } + + if (context->tickets.is_empty()) + DBUG_VOID_RETURN; + pthread_mutex_lock(&LOCK_mdl); - while ((lock_data= it++)) + while ((ticket= it++)) { - DBUG_PRINT("info", ("found lock to release lock_data=%p", lock_data)); - /* - Don't call release_lock() for a shared lock if has not been - granted. Lock state in this case is MDL_INITIALIZED. - We have pending and granted shared locks in the same context - when this function is called from the "back-off" path of - open_tables(). - */ - if (lock_data->state != MDL_INITIALIZED) - { - release_lock(lock_data); - lock_data->state= MDL_INITIALIZED; -#ifndef DBUG_OFF - lock_data->lock= 0; -#endif - } - /* - We will return lock request to its initial state only in - mdl_remove_all_locks() since we need to know type of lock - request in mdl_wait_for_locks(). - */ + DBUG_PRINT("info", ("found lock to release ticket=%p", ticket)); + release_ticket(context, ticket); } /* Inefficient but will do for a while */ pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); + + context->tickets.empty(); + DBUG_VOID_RETURN; } @@ -1361,20 +1407,17 @@ void mdl_release_locks(MDL_CONTEXT *cont Release a lock. @param context Context containing lock in question - @param lock_data Lock to be released + @param ticket Lock to be released */ -void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data) +void mdl_ticket_release(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket) { + DBUG_ASSERT(context == ticket->ctx); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - release_lock(lock_data); -#ifndef DBUG_OFF - lock_data->lock= 0; -#endif - lock_data->state= MDL_INITIALIZED; + release_ticket(context, ticket); pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); } @@ -1385,34 +1428,43 @@ void mdl_release_lock(MDL_CONTEXT *conte object as this lock request, remove lock requests from the context. @param context Context containing locks in question - @param lock_data One of the locks for the name/object for which all + @param ticket One of the locks for the name/object for which all locks should be released. */ -void mdl_release_and_remove_all_locks_for_name(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data) +void mdl_ticket_release_all_for_name(MDL_CONTEXT *context, + MDL_LOCK_TICKET *ticket) { MDL_LOCK *lock; - I_P_List_iterator it(context->locks); - - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); /* - We can use MDL_LOCK_DATA::lock here to identify other locks for the same - object since even altough MDL_LOCK object might be reused for different + We can use MDL_LOCK_TICKET::lock here to identify other locks for the same + object since even though MDL_LOCK object might be reused for different lock after the first lock for this object have been released we can't have references to this other MDL_LOCK object in this context. */ - lock= lock_data->lock; + lock= ticket->lock; + + /* Remove matching lock requests from the context. */ + MDL_LOCK_REQUEST *lock_req; + MDL_CONTEXT::Request_iterator it_lock_req(context->requests); - while ((lock_data= it++)) + while ((lock_req= it_lock_req++)) { - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); - if (lock_data->lock == lock) - { - mdl_release_lock(context, lock_data); - mdl_remove_lock(context, lock_data); - } + DBUG_ASSERT(lock_req->ticket && lock_req->ticket->state == MDL_ACQUIRED); + if (lock_req->ticket->lock == lock) + mdl_request_remove(context, lock_req); + } + + /* Remove matching lock tickets from the context. */ + MDL_LOCK_TICKET *lock_tkt; + MDL_CONTEXT::Ticket_iterator it_lock_tkt(context->tickets); + + while ((lock_tkt= it_lock_tkt++)) + { + DBUG_ASSERT(lock_tkt->state == MDL_ACQUIRED); + if (lock_tkt->lock == lock) + mdl_ticket_release(context, lock_tkt); } } @@ -1421,27 +1473,26 @@ void mdl_release_and_remove_all_locks_fo Downgrade an exclusive lock to shared metadata lock. @param context A context to which exclusive lock belongs - @param lock_data Satisfied request for exclusive lock to be downgraded + @param ticket Ticket for exclusive lock to be downgraded */ void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data) + MDL_LOCK_TICKET *ticket) { MDL_LOCK *lock; - safe_mutex_assert_not_owner(&LOCK_open); + DBUG_ASSERT(context == ticket->ctx); - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED); + safe_mutex_assert_not_owner(&LOCK_open); - if (is_shared(lock_data)) + if (is_shared(ticket)) return; - lock= lock_data->lock; + lock= ticket->lock; pthread_mutex_lock(&LOCK_mdl); - lock->active_exclusive.remove(lock_data); - lock_data->type= MDL_SHARED_UPGRADABLE; - lock->active_shared.push_front(lock_data); + lock->type= MDL_LOCK::SHARED; + ticket->type= MDL_SHARED_UPGRADABLE; pthread_cond_broadcast(&COND_mdl); pthread_mutex_unlock(&LOCK_mdl); } @@ -1479,30 +1530,29 @@ void mdl_release_global_shared_lock(MDL_ FALSE otherwise. */ -bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, +bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, const char *db, const char *name) { - char key[MAX_MDLKEY_LENGTH]; - uint key_length; - MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); + MDL_KEY key; + MDL_LOCK_TICKET *ticket; + MDL_CONTEXT::Ticket_iterator it(context->tickets); - int4store(key, type); - key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; + key.mdl_key_init(type, db, name); - while ((lock_data= it++) && - (lock_data->key_length != key_length || - memcmp(lock_data->key, key, key_length) || - !(lock_data->type == MDL_EXCLUSIVE && - lock_data->state == MDL_ACQUIRED))) - continue; - return lock_data; + while ((ticket= it++)) + { + if (ticket->lock->type == MDL_LOCK::EXCLUSIVE && + ticket->lock->key.is_equal(&key)) + break; + } + + return ticket; } /** - Auxiliary function which allows to check if we some kind of lock on - the object. + Auxiliary function which allows to check if we have some kind of lock on + a object. @param context Current context @param type Id of object type @@ -1513,24 +1563,22 @@ bool mdl_is_exclusive_lock_owner(MDL_CON FALSE otherwise. */ -bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, - const char *name) +bool mdl_is_lock_owner(MDL_CONTEXT *context, unsigned char type, + const char *db, const char *name) { - char key[MAX_MDLKEY_LENGTH]; - uint key_length; - MDL_LOCK_DATA *lock_data; - I_P_List_iterator it(context->locks); - - int4store(key, type); - key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1; - - while ((lock_data= it++) && - (lock_data->key_length != key_length || - memcmp(lock_data->key, key, key_length) || - lock_data->state != MDL_ACQUIRED)) - continue; + MDL_KEY key; + MDL_LOCK_TICKET *ticket; + MDL_CONTEXT::Ticket_iterator it(context->tickets); + + key.mdl_key_init(type, db, name); - return lock_data; + while ((ticket= it++)) + { + if (ticket->lock->key.is_equal(&key)) + break; + } + + return ticket; } @@ -1538,21 +1586,20 @@ bool mdl_is_lock_owner(MDL_CONTEXT *cont Check if we have any pending exclusive locks which conflict with existing shared lock. - @param lock_data Shared lock against which check should be performed. + @param ticket Shared lock against which check should be performed. @return TRUE if there are any conflicting locks, FALSE otherwise. */ -bool mdl_has_pending_conflicting_lock(MDL_LOCK_DATA *lock_data) +bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket) { bool result; - DBUG_ASSERT(is_shared(lock_data) && lock_data->state == MDL_ACQUIRED); + DBUG_ASSERT(is_shared(ticket)); safe_mutex_assert_not_owner(&LOCK_open); pthread_mutex_lock(&LOCK_mdl); - result= !(lock_data->lock->waiting_exclusive.is_empty() && - lock_data->lock->active_shared_waiting_upgrade.is_empty()); + result= !ticket->lock->waiting.is_empty(); pthread_mutex_unlock(&LOCK_mdl); return result; } @@ -1561,8 +1608,8 @@ bool mdl_has_pending_conflicting_lock(MD /** Associate pointer to an opaque object with a lock. - @param lock_data Lock request for the lock with which the - object should be associated. + @param ticket Lock ticket for the lock with which the object + should be associated. @param cached_object Pointer to the object @param release_hook Cleanup function to be called when MDL subsystem decides to remove lock or associate another object. @@ -1586,27 +1633,24 @@ bool mdl_has_pending_conflicting_lock(MD lock on this name is released. */ -void mdl_set_cached_object(MDL_LOCK_DATA *lock_data, void *cached_object, +void mdl_set_cached_object(MDL_LOCK_TICKET *ticket, void *cached_object, mdl_cached_object_release_hook release_hook) { + MDL_LOCK *lock= ticket->lock; DBUG_ENTER("mdl_set_cached_object"); - DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", lock_data->key + 4, - lock_data->key + 4 + strlen(lock_data->key + 4) + 1, - cached_object)); - - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED || - lock_data->state == MDL_PENDING_UPGRADE); - + DBUG_PRINT("enter", ("db=%s name=%s cached_object=%p", + lock->key.db_name(), lock->key.table_name(), + cached_object)); /* TODO: This assumption works now since we do mdl_get_cached_object() and mdl_set_cached_object() in the same critical section. Once this becomes false we will have to call release_hook here and use additional mutex protecting 'cached_object' member. */ - DBUG_ASSERT(!lock_data->lock->cached_object); + DBUG_ASSERT(!lock->cached_object); - lock_data->lock->cached_object= cached_object; - lock_data->lock->cached_object_release_hook= release_hook; + lock->cached_object= cached_object; + lock->cached_object_release_hook= release_hook; DBUG_VOID_RETURN; } @@ -1615,15 +1659,46 @@ void mdl_set_cached_object(MDL_LOCK_DATA /** Get a pointer to an opaque object that associated with the lock. - @param lock_data Lock request for the lock with which the object is - associated. + @param ticket Lock ticket for the lock which the object is associated to. @return Pointer to an opaque object associated with the lock. */ -void* mdl_get_cached_object(MDL_LOCK_DATA *lock_data) +void* mdl_get_cached_object(MDL_LOCK_TICKET *ticket) +{ + return ticket->lock->cached_object; +} + + + +/** + Releases metadata locks that were acquired after a specific savepoint. + + @note Used to release tickets acquired during a savepoint unit. + @note It's safe to iterate and unlock any locks after taken after this + savepoint because other statements that take other special locks + cause a implicit commit (ie LOCK TABLES). + + @param thd Current thread + @param sv Savepoint +*/ + +void mdl_rollback_to_savepoint(MDL_CONTEXT *ctx, + MDL_LOCK_TICKET *mdl_savepoint) { - DBUG_ASSERT(lock_data->state == MDL_ACQUIRED || - lock_data->state == MDL_PENDING_UPGRADE); - return lock_data->lock->cached_object; + MDL_LOCK_TICKET *mdl_lock_ticket; + MDL_CONTEXT::Ticket_iterator it(ctx->tickets); + DBUG_ENTER("mdl_rollback_to_savepoint"); + + while ((mdl_lock_ticket= it++)) + { + /* Stop when lock was acquired before this savepoint. */ + if (mdl_lock_ticket == mdl_savepoint) + break; + mdl_ticket_release(ctx, mdl_lock_ticket); + } + + DBUG_VOID_RETURN; } + + === modified file 'sql/mdl.h' --- a/sql/mdl.h 2008-06-20 13:11:20 +0000 +++ b/sql/mdl.h 2009-03-02 21:18:26 +0000 @@ -23,7 +23,8 @@ class THD; -struct MDL_LOCK_DATA; +struct MDL_LOCK_REQUEST; +struct MDL_LOCK_TICKET; struct MDL_LOCK; struct MDL_CONTEXT; @@ -43,87 +44,147 @@ enum enum_mdl_type {MDL_SHARED=0, MDL_SH MDL_SHARED_UPGRADABLE, MDL_EXCLUSIVE}; -/** States which metadata lock request can have. */ +/** States which a metadata lock ticket can have. */ -enum enum_mdl_state {MDL_INITIALIZED=0, MDL_PENDING, - MDL_ACQUIRED, MDL_PENDING_UPGRADE}; +enum enum_mdl_state { MDL_PENDING, MDL_ACQUIRED }; + + +/** Maximal length of key for metadata locking subsystem. */ +#define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1) /** - A pending lock request or a granted metadata lock. A lock is requested - or granted based on a fully qualified name and type. E.g. for a table - the key consists of <0 (=table)>++. - Later in this document this triple will be referred to simply as - "key" or "name". + Metadata lock object key. + + A lock is requested or granted based on a fully qualified name and type. + E.g. They key for a table consists of <0 (=table)>++
. + Elsewhere in the comments this triple will be referred to simply as "key" + or "name". */ -struct MDL_LOCK_DATA +class MDL_KEY { - char *key; - uint key_length; - enum enum_mdl_type type; - enum enum_mdl_state state; +public: + const uchar *ptr() const { return (uchar*) m_ptr; } + uint length() const { return m_length; } + + const char *db_name() const { return m_ptr + 1; } + uint db_name_length() const { return m_db_name_length; } + + const char *table_name() const { return m_ptr + m_db_name_length + 2; } + uint table_name_length() const { return m_length - m_db_name_length - 3; } -private: - /** - Pointers for participating in the list of lock requests for this context. - */ - MDL_LOCK_DATA *next_context; - MDL_LOCK_DATA **prev_context; /** - Pointers for participating in the list of satisfied/pending requests - for the lock. - */ - MDL_LOCK_DATA *next_lock; - MDL_LOCK_DATA **prev_lock; + Construct a metadata lock key from a triplet (type, database and name). - friend struct MDL_LOCK_DATA_context; - friend struct MDL_LOCK_DATA_lock; + @remark The key for a table is <0 (=table)>++
-public: - /* - Pointer to the lock object for this lock request. Valid only if this lock - request is satisified or is present in the list of pending lock requests - for particular lock. + @param type Id of type of object to be locked + @param db Name of database to which the object belongs + @param name Name of of the object + @param key Where to store the the MDL key. */ - MDL_LOCK *lock; - MDL_CONTEXT *ctx; + void mdl_key_init(char type, const char *db, const char *name) + { + m_ptr[0]= type; + m_db_name_length= (uint) (strmov(m_ptr + 1, db) - m_ptr - 1); + m_length= (uint) (strmov(m_ptr + m_db_name_length + 2, name) - m_ptr + 1); + } + void mdl_key_init(const MDL_KEY *rhs) + { + memcpy(m_ptr, rhs->m_ptr, rhs->m_length); + m_length= rhs->m_length; + m_db_name_length= rhs->m_db_name_length; + } + bool is_equal(const MDL_KEY *rhs) const + { + return (m_length == rhs->m_length && + memcmp(m_ptr, rhs->m_ptr, m_length) == 0); + } +private: + char m_ptr[MAX_MDLKEY_LENGTH]; + uint m_length; + uint m_db_name_length; }; + /** - Helper class which specifies which members of MDL_LOCK_DATA are used for - participation in the list lock requests belonging to one context. + Hook class which via its methods specifies which members + of T should be used for participating in MDL lists. */ -struct MDL_LOCK_DATA_context +template +struct I_P_List_adapter { - static inline MDL_LOCK_DATA **next_ptr(MDL_LOCK_DATA *l) - { - return &l->next_context; - } - static inline MDL_LOCK_DATA ***prev_ptr(MDL_LOCK_DATA *l) - { - return &l->prev_context; - } + static inline T **next_ptr(T *el) { return &(el->*next); } + + static inline T ***prev_ptr(T *el) { return &(el->*prev); } }; /** - Helper class which specifies which members of MDL_LOCK_DATA are used for - participation in the list of satisfied/pending requests for the lock. + A pending metadata lock request. + A pending lock request or a granted metadata lock share the same abstract + base but are presented individually because they have different allocation + sites and hence different lifetimes. The allocation of lock requests is + controlled from outside of the MDL subsystem, while allocation of granted + locks (tickets) is controlled within the MDL subsystem. */ -struct MDL_LOCK_DATA_lock +struct MDL_LOCK_REQUEST { - static inline MDL_LOCK_DATA **next_ptr(MDL_LOCK_DATA *l) - { - return &l->next_lock; - } - static inline MDL_LOCK_DATA ***prev_ptr(MDL_LOCK_DATA *l) - { - return &l->prev_lock; - } + /** Type of metadata lock. */ + enum enum_mdl_type type; + + /** + Pointers for participating in the list of lock requests for this context. + */ + MDL_LOCK_REQUEST *next_in_context; + MDL_LOCK_REQUEST **prev_in_context; + /** A lock is requested based on a fully qualified name and type. */ + MDL_KEY key; + + /** + Pointer to the lock ticket object for this lock request. + Valid only if this lock request is satisfied. + */ + MDL_LOCK_TICKET *ticket; +}; + + +/** + A granted metadata lock. + + @warning MDL_LOCK_TICKET members are private to the MDL subsystem. + + @note Multiple shared locks on a same object are represented by a + single ticket. The same does not apply for other lock types. +*/ + +struct MDL_LOCK_TICKET +{ + /** Type of metadata lock. */ + enum enum_mdl_type type; + /** State of the metadata lock ticket. */ + enum enum_mdl_state state; + + /** + Pointers for participating in the list of lock requests for this context. + */ + MDL_LOCK_TICKET *next_in_context; + MDL_LOCK_TICKET **prev_in_context; + /** + Pointers for participating in the list of satisfied/pending requests + for the lock. + */ + MDL_LOCK_TICKET *next_in_lock; + MDL_LOCK_TICKET **prev_in_lock; + /** Context of the owner of the metadata lock ticket. */ + MDL_CONTEXT *ctx; + + /** Pointer to the lock object for this lock ticket. */ + MDL_LOCK *lock; }; @@ -134,7 +195,24 @@ struct MDL_LOCK_DATA_lock struct MDL_CONTEXT { - I_P_List locks; + typedef I_P_List > + Request_list; + + typedef Request_list::Iterator Request_iterator; + + typedef I_P_List > + Ticket_list; + + typedef Ticket_list::Iterator Ticket_iterator; + + Request_list requests; + Ticket_list tickets; bool has_global_shared_lock; THD *thd; }; @@ -149,96 +227,80 @@ void mdl_context_backup_and_reset(MDL_CO void mdl_context_restore(MDL_CONTEXT *ctx, MDL_CONTEXT *backup); void mdl_context_merge(MDL_CONTEXT *target, MDL_CONTEXT *source); -/** Maximal length of key for metadata locking subsystem. */ -#define MAX_MDLKEY_LENGTH (4 + NAME_LEN + 1 + NAME_LEN + 1) - -void mdl_init_lock(MDL_LOCK_DATA *lock_data, char *key, int type, - const char *db, const char *name); -MDL_LOCK_DATA *mdl_alloc_lock(int type, const char *db, const char *name, - MEM_ROOT *root); -void mdl_add_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); -void mdl_remove_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); -void mdl_remove_all_locks(MDL_CONTEXT *context); +void mdl_request_init(MDL_LOCK_REQUEST *lock_req, unsigned char type, + const char *db, const char *name); +MDL_LOCK_REQUEST *mdl_request_alloc(unsigned char type, const char *db, + const char *name, MEM_ROOT *root); +void mdl_request_add(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req); +void mdl_request_remove(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req); +void mdl_request_remove_all(MDL_CONTEXT *context); /** Set type of lock request. Can be only applied to pending locks. */ -inline void mdl_set_lock_type(MDL_LOCK_DATA *lock_data, enum_mdl_type lock_type) +inline void mdl_request_set_type(MDL_LOCK_REQUEST *lock_req, enum_mdl_type lock_type) { - DBUG_ASSERT(lock_data->state == MDL_INITIALIZED); - lock_data->type= lock_type; + DBUG_ASSERT(lock_req->ticket == NULL); + lock_req->type= lock_type; } -bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data, +bool mdl_acquire_shared_lock(MDL_CONTEXT *context, MDL_LOCK_REQUEST *lock_req, bool *retry); bool mdl_acquire_exclusive_locks(MDL_CONTEXT *context); bool mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); + MDL_LOCK_TICKET *ticket); bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data, + MDL_LOCK_REQUEST *lock_req, bool *conflict); bool mdl_acquire_global_shared_lock(MDL_CONTEXT *context); bool mdl_wait_for_locks(MDL_CONTEXT *context); -void mdl_release_locks(MDL_CONTEXT *context); -void mdl_release_and_remove_all_locks_for_name(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); -void mdl_release_lock(MDL_CONTEXT *context, MDL_LOCK_DATA *lock_data); +void mdl_ticket_release_all(MDL_CONTEXT *context); +void mdl_ticket_release_all_for_name(MDL_CONTEXT *context, + MDL_LOCK_TICKET *ticket); +void mdl_ticket_release(MDL_CONTEXT *context, MDL_LOCK_TICKET *ticket); void mdl_downgrade_exclusive_lock(MDL_CONTEXT *context, - MDL_LOCK_DATA *lock_data); + MDL_LOCK_TICKET *ticket); void mdl_release_global_shared_lock(MDL_CONTEXT *context); -bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, const char *db, - const char *name); -bool mdl_is_lock_owner(MDL_CONTEXT *context, int type, const char *db, - const char *name); +bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, unsigned char type, + const char *db, const char *name); +bool mdl_is_lock_owner(MDL_CONTEXT *context, unsigned char type, + const char *db, const char *name); -bool mdl_has_pending_conflicting_lock(MDL_LOCK_DATA *lock_data); +bool mdl_has_pending_conflicting_lock(MDL_LOCK_TICKET *ticket); inline bool mdl_has_locks(MDL_CONTEXT *context) { - return !context->locks.is_empty(); + return !context->tickets.is_empty(); } - -/** - Get iterator for walking through all lock requests in the context. -*/ - -inline I_P_List_iterator -mdl_get_locks(MDL_CONTEXT *ctx) +inline MDL_LOCK_TICKET *mdl_savepoint(MDL_CONTEXT *ctx) { - I_P_List_iterator result(ctx->locks); - return result; + return ctx->tickets.head(); } -/** - Give metadata lock request object for the table get table definition - cache key corresponding to it. - - @param lock_data [in] Lock request object for the table. - @param key [out] LEX_STRING object where table definition cache key - should be put. +void mdl_rollback_to_savepoint(MDL_CONTEXT *ctx, + MDL_LOCK_TICKET *mdl_savepoint); - @note This key will have the same life-time as this lock request object. - - @note This is yet another place where border between MDL subsystem and the - rest of the server is broken. OTOH it allows to save some CPU cycles - and memory by avoiding generating these TDC keys from table list. +/** + Get iterator for walking through all lock requests in the context. */ -inline void mdl_get_tdc_key(MDL_LOCK_DATA *lock_data, LEX_STRING *key) +inline MDL_CONTEXT::Request_iterator +mdl_get_requests(MDL_CONTEXT *ctx) { - key->str= lock_data->key + 4; - key->length= lock_data->key_length - 4; + MDL_CONTEXT::Request_iterator result(ctx->requests); + return result; } +void mdl_get_tdc_key(MDL_LOCK_TICKET *ticket, LEX_STRING *key); typedef void (* mdl_cached_object_release_hook)(void *); -void* mdl_get_cached_object(MDL_LOCK_DATA *lock_data); -void mdl_set_cached_object(MDL_LOCK_DATA *lock_data, void *cached_object, +void *mdl_get_cached_object(MDL_LOCK_TICKET *ticket); +void mdl_set_cached_object(MDL_LOCK_TICKET *ticket, void *cached_object, mdl_cached_object_release_hook release_hook); === modified file 'sql/si_objects.cc' --- a/sql/si_objects.cc 2009-02-04 10:49:16 +0000 +++ b/sql/si_objects.cc 2009-03-02 21:18:26 +0000 @@ -223,33 +223,7 @@ void copy_warnings(THD *thd, Listkey.length; - return (uchar *) table_name_key->key.str; + *key_length= table_name_key->length(); + return (uchar*) table_name_key->ptr(); } /////////////////////////////////////////////////////////////////////////// @@ -1579,21 +1553,16 @@ Find_view_underlying_tables::execute_ser while ((table= it++)) { Table_name_key *table_name_key; - char *key_buff; /* If we expect a view, and it's a table, or vice versa, continue */ if ((int) m_base_obj_it->get_base_obj_kind() != test(table->view)) continue; if (! my_multi_malloc(MYF(MY_WME), - &table_name_key, sizeof(*table_name_key), - &key_buff, table->mdl_lock_data->key_length, - NullS)) + &table_name_key, sizeof(*table_name_key), NullS)) goto end; - table_name_key->init_from_mdl_key(table->mdl_lock_data->key, - table->mdl_lock_data->key_length, - key_buff); + table_name_key->mdl_key_init(&table->mdl_lock_request->key); if (my_hash_insert(m_table_names, (uchar*) table_name_key)) { @@ -1627,6 +1596,8 @@ bool View_base_obj_iterator::init(THD *t Obj *View_base_obj_iterator::next() { + LEX_STRING db_name; + LEX_STRING table_name; if (m_cur_idx >= m_table_names.records) return NULL; @@ -1635,7 +1606,13 @@ Obj *View_base_obj_iterator::next() ++m_cur_idx; - return create_obj(&table_name_key->db_name, &table_name_key->table_name); + db_name.str= (char*) table_name_key->db_name(); + db_name.length= table_name_key->db_name_length(); + + table_name.str= (char*) table_name_key->table_name(); + table_name.length= table_name_key->table_name_length(); + + return create_obj(&db_name, &table_name); } /////////////////////////////////////////////////////////////////////////// === modified file 'sql/sp_head.cc' --- a/sql/sp_head.cc 2009-02-13 16:30:54 +0000 +++ b/sql/sp_head.cc 2009-03-02 21:18:26 +0000 @@ -3969,10 +3969,10 @@ sp_head::add_used_tables_to_table_list(T table->prelocking_placeholder= 1; table->belong_to_view= belong_to_view; table->trg_event_map= stab->trg_event_map; - table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name, - thd->locked_tables_root ? - thd->locked_tables_root : - thd->mem_root); + table->mdl_lock_request= mdl_request_alloc(0, table->db, table->table_name, + thd->locked_tables_root ? + thd->locked_tables_root : + thd->mem_root); /* Everyting else should be zeroed */ @@ -4016,9 +4016,10 @@ sp_add_to_query_tables(THD *thd, LEX *le table->lock_transactional= 1; /* allow transactional locks */ table->select_lex= lex->current_select; table->cacheable_table= 1; - table->mdl_lock_data= mdl_alloc_lock(0, table->db, table->table_name, - thd->locked_tables_root ? - thd->locked_tables_root : thd->mem_root); + table->mdl_lock_request= mdl_request_alloc(0, table->db, table->table_name, + thd->locked_tables_root ? + thd->locked_tables_root : + thd->mem_root); lex->add_to_query_tables(table); return table; === modified file 'sql/sql_acl.cc' --- a/sql/sql_acl.cc 2009-02-18 10:23:38 +0000 +++ b/sql/sql_acl.cc 2009-03-02 21:18:26 +0000 @@ -693,7 +693,7 @@ my_bool acl_reload(THD *thd) tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; tables[0].skip_temporary= tables[1].skip_temporary= tables[2].skip_temporary= TRUE; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); if (simple_open_n_lock_tables(thd, tables)) { @@ -1591,7 +1591,7 @@ bool change_password(THD *thd, const cha bzero((char*) &tables, sizeof(tables)); tables.alias= tables.table_name= (char*) "user"; tables.db= (char*) "mysql"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); #ifdef HAVE_REPLICATION /* @@ -3027,7 +3027,7 @@ int mysql_table_grant(THD *thd, TABLE_LI ? tables+2 : 0); tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_WRITE; tables[0].db=tables[1].db=tables[2].db=(char*) "mysql"; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); /* This statement will be replicated as a statement, even when using @@ -3249,7 +3249,7 @@ bool mysql_routine_grant(THD *thd, TABLE tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); /* This statement will be replicated as a statement, even when using @@ -3391,7 +3391,7 @@ bool mysql_grant(THD *thd, const char *d tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type=tables[1].lock_type=TL_WRITE; tables[0].db=tables[1].db=(char*) "mysql"; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); /* This statement will be replicated as a statement, even when using @@ -3724,7 +3724,7 @@ static my_bool grant_reload_procs_priv(T table.db= (char *) "mysql"; table.lock_type= TL_READ; table.skip_temporary= 1; - alloc_mdl_locks(&table, thd->mem_root); + alloc_mdl_requests(&table, thd->mem_root); if (simple_open_n_lock_tables(thd, &table)) { @@ -3791,7 +3791,7 @@ my_bool grant_reload(THD *thd) tables[0].next_local= tables[0].next_global= tables+1; tables[0].lock_type= tables[1].lock_type= TL_READ; tables[0].skip_temporary= tables[1].skip_temporary= TRUE; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); /* To avoid deadlocks we should obtain table locks before @@ -5137,7 +5137,7 @@ int open_grant_tables(THD *thd, TABLE_LI (tables+4)->lock_type= TL_WRITE; tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db= (tables+4)->db= (char*) "mysql"; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); #ifdef HAVE_REPLICATION /* === modified file 'sql/sql_base.cc' --- a/sql/sql_base.cc 2009-02-16 21:18:45 +0000 +++ b/sql/sql_base.cc 2009-03-02 21:18:26 +0000 @@ -1042,7 +1042,7 @@ err_with_reopen: than picking only those tables that were flushed. */ for (TABLE *tab= thd->open_tables; tab; tab= tab->next) - mdl_downgrade_exclusive_lock(&thd->mdl_context, tab->mdl_lock_data); + mdl_downgrade_exclusive_lock(&thd->mdl_context, tab->mdl_lock_ticket); } DBUG_RETURN(result); } @@ -1469,10 +1469,10 @@ void close_thread_tables(THD *thd, if (thd->open_tables) close_open_tables(thd); - mdl_release_locks(&thd->mdl_context); + mdl_ticket_release_all(&thd->mdl_context); if (!skip_mdl) { - mdl_remove_all_locks(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); } DBUG_VOID_RETURN; } @@ -1491,7 +1491,7 @@ bool close_thread_table(THD *thd, TABLE *table_ptr=table->next; - table->mdl_lock_data= 0; + table->mdl_lock_ticket= NULL; if (table->needs_reopen() || thd->version != refresh_version || !table->db_stat) { @@ -2079,7 +2079,7 @@ bool wait_while_table_is_used(THD *thd, mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */ if (mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, - table->mdl_lock_data)) + table->mdl_lock_ticket)) { mysql_lock_downgrade_write(thd, table, old_lock_type); DBUG_RETURN(TRUE); @@ -2267,11 +2267,11 @@ void table_share_release_hook(void *shar static bool open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list, - MDL_LOCK_DATA *mdl_lock_data, + MDL_LOCK_REQUEST *mdl_lock_request, uint flags, enum_open_table_action *action) { - mdl_add_lock(&thd->mdl_context, mdl_lock_data); + mdl_request_add(&thd->mdl_context, mdl_lock_request); if (table_list->open_type) { @@ -2284,10 +2284,10 @@ open_table_get_mdl_lock(THD *thd, TABLE_ shared locks. This invariant is preserved here and is also enforced by asserts in metadata locking subsystem. */ - mdl_set_lock_type(mdl_lock_data, MDL_EXCLUSIVE); + mdl_request_set_type(mdl_lock_request, MDL_EXCLUSIVE); 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); return 1; } } @@ -2304,16 +2304,16 @@ open_table_get_mdl_lock(THD *thd, TABLE_ if (flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL && table_list->lock_type >= TL_WRITE_ALLOW_WRITE) - mdl_set_lock_type(mdl_lock_data, MDL_SHARED_UPGRADABLE); + mdl_request_set_type(mdl_lock_request, MDL_SHARED_UPGRADABLE); if (flags & MYSQL_LOCK_IGNORE_FLUSH) - mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO); + mdl_request_set_type(mdl_lock_request, MDL_SHARED_HIGH_PRIO); - if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_data, &retry)) + if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_request, &retry)) { if (retry) *action= OT_BACK_OFF_AND_RETRY; else - mdl_remove_lock(&thd->mdl_context, mdl_lock_data); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); return 1; } } @@ -2368,7 +2368,8 @@ bool open_table(THD *thd, TABLE_LIST *ta char key[MAX_DBKEY_LENGTH]; uint key_length; char *alias= table_list->alias; - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_REQUEST *mdl_lock_request; + MDL_LOCK_TICKET *mdl_lock_ticket; int error; TABLE_SHARE *share; DBUG_ENTER("open_table"); @@ -2560,10 +2561,10 @@ bool open_table(THD *thd, TABLE_LIST *ta This is the normal use case. */ - mdl_lock_data= table_list->mdl_lock_data; + mdl_lock_request= table_list->mdl_lock_request; if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) { - if (open_table_get_mdl_lock(thd, table_list, mdl_lock_data, flags, + if (open_table_get_mdl_lock(thd, table_list, mdl_lock_request, flags, action)) { DEBUG_SYNC(thd, "before_open_table_wait_refresh"); @@ -2571,6 +2572,13 @@ bool open_table(THD *thd, TABLE_LIST *ta } } + /* + Grab reference to the granted MDL lock ticket. Must be done after + open_table_get_mdl_lock as the lock on the table might have been + acquired previously (MYSQL_OPEN_HAS_MDL_LOCK). + */ + mdl_lock_ticket= mdl_lock_request->ticket; + pthread_mutex_lock(&LOCK_open); /* @@ -2612,7 +2620,7 @@ bool open_table(THD *thd, TABLE_LIST *ta DBUG_RETURN(FALSE); } - if (!(share= (TABLE_SHARE *)mdl_get_cached_object(mdl_lock_data))) + if (!(share= (TABLE_SHARE *)mdl_get_cached_object(mdl_lock_ticket))) { if (!(share= get_table_share_with_create(thd, table_list, key, key_length, OPEN_VIEW, @@ -2673,7 +2681,7 @@ bool open_table(THD *thd, TABLE_LIST *ta so we need to increase reference counter; */ reference_table_share(share); - mdl_set_cached_object(mdl_lock_data, share, table_share_release_hook); + mdl_set_cached_object(mdl_lock_ticket, share, table_share_release_hook); } else { @@ -2782,9 +2790,9 @@ bool open_table(THD *thd, TABLE_LIST *ta lock on this table to shared metadata lock. */ if (table_list->open_type == TABLE_LIST::OPEN_OR_CREATE) - mdl_downgrade_exclusive_lock(&thd->mdl_context, table_list->mdl_lock_data); + mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket); - table->mdl_lock_data= mdl_lock_data; + table->mdl_lock_ticket= mdl_lock_ticket; table->next=thd->open_tables; /* Link into simple list */ thd->open_tables=table; @@ -2834,8 +2842,8 @@ err_unlock2: pthread_mutex_unlock(&LOCK_open); if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK)) { - 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_ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); } DBUG_RETURN(TRUE); } @@ -2953,7 +2961,7 @@ Locked_tables_list::init_locked_tables(T dst_table_list->init_one_table(db, db_len, table_name, table_name_len, alias, src_table_list->table->reginfo.lock_type); - dst_table_list->mdl_lock_data= src_table_list->mdl_lock_data; + dst_table_list->mdl_lock_request= src_table_list->mdl_lock_request; dst_table_list->table= table; memcpy(db, src_table_list->db, db_len + 1); memcpy(table_name, src_table_list->table_name, table_name_len + 1); @@ -3004,6 +3012,8 @@ Locked_tables_list::unlock_locked_tables thd->locked_tables_mode= LTM_NONE; close_thread_tables(thd); + + mdl_ticket_release_all(&thd->mdl_context); } /* After closing tables we can free memory used for storing lock @@ -3485,20 +3495,21 @@ recover_from_failed_open_table_attempt(T enum_open_table_action action) { bool result= FALSE; + MDL_LOCK_REQUEST *mdl_lock_request= table->mdl_lock_request; switch (action) { case OT_BACK_OFF_AND_RETRY: result= (mdl_wait_for_locks(&thd->mdl_context) || tdc_wait_for_old_versions(thd, &thd->mdl_context)); - mdl_remove_all_locks(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); break; case OT_DISCOVER: - mdl_set_lock_type(table->mdl_lock_data, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, table->mdl_lock_data); + 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, table->mdl_lock_data); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); return TRUE; } pthread_mutex_lock(&LOCK_open); @@ -3508,15 +3519,15 @@ recover_from_failed_open_table_attempt(T thd->warning_info->clear_warning_info(thd->query_id); thd->clear_error(); // Clear error message - mdl_release_lock(&thd->mdl_context, table->mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, table->mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); break; case OT_REPAIR: - mdl_set_lock_type(table->mdl_lock_data, MDL_EXCLUSIVE); - mdl_add_lock(&thd->mdl_context, table->mdl_lock_data); + 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, table->mdl_lock_data); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); return TRUE; } pthread_mutex_lock(&LOCK_open); @@ -3524,8 +3535,8 @@ recover_from_failed_open_table_attempt(T pthread_mutex_unlock(&LOCK_open); result= auto_repair_table(thd, table); - mdl_release_lock(&thd->mdl_context, table->mdl_lock_data); - mdl_remove_lock(&thd->mdl_context, table->mdl_lock_data); + mdl_ticket_release(&thd->mdl_context, mdl_lock_request->ticket); + mdl_request_remove(&thd->mdl_context, mdl_lock_request); break; default: DBUG_ASSERT(0); @@ -7695,10 +7706,9 @@ void tdc_remove_table(THD *thd, enum_tdc static bool tdc_wait_for_old_versions(THD *thd, MDL_CONTEXT *context) { - MDL_LOCK_DATA *lock_data; TABLE_SHARE *share; const char *old_msg; - LEX_STRING key; + MDL_LOCK_REQUEST *lock_req; while (!thd->killed) { @@ -7711,18 +7721,15 @@ static bool tdc_wait_for_old_versions(TH mysql_ha_flush(thd); pthread_mutex_lock(&LOCK_open); - I_P_List_iterator it= mdl_get_locks(context); - while ((lock_data= it++)) - { - mdl_get_tdc_key(lock_data, &key); - if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache, - (uchar*) key.str, - key.length)) && + MDL_CONTEXT::Request_iterator it= mdl_get_requests(context); + while ((lock_req= it++)) + { + if ((share= get_cached_table_share(lock_req->key.db_name(), + lock_req->key.table_name())) && share->version != refresh_version) break; } - if (!lock_data) + if (!lock_req) { pthread_mutex_unlock(&LOCK_open); break; @@ -7952,7 +7959,7 @@ open_system_tables_for_read(THD *thd, TA DBUG_ENTER("open_system_tables_for_read"); - alloc_mdl_locks(table_list, thd->mem_root); + alloc_mdl_requests(table_list, thd->mem_root); /* Besides using new Open_tables_state for opening system tables, @@ -8027,7 +8034,7 @@ open_system_table_for_update(THD *thd, T { DBUG_ENTER("open_system_table_for_update"); - alloc_mdl_locks(one_table, thd->mem_root); + alloc_mdl_requests(one_table, thd->mem_root); TABLE *table= open_ltable(thd, one_table, one_table->lock_type, 0); if (table) @@ -8065,7 +8072,7 @@ open_performance_schema_table(THD *thd, thd->reset_n_backup_open_tables_state(backup); - alloc_mdl_locks(one_table, thd->mem_root); + alloc_mdl_requests(one_table, thd->mem_root); if ((table= open_ltable(thd, one_table, one_table->lock_type, flags))) { DBUG_ASSERT(table->s->table_category == TABLE_CATEGORY_PERFORMANCE); @@ -8142,8 +8149,8 @@ void close_performance_schema_table(THD pthread_mutex_unlock(&LOCK_open); - mdl_release_locks(&thd->mdl_context); - mdl_remove_all_locks(&thd->mdl_context); + mdl_ticket_release_all(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); thd->restore_backup_open_tables_state(backup); } === modified file 'sql/sql_class.h' --- a/sql/sql_class.h 2009-02-13 16:30:54 +0000 +++ b/sql/sql_class.h 2009-03-02 21:18:26 +0000 @@ -763,6 +763,8 @@ struct st_savepoint { char *name; uint length; Ha_trx_info *ha_list; + /** Last acquired lock before this savepoint was set. */ + MDL_LOCK_TICKET *mdl_savepoint; }; enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY}; === modified file 'sql/sql_delete.cc' --- a/sql/sql_delete.cc 2009-02-16 21:18:45 +0000 +++ b/sql/sql_delete.cc 2009-03-02 21:18:26 +0000 @@ -1035,7 +1035,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST TABLE *table; bool error; uint path_length; - MDL_LOCK_DATA *mdl_lock_data= 0; + MDL_LOCK_REQUEST *mdl_lock_request= NULL; Ha_global_schema_lock_guard global_schema_lock_guard(thd); DBUG_ENTER("mysql_truncate"); @@ -1100,13 +1100,13 @@ bool mysql_truncate(THD *thd, TABLE_LIST tries to get table enging and therefore accesses table in some way without holding any kind of meta-data lock. */ - 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(TRUE); } pthread_mutex_lock(&LOCK_open); @@ -1139,18 +1139,18 @@ end: write_bin_log(thd, TRUE, thd->query, thd->query_length); my_ok(thd); // This should return record count } - if (mdl_lock_data) + if (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); } } else if (error) { - if (mdl_lock_data) + if (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); === modified file 'sql/sql_handler.cc' --- a/sql/sql_handler.cc 2009-01-27 02:08:48 +0000 +++ b/sql/sql_handler.cc 2009-03-02 21:18:26 +0000 @@ -125,7 +125,7 @@ static void mysql_ha_hash_free(TABLE_LIS static void mysql_ha_close_table(THD *thd, TABLE_LIST *tables) { TABLE **table_ptr; - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_TICKET *mdl_lock_ticket; /* Though we could take the table pointer from hash_tables->table, @@ -141,7 +141,7 @@ static void mysql_ha_close_table(THD *th if (*table_ptr) { (*table_ptr)->file->ha_index_or_rnd_end(); - mdl_lock_data= (*table_ptr)->mdl_lock_data; + mdl_lock_ticket= (*table_ptr)->mdl_lock_ticket; pthread_mutex_lock(&LOCK_open); if (close_thread_table(thd, table_ptr)) { @@ -149,8 +149,8 @@ static void mysql_ha_close_table(THD *th broadcast_refresh(); } pthread_mutex_unlock(&LOCK_open); - mdl_release_lock(&thd->handler_mdl_context, mdl_lock_data); - mdl_remove_lock(&thd->handler_mdl_context, mdl_lock_data); + mdl_ticket_release(&thd->handler_mdl_context, mdl_lock_ticket); + mdl_request_remove(&thd->handler_mdl_context, tables->mdl_lock_request); } else if (tables->table) { @@ -188,12 +188,12 @@ static void mysql_ha_close_table(THD *th bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) { TABLE_LIST *hash_tables = NULL; - MDL_LOCK_DATA *mdl_lock_data; - char *db, *name, *alias, *mdlkey; + char *db, *name, *alias; uint dblen, namelen, aliaslen, counter; int error; TABLE *backup_open_tables; MDL_CONTEXT backup_mdl_context; + MDL_LOCK_REQUEST *mdl_lock_request; DBUG_ENTER("mysql_ha_open"); DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", tables->db, tables->table_name, tables->alias, @@ -236,8 +236,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST &db, (uint) dblen, &name, (uint) namelen, &alias, (uint) aliaslen, - &mdl_lock_data, sizeof(MDL_LOCK_DATA), - &mdlkey, MAX_MDLKEY_LENGTH, + &mdl_lock_request, sizeof(MDL_LOCK_REQUEST), NullS))) { DBUG_PRINT("exit",("ERROR")); @@ -251,8 +250,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST memcpy(hash_tables->db, tables->db, dblen); memcpy(hash_tables->table_name, tables->table_name, namelen); memcpy(hash_tables->alias, tables->alias, aliaslen); - mdl_init_lock(mdl_lock_data, mdlkey, 0, db, name); - hash_tables->mdl_lock_data= mdl_lock_data; + mdl_request_init(mdl_lock_request, 0, db, name); + hash_tables->mdl_lock_request= mdl_lock_request; /* add to hash */ if (my_hash_insert(&thd->handler_tables_hash, (uchar*) hash_tables)) @@ -500,7 +499,7 @@ retry: if (need_reopen) { - mysql_ha_close_table(thd, tables); + mysql_ha_close_table(thd, hash_tables); hash_tables->table= NULL; /* The lock might have been aborted, we need to manually reset @@ -774,11 +773,11 @@ void mysql_ha_flush(THD *thd) { hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); /* - TABLE::mdl_lock_data is 0 for temporary tables so we need extra check. + TABLE::mdl_lock_ticket is 0 for temporary tables so we need extra check. */ if (hash_tables->table && - (hash_tables->table->mdl_lock_data && - mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock_data) || + (hash_tables->table->mdl_lock_ticket && + mdl_has_pending_conflicting_lock(hash_tables->table->mdl_lock_ticket) || hash_tables->table->needs_reopen())) { mysql_ha_close_table(thd, hash_tables); === modified file 'sql/sql_insert.cc' --- a/sql/sql_insert.cc 2009-02-16 21:18:45 +0000 +++ b/sql/sql_insert.cc 2009-03-02 21:18:26 +0000 @@ -2382,7 +2382,7 @@ pthread_handler_t handle_delayed_insert( thd->lex->set_stmt_unsafe(); thd->set_current_stmt_binlog_row_based_if_mixed(); - alloc_mdl_locks(&di->table_list, thd->mem_root); + alloc_mdl_requests(&di->table_list, thd->mem_root); if (di->open_and_lock_table()) goto err; === modified file 'sql/sql_parse.cc' --- a/sql/sql_parse.cc 2009-02-16 21:18:45 +0000 +++ b/sql/sql_parse.cc 2009-03-02 21:18:26 +0000 @@ -1124,7 +1124,7 @@ bool dispatch_command(enum enum_server_c select_lex.table_list.link_in_list((uchar*) &table_list, (uchar**) &table_list.next_local); thd->lex->add_to_query_tables(&table_list); - alloc_mdl_locks(&table_list, thd->mem_root); + alloc_mdl_requests(&table_list, thd->mem_root); /* switch on VIEW optimisation: do not fill temporary tables */ thd->lex->sql_command= SQLCOM_SHOW_FIELDS; @@ -3510,7 +3510,7 @@ ddl_blocker_err: if (trans_commit_implicit(thd)) goto error; - alloc_mdl_locks(all_tables, thd->locked_tables_list.locked_tables_root()); + alloc_mdl_requests(all_tables, thd->locked_tables_list.locked_tables_root()); thd->options|= OPTION_TABLE_LOCK; thd->in_lock_tables=1; @@ -6131,9 +6131,9 @@ TABLE_LIST *st_select_lex::add_table_to_ ptr->next_name_resolution_table= NULL; /* Link table in global list (all used tables) */ lex->add_to_query_tables(ptr); - ptr->mdl_lock_data= mdl_alloc_lock(0 , ptr->db, ptr->table_name, - thd->locked_tables_root ? - thd->locked_tables_root : thd->mem_root); + ptr->mdl_lock_request= + mdl_request_alloc(0, ptr->db, ptr->table_name, thd->locked_tables_root ? + thd->locked_tables_root : thd->mem_root); DBUG_RETURN(ptr); } === modified file 'sql/sql_plist.h' --- a/sql/sql_plist.h 2008-05-27 09:45:34 +0000 +++ b/sql/sql_plist.h 2009-03-02 21:18:26 +0000 @@ -90,6 +90,7 @@ public: #ifndef _lint friend class I_P_List_iterator; #endif + typedef I_P_List_iterator Iterator; }; === modified file 'sql/sql_plugin.cc' --- a/sql/sql_plugin.cc 2009-02-16 21:18:45 +0000 +++ b/sql/sql_plugin.cc 2009-03-02 21:18:26 +0000 @@ -1343,7 +1343,7 @@ static void plugin_load(MEM_ROOT *tmp_ro tables.alias= tables.table_name= (char*)"plugin"; tables.lock_type= TL_READ; tables.db= new_thd->db; - alloc_mdl_locks(&tables, tmp_root); + alloc_mdl_requests(&tables, tmp_root); #ifdef EMBEDDED_LIBRARY /* @@ -1644,7 +1644,7 @@ bool mysql_install_plugin(THD *thd, cons if (check_table_access(thd, INSERT_ACL, &tables, FALSE, FALSE, 1)) DBUG_RETURN(TRUE); - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); /* need to open before acquiring LOCK_plugin or it will deadlock */ if (! (table = open_ltable(thd, &tables, TL_WRITE, 0))) @@ -1709,7 +1709,7 @@ bool mysql_uninstall_plugin(THD *thd, co bzero(&tables, sizeof(tables)); tables.db= (char *)"mysql"; tables.table_name= tables.alias= (char *)"plugin"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); /* need to open before acquiring LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) === modified file 'sql/sql_servers.cc' --- a/sql/sql_servers.cc 2009-01-27 02:08:48 +0000 +++ b/sql/sql_servers.cc 2009-03-02 21:18:26 +0000 @@ -233,7 +233,7 @@ bool servers_reload(THD *thd) tables[0].alias= tables[0].table_name= (char*) "servers"; tables[0].db= (char*) "mysql"; tables[0].lock_type= TL_READ; - alloc_mdl_locks(tables, thd->mem_root); + alloc_mdl_requests(tables, thd->mem_root); if (simple_open_n_lock_tables(thd, tables)) { @@ -364,7 +364,7 @@ insert_server(THD *thd, FOREIGN_SERVER * bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; tables.alias= tables.table_name= (char*) "servers"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); /* need to open before acquiring THR_LOCK_plugin or it will deadlock */ if (! (table= open_ltable(thd, &tables, TL_WRITE, 0))) @@ -583,7 +583,7 @@ int drop_server(THD *thd, LEX_SERVER_OPT bzero((char*) &tables, sizeof(tables)); tables.db= (char*) "mysql"; tables.alias= tables.table_name= (char*) "servers"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); rw_wrlock(&THR_LOCK_servers); @@ -708,7 +708,7 @@ int update_server(THD *thd, FOREIGN_SERV bzero((char*) &tables, sizeof(tables)); tables.db= (char*)"mysql"; tables.alias= tables.table_name= (char*)"servers"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (!(table= open_ltable(thd, &tables, TL_WRITE, 0))) { === modified file 'sql/sql_show.cc' --- a/sql/sql_show.cc 2009-02-18 12:57:37 +0000 +++ b/sql/sql_show.cc 2009-03-02 21:18:26 +0000 @@ -3088,12 +3088,8 @@ uint get_table_open_method(TABLE_LIST *t Acquire high priority share metadata lock on a table. @param thd Thread context. - @param mdl_lock_data Pointer to memory to be used for MDL_LOCK_DATA + @param mdl_lock_req Pointer to memory to be used for MDL_LOCK_REQUEST object for a lock request. - @param mdlkey Pointer to the buffer for key for the lock request - (should be at least strlen(db) + strlen(name) + 2 - bytes, or, if the lengths are not known, - MAX_MDLKEY_LENGTH) @param table Table list element for the table @note This is an auxiliary function to be used in cases when we want to @@ -3107,23 +3103,23 @@ uint get_table_open_method(TABLE_LIST *t */ static bool -acquire_high_prio_shared_mdl_lock(THD *thd, MDL_LOCK_DATA *mdl_lock_data, - char *mdlkey, TABLE_LIST *table) +acquire_high_prio_shared_mdl_lock(THD *thd, MDL_LOCK_REQUEST *mdl_lock_req, + TABLE_LIST *table) { bool retry; - mdl_init_lock(mdl_lock_data, mdlkey, 0, table->db, table->table_name); - table->mdl_lock_data= mdl_lock_data; - mdl_add_lock(&thd->mdl_context, mdl_lock_data); - mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO); + mdl_request_init(mdl_lock_req, 0, table->db, table->table_name); + table->mdl_lock_request= mdl_lock_req; + mdl_request_add(&thd->mdl_context, mdl_lock_req); + mdl_request_set_type(mdl_lock_req, MDL_SHARED_HIGH_PRIO); while (1) { - if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_data, &retry)) + if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_req, &retry)) { if (!retry || mdl_wait_for_locks(&thd->mdl_context)) { - mdl_remove_all_locks(&thd->mdl_context); + mdl_request_remove_all(&thd->mdl_context); return TRUE; } continue; @@ -3165,8 +3161,7 @@ static int fill_schema_table_from_frm(TH int error; char key[MAX_DBKEY_LENGTH]; uint key_length; - MDL_LOCK_DATA mdl_lock_data; - char mdlkey[MAX_MDLKEY_LENGTH]; + MDL_LOCK_REQUEST mdl_lock_request; bzero((char*) &table_list, sizeof(TABLE_LIST)); bzero((char*) &tbl, sizeof(TABLE)); @@ -3179,8 +3174,7 @@ static int fill_schema_table_from_frm(TH simply obtaining internal lock of data-dictionary (ATM it is LOCK_open) instead of obtaning full-blown metadata lock. */ - if (acquire_high_prio_shared_mdl_lock(thd, &mdl_lock_data, mdlkey, - &table_list)) + if (acquire_high_prio_shared_mdl_lock(thd, &mdl_lock_request, &table_list)) { /* Some error occured (most probably we have been killed while @@ -3264,8 +3258,8 @@ err_unlock: pthread_mutex_unlock(&LOCK_open); err: - 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); thd->clear_error(); return res; } @@ -7572,7 +7566,7 @@ bool show_create_trigger(THD *thd, const uint num_tables; /* NOTE: unused, only to pass to open_tables(). */ - alloc_mdl_locks(lst, thd->mem_root); + alloc_mdl_requests(lst, thd->mem_root); if (open_tables(thd, &lst, &num_tables, 0)) { === modified file 'sql/sql_table.cc' --- a/sql/sql_table.cc 2009-02-16 21:18:45 +0000 +++ b/sql/sql_table.cc 2009-03-02 21:18:26 +0000 @@ -1631,7 +1631,7 @@ int mysql_rm_table_part2(THD *thd, TABLE 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 { @@ -1644,7 +1644,7 @@ int mysql_rm_table_part2(THD *thd, TABLE 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; } } } @@ -1877,15 +1877,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); } } } @@ -3762,30 +3762,31 @@ 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; - } else { + *lock_req= NULL; + } + else + { DEBUG_SYNC(thd, "locked_table_name"); } return FALSE; @@ -3802,7 +3803,7 @@ bool mysql_create_table(THD *thd, const bool internal_tmp_table, uint select_field_count) { - MDL_LOCK_DATA *target_lock_data= 0; + MDL_LOCK_REQUEST *target_lock_req= NULL; bool result; Ha_global_schema_lock_guard global_schema_lock_guard(thd); DBUG_ENTER("mysql_create_table"); @@ -3831,12 +3832,12 @@ bool mysql_create_table(THD *thd, const 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) { @@ -3860,10 +3861,10 @@ bool mysql_create_table(THD *thd, const 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) @@ -4026,7 +4027,7 @@ static int prepare_for_repair(THD *thd, 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 | @@ -4045,13 +4046,13 @@ static int prepare_for_repair(THD *thd, 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); } @@ -4071,11 +4072,7 @@ static int prepare_for_repair(THD *thd, } 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. */ @@ -4186,10 +4183,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); } @@ -4888,7 +4885,7 @@ bool mysql_create_like_schema_frm(THD* t 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]; uint dst_path_length; char *db= table->db; @@ -4935,9 +4932,9 @@ bool mysql_create_like_table(THD* thd, T } 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), db, table_name, reg_ext, 0); @@ -4947,7 +4944,7 @@ bool mysql_create_like_table(THD* thd, T 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);); @@ -5111,10 +5108,10 @@ table_exists: my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name); 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); } @@ -6003,7 +6000,7 @@ mysql_fast_or_online_alter_table(THD *th Make the metadata lock available to open_table() called to reopen the table down the road. */ - table_list->mdl_lock_data= table->mdl_lock_data; + table_list->mdl_lock_request->ticket= table->mdl_lock_ticket; } /* @@ -6580,7 +6577,8 @@ bool mysql_alter_table(THD *thd,char *ne 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]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; @@ -6753,7 +6751,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 @@ -6806,9 +6804,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); @@ -6988,13 +6986,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); } @@ -7408,13 +7405,12 @@ end_online: { 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: @@ -7468,10 +7464,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); @@ -7483,12 +7479,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 */ === modified file 'sql/sql_trigger.cc' --- a/sql/sql_trigger.cc 2009-01-16 11:53:32 +0000 +++ b/sql/sql_trigger.cc 2009-03-02 21:18:26 +0000 @@ -329,6 +329,7 @@ bool mysql_create_or_drop_trigger(THD *t String stmt_query; bool need_start_waiting= FALSE; bool lock_upgrade_done= FALSE; + MDL_LOCK_TICKET *mdl_lock_ticket= NULL; DBUG_ENTER("mysql_create_or_drop_trigger"); @@ -451,8 +452,6 @@ bool mysql_create_or_drop_trigger(THD *t if (!(tables->table= find_write_locked_table(thd->open_tables, tables->db, tables->table_name))) goto end; - /* Later on we will need it to downgrade the lock */ - tables->mdl_lock_data= tables->table->mdl_lock_data; } else { @@ -465,6 +464,9 @@ bool mysql_create_or_drop_trigger(THD *t } table= tables->table; + /* Later on we will need it to downgrade the lock */ + mdl_lock_ticket= table->mdl_lock_ticket; + if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN)) goto end; @@ -511,8 +513,7 @@ end: TABLE instance created by open_n_lock_single_table() and metadata lock. */ if (thd->locked_tables_mode && tables && lock_upgrade_done) - mdl_downgrade_exclusive_lock(&thd->mdl_context, - tables->mdl_lock_data); + mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_lock_ticket); if (need_start_waiting) start_waiting_global_read_lock(thd); === modified file 'sql/sql_udf.cc' --- a/sql/sql_udf.cc 2009-01-27 02:08:48 +0000 +++ b/sql/sql_udf.cc 2009-03-02 21:18:26 +0000 @@ -141,7 +141,7 @@ void udf_init() tables.alias= tables.table_name= (char*) "func"; tables.lock_type = TL_READ; tables.db= db; - alloc_mdl_locks(&tables, new_thd->mem_root); + alloc_mdl_requests(&tables, new_thd->mem_root); if (simple_open_n_lock_tables(new_thd, &tables)) { @@ -480,7 +480,7 @@ int mysql_create_function(THD *thd,udf_f bzero((char*) &tables,sizeof(tables)); tables.db= (char*) "mysql"; tables.table_name= tables.alias= (char*) "func"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); /* Allow creation of functions even if we can't open func table */ if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; @@ -559,7 +559,7 @@ int mysql_drop_function(THD *thd,const L bzero((char*) &tables,sizeof(tables)); tables.db=(char*) "mysql"; tables.table_name= tables.alias= (char*) "func"; - alloc_mdl_locks(&tables, thd->mem_root); + alloc_mdl_requests(&tables, thd->mem_root); if (!(table = open_ltable(thd, &tables, TL_WRITE, 0))) goto err; table->use_all_columns(); === modified file 'sql/table.cc' --- a/sql/table.cc 2009-02-26 21:37:40 +0000 +++ b/sql/table.cc 2009-03-02 21:18:26 +0000 @@ -4921,12 +4921,11 @@ size_t max_row_length(TABLE *table, cons objects for all elements of table list. */ -void alloc_mdl_locks(TABLE_LIST *table_list, MEM_ROOT *root) +void alloc_mdl_requests(TABLE_LIST *table_list, MEM_ROOT *root) { for ( ; table_list ; table_list= table_list->next_global) - table_list->mdl_lock_data= mdl_alloc_lock(0, table_list->db, - table_list->table_name, - root); + table_list->mdl_lock_request= + mdl_request_alloc(0, table_list->db, table_list->table_name, root); } === modified file 'sql/table.h' --- a/sql/table.h 2009-02-26 21:37:40 +0000 +++ b/sql/table.h 2009-03-02 21:18:26 +0000 @@ -27,7 +27,8 @@ class st_select_lex; class partition_info; class COND_EQUAL; class Security_context; -struct MDL_LOCK_DATA; +struct MDL_LOCK_REQUEST; +struct MDL_LOCK_TICKET; /*************************************************************************/ @@ -790,7 +791,7 @@ public: partition_info *part_info; /* Partition related information */ bool no_partitions_used; /* If true, all partitions have been pruned away */ #endif - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_TICKET *mdl_lock_ticket; bool fill_item_list(List *item_list) const; void reset_item_list(List *item_list) const; @@ -1372,7 +1373,7 @@ struct TABLE_LIST uint table_open_method; enum enum_schema_table_state schema_table_state; - MDL_LOCK_DATA *mdl_lock_data; + MDL_LOCK_REQUEST *mdl_lock_request; void calc_md5(char *buffer); void set_underlying_merge(); @@ -1757,5 +1758,5 @@ static inline void dbug_tmp_restore_colu size_t max_row_length(TABLE *table, const uchar *data); -void alloc_mdl_locks(TABLE_LIST *table_list, MEM_ROOT *root); +void alloc_mdl_requests(TABLE_LIST *table_list, MEM_ROOT *root); === modified file 'storage/myisammrg/ha_myisammrg.cc' --- a/storage/myisammrg/ha_myisammrg.cc 2009-02-13 12:48:06 +0000 +++ b/storage/myisammrg/ha_myisammrg.cc 2009-03-02 21:18:26 +0000 @@ -434,16 +434,15 @@ int ha_myisammrg::add_children_list(void /* Copy select_lex. Used in unique_table() at least. */ child_l->select_lex= parent_l->select_lex; - child_l->mdl_lock_data= NULL; /* Safety, if alloc_mdl_locks fails. */ + child_l->mdl_lock_request= NULL; /* Safety, if alloc_mdl_requests fails. */ /* Break when this was the last child. */ if (&child_l->next_global == this->children_last_l) break; } - alloc_mdl_locks(children_l, - thd->locked_tables_root ? thd->locked_tables_root : - thd->mem_root); + alloc_mdl_requests(children_l, thd->locked_tables_root ? + thd->locked_tables_root : thd->mem_root); /* Insert children into the table list. */ if (parent_l->next_global) --===============3602214253238818122== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/davi.arnaut@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: davi.arnaut@stripped # target_branch: file:///home/davi/bzr/work/4284-6.0/ # testament_sha1: aca5a7322f98ce6731988a7d8430568a0ede5d2d # timestamp: 2009-03-02 18:18:45 -0300 # base_revision_id: kostja@stripped # # Begin bundle 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 --===============3602214253238818122==--