mirror of
https://github.com/MariaDB/server.git
synced 2025-06-13 13:01:51 +03:00
Backport of:
------------------------------------------------------------ revno: 2630.4.13 committer: Dmitry Lenev <dlenev@mysql.com> 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: Got rid of remove_table_from_cache() function. Now one should use exclusive metadata lock for waiting until all other connections will stop using particular table and use expel_table_from_cache() for removing table instances and table share from table definition cache. Removed unused mysql_wait_completed_table() function. sql/sql_base.cc: Got rid of remove_table_from_cache() function. Now one should use exclusive metadata lock for waiting until all other connections will stop using particular table and use expel_table_from_cache() for removing table instances and table share from table definition cache. Removed unused mysql_wait_completed_table() function. sql/sql_table.cc: Get rid of two last places where we use remove_table_from_cache() by using wait_while_table_is_used() (which uses metadata locks) and close_cached_tables() instead.
This commit is contained in:
@ -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,
|
||||
|
182
sql/sql_base.cc
182
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<TABLE, TABLE_share> 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.
|
||||
*/
|
||||
|
@ -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)
|
||||
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));
|
||||
|
Reference in New Issue
Block a user