From: Dmitry Lenev Date: November 3 2010 2:31pm Subject: bzr commit into mysql-5.5-runtime branch (Dmitry.Lenev:3187) List-Archive: http://lists.mysql.com/commits/122711 Message-Id: <20101103143115.0759F2F0E23@mockturtle> MIME-Version: 1.0 Content-Type: multipart/mixed; boundary="===============2132867028==" --===============2132867028== MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline #At file:///home/dlenev/src/bzr/mysql-5.5-rt-grl/ based on revid:kostja@stripped 3187 Dmitry Lenev 2010-11-03 More changes to draft patch refactoring global read lock implementation. Makes GRL yet another type of metadata lock and thus exposes it to deadlock detector in MDL subsystem. Solves bugs #54673 "It takes too long to get readlock for 'FLUSH TABLES WITH READ LOCK'" and #57006 "Deadlock between HANDLER and FLUSH TABLES WITH READ LOCK". Work-in-progress. Introduce concept of duration in MDL subsystem. Replace notion of transactional sentinel with locks with explicit duration. modified: sql/event_db_repository.cc sql/handler.cc sql/lock.cc sql/log_event.cc sql/mdl.cc sql/mdl.h sql/rpl_rli.cc sql/sp.cc sql/sp_head.cc sql/sql_admin.cc sql/sql_base.cc sql/sql_base.h sql/sql_class.cc sql/sql_class.h sql/sql_db.cc sql/sql_handler.cc sql/sql_handler.h sql/sql_insert.cc sql/sql_parse.cc sql/sql_prepare.cc sql/sql_show.cc sql/sql_table.cc sql/table.cc sql/table.h sql/transaction.cc === modified file 'sql/event_db_repository.cc' --- a/sql/event_db_repository.cc 2010-10-28 19:35:28 +0000 +++ b/sql/event_db_repository.cc 2010-11-03 14:30:33 +0000 @@ -627,7 +627,7 @@ Event_db_repository::create_event(THD *t table at the end but keep the global read lock and possible other locks taken by the caller. */ - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("Event_db_repository::create_event"); @@ -745,7 +745,7 @@ Event_db_repository::update_event(THD *t table at the end but keep the global read lock and possible other locks taken by the caller. */ - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); int ret= 1; DBUG_ENTER("Event_db_repository::update_event"); @@ -855,7 +855,7 @@ Event_db_repository::drop_event(THD *thd table at the end but keep the global read lock and possible other locks taken by the caller. */ - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); int ret= 1; DBUG_ENTER("Event_db_repository::drop_event"); @@ -958,7 +958,7 @@ Event_db_repository::drop_schema_events( TABLE *table= NULL; READ_RECORD read_record_info; enum enum_events_table_field field= ET_FIELD_DB; - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("Event_db_repository::drop_schema_events"); DBUG_PRINT("enter", ("field=%d schema=%s", field, schema.str)); === modified file 'sql/handler.cc' --- a/sql/handler.cc 2010-10-28 19:35:28 +0000 +++ b/sql/handler.cc 2010-11-03 14:30:33 +0000 @@ -1176,7 +1176,8 @@ int ha_commit_trans(THD *thd, bool all) We allow the owner of FTWRL to COMMIT; we assume that it knows what it does. */ - mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE); + mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE, + MDL_EXPLICIT); if (thd->mdl_context.acquire_lock(&mdl_request, thd->variables.lock_wait_timeout)) === modified file 'sql/lock.cc' --- a/sql/lock.cc 2010-10-26 04:25:15 +0000 +++ b/sql/lock.cc 2010-11-03 14:30:33 +0000 @@ -790,7 +790,7 @@ bool lock_schema_name(THD *thd, const ch thd->global_read_lock.init_protection_request(&global_request); - mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE); + mdl_request.init(MDL_key::SCHEMA, db, "", MDL_EXCLUSIVE, MDL_TRANSACTION); mdl_requests.push_front(&mdl_request); mdl_requests.push_front(&global_request); @@ -857,8 +857,9 @@ bool lock_object_name(THD *thd, MDL_key: DBUG_ASSERT(stmt_causes_implicit_commit(thd, CF_IMPLICIT_COMMIT_END)); thd->global_read_lock.init_protection_request(&global_request); - schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE); - mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE); + schema_request.init(MDL_key::SCHEMA, db, "", MDL_INTENTION_EXCLUSIVE, + MDL_TRANSACTION); + mdl_request.init(mdl_type, db, name, MDL_EXCLUSIVE, MDL_TRANSACTION); mdl_requests.push_front(&mdl_request); mdl_requests.push_front(&schema_request); @@ -982,13 +983,12 @@ bool Global_read_lock::lock_global_read_ DBUG_ASSERT(! thd->mdl_context.is_lock_owner(MDL_key::GLOBAL, "", "", MDL_SHARED)); - mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED); + mdl_request.init(MDL_key::GLOBAL, "", "", MDL_SHARED, MDL_EXPLICIT); if (thd->mdl_context.acquire_lock(&mdl_request, thd->variables.lock_wait_timeout)) DBUG_RETURN(1); - thd->mdl_context.move_ticket_after_trans_sentinel(mdl_request.ticket); m_mdl_global_shared_lock= mdl_request.ticket; m_state= GRL_ACQUIRED; } @@ -1060,33 +1060,8 @@ bool Global_read_lock::can_acquire_prote void Global_read_lock::init_protection_request(MDL_request *request) { - request->init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); -} - - -/** - Release protection against global read lock if it is set. - - See also "Handling of global read locks" above. - - @param thd Reference to thread. -*/ - -void Global_read_lock::release_protection_if_set(THD *thd) -{ - MDL_request mdl_request; - MDL_ticket *ticket; - - DBUG_ENTER("release_protection_if_set"); - - mdl_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE); - - if ((ticket= thd->mdl_context.find_ticket_at_front(&mdl_request))) - { - thd->mdl_context.release_lock(ticket); - } - - DBUG_VOID_RETURN; + request->init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE, + MDL_STATEMENT); } @@ -1116,13 +1091,12 @@ bool Global_read_lock::make_global_read_ if (m_state != GRL_ACQUIRED) DBUG_RETURN(0); - mdl_request.init(MDL_key::COMMIT, "", "", MDL_SHARED); + mdl_request.init(MDL_key::COMMIT, "", "", MDL_SHARED, MDL_EXPLICIT); if (thd->mdl_context.acquire_lock(&mdl_request, thd->variables.lock_wait_timeout)) DBUG_RETURN(TRUE); - thd->mdl_context.move_ticket_after_trans_sentinel(mdl_request.ticket); m_mdl_blocks_commits_lock= mdl_request.ticket; m_state= GRL_ACQUIRED_AND_BLOCKS_COMMIT; @@ -1131,18 +1105,17 @@ bool Global_read_lock::make_global_read_ /** - Move tickets for metadata locks which are used to implement GRL after - transactional sentinel. + Set explicit duration for metadata locks which are used to implement GRL. @param thd Reference to thread. */ -void Global_read_lock::move_tickets_after_trans_sentinel(THD *thd) +void Global_read_lock::set_explicit_lock_duration(THD *thd) { if (m_mdl_global_shared_lock) - thd->mdl_context.move_ticket_after_trans_sentinel(m_mdl_global_shared_lock); + thd->mdl_context.set_lock_duration(m_mdl_global_shared_lock, MDL_EXPLICIT); if (m_mdl_blocks_commits_lock) - thd->mdl_context.move_ticket_after_trans_sentinel(m_mdl_blocks_commits_lock); + thd->mdl_context.set_lock_duration(m_mdl_blocks_commits_lock, MDL_EXPLICIT); } /** === modified file 'sql/log_event.cc' --- a/sql/log_event.cc 2010-10-18 12:33:49 +0000 +++ b/sql/log_event.cc 2010-11-03 14:30:33 +0000 @@ -4960,7 +4960,7 @@ error: if (! thd->in_multi_stmt_transaction_mode()) thd->mdl_context.release_transactional_locks(); else - thd->global_read_lock.release_protection_if_set(thd); + thd->mdl_context.release_statement_locks(); DBUG_EXECUTE_IF("LOAD_DATA_INFILE_has_fatal_error", thd->is_slave_error= 0; thd->is_fatal_error= 1;); === modified file 'sql/mdl.cc' --- a/sql/mdl.cc 2010-10-27 09:55:52 +0000 +++ b/sql/mdl.cc 2010-11-03 14:30:33 +0000 @@ -731,8 +731,7 @@ void MDL_map::remove(MDL_lock *lock) */ MDL_context::MDL_context() - :m_trans_sentinel(NULL), - m_thd(NULL), + : m_thd(NULL), m_needs_thr_lock_abort(FALSE), m_waiting_for(NULL) { @@ -754,7 +753,9 @@ MDL_context::MDL_context() void MDL_context::destroy() { - DBUG_ASSERT(m_tickets.is_empty()); + DBUG_ASSERT(m_tickets[MDL_STATEMENT].is_empty() && + m_tickets[MDL_TRANSACTION].is_empty() && + m_tickets[MDL_EXPLICIT].is_empty()); mysql_prlock_destroy(&m_LOCK_waiting_for); } @@ -783,10 +784,12 @@ void MDL_context::destroy() void MDL_request::init(MDL_key::enum_mdl_namespace mdl_namespace, const char *db_arg, const char *name_arg, - enum enum_mdl_type mdl_type_arg) + enum_mdl_type mdl_type_arg, + enum_mdl_duration mdl_duration_arg) { key.mdl_key_init(mdl_namespace, db_arg, name_arg); type= mdl_type_arg; + duration= mdl_duration_arg; ticket= NULL; } @@ -801,49 +804,17 @@ void MDL_request::init(MDL_key::enum_mdl */ void MDL_request::init(const MDL_key *key_arg, - enum enum_mdl_type mdl_type_arg) + enum_mdl_type mdl_type_arg, + enum_mdl_duration mdl_duration_arg) { key.mdl_key_init(key_arg); type= mdl_type_arg; + duration= mdl_duration_arg; ticket= NULL; } /** - Allocate and initialize one lock request. - - Same as mdl_init_lock(), but allocates the lock and the key buffer - on a memory root. Necessary to lock ad-hoc tables, e.g. - mysql.* tables of grant and data dictionary subsystems. - - @param mdl_namespace Id of namespace of object to be locked - @param db Name of database to which object belongs - @param name Name of of object - @param root MEM_ROOT on which object should be allocated - - @note The allocated lock request will have MDL_SHARED type. - - @retval 0 Error if out of memory - @retval non-0 Pointer to an object representing a lock request -*/ - -MDL_request * -MDL_request::create(MDL_key::enum_mdl_namespace mdl_namespace, const char *db, - const char *name, enum_mdl_type mdl_type, - MEM_ROOT *root) -{ - MDL_request *mdl_request; - - if (!(mdl_request= (MDL_request*) alloc_root(root, sizeof(MDL_request)))) - return NULL; - - mdl_request->init(mdl_namespace, db, name, mdl_type); - - return mdl_request; -} - - -/** Auxiliary functions needed for creation/destruction of MDL_lock objects. @note Also chooses an MDL_lock descendant appropriate for object namespace. @@ -880,9 +851,17 @@ void MDL_lock::destroy(MDL_lock *lock) on memory allocation by reusing released objects. */ -MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg) -{ - return new MDL_ticket(ctx_arg, type_arg); +MDL_ticket *MDL_ticket::create(MDL_context *ctx_arg, enum_mdl_type type_arg +#ifndef DBUG_OFF + , enum_mdl_duration duration_arg +#endif + ) +{ + return new MDL_ticket(ctx_arg, type_arg +#ifndef DBUG_OFF + , duration_arg +#endif + ); } @@ -1451,13 +1430,11 @@ bool MDL_ticket::is_incompatible_when_wa /** Check whether the context already holds a compatible lock ticket on an object. - Start searching the transactional locks. If not - found in the list of transactional locks, look at LOCK TABLES - and HANDLER locks. + Start searching from list of locks for the same duration as lock + being requested. If not look at lists for other durations. @param mdl_request Lock request object for lock to be acquired - @param[out] is_transactional FALSE if we pass beyond m_trans_sentinel - while searching for ticket, otherwise TRUE. + @param[out] result_duration Duration of lock which was found. @note Tickets which correspond to lock types "stronger" than one being requested are also considered compatible. @@ -1467,48 +1444,27 @@ bool MDL_ticket::is_incompatible_when_wa MDL_ticket * MDL_context::find_ticket(MDL_request *mdl_request, - bool *is_transactional) + enum_mdl_duration *result_duration) { MDL_ticket *ticket; - Ticket_iterator it(m_tickets); - - *is_transactional= TRUE; + int i; - while ((ticket= it++)) + for (i= 0; i < MDL_DURATION_END; i++) { - if (ticket == m_trans_sentinel) - *is_transactional= FALSE; + enum_mdl_duration duration= (enum_mdl_duration)((mdl_request->duration+i) % + MDL_DURATION_END); + Ticket_iterator it(m_tickets[duration]); - if (mdl_request->key.is_equal(&ticket->m_lock->key) && - ticket->has_stronger_or_equal_type(mdl_request->type)) - break; + while ((ticket= it++)) + { + if (mdl_request->key.is_equal(&ticket->m_lock->key) && + ticket->has_stronger_or_equal_type(mdl_request->type)) + { + *result_duration= duration; + return ticket; + } + } } - - return ticket; -} - - -/** - Check whether the ticket at the front of context's ticket list - is compatible with request. - - @param mdl_request Lock request object for ticket to find. - - @note Tickets which correspond to lock types "stronger" than one - being requested are also considered compatible. - - @return A pointer to the lock ticket for the object or NULL otherwise. -*/ - -MDL_ticket * MDL_context::find_ticket_at_front(MDL_request *mdl_request) -{ - MDL_ticket *ticket= m_tickets.front(); - - if (ticket && - mdl_request->key.is_equal(&ticket->m_lock->key) && - ticket->has_stronger_or_equal_type(mdl_request->type)) - return ticket; - return NULL; } @@ -1587,7 +1543,7 @@ MDL_context::try_acquire_lock_impl(MDL_r MDL_lock *lock; MDL_key *key= &mdl_request->key; MDL_ticket *ticket; - bool is_transactional; + enum_mdl_duration found_duration; DBUG_ASSERT(mdl_request->type != MDL_EXCLUSIVE || is_lock_owner(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE)); @@ -1601,7 +1557,7 @@ MDL_context::try_acquire_lock_impl(MDL_r Check whether the context already holds a shared lock on the object, and if so, grant the request. */ - if ((ticket= find_ticket(mdl_request, &is_transactional))) + if ((ticket= find_ticket(mdl_request, &found_duration))) { DBUG_ASSERT(ticket->m_lock); DBUG_ASSERT(ticket->has_stronger_or_equal_type(mdl_request->type)); @@ -1623,7 +1579,9 @@ MDL_context::try_acquire_lock_impl(MDL_r a different alias. */ mdl_request->ticket= ticket; - if (!is_transactional && clone_ticket(mdl_request)) + if ((found_duration != mdl_request->duration || + mdl_request->duration == MDL_EXPLICIT) && + clone_ticket(mdl_request)) { /* Clone failed. */ mdl_request->ticket= NULL; @@ -1632,7 +1590,11 @@ MDL_context::try_acquire_lock_impl(MDL_r return FALSE; } - if (!(ticket= MDL_ticket::create(this, mdl_request->type))) + if (!(ticket= MDL_ticket::create(this, mdl_request->type +#ifndef DBUG_OFF + , mdl_request->duration +#endif + ))) return TRUE; /* The below call implicitly locks MDL_lock::m_rwlock on success. */ @@ -1650,7 +1612,7 @@ MDL_context::try_acquire_lock_impl(MDL_r mysql_prlock_unlock(&lock->m_rwlock); - m_tickets.push_front(ticket); + m_tickets[mdl_request->duration].push_front(ticket); mdl_request->ticket= ticket; } @@ -1685,7 +1647,11 @@ MDL_context::clone_ticket(MDL_request *m we effectively downgrade the cloned lock to the level of the request. */ - if (!(ticket= MDL_ticket::create(this, mdl_request->type))) + if (!(ticket= MDL_ticket::create(this, mdl_request->type +#ifndef DBUG_OFF + , mdl_request->duration +#endif + ))) return TRUE; /* clone() is not supposed to be used to get a stronger lock. */ @@ -1698,7 +1664,7 @@ MDL_context::clone_ticket(MDL_request *m ticket->m_lock->m_granted.add_ticket(ticket); mysql_prlock_unlock(&ticket->m_lock->m_rwlock); - m_tickets.push_front(ticket); + m_tickets[mdl_request->duration].push_front(ticket); return FALSE; } @@ -1894,7 +1860,7 @@ MDL_context::acquire_lock(MDL_request *m */ DBUG_ASSERT(wait_status == MDL_wait::GRANTED); - m_tickets.push_front(ticket); + m_tickets[mdl_request->duration].push_front(ticket); mdl_request->ticket= ticket; @@ -1935,7 +1901,7 @@ bool MDL_context::acquire_locks(MDL_requ { MDL_request_list::Iterator it(*mdl_requests); MDL_request **sort_buf, **p_req; - MDL_ticket *mdl_svp= mdl_savepoint(); + MDL_savepoint mdl_svp= mdl_savepoint(); ssize_t req_count= static_cast(mdl_requests->elements()); if (req_count == 0) @@ -2004,7 +1970,7 @@ MDL_context::upgrade_shared_lock_to_excl ulong lock_wait_timeout) { MDL_request mdl_xlock_request; - MDL_ticket *mdl_svp= mdl_savepoint(); + MDL_savepoint mdl_svp= mdl_savepoint(); bool is_new_ticket; DBUG_ENTER("MDL_ticket::upgrade_shared_lock_to_exclusive"); @@ -2021,7 +1987,8 @@ MDL_context::upgrade_shared_lock_to_excl DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_NO_WRITE || mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE); - mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE); + mdl_xlock_request.init(&mdl_ticket->m_lock->key, MDL_EXCLUSIVE, + MDL_TRANSACTION); if (acquire_lock(&mdl_xlock_request, lock_wait_timeout)) DBUG_RETURN(TRUE); @@ -2045,7 +2012,7 @@ MDL_context::upgrade_shared_lock_to_excl if (is_new_ticket) { - m_tickets.remove(mdl_xlock_request.ticket); + m_tickets[MDL_TRANSACTION].remove(mdl_xlock_request.ticket); MDL_ticket::destroy(mdl_xlock_request.ticket); } @@ -2306,10 +2273,12 @@ void MDL_context::find_deadlock() /** Release lock. - @param ticket Ticket for lock to be released. + @param duration Lock duration. + @param ticket Ticket for lock to be released. + */ -void MDL_context::release_lock(MDL_ticket *ticket) +void MDL_context::release_lock(enum_mdl_duration duration, MDL_ticket *ticket) { MDL_lock *lock= ticket->m_lock; DBUG_ENTER("MDL_context::release_lock"); @@ -2319,12 +2288,9 @@ void MDL_context::release_lock(MDL_ticke DBUG_ASSERT(this == ticket->get_ctx()); mysql_mutex_assert_not_owner(&LOCK_open); - if (ticket == m_trans_sentinel) - m_trans_sentinel= ++Ticket_list::Iterator(m_tickets, ticket); - lock->remove_ticket(&MDL_lock::m_granted, ticket); - m_tickets.remove(ticket); + m_tickets[duration].remove(ticket); MDL_ticket::destroy(ticket); DBUG_VOID_RETURN; @@ -2332,50 +2298,56 @@ void MDL_context::release_lock(MDL_ticke /** + Release lock with explicit duration. + + @param ticket Ticket for lock to be released. + +*/ + +void MDL_context::release_lock(MDL_ticket *ticket) +{ + DBUG_ASSERT(ticket->m_duration == MDL_EXPLICIT); + + release_lock(MDL_EXPLICIT, ticket); +} + + +/** Release all locks associated with the context. If the sentinel is not NULL, do not release locks stored in the list after and including the sentinel. - Transactional locks are added to the beginning of the list, i.e. - stored in reverse temporal order. This allows to employ this - function to: + Statement and transactional locks are added to the beginning of + the corresponding lists, i.e. stored in reverse temporal order. + This allows to employ this function to: - back off in case of a lock conflict. - - release all locks in the end of a transaction + - release all locks in the end of a statment or transaction - rollback to a savepoint. - - The sentinel semantics is used to support LOCK TABLES - mode and HANDLER statements: locks taken by these statements - survive COMMIT, ROLLBACK, ROLLBACK TO SAVEPOINT. */ -void MDL_context::release_locks_stored_before(MDL_ticket *sentinel) +void MDL_context::release_locks_stored_before(enum_mdl_duration duration, + MDL_ticket *sentinel) { MDL_ticket *ticket; - Ticket_iterator it(m_tickets); + Ticket_iterator it(m_tickets[duration]); DBUG_ENTER("MDL_context::release_locks_stored_before"); - if (m_tickets.is_empty()) + if (m_tickets[duration].is_empty()) DBUG_VOID_RETURN; while ((ticket= it++) && ticket != sentinel) { DBUG_PRINT("info", ("found lock to release ticket=%p", ticket)); - release_lock(ticket); + release_lock(duration, ticket); } - /* - If all locks were released, then the sentinel was not present - in the list. It must never happen because the sentinel was - bogus, i.e. pointed to a ticket that no longer exists. - */ - DBUG_ASSERT(! m_tickets.is_empty() || sentinel == NULL); DBUG_VOID_RETURN; } /** - Release all locks in the context which correspond to the same name/ - object as this lock request. + Release all explicit locks in the context which correspond to the + same name/object as this lock request. @param ticket One of the locks for the name/object for which all locks should be released. @@ -2388,17 +2360,13 @@ void MDL_context::release_all_locks_for_ /* Remove matching lock tickets from the context. */ MDL_ticket *ticket; - Ticket_iterator it_ticket(m_tickets); + Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]); while ((ticket= it_ticket++)) { DBUG_ASSERT(ticket->m_lock); - /* - We rarely have more than one ticket in this loop, - let's not bother saving on pthread_cond_broadcast(). - */ if (ticket->m_lock == lock) - release_lock(ticket); + release_lock(MDL_EXPLICIT, ticket); } } @@ -2453,9 +2421,10 @@ MDL_context::is_lock_owner(MDL_key::enum enum_mdl_type mdl_type) { MDL_request mdl_request; - bool is_transactional_unused; - mdl_request.init(mdl_namespace, db, name, mdl_type); - MDL_ticket *ticket= find_ticket(&mdl_request, &is_transactional_unused); + enum_mdl_duration not_unused; + /* We don't care about exact duration of lock here. */ + mdl_request.init(mdl_namespace, db, name, mdl_type, MDL_TRANSACTION); + MDL_ticket *ticket= find_ticket(&mdl_request, ¬_unused); DBUG_ASSERT(ticket == NULL || ticket->m_lock); @@ -2484,18 +2453,15 @@ bool MDL_ticket::has_pending_conflicting @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 mdl_savepont The last acquired MDL lock when the - savepoint was set. */ -void MDL_context::rollback_to_savepoint(MDL_ticket *mdl_savepoint) +void MDL_context::rollback_to_savepoint(const MDL_savepoint &mdl_savepoint) { DBUG_ENTER("MDL_context::rollback_to_savepoint"); /* If savepoint is NULL, it is from the start of the transaction. */ - release_locks_stored_before(mdl_savepoint ? - mdl_savepoint : m_trans_sentinel); + release_locks_stored_before(MDL_STATEMENT, mdl_savepoint.m_stmt_ticket); + release_locks_stored_before(MDL_TRANSACTION, mdl_savepoint.m_trans_ticket); DBUG_VOID_RETURN; } @@ -2513,78 +2479,150 @@ void MDL_context::rollback_to_savepoint( void MDL_context::release_transactional_locks() { DBUG_ENTER("MDL_context::release_transactional_locks"); - release_locks_stored_before(m_trans_sentinel); + release_locks_stored_before(MDL_STATEMENT, NULL); + release_locks_stored_before(MDL_TRANSACTION, NULL); + DBUG_VOID_RETURN; +} + + +void MDL_context::release_statement_locks() +{ + DBUG_ENTER("MDL_context::release_transactional_locks"); + release_locks_stored_before(MDL_STATEMENT, NULL); DBUG_VOID_RETURN; } /** Does this savepoint have this lock? - - @retval TRUE The ticket is older than the savepoint and - is not LT, HA or GLR ticket. Thus it belongs - to the savepoint. - @retval FALSE The ticket is newer than the savepoint - or is an LT, HA or GLR ticket. + + @retval TRUE The ticket is older than the savepoint or + is an LT, HA or GLR ticket. Thus it belongs + to the savepoint or has explicit duration. + @retval FALSE The ticket is newer than the savepoint. + and is not an LT, HA or GLR ticket. */ -bool MDL_context::has_lock(MDL_ticket *mdl_savepoint, +bool MDL_context::has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket) { MDL_ticket *ticket; /* Start from the beginning, most likely mdl_ticket's been just acquired. */ - MDL_context::Ticket_iterator it(m_tickets); - bool found_savepoint= FALSE; + MDL_context::Ticket_iterator s_it(m_tickets[MDL_STATEMENT]); + MDL_context::Ticket_iterator t_it(m_tickets[MDL_TRANSACTION]); - while ((ticket= it++) && ticket != m_trans_sentinel) + while ((ticket= s_it++) && ticket != mdl_savepoint.m_stmt_ticket) { - /* - First met the savepoint. The ticket must be - somewhere after it. - */ - if (ticket == mdl_savepoint) - found_savepoint= TRUE; - /* - Met the ticket. If we haven't yet met the savepoint, - the ticket is newer than the savepoint. - */ if (ticket == mdl_ticket) - return found_savepoint; + return FALSE; } - /* Reached m_trans_sentinel. The ticket must be LT, HA or GRL ticket. */ - return FALSE; + + while ((ticket= t_it++) && ticket != mdl_savepoint.m_trans_ticket) + { + if (ticket == mdl_ticket) + return FALSE; + } + return TRUE; +} + + +/** + Change lock duration for transactional lock. + + @param ticket Ticket representing lock. + @param duration Lock duration to be set. + + @note This method only supports changing duration of + transactional lock to some other duration. +*/ + +void MDL_context::set_lock_duration(MDL_ticket *mdl_ticket, + enum_mdl_duration duration) +{ + DBUG_ASSERT(mdl_ticket->m_duration == MDL_TRANSACTION && + duration != MDL_TRANSACTION); + + m_tickets[MDL_TRANSACTION].remove(mdl_ticket); + m_tickets[duration].push_front(mdl_ticket); +#ifndef DBUG_OFF + mdl_ticket->m_duration= duration; +#endif } /** - Rearrange the ticket to reside in the part of the list that's - beyond m_trans_sentinel. This effectively changes the ticket - life cycle, from automatic to manual: i.e. the ticket is no - longer released by MDL_context::release_transactional_locks() or - MDL_context::rollback_to_savepoint(), it must be released manually. + Set explicit duration for all locks in the context. */ -void MDL_context::move_ticket_after_trans_sentinel(MDL_ticket *mdl_ticket) +void MDL_context::set_explicit_duration_for_all_locks() { - m_tickets.remove(mdl_ticket); - if (m_trans_sentinel == NULL) + int i; + MDL_ticket *ticket; + + /* + In the most common case when this function is called list + of transactional locks is bigger than list of locks with + explicit duration. So we start by swapping these two lists + and then move elements from new list of transactional + locks and list of statement locks to list of locks with + explicit duration. + */ + + m_tickets[MDL_EXPLICIT].swap(m_tickets[MDL_TRANSACTION]); + + for (i= 0; i < MDL_EXPLICIT; i++) { - m_trans_sentinel= mdl_ticket; - m_tickets.push_back(mdl_ticket); + Ticket_iterator it_ticket(m_tickets[i]); + + while ((ticket= it_ticket++)) + { + m_tickets[i].remove(ticket); + m_tickets[MDL_EXPLICIT].push_front(ticket); + } } - else - m_tickets.insert_after(m_trans_sentinel, mdl_ticket); + +#ifndef DBUG_OFF + Ticket_iterator exp_it(m_tickets[MDL_EXPLICIT]); + + while ((ticket= exp_it++)) + ticket->m_duration= MDL_EXPLICIT; +#endif } /** - Move ticket to the front of the context's ticket list. - - @param mdl_ticket Ticket to move. + Set transactional duration for all locks in the context. */ -void MDL_context::move_ticket_to_front(MDL_ticket *mdl_ticket) +void MDL_context::set_transaction_duration_for_all_locks() { - m_tickets.remove(mdl_ticket); - m_tickets.push_front(mdl_ticket); + MDL_ticket *ticket; + + /* + In the most common case when this function is called list + of explicit locks is bigger than two other lists (in fact, + list of statement locks is always empty). So we start by + swapping list of explicit and transactional locks and then + move contents of new list of explicit locks to list of + locks with transactional duration. + */ + + DBUG_ASSERT(m_tickets[MDL_STATEMENT].is_empty()); + + m_tickets[MDL_TRANSACTION].swap(m_tickets[MDL_EXPLICIT]); + + Ticket_iterator it_ticket(m_tickets[MDL_EXPLICIT]); + + while ((ticket= it_ticket++)) + { + m_tickets[MDL_EXPLICIT].remove(ticket); + m_tickets[MDL_TRANSACTION].push_front(ticket); + } + +#ifndef DBUG_OFF + Ticket_iterator trans_it(m_tickets[MDL_TRANSACTION]); + + while ((ticket= trans_it++)) + ticket->m_duration= MDL_TRANSACTION; +#endif } === modified file 'sql/mdl.h' --- a/sql/mdl.h 2010-10-25 15:16:12 +0000 +++ b/sql/mdl.h 2010-11-03 14:30:33 +0000 @@ -150,6 +150,15 @@ enum enum_mdl_type { MDL_TYPE_END}; +/** Duration of metadata lock. */ + +enum enum_mdl_duration { MDL_STATEMENT= 0, + MDL_TRANSACTION, + MDL_EXPLICIT, + /* This should be the last ! */ + MDL_DURATION_END }; + + /** Maximal length of key for metadata locking subsystem. */ #define MAX_MDLKEY_LENGTH (1 + NAME_LEN + 1 + NAME_LEN + 1) @@ -310,6 +319,8 @@ class MDL_request public: /** Type of metadata lock. */ enum enum_mdl_type type; + /** Duration for requested lock. */ + enum enum_mdl_duration duration; /** Pointers for participating in the list of lock requests for this context. @@ -332,17 +343,16 @@ public: void init(MDL_key::enum_mdl_namespace namespace_arg, const char *db_arg, const char *name_arg, - enum_mdl_type mdl_type_arg); - void init(const MDL_key *key_arg, enum_mdl_type mdl_type_arg); + enum_mdl_type mdl_type_arg, + enum_mdl_duration mdl_duration_arg); + void init(const MDL_key *key_arg, enum_mdl_type mdl_type_arg, + enum_mdl_duration mdl_duration_arg); /** Set type of lock request. Can be only applied to pending locks. */ inline void set_type(enum_mdl_type type_arg) { DBUG_ASSERT(ticket == NULL); type= type_arg; } - static MDL_request *create(MDL_key::enum_mdl_namespace mdl_namespace, - const char *db, const char *name, - enum_mdl_type mdl_type, MEM_ROOT *root); /* This is to work around the ugliness of TABLE_LIST @@ -368,6 +378,7 @@ public: MDL_request(const MDL_request *rhs) :type(rhs->type), + duration(rhs->duration), ticket(NULL), key(&rhs->key) {} @@ -489,17 +500,35 @@ public: private: friend class MDL_context; - MDL_ticket(MDL_context *ctx_arg, enum_mdl_type type_arg) + MDL_ticket(MDL_context *ctx_arg, enum_mdl_type type_arg +#ifndef DBUG_OFF + , enum_mdl_duration duration_arg +#endif + ) : m_type(type_arg), +#ifndef DBUG_OFF + m_duration(duration_arg), +#endif m_ctx(ctx_arg), m_lock(NULL) {} - static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg); + static MDL_ticket *create(MDL_context *ctx_arg, enum_mdl_type type_arg +#ifndef DBUG_OFF + , enum_mdl_duration duration_arg +#endif + ); static void destroy(MDL_ticket *ticket); private: /** Type of metadata lock. Externally accessible. */ enum enum_mdl_type m_type; +#ifndef DBUG_OFF + /** + Duration of lock represented by this ticket. + Context private. Debug-only. + */ + enum_mdl_duration m_duration; +#endif /** Context of the owner of the metadata lock ticket. Externally accessible. */ @@ -517,6 +546,39 @@ private: /** + Savepoint for MDL context. + + Doesn't include metadata locks with explicit duration as + they are not released during rollback to savepoint. +*/ + +class MDL_savepoint +{ +public: + MDL_savepoint() {}; + +private: + MDL_savepoint(MDL_ticket *stmt_ticket, MDL_ticket *trans_ticket) + : m_stmt_ticket(stmt_ticket), m_trans_ticket(trans_ticket) + {} + + friend class MDL_context; + +private: + /** + Pointer to last lock with statement duration which was taken + before creation of savepoint. + */ + MDL_ticket *m_stmt_ticket; + /** + Pointer to last lock with transaction duration which was taken + before creation of savepoint. + */ + MDL_ticket *m_trans_ticket; +}; + + +/** A reliable way to wait on an MDL lock. */ @@ -564,9 +626,7 @@ public: typedef I_P_List, - I_P_List_null_counter, - I_P_List_fast_push_back > + &MDL_ticket::prev_in_context> > Ticket_list; typedef Ticket_list::Iterator Ticket_iterator; @@ -589,38 +649,28 @@ public: const char *db, const char *name, enum_mdl_type mdl_type); - bool has_lock(MDL_ticket *mdl_savepoint, MDL_ticket *mdl_ticket); + bool has_lock(const MDL_savepoint &mdl_savepoint, MDL_ticket *mdl_ticket); inline bool has_locks() const { - return !m_tickets.is_empty(); + return !(m_tickets[MDL_STATEMENT].is_empty() && + m_tickets[MDL_TRANSACTION].is_empty() && + m_tickets[MDL_EXPLICIT].is_empty()); } - MDL_ticket *mdl_savepoint() + MDL_savepoint mdl_savepoint() { - /* - NULL savepoint represents the start of the transaction. - Checking for m_trans_sentinel also makes sure we never - return a pointer to HANDLER ticket as a savepoint. - */ - return m_tickets.front() == m_trans_sentinel ? NULL : m_tickets.front(); + return MDL_savepoint(m_tickets[MDL_STATEMENT].front(), + m_tickets[MDL_TRANSACTION].front()); } - void set_trans_sentinel() - { - m_trans_sentinel= m_tickets.front(); - } - MDL_ticket *trans_sentinel() const { return m_trans_sentinel; } - - void reset_trans_sentinel(MDL_ticket *sentinel_arg) - { - m_trans_sentinel= sentinel_arg; - } - void move_ticket_after_trans_sentinel(MDL_ticket *mdl_ticket); - void move_ticket_to_front(MDL_ticket *mdl_ticket); + void set_explicit_duration_for_all_locks(); + void set_transaction_duration_for_all_locks(); + void set_lock_duration(MDL_ticket *mdl_ticket, enum_mdl_duration duration); + void release_statement_locks(); void release_transactional_locks(); - void rollback_to_savepoint(MDL_ticket *mdl_savepoint); + void rollback_to_savepoint(const MDL_savepoint &mdl_savepoint); inline THD *get_thd() const { return m_thd; } @@ -661,46 +711,43 @@ public: MDL_wait m_wait; private: /** - All MDL tickets acquired by this connection. + Lists of all MDL tickets acquired by this connection. - The order of tickets in m_tickets list. - --------------------------------------- - The entire set of locks acquired by a connection - can be separated in two subsets: transactional and - non-transactional locks. - - Transactional locks are locks with automatic scope. They - are accumulated in the course of a transaction, and - released only on COMMIT, ROLLBACK or ROLLBACK TO SAVEPOINT. - They must not be (and never are) released manually, + Lists of MDL tickets: + --------------------- + The entire set of locks acquired by a connection can be separated + in three subsets according to their: locks released at the end of + statement, at the end of transaction and locks are released + explicitly. + + Statement and transactional locks are locks with automatic scope. + They are accumulated in the course of a transaction, and released + either at the end of uppermost statement (for statement locks) or + on COMMIT, ROLLBACK or ROLLBACK TO SAVEPOINT (for transactional + locks). They must not be (and never are) released manually, i.e. with release_lock() call. - Non-transactional locks are taken for locks that span + Locks with explicit duration are taken for locks that span multiple transactions or savepoints. These are: HANDLER SQL locks (HANDLER SQL is transaction-agnostic), LOCK TABLES locks (you can COMMIT/etc under LOCK TABLES, and the locked tables stay locked), and - SET GLOBAL READ_ONLY=1 global shared lock. + locks implementing "global read lock". - Transactional locks are always prepended to the beginning - of the list. In other words, they are stored in reverse - temporal order. Thus, when we rollback to a savepoint, - we start popping and releasing tickets from the front - until we reach the last ticket acquired after the - savepoint. - - Non-transactional locks are always stored after - transactional ones, and among each other can be - split into three sets: + Statement/transactional locks are always prepended to the + beginning of the appropriate list. In other words, they are + stored in reverse temporal order. Thus, when we rollback to + a savepoint, we start popping and releasing tickets from the + front until we reach the last ticket acquired after the savepoint. + + Locks with explicit duration stored are not stored in any + particular order, and among each other can be split into + three sets: [LOCK TABLES locks] [HANDLER locks] [GLOBAL READ LOCK locks] The following is known about these sets: - * we can never have both HANDLER and LOCK TABLES locks - together -- HANDLER statements are prohibited under LOCK - TABLES, entering LOCK TABLES implicitly closes all open - HANDLERs. * GLOBAL READ LOCK locks are always stored after LOCK TABLES locks and after HANDLER locks. This is because one can't say SET GLOBAL read_only=1 or FLUSH TABLES WITH READ LOCK @@ -715,14 +762,9 @@ private: However, one can open a few HANDLERs after entering the read only mode. * LOCK TABLES locks include intention exclusive locks on - involved schemas. + involved schemas and global intention exclusive lock. */ - Ticket_list m_tickets; - /** - Separates transactional and non-transactional locks - in m_tickets list, @sa m_tickets. - */ - MDL_ticket *m_trans_sentinel; + Ticket_list m_tickets[MDL_DURATION_END]; THD *m_thd; /** TRUE - if for this context we will break protocol and try to @@ -751,13 +793,11 @@ private: readily available to the wait-for graph iterator. */ MDL_wait_for_subgraph *m_waiting_for; -public: - MDL_ticket *find_ticket(MDL_request *mdl_req, - bool *is_transactional); - - MDL_ticket *find_ticket_at_front(MDL_request *mdl_req); private: - void release_locks_stored_before(MDL_ticket *sentinel); + MDL_ticket *find_ticket(MDL_request *mdl_req, + enum_mdl_duration *duration); + void release_locks_stored_before(enum_mdl_duration duration, MDL_ticket *sentinel); + void release_lock(enum_mdl_duration duration, MDL_ticket *ticket); bool try_acquire_lock_impl(MDL_request *mdl_request, MDL_ticket **out_ticket); === modified file 'sql/rpl_rli.cc' --- a/sql/rpl_rli.cc 2010-10-18 12:33:49 +0000 +++ b/sql/rpl_rli.cc 2010-11-03 14:30:33 +0000 @@ -1275,7 +1275,7 @@ void Relay_log_info::slave_close_thread_ if (! thd->in_multi_stmt_transaction_mode()) thd->mdl_context.release_transactional_locks(); else - thd->global_read_lock.release_protection_if_set(thd); + thd->mdl_context.release_statement_locks(); clear_tables_to_lock(); } === modified file 'sql/sp.cc' --- a/sql/sp.cc 2010-10-25 15:16:12 +0000 +++ b/sql/sp.cc 2010-11-03 14:30:33 +0000 @@ -440,7 +440,7 @@ static TABLE *open_proc_table_for_update { TABLE_LIST table_list; TABLE *table; - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_proc_table_for_update"); table_list.init_one_table("mysql", 5, "proc", 4, "proc", TL_WRITE); @@ -1370,7 +1370,7 @@ sp_drop_db_routines(THD *thd, char *db) TABLE *table; int ret; uint key_len; - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("sp_drop_db_routines"); DBUG_PRINT("enter", ("db: %s", db)); @@ -1694,7 +1694,7 @@ bool sp_add_used_routine(Query_tables_li (Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry)); if (!rn) // OOM. Error will be reported using fatal_error(). return FALSE; - rn->mdl_request.init(key, MDL_SHARED); + rn->mdl_request.init(key, MDL_SHARED, MDL_TRANSACTION); if (my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn)) return FALSE; prelocking_ctx->sroutines_list.link_in_list(rn, &rn->next); === modified file 'sql/sp_head.cc' --- a/sql/sp_head.cc 2010-10-28 19:35:28 +0000 +++ b/sql/sp_head.cc 2010-11-03 14:30:33 +0000 @@ -2129,7 +2129,7 @@ sp_head::execute_procedure(THD *thd, Lis else if (! thd->locked_tables_mode) { DBUG_ASSERT(! thd->in_sub_stmt); - thd->global_read_lock.release_protection_if_set(thd); + thd->mdl_context.release_statement_locks(); } thd->rollback_item_tree_changes(); @@ -2967,7 +2967,7 @@ sp_lex_keeper::reset_lex_and_exec_core(T if (! thd->in_sub_stmt && ! thd->in_multi_stmt_transaction_mode()) thd->mdl_context.release_transactional_locks(); else if (! thd->in_sub_stmt && !thd->locked_tables_mode) - thd->global_read_lock.release_protection_if_set(thd); + thd->mdl_context.release_statement_locks(); } if (m_lex->query_tables_own_last) @@ -4163,7 +4163,8 @@ sp_head::add_used_tables_to_table_list(T */ table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, table->lock_type >= TL_WRITE_ALLOW_WRITE ? - MDL_SHARED_WRITE : MDL_SHARED_READ); + MDL_SHARED_WRITE : MDL_SHARED_READ, + MDL_TRANSACTION); /* Everyting else should be zeroed */ @@ -4207,7 +4208,7 @@ sp_add_to_query_tables(THD *thd, LEX *le table->select_lex= lex->current_select; table->cacheable_table= 1; table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, - mdl_type); + mdl_type, MDL_TRANSACTION); lex->add_to_query_tables(table); return table; === modified file 'sql/sql_admin.cc' --- a/sql/sql_admin.cc 2010-09-22 08:15:41 +0000 +++ b/sql/sql_admin.cc 2010-11-03 14:30:33 +0000 @@ -85,7 +85,7 @@ static int prepare_for_repair(THD *thd, key_length= create_table_def_key(thd, key, table_list, 0); table_list->mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, - MDL_EXCLUSIVE); + MDL_EXCLUSIVE, MDL_TRANSACTION); if (lock_table_names(thd, table_list, table_list->next_global, thd->variables.lock_wait_timeout, === modified file 'sql/sql_base.cc' --- a/sql/sql_base.cc 2010-10-22 13:26:29 +0000 +++ b/sql/sql_base.cc 2010-11-03 14:30:33 +0000 @@ -2450,7 +2450,8 @@ open_table_get_mdl_lock(THD *thd, Open_t mdl_request_shared.init(&mdl_request->key, (flags & MYSQL_OPEN_FORCE_SHARED_MDL) ? - MDL_SHARED : MDL_SHARED_HIGH_PRIO); + MDL_SHARED : MDL_SHARED_HIGH_PRIO, + MDL_TRANSACTION); mdl_request= &mdl_request_shared; } @@ -2812,7 +2813,8 @@ bool open_table(THD *thd, TABLE_LIST *ta MYSQL_OPEN_FORCE_SHARED_MDL | MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL | MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) && - ! thd->locked_tables_mode) + ! thd->locked_tables_mode && + ! ot_ctx->has_protection_against_grl()) { MDL_request protection_request; MDL_deadlock_handler mdl_deadlock_handler(ot_ctx); @@ -2833,8 +2835,7 @@ bool open_table(THD *thd, TABLE_LIST *ta if (result) DBUG_RETURN(TRUE); - ot_ctx->set_protection_against_grl(&thd->mdl_context, - protection_request.ticket); + ot_ctx->set_has_protection_against_grl(); } @@ -3872,7 +3873,7 @@ Open_table_context::Open_table_context(T m_flags(flags), m_action(OT_NO_ACTION), m_has_locks(thd->mdl_context.has_locks()), - m_protection_against_grl(NULL) + m_has_protection_against_grl(FALSE) {} @@ -4030,11 +4031,11 @@ recover_from_failed_open(THD *thd) */ m_failed_table= NULL; /* - Reset pointer to ticket for metadata lock protecting - against GRL. It is no longer valid as the lock was + Reset flag indicating that we have already acquired protection + against GRL. It is no longer valid as the corresponding lock was released by close_tables_for_reopen(). */ - m_protection_against_grl= NULL; + m_has_protection_against_grl= FALSE; /* Prepare for possible another back-off. */ m_action= OT_NO_ACTION; return result; @@ -4573,7 +4574,8 @@ lock_table_names(THD *thd, if (schema_request == NULL) return TRUE; schema_request->init(MDL_key::SCHEMA, table->db, "", - MDL_INTENTION_EXCLUSIVE); + MDL_INTENTION_EXCLUSIVE, + MDL_TRANSACTION); mdl_requests.push_front(schema_request); } @@ -4926,16 +4928,6 @@ err: thd_proc_info(thd, 0); free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block - if (ot_ctx.get_protection_against_grl()) - { - /* - Move ticket for metadata lock which protects this statement from - a GRL to the front of ticket list. This allows to find it quickly - when this lock has to be released at the end of statement. - */ - thd->mdl_context.move_ticket_to_front(ot_ctx.get_protection_against_grl()); - } - if (error && *table_to_open) { (*table_to_open)->table= NULL; @@ -5376,16 +5368,6 @@ end: close_thread_tables(thd); } - if (ot_ctx.get_protection_against_grl()) - { - /* - Move ticket for metadata lock which protects this statement from - a GRL to the front of ticket list. This allows to find it quickly - when this lock has to be released at the end of statement. - */ - thd->mdl_context.move_ticket_to_front(ot_ctx.get_protection_against_grl()); - } - thd_proc_info(thd, 0); DBUG_RETURN(table); } @@ -5416,7 +5398,7 @@ bool open_and_lock_tables(THD *thd, TABL Prelocking_strategy *prelocking_strategy) { uint counter; - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_and_lock_tables"); DBUG_PRINT("enter", ("derived handling: %d", derived)); @@ -5473,7 +5455,7 @@ bool open_normal_and_derived_tables(THD { DML_prelocking_strategy prelocking_strategy; uint counter; - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("open_normal_and_derived_tables"); DBUG_ASSERT(!thd->fill_derived_tables()); if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) || @@ -5730,7 +5712,7 @@ bool lock_tables(THD *thd, TABLE_LIST *t */ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, - MDL_ticket *start_of_statement_svp) + const MDL_savepoint &start_of_statement_svp) { TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); TABLE_LIST *tmp; === modified file 'sql/sql_base.h' --- a/sql/sql_base.h 2010-10-18 12:33:49 +0000 +++ b/sql/sql_base.h 2010-11-03 14:30:33 +0000 @@ -159,7 +159,7 @@ thr_lock_type read_lock_type_for_table(T my_bool mysql_rm_tmp_tables(void); bool rm_temporary_table(handlerton *base, char *path); void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, - MDL_ticket *start_of_statement_svp); + const MDL_savepoint &start_of_statement_svp); TABLE_LIST *find_table_in_list(TABLE_LIST *table, TABLE_LIST *TABLE_LIST::*link, const char *db_name, @@ -508,7 +508,7 @@ public: the statement, so that we can rollback to it before waiting on locks. */ - MDL_ticket *start_of_statement_svp() const + const MDL_savepoint &start_of_statement_svp() const { return m_start_of_statement_svp; } @@ -521,19 +521,17 @@ public: uint get_flags() const { return m_flags; } /** - Set ticket for metadata lock which protects this statement against GRL - if it was acquired while opening tables. + Set flag indicating that we have already acquired metadata lock + protecting this statement against GRL while opening tables. */ - void set_protection_against_grl(MDL_context *mdl_ctx, MDL_ticket *ticket) + void set_has_protection_against_grl() { - if (! m_protection_against_grl && - ! mdl_ctx->has_lock(m_start_of_statement_svp, ticket)) - m_protection_against_grl= ticket; + m_has_protection_against_grl= TRUE; } - MDL_ticket* get_protection_against_grl() const + bool has_protection_against_grl() const { - return m_protection_against_grl; + return m_has_protection_against_grl; } private: @@ -543,7 +541,7 @@ private: should be repaired. */ TABLE_LIST *m_failed_table; - MDL_ticket *m_start_of_statement_svp; + MDL_savepoint m_start_of_statement_svp; /** Lock timeout in seconds. Initialized to LONG_TIMEOUT when opening system tables or to the "lock_wait_timeout" system variable for regular tables. @@ -560,12 +558,10 @@ private: */ bool m_has_locks; /** - Metadata lock which protects this statement against global read lock - and which might needed to be individually released at the end of - statement execution. NULL in cases when such protection is unneeded - or when we know that this statement will release all locks at its end. + Indicates that in the process of opening tables we have acquired + protection against global read lock. */ - MDL_ticket *m_protection_against_grl; + bool m_has_protection_against_grl; }; === modified file 'sql/sql_class.cc' --- a/sql/sql_class.cc 2010-10-19 13:31:53 +0000 +++ b/sql/sql_class.cc 2010-11-03 14:30:33 +0000 @@ -3458,15 +3458,15 @@ void THD::set_mysys_var(struct st_my_thr void THD::leave_locked_tables_mode() { locked_tables_mode= LTM_NONE; - mdl_context.reset_trans_sentinel(NULL); + mdl_context.set_transaction_duration_for_all_locks(); /* Make sure we don't release the global read lock and commit blocker when leaving LTM. */ - global_read_lock.move_tickets_after_trans_sentinel(this); + global_read_lock.set_explicit_lock_duration(this); /* Also ensure that we don't release metadata locks for open HANDLERs. */ if (handler_tables_hash.records) - mysql_ha_move_tickets_after_trans_sentinel(this); + mysql_ha_set_explicit_lock_duration(this); } void THD::get_definer(LEX_USER *definer) === modified file 'sql/sql_class.h' --- a/sql/sql_class.h 2010-10-26 04:25:15 +0000 +++ b/sql/sql_class.h 2010-11-03 14:30:33 +0000 @@ -822,8 +822,8 @@ struct st_savepoint { char *name; uint length; Ha_trx_info *ha_list; - /** Last acquired lock before this savepoint was set. */ - MDL_ticket *mdl_savepoint; + /** State of metadata locks before this savepoint was set. */ + MDL_savepoint mdl_savepoint; }; enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED, XA_ROLLBACK_ONLY}; @@ -1058,12 +1058,12 @@ class Open_tables_backup: public Open_ta public: /** When we backup the open tables state to open a system - table or tables, points at the last metadata lock - acquired before the backup. Is used to release - metadata locks on system tables after they are + table or tables, we want to save state of metadata + locks which were acquired before the backup. It is used + to release metadata locks on system tables after they are no longer used. */ - MDL_ticket *mdl_system_tables_svp; + MDL_savepoint mdl_system_tables_svp; }; /** @@ -1346,10 +1346,9 @@ public: bool can_acquire_protection(); bool has_read_lock() const { return (m_state != GRL_NONE); } void init_protection_request(MDL_request *request); - void release_protection_if_set(THD *thd); bool make_global_read_lock_block_commit(THD *thd); bool is_acquired() const { return m_state != GRL_NONE; } - void move_tickets_after_trans_sentinel(THD *thd); + void set_explicit_lock_duration(THD *thd); private: enum_grl_state m_state; /** @@ -2699,7 +2698,7 @@ public: { DBUG_ASSERT(locked_tables_mode == LTM_NONE); - mdl_context.set_trans_sentinel(); + mdl_context.set_explicit_duration_for_all_locks(); locked_tables_mode= mode_arg; } void leave_locked_tables_mode(); === modified file 'sql/sql_db.cc' --- a/sql/sql_db.cc 2010-07-19 08:27:53 +0000 +++ b/sql/sql_db.cc 2010-11-03 14:30:33 +0000 @@ -1054,7 +1054,8 @@ static long mysql_rm_known_files(THD *th table_list->alias= table_list->table_name; // If lower_case_table_names=2 table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix); table_list->mdl_request.init(MDL_key::TABLE, table_list->db, - table_list->table_name, MDL_EXCLUSIVE); + table_list->table_name, MDL_EXCLUSIVE, + MDL_TRANSACTION); /* Link into list */ (*tot_list_next_local)= table_list; (*tot_list_next_global)= table_list; === modified file 'sql/sql_handler.cc' --- a/sql/sql_handler.cc 2010-10-18 12:33:49 +0000 +++ b/sql/sql_handler.cc 2010-11-03 14:30:33 +0000 @@ -179,7 +179,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST uint dblen, namelen, aliaslen, counter; bool error; TABLE *backup_open_tables; - MDL_ticket *mdl_savepoint; + MDL_savepoint mdl_savepoint; DBUG_ENTER("mysql_ha_open"); DBUG_PRINT("enter",("'%s'.'%s' as '%s' reopen: %d", tables->db, tables->table_name, tables->alias, @@ -248,7 +248,13 @@ 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); - hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED); + /* + We can't request lock with explicit duration for this table + right from the start as open_tables() can't handle properly + back-off for such locks. + */ + hash_tables->mdl_request.init(MDL_key::TABLE, db, name, MDL_SHARED, + MDL_TRANSACTION); /* for now HANDLER can be used only for real TABLES */ hash_tables->required_type= FRMTYPE_TABLE; @@ -328,8 +334,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST thd->set_open_tables(backup_open_tables); if (hash_tables->mdl_request.ticket) { - thd->mdl_context. - move_ticket_after_trans_sentinel(hash_tables->mdl_request.ticket); + thd->mdl_context.set_lock_duration(hash_tables->mdl_request.ticket, + MDL_EXPLICIT); thd->mdl_context.set_needs_thr_lock_abort(TRUE); } @@ -965,24 +971,23 @@ void mysql_ha_cleanup(THD *thd) /** - Move tickets for metadata locks corresponding to open HANDLERs - after transaction sentinel in order to protect them from being - released at the end of transaction. + Set explicit duration for metadata locks corresponding to open HANDLERs + to protect them from being released at the end of transaction. @param thd Thread identifier. */ -void mysql_ha_move_tickets_after_trans_sentinel(THD *thd) +void mysql_ha_set_explicit_lock_duration(THD *thd) { TABLE_LIST *hash_tables; - DBUG_ENTER("mysql_ha_move_tickets_after_trans_sentinel"); + DBUG_ENTER("mysql_ha_set_explicit_lock_duration"); for (uint i= 0; i < thd->handler_tables_hash.records; i++) { hash_tables= (TABLE_LIST*) my_hash_element(&thd->handler_tables_hash, i); if (hash_tables->table && hash_tables->table->mdl_ticket) - thd->mdl_context. - move_ticket_after_trans_sentinel(hash_tables->table->mdl_ticket); + thd->mdl_context.set_lock_duration(hash_tables->table->mdl_ticket, + MDL_EXPLICIT); } DBUG_VOID_RETURN; } === modified file 'sql/sql_handler.h' --- a/sql/sql_handler.h 2010-06-09 08:39:09 +0000 +++ b/sql/sql_handler.h 2010-11-03 14:30:33 +0000 @@ -31,6 +31,6 @@ void mysql_ha_flush(THD *thd); void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables); void mysql_ha_rm_tables(THD *thd, TABLE_LIST *tables); void mysql_ha_cleanup(THD *thd); -void mysql_ha_move_tickets_after_trans_sentinel(THD *thd); +void mysql_ha_set_explicit_lock_duration(THD *thd); #endif /* SQL_HANDLER_INCLUDED */ === modified file 'sql/sql_insert.cc' --- a/sql/sql_insert.cc 2010-10-27 10:39:04 +0000 +++ b/sql/sql_insert.cc 2010-11-03 14:30:33 +0000 @@ -591,12 +591,7 @@ bool open_and_lock_for_insert_delayed(TH this or another tables (updating the same table is of course illegal, but such an attempt can be discovered only later during statement execution). - - Move ticket protecting from a GRL to the front of ticket list. - This allows to find it quickly when this lock has to be released - at the end of statement. */ - thd->mdl_context.move_ticket_to_front(protection_request.ticket); /* Reset the ticket in case we end up having to use normal insert and @@ -2120,7 +2115,7 @@ bool delayed_get_table(THD *thd, MDL_req di->table_list.db= di->thd.db; /* We need the tickets so that they can be cloned in handle_delayed_insert */ di->grl_protection.init(MDL_key::GLOBAL, "", "", - MDL_INTENTION_EXCLUSIVE); + MDL_INTENTION_EXCLUSIVE, MDL_STATEMENT); di->grl_protection.ticket= grl_protection_request->ticket; init_mdl_requests(&di->table_list); di->table_list.mdl_request.ticket= table_list->mdl_request.ticket; === modified file 'sql/sql_parse.cc' --- a/sql/sql_parse.cc 2010-10-26 04:25:15 +0000 +++ b/sql/sql_parse.cc 2010-11-03 14:30:33 +0000 @@ -1085,7 +1085,7 @@ bool dispatch_command(enum enum_server_c SHOW statements should not add the used tables to the list of tables used in a transaction. */ - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); status_var_increment(thd->status_var.com_stat[SQLCOM_SHOW_FIELDS]); if (thd->copy_db_to(&db.str, &db.length)) @@ -4362,7 +4362,7 @@ finish: } else if (! thd->in_sub_stmt && !thd->locked_tables_mode) { - thd->global_read_lock.release_protection_if_set(thd); + thd->mdl_context.release_statement_locks(); } DBUG_RETURN(res || thd->is_error()); @@ -5826,7 +5826,8 @@ 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_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type); + ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type, + MDL_TRANSACTION); DBUG_RETURN(ptr); } === modified file 'sql/sql_prepare.cc' --- a/sql/sql_prepare.cc 2010-08-18 09:35:41 +0000 +++ b/sql/sql_prepare.cc 2010-11-03 14:30:33 +0000 @@ -3168,7 +3168,6 @@ bool Prepared_statement::prepare(const c bool error; Statement stmt_backup; Query_arena *old_stmt_arena; - MDL_ticket *mdl_savepoint= NULL; DBUG_ENTER("Prepared_statement::prepare"); /* If this is an SQLCOM_PREPARE, we also increase Com_prepare_sql. @@ -3240,7 +3239,7 @@ bool Prepared_statement::prepare(const c Marker used to release metadata locks acquired while the prepared statement is being checked. */ - mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); /* The only case where we should have items in the thd->free_list is === modified file 'sql/sql_show.cc' --- a/sql/sql_show.cc 2010-10-14 16:56:56 +0000 +++ b/sql/sql_show.cc 2010-11-03 14:30:33 +0000 @@ -675,7 +675,7 @@ mysqld_show_create(THD *thd, TABLE_LIST Metadata locks taken during SHOW CREATE should be released when the statmement completes as it is an information statement. */ - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); /* We want to preserve the tree for views. */ thd->lex->view_prepare_mode= TRUE; @@ -3190,7 +3190,7 @@ try_acquire_high_prio_shared_mdl_lock(TH { bool error; table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name, - MDL_SHARED_HIGH_PRIO); + MDL_SHARED_HIGH_PRIO, MDL_TRANSACTION); if (can_deadlock) { @@ -7749,7 +7749,7 @@ bool show_create_trigger(THD *thd, const Metadata locks taken during SHOW CREATE TRIGGER should be released when the statement completes as it is an information statement. */ - MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint(); + MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); /* Open the table by name in order to load Table_triggers_list object. === modified file 'sql/sql_table.cc' --- a/sql/sql_table.cc 2010-10-18 12:33:49 +0000 +++ b/sql/sql_table.cc 2010-11-03 14:30:33 +0000 @@ -5579,7 +5579,6 @@ bool mysql_alter_table(THD *thd,char *ne TABLE *table, *new_table= 0; MDL_ticket *mdl_ticket; MDL_request target_mdl_request; - bool has_target_mdl_lock= FALSE; int error= 0; char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN + 1]; char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias; @@ -5741,7 +5740,7 @@ bool mysql_alter_table(THD *thd,char *ne else { target_mdl_request.init(MDL_key::TABLE, new_db, new_name, - MDL_EXCLUSIVE); + MDL_EXCLUSIVE, MDL_TRANSACTION); /* Global intention exclusive lock must have been already acquired when table to be altered was open, so there is no need to do it here. @@ -5759,7 +5758,6 @@ bool mysql_alter_table(THD *thd,char *ne DBUG_RETURN(TRUE); } DEBUG_SYNC(thd, "locked_table_name"); - has_target_mdl_lock= TRUE; /* Table maybe does not exist, but we got an exclusive lock on the name, now we can safely try to find out for sure. @@ -5946,10 +5944,7 @@ bool mysql_alter_table(THD *thd,char *ne along with the implicit commit. */ if (new_name != table_name || new_db != db) - { - thd->mdl_context.release_lock(target_mdl_request.ticket); thd->mdl_context.release_all_locks_for_name(mdl_ticket); - } else mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); } @@ -6654,10 +6649,7 @@ bool mysql_alter_table(THD *thd,char *ne thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) { if ((new_name != table_name || new_db != db)) - { - thd->mdl_context.release_lock(target_mdl_request.ticket); thd->mdl_context.release_all_locks_for_name(mdl_ticket); - } else mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE); } @@ -6718,8 +6710,6 @@ err: alter_info->datetime_field->field_name); thd->abort_on_warning= save_abort_on_warning; } - if (has_target_mdl_lock) - thd->mdl_context.release_lock(target_mdl_request.ticket); DBUG_RETURN(TRUE); @@ -6731,9 +6721,6 @@ err_with_mdl: tables and release the exclusive metadata lock. */ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0); - if (has_target_mdl_lock) - thd->mdl_context.release_lock(target_mdl_request.ticket); - thd->mdl_context.release_all_locks_for_name(mdl_ticket); DBUG_RETURN(TRUE); } === modified file 'sql/table.cc' --- a/sql/table.cc 2010-10-04 12:42:16 +0000 +++ b/sql/table.cc 2010-11-03 14:30:33 +0000 @@ -5219,7 +5219,8 @@ void init_mdl_requests(TABLE_LIST *table table_list->mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name, table_list->lock_type >= TL_WRITE_ALLOW_WRITE ? - MDL_SHARED_WRITE : MDL_SHARED_READ); + MDL_SHARED_WRITE : MDL_SHARED_READ, + MDL_TRANSACTION); } === modified file 'sql/table.h' --- a/sql/table.h 2010-10-06 14:34:28 +0000 +++ b/sql/table.h 2010-11-03 14:30:33 +0000 @@ -1384,7 +1384,8 @@ struct TABLE_LIST lock_type= lock_type_arg; mdl_request.init(MDL_key::TABLE, db, table_name, (lock_type >= TL_WRITE_ALLOW_WRITE) ? - MDL_SHARED_WRITE : MDL_SHARED_READ); + MDL_SHARED_WRITE : MDL_SHARED_READ, + MDL_TRANSACTION); } /* === modified file 'sql/transaction.cc' --- a/sql/transaction.cc 2010-10-28 19:35:28 +0000 +++ b/sql/transaction.cc 2010-11-03 14:30:33 +0000 @@ -392,15 +392,15 @@ bool trans_savepoint(THD *thd, LEX_STRIN thd->transaction.savepoints= newsv; /* - Remember the last acquired lock before the savepoint was set. - This is used as a marker to only release locks acquired after + Remember locks acquired before the savepoint was set. + They are used as a marker to only release locks acquired after the setting of this savepoint. Note: this works just fine if we're under LOCK TABLES, since mdl_savepoint() is guaranteed to be beyond the last locked table. This allows to release some locks acquired during LOCK TABLES. */ - newsv->mdl_savepoint = thd->mdl_context.mdl_savepoint(); + newsv->mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_RETURN(FALSE); } @@ -655,7 +655,8 @@ bool trans_xa_commit(THD *thd) We allow FLUSHer to COMMIT; we assume FLUSHer knows what it does. */ - mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE); + mdl_request.init(MDL_key::COMMIT, "", "", MDL_INTENTION_EXCLUSIVE, + MDL_TRANSACTION); if (thd->mdl_context.acquire_lock(&mdl_request, thd->variables.lock_wait_timeout)) --===============2132867028== MIME-Version: 1.0 Content-Type: text/bzr-bundle; charset="us-ascii"; name="bzr/dmitry.lenev@stripped" Content-Transfer-Encoding: 7bit Content-Disposition: inline # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: dmitry.lenev@stripped # target_branch: file:///home/dlenev/src/bzr/mysql-5.5-rt-grl/ # testament_sha1: febd7b8375e4ce6ec635a2cb2904b0c0c1b9b25c # timestamp: 2010-11-03 17:31:14 +0300 # base_revision_id: kostja@stripped # # Begin bundle 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 pwoSGgk2dMA= --===============2132867028==--