summaryrefslogtreecommitdiff
path: root/sql/sql_show.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_show.cc')
-rw-r--r--sql/sql_show.cc185
1 files changed, 144 insertions, 41 deletions
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index a745fe151a5..6d9554919ee 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -2868,7 +2868,11 @@ make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex,
@param[in] thd thread handler
@param[in] tables TABLE_LIST for I_S table
@param[in] schema_table pointer to I_S structure
- @param[in] open_tables_state_backup pointer to Open_tables_state object
+ @param[in] can_deadlock Indicates that deadlocks are possible
+ due to metadata locks, so to avoid
+ them we should not wait in case if
+ conflicting lock is present.
+ @param[in] open_tables_state_backup pointer to Open_tables_backup object
which is used to save|restore original
status of variables related to
open tables state
@@ -2881,7 +2885,8 @@ make_table_name_list(THD *thd, List<LEX_STRING> *table_names, LEX *lex,
static int
fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables,
ST_SCHEMA_TABLE *schema_table,
- Open_tables_state *open_tables_state_backup)
+ bool can_deadlock,
+ Open_tables_backup *open_tables_state_backup)
{
LEX *lex= thd->lex;
bool res;
@@ -2909,7 +2914,9 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables,
*/
lex->sql_command= SQLCOM_SHOW_FIELDS;
res= open_normal_and_derived_tables(thd, show_table_list,
- MYSQL_LOCK_IGNORE_FLUSH);
+ (MYSQL_LOCK_IGNORE_FLUSH |
+ (can_deadlock ?
+ MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)));
lex->sql_command= save_sql_command;
/*
get_all_tables() returns 1 on failure and 0 on success thus
@@ -2935,7 +2942,8 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables,
table, res, db_name,
table_name));
thd->temporary_tables= 0;
- close_tables_for_reopen(thd, &show_table_list);
+ close_tables_for_reopen(thd, &show_table_list,
+ open_tables_state_backup->mdl_system_tables_svp);
DBUG_RETURN(error);
}
@@ -3048,6 +3056,47 @@ uint get_table_open_method(TABLE_LIST *tables,
/**
+ Try acquire high priority share metadata lock on a table (with
+ optional wait for conflicting locks to go away).
+
+ @param thd Thread context.
+ @param mdl_request Pointer to memory to be used for MDL_request
+ object for a lock request.
+ @param table Table list element for the table
+ @param can_deadlock Indicates that deadlocks are possible due to
+ metadata locks, so to avoid them we should not
+ wait in case if conflicting lock is present.
+
+ @note This is an auxiliary function to be used in cases when we want to
+ access table's description by looking up info in TABLE_SHARE without
+ going through full-blown table open.
+ @note This function assumes that there are no other metadata lock requests
+ in the current metadata locking context.
+
+ @retval FALSE No error, if lock was obtained TABLE_LIST::mdl_request::ticket
+ is set to non-NULL value.
+ @retval TRUE Some error occured (probably thread was killed).
+*/
+
+static bool
+try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table,
+ bool can_deadlock)
+{
+ bool error;
+ table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
+ MDL_SHARED_HIGH_PRIO);
+ while (!(error=
+ thd->mdl_context.try_acquire_lock(&table->mdl_request)) &&
+ !table->mdl_request.ticket && !can_deadlock)
+ {
+ if ((error= thd->mdl_context.wait_for_lock(&table->mdl_request)))
+ break;
+ }
+ return error;
+}
+
+
+/**
@brief Fill I_S table with data from FRM file only
@param[in] thd thread handler
@@ -3056,6 +3105,10 @@ uint get_table_open_method(TABLE_LIST *tables,
@param[in] db_name database name
@param[in] table_name table name
@param[in] schema_table_idx I_S table index
+ @param[in] can_deadlock Indicates that deadlocks are possible
+ due to metadata locks, so to avoid
+ them we should not wait in case if
+ conflicting lock is present.
@return Operation status
@retval 0 Table is processed and we can continue
@@ -3068,13 +3121,14 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
ST_SCHEMA_TABLE *schema_table,
LEX_STRING *db_name,
LEX_STRING *table_name,
- enum enum_schema_tables schema_table_idx)
+ enum enum_schema_tables schema_table_idx,
+ bool can_deadlock)
{
TABLE_SHARE *share;
TABLE tbl;
TABLE_LIST table_list;
uint res= 0;
- int error;
+ int not_used;
char key[MAX_DBKEY_LENGTH];
uint key_length;
char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
@@ -3102,14 +3156,45 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
table_list.db= db_name->str;
}
+ /*
+ TODO: investigate if in this particular situation we can get by
+ simply obtaining internal lock of data-dictionary (ATM it
+ is LOCK_open) instead of obtaning full-blown metadata lock.
+ */
+ if (try_acquire_high_prio_shared_mdl_lock(thd, &table_list, can_deadlock))
+ {
+ /*
+ Some error occured (most probably we have been killed while
+ waiting for conflicting locks to go away), let the caller to
+ handle the situation.
+ */
+ return 1;
+ }
+
+ if (! table_list.mdl_request.ticket)
+ {
+ /*
+ We are in situation when we have encountered conflicting metadata
+ lock and deadlocks can occur due to waiting for it to go away.
+ So instead of waiting skip this table with an appropriate warning.
+ */
+ DBUG_ASSERT(can_deadlock);
+
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_I_S_SKIPPED_TABLE,
+ ER(ER_WARN_I_S_SKIPPED_TABLE),
+ table_list.db, table_list.table_name);
+ return 0;
+ }
+
key_length= create_table_def_key(thd, key, &table_list, 0);
mysql_mutex_lock(&LOCK_open);
share= get_table_share(thd, &table_list, key,
- key_length, OPEN_VIEW, &error);
+ key_length, OPEN_VIEW, &not_used);
if (!share)
{
res= 0;
- goto err;
+ goto end_unlock;
}
if (share->is_view)
@@ -3118,7 +3203,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
{
/* skip view processing */
res= 0;
- goto err1;
+ goto end_share;
}
else if (schema_table->i_s_requested_object & OPEN_VIEW_FULL)
{
@@ -3127,7 +3212,7 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
open_normal_and_derived_tables()
*/
res= 1;
- goto err1;
+ goto end_share;
}
}
@@ -3143,14 +3228,20 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
res= schema_table->process_table(thd, &table_list, table,
res, db_name, table_name);
closefrm(&tbl, true);
- goto err;
+ goto end_unlock;
}
-err1:
- release_table_share(share, RELEASE_NORMAL);
+end_share:
+ release_table_share(share);
-err:
+end_unlock:
mysql_mutex_unlock(&LOCK_open);
+ /*
+ Don't release the MDL lock, it can be part of a transaction.
+ If it is not, it will be released by the call to
+ MDL_context::rollback_to_savepoint() in the caller.
+ */
+
thd->clear_error();
return res;
}
@@ -3194,22 +3285,35 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
COND *partial_cond= 0;
uint derived_tables= lex->derived_tables;
int error= 1;
- Open_tables_state open_tables_state_backup;
+ Open_tables_backup open_tables_state_backup;
bool save_view_prepare_mode= lex->view_prepare_mode;
Query_tables_list query_tables_list_backup;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *sctx= thd->security_ctx;
#endif
uint table_open_method;
+ bool can_deadlock;
DBUG_ENTER("get_all_tables");
+ /*
+ In cases when SELECT from I_S table being filled by this call is
+ part of statement which also uses other tables or is being executed
+ under LOCK TABLES or is part of transaction which also uses other
+ tables waiting for metadata locks which happens below might result
+ in deadlocks.
+ To avoid them we don't wait if conflicting metadata lock is
+ encountered and skip table with emitting an appropriate warning.
+ */
+ can_deadlock= thd->mdl_context.has_locks();
+
lex->view_prepare_mode= TRUE;
lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
/*
We should not introduce deadlocks even if we already have some
tables open and locked, since we won't lock tables which we will
- open and will ignore possible name-locks for these tables.
+ open and will ignore pending exclusive metadata locks for these
+ tables by using high-priority requests for shared metadata locks.
*/
thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
@@ -3221,6 +3325,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
if (lsel && lsel->table_list.first)
{
error= fill_schema_show_cols_or_idxs(thd, tables, schema_table,
+ can_deadlock,
&open_tables_state_backup);
goto err;
}
@@ -3336,7 +3441,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
!with_i_schema)
{
if (!fill_schema_table_from_frm(thd, table, schema_table, db_name,
- table_name, schema_table_idx))
+ table_name, schema_table_idx,
+ can_deadlock))
continue;
}
@@ -3361,7 +3467,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
show_table_list->i_s_requested_object=
schema_table->i_s_requested_object;
res= open_normal_and_derived_tables(thd, show_table_list,
- MYSQL_LOCK_IGNORE_FLUSH);
+ (MYSQL_LOCK_IGNORE_FLUSH |
+ (can_deadlock ? MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)));
lex->sql_command= save_sql_command;
/*
XXX: show_table_list has a flag i_is_requested,
@@ -3397,7 +3504,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
res= schema_table->process_table(thd, show_table_list, table,
res, &orig_db_name,
&tmp_lex_string);
- close_tables_for_reopen(thd, &show_table_list);
+ close_tables_for_reopen(thd, &show_table_list,
+ open_tables_state_backup.mdl_system_tables_svp);
}
DBUG_ASSERT(!lex->query_tables_own_last);
if (res)
@@ -4199,7 +4307,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
bool full_access;
char definer[USER_HOST_BUFF_SIZE];
- Open_tables_state open_tables_state_backup;
+ Open_tables_backup open_tables_state_backup;
DBUG_ENTER("fill_schema_proc");
strxmov(definer, thd->security_ctx->priv_user, "@",
@@ -7160,14 +7268,14 @@ static bool show_create_trigger_impl(THD *thd,
- do not update Lex::query_tables in add_table_to_list().
*/
-static TABLE_LIST *get_trigger_table_impl(
- THD *thd,
- const sp_name *trg_name)
+static
+TABLE_LIST *get_trigger_table_impl(THD *thd, const sp_name *trg_name)
{
char trn_path_buff[FN_REFLEN];
-
LEX_STRING trn_path= { trn_path_buff, 0 };
+ LEX_STRING db;
LEX_STRING tbl_name;
+ TABLE_LIST *table;
build_trn_path(thd, trg_name, &trn_path);
@@ -7181,25 +7289,19 @@ static TABLE_LIST *get_trigger_table_impl(
return NULL;
/* We need to reset statement table list to be PS/SP friendly. */
-
- TABLE_LIST *table;
-
- if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST))))
- {
- my_error(ER_OUTOFMEMORY, MYF(0), sizeof(TABLE_LIST));
+ if (!(table= (TABLE_LIST*) thd->alloc(sizeof(TABLE_LIST))))
return NULL;
- }
- table->db_length= trg_name->m_db.length;
- table->db= thd->strmake(trg_name->m_db.str, trg_name->m_db.length);
+ db= trg_name->m_db;
- table->table_name_length= tbl_name.length;
- table->table_name= thd->strmake(tbl_name.str, tbl_name.length);
+ db.str= thd->strmake(db.str, db.length);
+ tbl_name.str= thd->strmake(tbl_name.str, tbl_name.length);
- table->alias= thd->strmake(tbl_name.str, tbl_name.length);
+ if (db.str == NULL || tbl_name.str == NULL)
+ return NULL;
- table->lock_type= TL_IGNORE;
- table->cacheable_table= 0;
+ table->init_one_table(db.str, db.length, tbl_name.str, tbl_name.length,
+ tbl_name.str, TL_IGNORE);
return table;
}
@@ -7215,7 +7317,8 @@ static TABLE_LIST *get_trigger_table_impl(
@return TABLE_LIST object corresponding to the base table.
*/
-static TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
+static
+TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name)
{
/* Acquire LOCK_open (stop the server). */
@@ -7265,8 +7368,8 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name)
Open the table by name in order to load Table_triggers_list object.
NOTE: there is race condition here -- the table can be dropped after
- LOCK_open is released. It will be fixed later by introducing
- acquire-shared-table-name-lock functionality.
+ LOCK_open is released. It will be fixed later by acquiring shared
+ metadata lock on trigger or table name.
*/
uint num_tables; /* NOTE: unused, only to pass to open_tables(). */