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.cc232
1 files changed, 118 insertions, 114 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index be0ea9c0815..9aea868a197 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -314,8 +314,6 @@ void table_def_start_shutdown(void)
if (table_def_inited)
{
mysql_mutex_lock(&LOCK_open);
- /* Free all cached but unused TABLEs and TABLE_SHAREs first. */
- close_cached_tables(NULL, NULL, TRUE, FALSE);
/*
Ensure that TABLE and TABLE_SHARE objects which are created for
tables that are open during process of plugins' shutdown are
@@ -324,6 +322,8 @@ void table_def_start_shutdown(void)
*/
table_def_shutdown_in_progress= TRUE;
mysql_mutex_unlock(&LOCK_open);
+ /* Free all cached but unused TABLEs and TABLE_SHAREs. */
+ close_cached_tables(NULL, NULL, FALSE);
}
}
@@ -516,10 +516,10 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
}
/*
- We assign a new table id under the protection of the LOCK_open and
- the share's own mutex. We do this insted of creating a new mutex
+ We assign a new table id under the protection of the LOCK_open.
+ We do this instead of creating a new mutex
and using it for the sole purpose of serializing accesses to a
- static variable, we assign the table id here. We assign it to the
+ static variable, we assign the table id here. We assign it to the
share before inserting it into the table_def_cache to be really
sure that it cannot be read from the cache without having a table
id assigned.
@@ -547,7 +547,7 @@ TABLE_SHARE *get_table_share(THD *thd, TABLE_LIST *table_list, char *key,
DBUG_RETURN(share);
found:
- /*
+ /*
We found an existing table definition. Return it if we didn't get
an error when reading the table definition from file.
*/
@@ -589,21 +589,21 @@ found:
}
-/*
+/**
Get a table share. If it didn't exist, try creating it from engine
- For arguments and return values, see get_table_from_share()
+ For arguments and return values, see get_table_share()
*/
-static TABLE_SHARE
-*get_table_share_with_create(THD *thd, TABLE_LIST *table_list,
- char *key, uint key_length,
- uint db_flags, int *error,
- my_hash_value_type hash_value)
+static TABLE_SHARE *
+get_table_share_with_discover(THD *thd, TABLE_LIST *table_list,
+ char *key, uint key_length,
+ uint db_flags, int *error,
+ my_hash_value_type hash_value)
{
TABLE_SHARE *share;
- int tmp;
+ bool exists;
DBUG_ENTER("get_table_share_with_create");
share= get_table_share(thd, table_list, key, key_length, db_flags, error,
@@ -617,10 +617,15 @@ static TABLE_SHARE
from the pre-locking list. In this case we still need to try
auto-discover before returning a NULL share.
+ Or, we're inside SHOW CREATE VIEW, which
+ also installs a silencer for ER_NO_SUCH_TABLE error.
+
If share is NULL and the error is ER_NO_SUCH_TABLE, this is
- the same as above, only that the error was not silenced by
- pre-locking. Once again, we need to try to auto-discover
- the share.
+ the same as above, only that the error was not silenced by
+ pre-locking or SHOW CREATE VIEW.
+
+ In both these cases it won't harm to try to discover the
+ table.
Finally, if share is still NULL, it's a real error and we need
to abort.
@@ -628,20 +633,25 @@ static TABLE_SHARE
@todo Rework alternative ways to deal with ER_NO_SUCH TABLE.
*/
if (share || (thd->is_error() && thd->stmt_da->sql_errno() != ER_NO_SUCH_TABLE))
-
DBUG_RETURN(share);
+ *error= 0;
+
/* Table didn't exist. Check if some engine can provide it */
- tmp= ha_create_table_from_engine(thd, table_list->db,
- table_list->table_name);
- if (tmp < 0)
+ if (ha_check_if_table_exists(thd, table_list->db, table_list->table_name,
+ &exists))
+ {
+ thd->clear_error();
+ /* Conventionally, the storage engine API does not report errors. */
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ }
+ else if (! exists)
{
/*
No such table in any engine.
Hide "Table doesn't exist" errors if the table belongs to a view.
The check for thd->is_error() is necessary to not push an
- unwanted error in case of pre-locking, which silences
- "no such table" errors.
+ unwanted error in case the error was already silenced.
@todo Rework the alternative ways to deal with ER_NO_SUCH TABLE.
*/
if (thd->is_error())
@@ -659,27 +669,16 @@ static TABLE_SHARE
view->view_db.str, view->view_name.str);
}
}
- DBUG_RETURN(0);
}
- if (tmp)
+ else
{
- /* Give right error message */
thd->clear_error();
- DBUG_PRINT("error", ("Discovery of %s/%s failed", table_list->db,
- table_list->table_name));
- my_printf_error(ER_UNKNOWN_ERROR,
- "Failed to open '%-.64s', error while "
- "unpacking from engine",
- MYF(0), table_list->table_name);
- DBUG_RETURN(0);
+ *error= 7; /* Run auto-discover. */
}
- /* Table existed in engine. Let's open it */
- thd->warning_info->clear_warning_info(thd->query_id);
- thd->clear_error(); // Clear error message
- DBUG_RETURN(get_table_share(thd, table_list, key, key_length,
- db_flags, error, hash_value));
+ DBUG_RETURN(NULL);
}
+
/**
Mark that we are not using table share anymore.
@@ -926,7 +925,6 @@ static void kill_delayed_threads_for_table(TABLE_SHARE *share)
@param thd Thread context
@param tables List of tables to remove from the cache
- @param have_lock If LOCK_open is locked
@param wait_for_refresh Wait for a impending flush
@note THD can be NULL, but then wait_for_refresh must be FALSE
@@ -940,16 +938,14 @@ static void kill_delayed_threads_for_table(TABLE_SHARE *share)
lock taken by thread trying to obtain global read lock.
*/
-bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
- bool wait_for_refresh)
+bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool wait_for_refresh)
{
bool result= FALSE;
bool found= TRUE;
DBUG_ENTER("close_cached_tables");
DBUG_ASSERT(thd || (!wait_for_refresh && !tables));
- if (!have_lock)
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&LOCK_open);
if (!tables)
{
refresh_version++; // Force close of open tables
@@ -978,7 +974,7 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
kill_delayed_threads_for_table(share);
/* tdc_remove_table() also sets TABLE_SHARE::version to 0. */
tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED, table->db,
- table->table_name);
+ table->table_name, TRUE);
found=1;
}
}
@@ -986,15 +982,11 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
wait_for_refresh=0; // Nothing to wait for
}
- if (!have_lock)
- mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_unlock(&LOCK_open);
if (!wait_for_refresh)
DBUG_RETURN(result);
- /* Code below assume that LOCK_open is released. */
- DBUG_ASSERT(!have_lock);
-
if (thd->locked_tables_mode)
{
/*
@@ -1108,7 +1100,7 @@ err_with_reopen:
*/
bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh,
- LEX_STRING *connection, bool have_lock)
+ LEX_STRING *connection)
{
uint idx;
TABLE_LIST tmp, *tables= NULL;
@@ -1118,8 +1110,7 @@ bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh,
bzero(&tmp, sizeof(TABLE_LIST));
- if (!have_lock)
- mysql_mutex_lock(&LOCK_open);
+ mysql_mutex_lock(&LOCK_open);
for (idx= 0; idx < table_def_cache.records; idx++)
{
@@ -1147,12 +1138,10 @@ bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh,
tables= (TABLE_LIST *) memdup_root(thd->mem_root, (char*)&tmp,
sizeof(TABLE_LIST));
}
+ mysql_mutex_unlock(&LOCK_open);
if (tables)
- result= close_cached_tables(thd, tables, TRUE, FALSE);
-
- if (!have_lock)
- mysql_mutex_unlock(&LOCK_open);
+ result= close_cached_tables(thd, tables, FALSE);
if (if_wait_for_refresh)
{
@@ -1355,9 +1344,8 @@ close_all_tables_for_name(THD *thd, TABLE_SHARE *share,
}
}
/* Remove the table share from the cache. */
- mysql_mutex_lock(&LOCK_open);
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name);
- mysql_mutex_unlock(&LOCK_open);
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db, table_name,
+ FALSE);
/*
There could be a FLUSH thread waiting
on the table to go away. Wake it up.
@@ -2201,10 +2189,9 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
table->mdl_ticket, thd->variables.lock_wait_timeout))
DBUG_RETURN(TRUE);
- mysql_mutex_lock(&LOCK_open);
tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN,
- table->s->db.str, table->s->table_name.str);
- mysql_mutex_unlock(&LOCK_open);
+ table->s->db.str, table->s->table_name.str,
+ FALSE);
/* extra() call must come only after all instances above are closed */
(void) table->file->extra(function);
DBUG_RETURN(FALSE);
@@ -2245,9 +2232,8 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name,
table->file->extra(HA_EXTRA_PREPARE_FOR_DROP);
close_thread_table(thd, &thd->open_tables);
/* Remove the table share from the table cache. */
- mysql_mutex_lock(&LOCK_open);
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db_name, table_name);
- mysql_mutex_unlock(&LOCK_open);
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, db_name, table_name,
+ FALSE);
/* Remove the table from the storage engine and rm the .frm. */
quick_rm_table(table_type, db_name, table_name, 0);
}
@@ -2279,16 +2265,20 @@ void drop_open_table(THD *thd, TABLE *table, const char *db_name,
bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists)
{
char path[FN_REFLEN + 1];
- int rc= 0;
+ TABLE_SHARE *share;
DBUG_ENTER("check_if_table_exists");
- mysql_mutex_assert_not_owner(&LOCK_open);
-
*exists= TRUE;
+ DBUG_ASSERT(thd->mdl_context.
+ is_lock_owner(MDL_key::TABLE, table->db,
+ table->table_name, MDL_SHARED));
+
mysql_mutex_lock(&LOCK_open);
+ share= get_cached_table_share(table->db, table->table_name);
+ mysql_mutex_unlock(&LOCK_open);
- if (get_cached_table_share(table->db, table->table_name))
+ if (share)
goto end;
build_table_filename(path, sizeof(path) - 1, table->db, table->table_name,
@@ -2298,24 +2288,14 @@ bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists)
goto end;
/* .FRM file doesn't exist. Check if some engine can provide it. */
-
- rc= ha_create_table_from_engine(thd, table->db, table->table_name);
-
- if (rc < 0)
- {
- /* Table does not exists in engines as well. */
- *exists= FALSE;
- rc= 0;
- }
- else if (rc)
+ if (ha_check_if_table_exists(thd, table->db, table->table_name, exists))
{
- my_printf_error(ER_UNKNOWN_ERROR, "Failed to open '%-.64s', error while "
+ my_printf_error(ER_OUT_OF_RESOURCES, "Failed to open '%-.64s', error while "
"unpacking from engine", MYF(0), table->table_name);
+ DBUG_RETURN(TRUE);
}
-
end:
- mysql_mutex_unlock(&LOCK_open);
- DBUG_RETURN(test(rc));
+ DBUG_RETURN(FALSE);
}
@@ -2812,11 +2792,25 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
mysql_mutex_lock(&LOCK_open);
- if (!(share= get_table_share_with_create(thd, table_list, key,
- key_length, OPEN_VIEW,
- &error,
- hash_value)))
- goto err_unlock2;
+ if (!(share= get_table_share_with_discover(thd, table_list, key,
+ key_length, OPEN_VIEW,
+ &error,
+ hash_value)))
+ {
+ mysql_mutex_unlock(&LOCK_open);
+ /*
+ If thd->is_error() is not set, we either need discover
+ (error == 7), or the error was silenced by the prelocking
+ handler (error == 0), in which case we should skip this
+ table.
+ */
+ if (error == 7 && !thd->is_error())
+ {
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
+ table_list);
+ }
+ DBUG_RETURN(TRUE);
+ }
if (share->is_view)
{
@@ -3010,7 +3004,6 @@ err_lock:
mysql_mutex_lock(&LOCK_open);
err_unlock:
release_table_share(share);
-err_unlock2:
mysql_mutex_unlock(&LOCK_open);
DBUG_RETURN(TRUE);
@@ -3633,10 +3626,10 @@ bool tdc_open_view(THD *thd, TABLE_LIST *table_list, const char *alias,
cache_key_length);
mysql_mutex_lock(&LOCK_open);
- if (!(share= get_table_share_with_create(thd, table_list, cache_key,
- cache_key_length,
- OPEN_VIEW, &error,
- hash_value)))
+ if (!(share= get_table_share(thd, table_list, cache_key,
+ cache_key_length,
+ OPEN_VIEW, &error,
+ hash_value)))
goto err;
if (share->is_view &&
@@ -3738,10 +3731,10 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
cache_key_length);
mysql_mutex_lock(&LOCK_open);
- if (!(share= get_table_share_with_create(thd, table_list, cache_key,
- cache_key_length,
- OPEN_VIEW, &not_used,
- hash_value)))
+ if (!(share= get_table_share(thd, table_list, cache_key,
+ cache_key_length,
+ OPEN_VIEW, &not_used,
+ hash_value)))
goto end_unlock;
if (share->is_view)
@@ -3786,7 +3779,8 @@ static bool auto_repair_table(THD *thd, TABLE_LIST *table_list)
release_table_share(share);
/* Remove the repaired share from the table cache. */
tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
- table_list->db, table_list->table_name);
+ table_list->db, table_list->table_name,
+ TRUE);
end_unlock:
mysql_mutex_unlock(&LOCK_open);
return result;
@@ -3908,12 +3902,10 @@ recover_from_failed_open(THD *thd)
MYSQL_OPEN_SKIP_TEMPORARY)))
break;
- mysql_mutex_lock(&LOCK_open);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
- m_failed_table->table_name);
+ m_failed_table->table_name, FALSE);
ha_create_table_from_engine(thd, m_failed_table->db,
m_failed_table->table_name);
- mysql_mutex_unlock(&LOCK_open);
thd->warning_info->clear_warning_info(thd->query_id);
thd->clear_error(); // Clear error message
@@ -3927,10 +3919,8 @@ recover_from_failed_open(THD *thd)
MYSQL_OPEN_SKIP_TEMPORARY)))
break;
- mysql_mutex_lock(&LOCK_open);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
- m_failed_table->table_name);
- mysql_mutex_unlock(&LOCK_open);
+ m_failed_table->table_name, FALSE);
result= auto_repair_table(thd, m_failed_table);
thd->mdl_context.release_transactional_locks();
@@ -8541,7 +8531,7 @@ void tdc_flush_unused_tables()
mysql_mutex_lock(&LOCK_open);
while (unused_tables)
free_cache_entry(unused_tables);
- (void) mysql_mutex_unlock(&LOCK_open);
+ mysql_mutex_unlock(&LOCK_open);
}
@@ -8646,20 +8636,25 @@ bool mysql_notify_thread_having_shared_lock(THD *thd, THD *in_use,
remove TABLE_SHARE).
@param db Name of database
@param table_name Name of table
+ @param has_lock If TRUE, LOCK_open is already acquired
@note It assumes that table instances are already not used by any
(other) thread (this should be achieved by using meta-data locks).
*/
void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
- const char *db, const char *table_name)
+ const char *db, const char *table_name,
+ bool has_lock)
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
TABLE *table;
TABLE_SHARE *share;
- mysql_mutex_assert_owner(&LOCK_open);
+ if (! has_lock)
+ mysql_mutex_lock(&LOCK_open);
+ else
+ mysql_mutex_assert_owner(&LOCK_open);
DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED ||
thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
@@ -8700,6 +8695,9 @@ void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
else
(void) my_hash_delete(&table_def_cache, (uchar*) share);
}
+
+ if (! has_lock)
+ mysql_mutex_unlock(&LOCK_open);
}
@@ -8905,11 +8903,6 @@ static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
TABLE *table;
DBUG_ENTER("alter_close_tables");
/*
- We must keep LOCK_open while manipulating with thd->open_tables.
- Another thread may be working on it.
- */
- mysql_mutex_lock(&LOCK_open);
- /*
We can safely remove locks for all tables with the same name:
later they will all be closed anyway in
alter_partition_lock_handling().
@@ -8919,9 +8912,20 @@ static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
if (!strcmp(table->s->table_name.str, share->table_name.str) &&
!strcmp(table->s->db.str, share->db.str))
{
+ /*
+ No need to take LOCK_thd_data to protect mysql_lock_remove(),
+ since mysql_lock_abort_for_thread() only aborts waiting
+ locks, and our lock is already granted.
+ */
mysql_lock_remove(thd, thd->lock, table);
+ /*
+ Protect members of thd->open_tables concurrently used
+ in mysql_notify_thread_having_shared_lock().
+ */
+ mysql_mutex_lock(&thd->LOCK_thd_data);
table->file->close();
table->db_stat= 0; // Mark file closed
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
/*
Ensure that we won't end up with a crippled table instance
in the table cache if an error occurs before we reach
@@ -8930,10 +8934,10 @@ static int alter_close_tables(ALTER_PARTITION_PARAM_TYPE *lpt)
*/
tdc_remove_table(thd, TDC_RT_REMOVE_UNUSED,
table->s->db.str,
- table->s->table_name.str);
+ table->s->table_name.str,
+ FALSE);
}
}
- mysql_mutex_unlock(&LOCK_open);
DBUG_RETURN(0);
}