summaryrefslogtreecommitdiff
path: root/sql/sql_base.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r--sql/sql_base.cc283
1 files changed, 153 insertions, 130 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 02871c118ca..7650d854efb 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -125,7 +125,8 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
uint db_stat, uint prgflag,
uint ha_open_flags, TABLE *outparam,
TABLE_LIST *table_desc, MEM_ROOT *mem_root);
-static bool tdc_wait_for_old_versions(THD *thd, MDL_context *context);
+static bool tdc_wait_for_old_versions(THD *thd,
+ MDL_request_list *mdl_requests);
static bool
has_write_table_with_auto_increment(TABLE_LIST *tables);
@@ -1360,8 +1361,7 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
leave prelocked mode if needed.
*/
-void close_thread_tables(THD *thd,
- bool is_back_off)
+void close_thread_tables(THD *thd)
{
TABLE *table;
DBUG_ENTER("close_thread_tables");
@@ -1471,10 +1471,6 @@ void close_thread_tables(THD *thd,
thd->locked_tables_mode= LTM_NONE;
- /*
- Note that we are leaving prelocked mode so we don't need
- to care about THD::locked_tables_root.
- */
/* Fallthrough */
}
@@ -1503,11 +1499,6 @@ void close_thread_tables(THD *thd,
if (thd->open_tables)
close_open_tables(thd);
- if (!is_back_off)
- {
- thd->mdl_context.remove_all_requests();
- }
-
/*
Defer the release of metadata locks until the current transaction
is either committed or rolled back. This prevents other statements
@@ -2316,10 +2307,10 @@ void table_share_release_hook(void *share)
static bool
open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
MDL_request *mdl_request,
- uint flags,
- enum_open_table_action *action)
+ Open_table_context *ot_ctx,
+ uint flags)
{
- thd->mdl_context.add_request(mdl_request);
+ ot_ctx->add_request(mdl_request);
if (table_list->lock_strategy)
{
@@ -2333,16 +2324,13 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
enforced by asserts in metadata locking subsystem.
*/
mdl_request->set_type(MDL_EXCLUSIVE);
- if (thd->mdl_context.acquire_exclusive_locks())
- {
- thd->mdl_context.remove_request(mdl_request);
+ DBUG_ASSERT(! thd->mdl_context.has_locks());
+
+ if (thd->mdl_context.acquire_exclusive_lock(mdl_request))
return 1;
- }
}
else
{
- bool retry;
-
/*
There is no MDL_SHARED_UPGRADABLE_HIGH_PRIO type of metadata lock so we
want to be sure that caller doesn't pass us both flags simultaneously.
@@ -2356,12 +2344,11 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
if (flags & MYSQL_LOCK_IGNORE_FLUSH)
mdl_request->set_type(MDL_SHARED_HIGH_PRIO);
- if (thd->mdl_context.acquire_shared_lock(mdl_request, &retry))
+ if (thd->mdl_context.try_acquire_shared_lock(mdl_request))
+ return 1;
+ if (mdl_request->ticket == NULL)
{
- if (retry)
- *action= OT_BACK_OFF_AND_RETRY;
- else
- thd->mdl_context.remove_request(mdl_request);
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT);
return 1;
}
}
@@ -2416,7 +2403,7 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
- enum_open_table_action *action, uint flags)
+ Open_table_context *ot_ctx, uint flags)
{
reg1 TABLE *table;
char key[MAX_DBKEY_LENGTH];
@@ -2428,11 +2415,6 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
TABLE_SHARE *share;
DBUG_ENTER("open_table");
- /* Parsing of partitioning information from .frm needs thd->lex set up. */
- DBUG_ASSERT(thd->lex->is_lex_started);
-
- *action= OT_NO_ACTION;
-
/* an open table operation needs a lot of the stack space */
if (check_stack_overrun(thd, STACK_MIN_SIZE_FOR_OPEN, (uchar *)&alias))
DBUG_RETURN(TRUE);
@@ -2602,12 +2584,15 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
This is the normal use case.
*/
- mdl_request= table_list->mdl_request;
+ mdl_request= &table_list->mdl_request;
if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
{
- if (open_table_get_mdl_lock(thd, table_list, mdl_request, flags,
- action))
+ if (open_table_get_mdl_lock(thd, table_list, mdl_request, ot_ctx, flags))
+ {
+ DEBUG_SYNC(thd, "before_open_table_wait_refresh");
DBUG_RETURN(TRUE);
+ }
+ DEBUG_SYNC(thd, "after_open_table_mdl_shared");
}
/*
@@ -2633,8 +2618,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
! (flags & MYSQL_LOCK_IGNORE_FLUSH))
{
/* Someone did a refresh while thread was opening tables */
- *action= OT_BACK_OFF_AND_RETRY;
pthread_mutex_unlock(&LOCK_open);
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT);
DBUG_RETURN(TRUE);
}
@@ -2766,9 +2751,9 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
separately in the caller for old table versions to go away
(see tdc_wait_for_old_versions()).
*/
- *action= OT_BACK_OFF_AND_RETRY;
release_table_share(share);
pthread_mutex_unlock(&LOCK_open);
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT);
DBUG_RETURN(TRUE);
}
/* Force close at once after usage */
@@ -2808,12 +2793,12 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (error == 7)
{
share->version= 0;
- *action= OT_DISCOVER;
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER);
}
else if (share->crashed)
{
share->version= 0;
- *action= OT_REPAIR;
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR);
}
goto err_unlock;
@@ -2891,10 +2876,8 @@ err_unlock:
err_unlock2:
pthread_mutex_unlock(&LOCK_open);
if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
- {
thd->mdl_context.release_lock(mdl_ticket);
- thd->mdl_context.remove_request(mdl_request);
- }
+
DBUG_RETURN(TRUE);
}
@@ -3004,6 +2987,9 @@ Locked_tables_list::init_locked_tables(THD *thd)
return TRUE;
}
+ memcpy(db, src_table_list->db, db_len + 1);
+ memcpy(table_name, src_table_list->table_name, table_name_len + 1);
+ memcpy(alias, src_table_list->alias, alias_len + 1);
/**
Sic: remember the *actual* table level lock type taken, to
acquire the exact same type in reopen_tables().
@@ -3014,11 +3000,9 @@ Locked_tables_list::init_locked_tables(THD *thd)
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_request= src_table_list->mdl_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);
- memcpy(alias, src_table_list->alias, alias_len + 1);
+ dst_table_list->mdl_request.ticket= src_table_list->mdl_request.ticket;
+
/* Link last into the list of tables */
*(dst_table_list->prev_global= m_locked_tables_last)= dst_table_list;
m_locked_tables_last= &dst_table_list->next_global;
@@ -3227,7 +3211,7 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count)
bool
Locked_tables_list::reopen_tables(THD *thd)
{
- enum enum_open_table_action ot_action_unused;
+ Open_table_context ot_ctx_unused(thd);
bool lt_refresh_unused;
size_t reopen_count= 0;
MYSQL_LOCK *lock;
@@ -3240,7 +3224,7 @@ Locked_tables_list::reopen_tables(THD *thd)
continue;
/* Links into thd->open_tables upon success */
- if (open_table(thd, table_list, thd->mem_root, &ot_action_unused,
+ if (open_table(thd, table_list, thd->mem_root, &ot_ctx_unused,
MYSQL_OPEN_REOPEN))
{
unlink_all_closed_tables(thd, 0, reopen_count);
@@ -3591,6 +3575,43 @@ end_with_lock_open:
}
+/** Open_table_context */
+
+Open_table_context::Open_table_context(THD *thd)
+ :m_action(OT_NO_ACTION),
+ m_can_deadlock(thd->in_multi_stmt_transaction() &&
+ thd->mdl_context.has_locks())
+{}
+
+
+/**
+ Check if we can back-off and set back off action if we can.
+ Otherwise report and return error.
+
+ @retval TRUE if back-off is impossible.
+ @retval FALSE if we can back off. Back off action has been set.
+*/
+
+bool
+Open_table_context::
+request_backoff_action(enum_open_table_action action_arg)
+{
+ /*
+ We have met a exclusive metadata lock or a old version of
+ table and we are inside a transaction that already hold locks.
+ We can't follow the locking protocol in this scenario as it
+ might lead to deadlocks.
+ */
+ if (m_can_deadlock)
+ {
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ return TRUE;
+ }
+ m_action= action_arg;
+ return FALSE;
+}
+
+
/**
Recover from failed attempt ot open table by performing requested action.
@@ -3604,57 +3625,58 @@ end_with_lock_open:
@retval TRUE - Error
*/
-static bool
-recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table,
- enum_open_table_action action)
+bool
+Open_table_context::
+recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table)
{
bool result= FALSE;
- MDL_request *mdl_request= table->mdl_request;
-
- switch (action)
+ /* Execute the action. */
+ switch (m_action)
{
- case OT_BACK_OFF_AND_RETRY:
- result= (thd->mdl_context.wait_for_locks() ||
- tdc_wait_for_old_versions(thd, &thd->mdl_context));
- thd->mdl_context.remove_all_requests();
+ case OT_WAIT:
+ result= (thd->mdl_context.wait_for_locks(&m_mdl_requests) ||
+ tdc_wait_for_old_versions(thd, &m_mdl_requests));
break;
case OT_DISCOVER:
- mdl_request->set_type(MDL_EXCLUSIVE);
- thd->mdl_context.add_request(mdl_request);
- if (thd->mdl_context.acquire_exclusive_locks())
{
- thd->mdl_context.remove_request(mdl_request);
- return TRUE;
+ MDL_request mdl_xlock_request(&table->mdl_request);
+ mdl_xlock_request.set_type(MDL_EXCLUSIVE);
+ if ((result=
+ thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request)))
+ break;
+ pthread_mutex_lock(&LOCK_open);
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
+ ha_create_table_from_engine(thd, table->db, table->table_name);
+ pthread_mutex_unlock(&LOCK_open);
+
+ thd->warning_info->clear_warning_info(thd->query_id);
+ thd->clear_error(); // Clear error message
+ thd->mdl_context.release_lock(mdl_xlock_request.ticket);
+ break;
}
- pthread_mutex_lock(&LOCK_open);
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
- ha_create_table_from_engine(thd, table->db, table->table_name);
- pthread_mutex_unlock(&LOCK_open);
-
- thd->warning_info->clear_warning_info(thd->query_id);
- thd->clear_error(); // Clear error message
- thd->mdl_context.release_lock(mdl_request->ticket);
- thd->mdl_context.remove_request(mdl_request);
- break;
case OT_REPAIR:
- mdl_request->set_type(MDL_EXCLUSIVE);
- thd->mdl_context.add_request(mdl_request);
- if (thd->mdl_context.acquire_exclusive_locks())
{
- thd->mdl_context.remove_request(mdl_request);
- return TRUE;
- }
- pthread_mutex_lock(&LOCK_open);
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
- pthread_mutex_unlock(&LOCK_open);
+ MDL_request mdl_xlock_request(&table->mdl_request);
+ mdl_xlock_request.set_type(MDL_EXCLUSIVE);
+ if ((result=
+ thd->mdl_context.acquire_exclusive_lock(&mdl_xlock_request)))
+ break;
- result= auto_repair_table(thd, table);
- thd->mdl_context.release_lock(mdl_request->ticket);
- thd->mdl_context.remove_request(mdl_request);
- break;
+ pthread_mutex_lock(&LOCK_open);
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
+ pthread_mutex_unlock(&LOCK_open);
+
+ result= auto_repair_table(thd, table);
+ thd->mdl_context.release_lock(mdl_xlock_request.ticket);
+ break;
+ }
default:
DBUG_ASSERT(0);
}
+ /* Remove all old requests, they will be re-added. */
+ m_mdl_requests.empty();
+ /* Prepare for possible another back-off. */
+ m_action= OT_NO_ACTION;
return result;
}
@@ -3722,14 +3744,13 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table)
int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
{
TABLE_LIST *tables= NULL;
- enum_open_table_action action;
+ Open_table_context ot_ctx(thd);
int result=0;
bool error;
MEM_ROOT new_frm_mem;
/* Also used for indicating that prelocking is need */
TABLE_LIST **query_tables_last_own;
bool safe_to_ignore_table;
- bool has_locks= thd->mdl_context.has_locks();
DBUG_ENTER("open_tables");
/*
temporary mem_root for new .frm parsing.
@@ -3856,32 +3877,20 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
*/
Prelock_error_handler prelock_handler;
thd->push_internal_handler(& prelock_handler);
- error= open_table(thd, tables, &new_frm_mem, &action, flags);
+ error= open_table(thd, tables, &new_frm_mem, &ot_ctx, flags);
thd->pop_internal_handler();
safe_to_ignore_table= prelock_handler.safely_trapped_errors();
}
else
- error= open_table(thd, tables, &new_frm_mem, &action, flags);
+ error= open_table(thd, tables, &new_frm_mem, &ot_ctx, flags);
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
if (error)
{
- if (action)
+ if (ot_ctx.can_recover_from_failed_open_table())
{
/*
- We have met a exclusive metadata lock or a old version of table and
- we are inside a transaction that already hold locks. We can't follow
- the locking protocol in this scenario as it might lead to deadlocks.
- */
- if (thd->in_multi_stmt_transaction() && has_locks)
- {
- my_error(ER_LOCK_DEADLOCK, MYF(0));
- result= -1;
- goto err;
- }
-
- /*
We have met exclusive metadata lock or old version of table. Now we
have to close all tables which are not up to date/release metadata
locks. We also have to throw away set of prelocked tables (and thus
@@ -3897,13 +3906,13 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
*/
if (query_tables_last_own)
thd->lex->mark_as_requiring_prelocking(query_tables_last_own);
- close_tables_for_reopen(thd, start, (action == OT_BACK_OFF_AND_RETRY));
+ close_tables_for_reopen(thd, start);
/*
Here we rely on the fact that 'tables' still points to the valid
TABLE_LIST element. Altough currently this assumption is valid
it may change in future.
*/
- if (recover_from_failed_open_table_attempt(thd, tables, action))
+ if (ot_ctx.recover_from_failed_open_table_attempt(thd, tables))
{
result= -1;
goto err;
@@ -4210,7 +4219,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
uint lock_flags)
{
TABLE *table;
- enum_open_table_action action;
+ Open_table_context ot_ctx(thd);
bool refresh;
bool error;
DBUG_ENTER("open_ltable");
@@ -4224,16 +4233,18 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
table_list->required_type= FRMTYPE_TABLE;
retry:
- while ((error= open_table(thd, table_list, thd->mem_root, &action, 0)) &&
- action)
+ while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, 0)) &&
+ ot_ctx.can_recover_from_failed_open_table())
{
/*
- Even altough we have failed to open table we still need to
- call close_thread_tables() to release metadata locks which
+ Even though we have failed to open table we still need to
+ call release_all_locks() to release metadata locks which
might have been acquired successfully.
*/
- close_thread_tables(thd, (action == OT_BACK_OFF_AND_RETRY));
- if (recover_from_failed_open_table_attempt(thd, table_list, action))
+ if (! thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
+ table_list->mdl_request.ticket= 0;
+ if (ot_ctx.recover_from_failed_open_table_attempt(thd, table_list))
break;
}
@@ -4272,8 +4283,20 @@ retry:
{
if (refresh)
{
- close_thread_tables(thd);
- goto retry;
+ if (ot_ctx.can_deadlock())
+ {
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ table= 0;
+ }
+ else
+ {
+ close_thread_tables(thd);
+ table_list->table= NULL;
+ table_list->mdl_request.ticket= NULL;
+ if (! thd->locked_tables_mode)
+ thd->mdl_context.release_all_locks();
+ goto retry;
+ }
}
else
table= 0;
@@ -4283,7 +4306,7 @@ retry:
else
table= 0;
- end:
+end:
thd_proc_info(thd, 0);
DBUG_RETURN(table);
}
@@ -4320,6 +4343,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
{
uint counter;
bool need_reopen;
+ bool has_locks= thd->mdl_context.has_locks();
DBUG_ENTER("open_and_lock_tables_derived");
DBUG_PRINT("enter", ("derived handling: %d", derived));
@@ -4339,7 +4363,12 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
break;
if (!need_reopen)
DBUG_RETURN(-1);
- close_tables_for_reopen(thd, &tables, FALSE);
+ if (thd->in_multi_stmt_transaction() && has_locks)
+ {
+ my_error(ER_LOCK_DEADLOCK, MYF(0));
+ DBUG_RETURN(-1);
+ }
+ close_tables_for_reopen(thd, &tables);
}
if (derived &&
(mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
@@ -4677,10 +4706,6 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count,
table->table->query_id= thd->query_id;
if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
{
- /*
- This was an attempt to enter prelocked mode so there is no
- need to care about THD::locked_tables_root here.
- */
mysql_unlock_tables(thd, thd->lock);
thd->lock= 0;
DBUG_RETURN(-1);
@@ -4767,7 +4792,7 @@ int lock_tables(THD *thd, TABLE_LIST *tables, uint count,
*/
-void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool is_back_off)
+void close_tables_for_reopen(THD *thd, TABLE_LIST **tables)
{
/*
If table list consists only from tables from prelocking set, table list
@@ -4778,8 +4803,11 @@ void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool is_back_off)
thd->lex->chop_off_not_own_tables();
sp_remove_not_own_routines(thd->lex);
for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global)
+ {
tmp->table= 0;
- close_thread_tables(thd, is_back_off);
+ tmp->mdl_request.ticket= NULL;
+ }
+ close_thread_tables(thd);
if (!thd->locked_tables_mode)
thd->mdl_context.release_all_locks();
}
@@ -7855,7 +7883,8 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
@param context Metadata locking context with locks.
*/
-static bool tdc_wait_for_old_versions(THD *thd, MDL_context *mdl_context)
+static bool
+tdc_wait_for_old_versions(THD *thd, MDL_request_list *mdl_requests)
{
TABLE_SHARE *share;
const char *old_msg;
@@ -7872,7 +7901,7 @@ static bool tdc_wait_for_old_versions(THD *thd, MDL_context *mdl_context)
mysql_ha_flush(thd);
pthread_mutex_lock(&LOCK_open);
- MDL_context::Request_iterator it= mdl_context->get_requests();
+ MDL_request_list::Iterator it(*mdl_requests);
while ((mdl_request= it++))
{
if ((share= get_cached_table_share(mdl_request->key.db_name(),
@@ -8095,8 +8124,6 @@ open_system_tables_for_read(THD *thd, TABLE_LIST *table_list,
DBUG_ENTER("open_system_tables_for_read");
- alloc_mdl_requests(table_list, thd->mem_root);
-
/*
Besides using new Open_tables_state for opening system tables,
we also have to backup and reset/and then restore part of LEX
@@ -8170,8 +8197,6 @@ open_system_table_for_update(THD *thd, TABLE_LIST *one_table)
{
DBUG_ENTER("open_system_table_for_update");
- alloc_mdl_requests(one_table, thd->mem_root);
-
TABLE *table= open_ltable(thd, one_table, one_table->lock_type, 0);
if (table)
{
@@ -8208,7 +8233,6 @@ open_performance_schema_table(THD *thd, TABLE_LIST *one_table,
thd->reset_n_backup_open_tables_state(backup);
- 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);
@@ -8286,7 +8310,6 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup)
pthread_mutex_unlock(&LOCK_open);
thd->mdl_context.release_all_locks();
- thd->mdl_context.remove_all_requests();
thd->restore_backup_open_tables_state(backup);
}