diff options
Diffstat (limited to 'sql/sql_base.cc')
-rw-r--r-- | sql/sql_base.cc | 104 |
1 files changed, 75 insertions, 29 deletions
diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 486d815026f..9e4dceff87d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -130,7 +130,7 @@ void table_cache_free(void) DBUG_ENTER("table_cache_free"); if (table_def_inited) { - close_cached_tables((THD*) 0,0,(TABLE_LIST*) 0); + close_cached_tables(NULL, NULL, FALSE, FALSE, FALSE); if (!open_cache.records) // Safety first hash_free(&open_cache); } @@ -491,9 +491,28 @@ static TABLE_SHARE int tmp; DBUG_ENTER("get_table_share_with_create"); - if ((share= get_table_share(thd, table_list, key, key_length, - db_flags, error)) || - thd->net.last_errno != ER_NO_SUCH_TABLE) + share= get_table_share(thd, table_list, key, key_length, db_flags, error); + /* + If share is not NULL, we found an existing share. + + If share is NULL, and there is no error, we're inside + pre-locking, which silences 'ER_NO_SUCH_TABLE' errors + with the intention to silently drop non-existing tables + from the pre-locking list. In this case we still need to try + auto-discover before returning a NULL share. + + 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. + + Finally, if share is still NULL, it's a real error and we need + to abort. + + @todo Rework alternative ways to deal with ER_NO_SUCH TABLE. + */ + if (share || thd->is_error() && thd->main_da.sql_errno() != ER_NO_SUCH_TABLE) + DBUG_RETURN(share); /* Table didn't exist. Check if some engine can provide it */ @@ -502,9 +521,13 @@ static TABLE_SHARE { /* No such table in any engine. - Hide "Table doesn't exist" errors if table belong to view + 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. + @todo Rework the alternative ways to deal with ER_NO_SUCH TABLE. */ - if (table_list->belong_to_view) + if (thd->is_error() && table_list->belong_to_view) { TABLE_LIST *view= table_list->belong_to_view; thd->clear_error(); @@ -885,16 +908,24 @@ void free_io_cache(TABLE *table) /* Close all tables which aren't in use by any thread - THD can be NULL, but then if_wait_for_refresh must be FALSE - and tables must be NULL. + @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 + @param wait_for_placeholders Wait for tables being reopened so that the GRL + won't proceed while write-locked tables are being reopened by other + threads. + + @remark THD can be NULL, but then wait_for_refresh must be FALSE + and tables must be NULL. */ -bool close_cached_tables(THD *thd, bool if_wait_for_refresh, - TABLE_LIST *tables, bool have_lock) +bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, + bool wait_for_refresh, bool wait_for_placeholders) { bool result=0; DBUG_ENTER("close_cached_tables"); - DBUG_ASSERT(thd || (!if_wait_for_refresh && !tables)); + DBUG_ASSERT(thd || (!wait_for_refresh && !tables)); if (!have_lock) VOID(pthread_mutex_lock(&LOCK_open)); @@ -918,7 +949,7 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, } DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu", refresh_version)); - if (if_wait_for_refresh) + if (wait_for_refresh) { /* Other threads could wait in a loop in open_and_lock_tables(), @@ -975,13 +1006,13 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, found=1; } if (!found) - if_wait_for_refresh=0; // Nothing to wait for + wait_for_refresh=0; // Nothing to wait for } #ifndef EMBEDDED_LIBRARY if (!tables) kill_delayed_threads(); #endif - if (if_wait_for_refresh) + if (wait_for_refresh) { /* If there is any table that has a lower refresh_version, wait until @@ -1004,6 +1035,9 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, for (uint idx=0 ; idx < open_cache.records ; idx++) { TABLE *table=(TABLE*) hash_element(&open_cache,idx); + /* Avoid a self-deadlock. */ + if (table->in_use == thd) + continue; /* Note that we wait here only for tables which are actually open, and not for placeholders with TABLE::open_placeholder set. Waiting for @@ -1018,7 +1052,8 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, are employed by CREATE TABLE as in this case table simply does not exist yet. */ - if (table->needs_reopen_or_name_lock() && table->db_stat) + if (table->needs_reopen_or_name_lock() && (table->db_stat || + (table->open_placeholder && wait_for_placeholders))) { found=1; DBUG_PRINT("signal", ("Waiting for COND_refresh")); @@ -1037,11 +1072,18 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh, thd->in_lock_tables=0; /* Set version for table */ for (TABLE *table=thd->open_tables; table ; table= table->next) - table->s->version= refresh_version; + { + /* + Preserve the version (0) of write locked tables so that a impending + global read lock won't sneak in. + */ + if (table->reginfo.lock_type < TL_WRITE_ALLOW_WRITE) + table->s->version= refresh_version; + } } if (!have_lock) VOID(pthread_mutex_unlock(&LOCK_open)); - if (if_wait_for_refresh) + if (wait_for_refresh) { pthread_mutex_lock(&thd->mysys_var->mutex); thd->mysys_var->current_mutex= 0; @@ -1068,10 +1110,10 @@ bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh, DBUG_ASSERT(thd); bzero(&tmp, sizeof(TABLE_LIST)); - + if (!have_lock) VOID(pthread_mutex_lock(&LOCK_open)); - + for (idx= 0; idx < table_def_cache.records; idx++) { TABLE_SHARE *share= (TABLE_SHARE *) hash_element(&table_def_cache, idx); @@ -1100,11 +1142,11 @@ bool close_cached_connection_tables(THD *thd, bool if_wait_for_refresh, } if (tables) - result= close_cached_tables(thd, FALSE, tables, TRUE); - + result= close_cached_tables(thd, tables, TRUE, FALSE, FALSE); + if (!have_lock) VOID(pthread_mutex_unlock(&LOCK_open)); - + if (if_wait_for_refresh) { pthread_mutex_lock(&thd->mysys_var->mutex); @@ -2204,7 +2246,7 @@ void wait_for_condition(THD *thd, pthread_mutex_t *mutex, pthread_cond_t *cond) current thread. @param thd current thread context - @param tables able list containing one table to open. + @param tables table list containing one table to open. @return FALSE on success, TRUE otherwise. */ @@ -3272,8 +3314,8 @@ static bool reattach_merge(THD *thd, TABLE **err_tables_p) @param thd Thread context @param get_locks Should we get locks after reopening tables ? - @param in_refresh Are we in FLUSH TABLES ? TODO: It seems that - we can remove this parameter. + @param mark_share_as_old Mark share as old to protect from a impending + global read lock. @note Since this function can't properly handle prelocking and create placeholders it should be used in very special @@ -3287,13 +3329,17 @@ static bool reattach_merge(THD *thd, TABLE **err_tables_p) @return FALSE in case of success, TRUE - otherwise. */ -bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) +bool reopen_tables(THD *thd, bool get_locks, bool mark_share_as_old) { TABLE *table,*next,**prev; TABLE **tables,**tables_ptr; // For locks TABLE *err_tables= NULL; bool error=0, not_used; bool merge_table_found= FALSE; + const uint flags= MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN | + MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK | + MYSQL_LOCK_IGNORE_FLUSH; + DBUG_ENTER("reopen_tables"); if (!thd->open_tables) @@ -3354,7 +3400,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) /* Do not handle locks of MERGE children. */ if (get_locks && !db_stat && !table->parent) *tables_ptr++= table; // need new lock on this - if (in_refresh) + if (mark_share_as_old) { table->s->version=0; table->open_placeholder= 0; @@ -3387,7 +3433,7 @@ bool reopen_tables(THD *thd,bool get_locks,bool in_refresh) */ thd->some_tables_deleted=0; if ((lock= mysql_lock_tables(thd, tables, (uint) (tables_ptr - tables), - MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN, ¬_used))) + flags, ¬_used))) { thd->locked_tables=mysql_lock_merge(thd->locked_tables,lock); } @@ -8414,7 +8460,7 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) We also downgrade locks after the upgrade to WRITE_ONLY */ -/* purecov: begin unused */ +/* purecov: begin deadcode */ void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt) { VOID(pthread_mutex_lock(&LOCK_open)); |