From af4ee7432a83f86a0bf8c7b684ae74e0ef093bcd Mon Sep 17 00:00:00 2001 From: Konstantin Osipov Date: Tue, 1 Dec 2009 00:44:05 +0300 Subject: [PATCH] Backport of: ------------------------------------------------------------ revno: 2630.4.13 committer: Dmitry Lenev branch nick: mysql-6.0-3726-w timestamp: Wed 2008-05-28 12:07:30 +0400 message: WL#3726 "DDL locking for all metadata objects". After review fixes in progress. Get rid of remove_table_from_cache() function since it was doing two things at once -- waiting while no one uses particular table (now job of metadata locking) and removing TABLE/TABLE_SHARE instances from table definition cache (now job of expel_table_from_cache()). --- sql/mysql_priv.h | 8 --- sql/sql_base.cc | 182 +---------------------------------------------- sql/sql_table.cc | 29 ++++---- 3 files changed, 15 insertions(+), 204 deletions(-) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 063b36c4ddc..e96534bad15 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1542,13 +1542,6 @@ char *generate_partition_syntax(partition_info *part_info, Alter_info *alter_info); #endif -/* bits for last argument to remove_table_from_cache() */ -#define RTFC_NO_FLAG 0x0000 -#define RTFC_OWNED_BY_THD_FLAG 0x0001 -#define RTFC_WAIT_OTHER_THREAD_FLAG 0x0002 -#define RTFC_CHECK_KILLED_FLAG 0x0004 -bool remove_table_from_cache(THD *thd, const char *db, const char *table, - uint flags); bool notify_thread_having_shared_lock(THD *thd, THD *in_use); void expel_table_from_cache(THD *leave_thd, const char *db, const char *table_name); @@ -1670,7 +1663,6 @@ extern pthread_mutex_t LOCK_gdl; bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags); int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt); void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt); -void mysql_wait_completed_table(ALTER_PARTITION_PARAM_TYPE *lpt, TABLE *my_table); /* Functions to work with system tables. */ bool open_system_tables_for_read(THD *thd, TABLE_LIST *table_list, diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1356a2cd16f..40377a99d21 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1482,7 +1482,7 @@ void close_thread_tables(THD *thd, /* Note that we need to hold LOCK_open while changing the open_tables list. Another thread may work on it. - (See: remove_table_from_cache(), mysql_wait_completed_table()) + (See: notify_thread_having_shared_lock()) Closing a MERGE child before the parent would be fatal if the other thread tries to abort the MERGE lock in between. */ @@ -2228,7 +2228,7 @@ void unlink_open_table(THD *thd, TABLE *find, bool unlock) /* Note that we need to hold LOCK_open while changing the open_tables list. Another thread may work on it. - (See: remove_table_from_cache(), mysql_wait_completed_table()) + (See: notify_thread_having_shared_lock()) Closing a MERGE child before the parent would be fatal if the other thread tries to abort the MERGE lock in between. */ @@ -8444,154 +8444,6 @@ void flush_tables() } -/* - Mark all entries with the table as deleted to force an reopen of the table - - The table will be closed (not stored in cache) by the current thread when - close_thread_tables() is called. - - PREREQUISITES - Lock on LOCK_open() - - RETURN - 0 This thread now have exclusive access to this table and no other thread - can access the table until close_thread_tables() is called. - 1 Table is in use by another thread -*/ - -bool remove_table_from_cache(THD *thd, const char *db, const char *table_name, - uint flags) -{ - char key[MAX_DBKEY_LENGTH]; - uint key_length; - TABLE *table; - TABLE_SHARE *share; - bool result= 0, signalled= 0; - DBUG_ENTER("remove_table_from_cache"); - DBUG_PRINT("enter", ("table: '%s'.'%s' flags: %u", db, table_name, flags)); - - key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; - for (;;) - { - result= signalled= 0; - - if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache, (uchar*) key, - key_length))) - { - I_P_List_iterator it(share->free_tables); - share->version= 0; - while ((table= it++)) - relink_unused(table); - - it.init(share->used_tables); - while ((table= it++)) - { - THD *in_use= table->in_use; - DBUG_ASSERT(in_use); - if (in_use != thd) - { - DBUG_PRINT("info", ("Table was in use by other thread")); - /* - Mark that table is going to be deleted from cache. This will - force threads that are in mysql_lock_tables() (but not yet - in thr_multi_lock()) to abort it's locks, close all tables and retry - */ - in_use->some_tables_deleted= 1; - - if (table->is_name_opened()) - { - DBUG_PRINT("info", ("Found another active instance of the table")); - result=1; - } - /* Kill delayed insert threads */ - if ((in_use->system_thread & SYSTEM_THREAD_DELAYED_INSERT) && - ! in_use->killed) - { - in_use->killed= THD::KILL_CONNECTION; - pthread_mutex_lock(&in_use->mysys_var->mutex); - if (in_use->mysys_var->current_cond) - { - pthread_mutex_lock(in_use->mysys_var->current_mutex); - signalled= 1; - pthread_cond_broadcast(in_use->mysys_var->current_cond); - pthread_mutex_unlock(in_use->mysys_var->current_mutex); - } - pthread_mutex_unlock(&in_use->mysys_var->mutex); - } - /* - Now we must abort all tables locks used by this thread - as the thread may be waiting to get a lock for another table. - Note that we need to hold LOCK_open while going through the - list. So that the other thread cannot change it. The other - thread must also hold LOCK_open whenever changing the - open_tables list. Aborting the MERGE lock after a child was - closed and before the parent is closed would be fatal. - */ - for (TABLE *thd_table= in_use->open_tables; - thd_table ; - thd_table= thd_table->next) - { - /* Do not handle locks of MERGE children. */ - if (thd_table->db_stat && !thd_table->parent) // If table is open - signalled|= mysql_lock_abort_for_thread(thd, thd_table); - } - } - else - { - DBUG_PRINT("info", ("Table was in use by current thread. db_stat: %u", - table->db_stat)); - result= result || (flags & RTFC_OWNED_BY_THD_FLAG); - } - } - - while (unused_tables && !unused_tables->s->version) - free_cache_entry(unused_tables); - - DBUG_PRINT("info", ("share version: %lu ref_count: %u", - share->version, share->ref_count)); - if (share->ref_count == 0) - my_hash_delete(&table_def_cache, (uchar*) share); - } - - if (result && (flags & RTFC_WAIT_OTHER_THREAD_FLAG)) - { - /* - Signal any thread waiting for tables to be freed to - reopen their tables - */ - broadcast_refresh(); - DBUG_PRINT("info", ("Waiting for refresh signal")); - if (!(flags & RTFC_CHECK_KILLED_FLAG) || !thd->killed) - { - dropping_tables++; - if (likely(signalled)) - (void) pthread_cond_wait(&COND_refresh, &LOCK_open); - else - { - struct timespec abstime; - /* - It can happen that another thread has opened the - table but has not yet locked any table at all. Since - it can be locked waiting for a table that our thread - has done LOCK TABLE x WRITE on previously, we need to - ensure that the thread actually hears our signal - before we go to sleep. Thus we wait for a short time - and then we retry another loop in the - remove_table_from_cache routine. - */ - set_timespec(abstime, 10); - pthread_cond_timedwait(&COND_refresh, &LOCK_open, &abstime); - } - dropping_tables--; - continue; - } - } - break; - } - DBUG_RETURN(result); -} - - /** A callback to the server internals that is used to address special cases of the locking protocol. @@ -8894,34 +8746,6 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) } -/* - SYNOPSIS - close_open_tables_and_downgrade() - RESULT VALUES - NONE - DESCRIPTION - We need to ensure that any thread that has managed to open the table - but not yet encountered our lock on the table is also thrown out to - ensure that no threads see our frm changes premature to the final - version. The intermediate versions are only meant for use after a - crash and later REPAIR TABLE. - We also downgrade locks after the upgrade to WRITE_ONLY -*/ - -/* purecov: begin deadcode */ -void close_open_tables_and_downgrade(ALTER_PARTITION_PARAM_TYPE *lpt) -{ - pthread_mutex_lock(&LOCK_open); - remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, - RTFC_WAIT_OTHER_THREAD_FLAG); - pthread_mutex_unlock(&LOCK_open); - /* If MERGE child, forward lock handling to parent. */ - mysql_lock_downgrade_write(lpt->thd, lpt->table->parent ? lpt->table->parent : - lpt->table, lpt->old_lock_type); -} -/* purecov: end */ - - /* Tells if two (or more) tables have auto_increment columns and we want to lock those tables with a write lock. @@ -9165,7 +8989,7 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup) /* Note that we need to hold LOCK_open while changing the open_tables list. Another thread may work on it. - (See: remove_table_from_cache(), mysql_wait_completed_table()) + (See: notify_thread_having_shared_lock()) Closing a MERGE child before the parent would be fatal if the other thread tries to abort the MERGE lock in between. */ diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 640cd71fc62..5dee7015bd4 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -4800,19 +4800,13 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, /* Close all instances of the table to allow repair to rename files */ if (lock_type == TL_WRITE && table->table->s->version) { - DBUG_PRINT("admin", ("removing table from cache")); - pthread_mutex_lock(&LOCK_open); - const char *old_message=thd->enter_cond(&COND_refresh, &LOCK_open, - "Waiting to get writelock"); - mysql_lock_abort(thd,table->table, TRUE); - remove_table_from_cache(thd, table->table->s->db.str, - table->table->s->table_name.str, - RTFC_WAIT_OTHER_THREAD_FLAG | - RTFC_CHECK_KILLED_FLAG); - thd->exit_cond(old_message); - DBUG_EXECUTE_IF("wait_in_mysql_admin_table", wait_for_kill_signal(thd);); - if (thd->killed) - goto err; + if (wait_while_table_is_used(thd, table->table, + HA_EXTRA_PREPARE_FOR_RENAME)) + goto err; + DBUG_EXECUTE_IF("wait_in_mysql_admin_table", + wait_for_kill_signal(thd); + if (thd->killed) + goto err;); /* Flush entries in the query cache involving this table. */ query_cache_invalidate3(thd, table->table, 0); open_for_modify= 0; @@ -5064,10 +5058,10 @@ send_result_message: table->table->file->info(HA_STATUS_CONST); else { - pthread_mutex_lock(&LOCK_open); - remove_table_from_cache(thd, table->table->s->db.str, - table->table->s->table_name.str, RTFC_NO_FLAG); - pthread_mutex_unlock(&LOCK_open); + TABLE_LIST *save_next_global= table->next_global; + table->next_global= 0; + close_cached_tables(thd, table, FALSE, FALSE); + table->next_global= save_next_global; } /* May be something modified consequently we have to invalidate cache */ query_cache_invalidate3(thd, table->table, 0); @@ -5133,6 +5127,7 @@ bool mysql_repair_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) bool mysql_optimize_table(THD* thd, TABLE_LIST* tables, HA_CHECK_OPT* check_opt) { DBUG_ENTER("mysql_optimize_table"); + set_all_mdl_upgradable(tables); DBUG_RETURN(mysql_admin_table(thd, tables, check_opt, "optimize", TL_WRITE, 1,0,0,0, &handler::ha_optimize, 0));