Commit cf4a4ba6 authored by Konstantin Osipov's avatar Konstantin Osipov

Backport of:

```---------------------------------------------------------
revno: 2630.9.3
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w3
timestamp: Wed 2008-06-11 08:33:36 +0400
message:
  WL#3726 "DDL locking for all metadata objects".

  After review fixes in progress.

  Changed close_cached_tables() not to flush all unused TABLE
  instances when flushing individual table.
  Renamed expel_table_from_cache() to tdc_remove_table() and
  added enum parameter to be able more explicitly specify type
  of removal, rewrote its code to be more efficient.

******
Backport of:
```

---------------------------------------------------------
revno: 2630.9.4
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w3
timestamp: Wed 2008-06-11 15:53:53 +0400
message:
  WL#3726 "DDL locking for all metadata objects".

  After-review fixes in progress.

  Minor changes in order to improve code readability
  and simplify debugging.
parent b474ad26
......@@ -850,7 +850,7 @@ flush table t1;
execute stmt;
f1()
6
call p_verify_reprepare_count(1);
call p_verify_reprepare_count(0);
SUCCESS
execute stmt;
......
......@@ -745,7 +745,7 @@ execute stmt;
call p_verify_reprepare_count(1);
flush table t1;
execute stmt;
call p_verify_reprepare_count(1);
call p_verify_reprepare_count(0);
execute stmt;
--echo # Test 18-c: dependent VIEW has changed
......
......@@ -1543,8 +1543,11 @@ char *generate_partition_syntax(partition_info *part_info,
#endif
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);
enum enum_tdc_remove_table_type {TDC_RT_REMOVE_ALL, TDC_RT_REMOVE_NOT_OWN,
TDC_RT_REMOVE_UNUSED};
void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
const char *db, const char *table_name);
#define NORMAL_PART_NAME 0
#define TEMP_PART_NAME 1
......
......@@ -921,12 +921,17 @@ void free_io_cache(TABLE *table)
particular table identified by its share.
@param share Table share.
@pre Caller should have LOCK_open mutex acquired.
*/
static void kill_delayed_threads_for_table(TABLE_SHARE *share)
{
I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
TABLE *tab;
safe_mutex_assert_owner(&LOCK_open);
while ((tab= it++))
{
THD *in_use= tab->in_use;
......@@ -983,6 +988,15 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu",
refresh_version));
kill_delayed_threads();
/*
Get rid of all unused TABLE and TABLE_SHARE instances. By doing
this we automatically close all tables which were marked as "old".
*/
while (unused_tables)
free_cache_entry(unused_tables);
/* Free table shares which were not freed implicitly by loop above. */
while (oldest_unused_share->next)
(void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
}
else
{
......@@ -993,8 +1007,10 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
if (share)
{
share->version= 0;
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);
found=1;
}
}
......@@ -1002,28 +1018,14 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
wait_for_refresh=0; // Nothing to wait for
}
/*
Get rid of all unused TABLE and TABLE_SHARE instances. By doing
this we automatically close all tables which were marked as "old".
FIXME: Do not close all unused TABLE instances when flushing
particular table.
*/
while (unused_tables)
free_cache_entry(unused_tables);
/* Free table shares */
while (oldest_unused_share->next)
(void) my_hash_delete(&table_def_cache, (uchar*) oldest_unused_share);
if (!wait_for_refresh)
{
if (!have_lock)
pthread_mutex_unlock(&LOCK_open);
if (!wait_for_refresh)
DBUG_RETURN(result);
}
/* Code below assume that LOCK_open is released. */
DBUG_ASSERT(!have_lock);
pthread_mutex_unlock(&LOCK_open);
if (thd->locked_tables_mode)
{
......@@ -2109,27 +2111,6 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db,
}
/* move table first in unused links */
static void relink_unused(TABLE *table)
{
/* Assert that MERGE children are not attached in unused_tables. */
DBUG_ASSERT(!table->is_children_attached());
if (table != unused_tables)
{
table->prev->next=table->next; /* Remove from unused list */
table->next->prev=table->prev;
table->next=unused_tables; /* Link in unused tables */
table->prev=unused_tables->prev;
unused_tables->prev->next=table;
unused_tables->prev=table;
unused_tables=table;
check_unused();
}
}
/**
Prepare an open merge table for close.
......@@ -2241,7 +2222,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
}
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(thd, table->s->db.str, table->s->table_name.str);
tdc_remove_table(thd, TDC_RT_REMOVE_NOT_OWN,
table->s->db.str, table->s->table_name.str);
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(FALSE);
}
......@@ -4075,7 +4057,7 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table,
if (mdl_acquire_exclusive_locks(&thd->mdl_context))
return TRUE;
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(0, table->db, table->table_name);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
ha_create_table_from_engine(thd, table->db, table->table_name);
pthread_mutex_unlock(&LOCK_open);
......@@ -4089,7 +4071,7 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table,
if (mdl_acquire_exclusive_locks(&thd->mdl_context))
return TRUE;
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(0, table->db, table->table_name);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
pthread_mutex_unlock(&LOCK_open);
result= auto_repair_table(thd, table);
......@@ -8549,13 +8531,27 @@ bool notify_thread_having_shared_lock(THD *thd, THD *in_use)
/**
Remove all instances of the table from cache assuming that current thread
has exclusive meta-data lock on it (optionally leave instances belonging
to the current thread in cache).
Remove all or some (depending on parameter) instances of TABLE and
TABLE_SHARE from the table definition cache.
@param leave_thd 0 If we should remove all instances
non-0 Pointer to current thread context if we should
leave instances belonging to this thread.
@param thd Thread context
@param remove_type Type of removal:
TDC_RT_REMOVE_ALL - remove all TABLE instances and
TABLE_SHARE instance. There
should be no used TABLE objects
and caller should have exclusive
metadata lock on the table.
TDC_RT_REMOVE_NOT_OWN - remove all TABLE instances
except those that belong to
this thread. There should be
no TABLE objects used by other
threads and caller should have
exclusive metadata lock on the
table.
TDC_RT_REMOVE_UNUSED - remove all unused TABLE
instances (if there are no
used instances will also
remove TABLE_SHARE).
@param db Name of database
@param table_name Name of table
......@@ -8564,35 +8560,53 @@ bool notify_thread_having_shared_lock(THD *thd, THD *in_use)
by using meta-data locks).
*/
void expel_table_from_cache(THD *leave_thd, const char *db, const char *table_name)
void tdc_remove_table(THD *thd, enum_tdc_remove_table_type remove_type,
const char *db, const char *table_name)
{
char key[MAX_DBKEY_LENGTH];
uint key_length;
TABLE *table;
TABLE_SHARE *share;
safe_mutex_assert_owner(&LOCK_open);
DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED ||
mdl_is_exclusive_lock_owner(&thd->mdl_context, 0,
db, table_name));
key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key,
key_length)))
{
if (share->ref_count)
{
I_P_List_iterator<TABLE, TABLE_share> it(share->free_tables);
#ifndef DBUG_OFF
if (remove_type == TDC_RT_REMOVE_ALL)
{
DBUG_ASSERT(share->used_tables.is_empty());
}
else if (remove_type == TDC_RT_REMOVE_NOT_OWN)
{
I_P_List_iterator<TABLE, TABLE_share> it2(share->used_tables);
while ((table= it2++))
if (table->in_use != thd)
{
DBUG_ASSERT(0);
}
}
#endif
/*
Set share's version to zero in order to ensure that it gets
automatically deleted once it is no longer referenced.
*/
share->version= 0;
while ((table= it++))
relink_unused(table);
free_cache_entry(table);
}
/* This may destroy share so we have to do new look-up later. */
while (unused_tables && !unused_tables->s->version)
free_cache_entry(unused_tables);
if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key,
key_length)))
{
DBUG_ASSERT(leave_thd || share->ref_count == 0);
if (share->ref_count == 0)
my_hash_delete(&table_def_cache, (uchar*) share);
else
(void) my_hash_delete(&table_def_cache, (uchar*) share);
}
}
......@@ -8793,7 +8807,7 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
DBUG_RETURN(1);
}
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(lpt->thd, lpt->db, lpt->table_name);
tdc_remove_table(lpt->thd, TDC_RT_REMOVE_NOT_OWN, lpt->db, lpt->table_name);
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(0);
}
......
......@@ -1172,7 +1172,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
if (mdl_acquire_exclusive_locks(&thd->mdl_context))
DBUG_RETURN(TRUE);
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(0, table_list->db, table_list->table_name);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db,
table_list->table_name);
pthread_mutex_unlock(&LOCK_open);
}
......
......@@ -139,7 +139,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
pthread_mutex_lock(&LOCK_open);
for (ren_table= table_list; ren_table; ren_table= ren_table->next_local)
expel_table_from_cache(0, ren_table->db, ren_table->table_name);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, ren_table->db,
ren_table->table_name);
error=0;
if ((ren_table=rename_tables(thd,table_list,0)))
......
......@@ -3075,6 +3075,56 @@ uint get_table_open_method(TABLE_LIST *tables,
}
/**
Acquire high priority share metadata lock on a table.
@param thd Thread context.
@param mdl_lock_data Pointer to memory to be used for MDL_LOCK_DATA
object for a lock request.
@param mdlkey Pointer to the buffer for key for the lock request
(should be at least strlen(db) + strlen(name) + 2
bytes, or, if the lengths are not known,
MAX_DBNAME_LENGTH)
@param table Table list element for the table
@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 Success
@retval TRUE Some error occured (probably thread was killed).
*/
static bool
acquire_high_prio_shared_mdl_lock(THD *thd, MDL_LOCK_DATA *mdl_lock_data,
char *mdlkey, TABLE_LIST *table)
{
bool retry;
mdl_init_lock(mdl_lock_data, mdlkey, 0, table->db, table->table_name);
table->mdl_lock_data= mdl_lock_data;
mdl_add_lock(&thd->mdl_context, mdl_lock_data);
mdl_set_lock_type(mdl_lock_data, MDL_SHARED_HIGH_PRIO);
while (1)
{
if (mdl_acquire_shared_lock(&thd->mdl_context, mdl_lock_data, &retry))
{
if (!retry || mdl_wait_for_locks(&thd->mdl_context))
{
mdl_remove_all_locks(&thd->mdl_context);
return TRUE;
}
continue;
}
break;
}
return FALSE;
}
/**
@brief Fill I_S table with data from FRM file only
......@@ -3108,7 +3158,6 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
MDL_LOCK_DATA mdl_lock_data;
char mdlkey[MAX_DBKEY_LENGTH];
bool retry;
bzero((char*) &table_list, sizeof(TABLE_LIST));
bzero((char*) &tbl, sizeof(TABLE));
......@@ -3133,33 +3182,21 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
table_list.db= db_name->str;
}
mdl_init_lock(&mdl_lock_data, mdlkey, 0, db_name->str, table_name->str);
table_list.mdl_lock_data= &mdl_lock_data;
mdl_add_lock(&thd->mdl_context, &mdl_lock_data);
mdl_set_lock_type(&mdl_lock_data, MDL_SHARED_HIGH_PRIO);
/*
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.
*/
while (1)
{
if (mdl_acquire_shared_lock(&thd->mdl_context, &mdl_lock_data, &retry))
{
if (!retry || mdl_wait_for_locks(&thd->mdl_context))
if (acquire_high_prio_shared_mdl_lock(thd, &mdl_lock_data, mdlkey,
&table_list))
{
/*
Some error occured or we have been killed while waiting
for conflicting locks to go away, let the caller to handle
the situation.
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;
}
continue;
}
break;
}
key_length= create_table_def_key(thd, key, &table_list, 0);
pthread_mutex_lock(&LOCK_open);
......
......@@ -1897,7 +1897,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
DBUG_RETURN(1);
pthread_mutex_lock(&LOCK_open);
for (table= tables; table; table= table->next_local)
expel_table_from_cache(0, table->db, table->table_name);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table->db, table->table_name);
pthread_mutex_unlock(&LOCK_open);
}
else
......@@ -4345,7 +4345,7 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
if (mdl_acquire_exclusive_locks(&thd->mdl_context))
DBUG_RETURN(TRUE);
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(0, table->db, table->table_name);
tdc_remove_table(0, TDC_RT_REMOVE_UNUSED, table->db, table->table_name);
pthread_mutex_unlock(&LOCK_open);
if (my_copy(src_path, dst_path, MYF(MY_WME)))
......
......@@ -469,7 +469,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
goto end;
pthread_mutex_lock(&LOCK_open);
expel_table_from_cache(0, tables->db, tables->table_name);
tdc_remove_table(thd, TDC_RT_REMOVE_ALL, tables->db, tables->table_name);
if (reopen_name_locked_table(thd, tables))
goto end_unlock;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment