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; ...@@ -850,7 +850,7 @@ flush table t1;
execute stmt; execute stmt;
f1() f1()
6 6
call p_verify_reprepare_count(1); call p_verify_reprepare_count(0);
SUCCESS SUCCESS
execute stmt; execute stmt;
......
...@@ -745,7 +745,7 @@ execute stmt; ...@@ -745,7 +745,7 @@ execute stmt;
call p_verify_reprepare_count(1); call p_verify_reprepare_count(1);
flush table t1; flush table t1;
execute stmt; execute stmt;
call p_verify_reprepare_count(1); call p_verify_reprepare_count(0);
execute stmt; execute stmt;
--echo # Test 18-c: dependent VIEW has changed --echo # Test 18-c: dependent VIEW has changed
......
...@@ -1543,8 +1543,11 @@ char *generate_partition_syntax(partition_info *part_info, ...@@ -1543,8 +1543,11 @@ char *generate_partition_syntax(partition_info *part_info,
#endif #endif
bool notify_thread_having_shared_lock(THD *thd, THD *in_use); 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 NORMAL_PART_NAME 0
#define TEMP_PART_NAME 1 #define TEMP_PART_NAME 1
......
...@@ -921,12 +921,17 @@ void free_io_cache(TABLE *table) ...@@ -921,12 +921,17 @@ void free_io_cache(TABLE *table)
particular table identified by its share. particular table identified by its share.
@param share Table share. @param share Table share.
@pre Caller should have LOCK_open mutex acquired.
*/ */
static void kill_delayed_threads_for_table(TABLE_SHARE *share) static void kill_delayed_threads_for_table(TABLE_SHARE *share)
{ {
I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables); I_P_List_iterator<TABLE, TABLE_share> it(share->used_tables);
TABLE *tab; TABLE *tab;
safe_mutex_assert_owner(&LOCK_open);
while ((tab= it++)) while ((tab= it++))
{ {
THD *in_use= tab->in_use; THD *in_use= tab->in_use;
...@@ -983,6 +988,15 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, ...@@ -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", DBUG_PRINT("tcache", ("incremented global refresh_version to: %lu",
refresh_version)); refresh_version));
kill_delayed_threads(); 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 else
{ {
...@@ -993,8 +1007,10 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, ...@@ -993,8 +1007,10 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
if (share) if (share)
{ {
share->version= 0;
kill_delayed_threads_for_table(share); 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; found=1;
} }
} }
...@@ -1002,28 +1018,14 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock, ...@@ -1002,28 +1018,14 @@ bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool have_lock,
wait_for_refresh=0; // Nothing to wait for wait_for_refresh=0; // Nothing to wait for
} }
/* if (!have_lock)
Get rid of all unused TABLE and TABLE_SHARE instances. By doing pthread_mutex_unlock(&LOCK_open);
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 (!wait_for_refresh)
{
if (!have_lock)
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(result); DBUG_RETURN(result);
}
/* Code below assume that LOCK_open is released. */
DBUG_ASSERT(!have_lock); DBUG_ASSERT(!have_lock);
pthread_mutex_unlock(&LOCK_open);
if (thd->locked_tables_mode) if (thd->locked_tables_mode)
{ {
...@@ -2109,27 +2111,6 @@ bool rename_temporary_table(THD* thd, TABLE *table, const char *db, ...@@ -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. Prepare an open merge table for close.
...@@ -2241,7 +2222,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table, ...@@ -2241,7 +2222,8 @@ bool wait_while_table_is_used(THD *thd, TABLE *table,
} }
pthread_mutex_lock(&LOCK_open); 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); pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(FALSE); DBUG_RETURN(FALSE);
} }
...@@ -4075,7 +4057,7 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, ...@@ -4075,7 +4057,7 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table,
if (mdl_acquire_exclusive_locks(&thd->mdl_context)) if (mdl_acquire_exclusive_locks(&thd->mdl_context))
return TRUE; return TRUE;
pthread_mutex_lock(&LOCK_open); 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); ha_create_table_from_engine(thd, table->db, table->table_name);
pthread_mutex_unlock(&LOCK_open); pthread_mutex_unlock(&LOCK_open);
...@@ -4089,7 +4071,7 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table, ...@@ -4089,7 +4071,7 @@ recover_from_failed_open_table_attempt(THD *thd, TABLE_LIST *table,
if (mdl_acquire_exclusive_locks(&thd->mdl_context)) if (mdl_acquire_exclusive_locks(&thd->mdl_context))
return TRUE; return TRUE;
pthread_mutex_lock(&LOCK_open); 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); pthread_mutex_unlock(&LOCK_open);
result= auto_repair_table(thd, table); result= auto_repair_table(thd, table);
...@@ -8549,50 +8531,82 @@ bool notify_thread_having_shared_lock(THD *thd, THD *in_use) ...@@ -8549,50 +8531,82 @@ bool notify_thread_having_shared_lock(THD *thd, THD *in_use)
/** /**
Remove all instances of the table from cache assuming that current thread Remove all or some (depending on parameter) instances of TABLE and
has exclusive meta-data lock on it (optionally leave instances belonging TABLE_SHARE from the table definition cache.
to the current thread in cache).
@param thd Thread context
@param leave_thd 0 If we should remove all instances @param remove_type Type of removal:
non-0 Pointer to current thread context if we should TDC_RT_REMOVE_ALL - remove all TABLE instances and
leave instances belonging to this thread. TABLE_SHARE instance. There
@param db Name of database should be no used TABLE objects
@param table_name Name of table 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
@note Unlike remove_table_from_cache() it assumes that table instances @note Unlike remove_table_from_cache() it assumes that table instances
are already not used by any (other) thread (this should be achieved are already not used by any (other) thread (this should be achieved
by using meta-data locks). 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]; char key[MAX_DBKEY_LENGTH];
uint key_length; uint key_length;
TABLE *table; TABLE *table;
TABLE_SHARE *share; TABLE_SHARE *share;
key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1; safe_mutex_assert_owner(&LOCK_open);
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++)) DBUG_ASSERT(remove_type == TDC_RT_REMOVE_UNUSED ||
relink_unused(table); mdl_is_exclusive_lock_owner(&thd->mdl_context, 0,
} db, table_name));
/* This may destroy share so we have to do new look-up later. */ key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
while (unused_tables && !unused_tables->s->version)
free_cache_entry(unused_tables);
if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key, if ((share= (TABLE_SHARE*) my_hash_search(&table_def_cache,(uchar*) key,
key_length))) key_length)))
{ {
DBUG_ASSERT(leave_thd || share->ref_count == 0); if (share->ref_count)
if (share->ref_count == 0) {
my_hash_delete(&table_def_cache, (uchar*) share); 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++))
free_cache_entry(table);
}
else
(void) my_hash_delete(&table_def_cache, (uchar*) share);
} }
} }
...@@ -8793,7 +8807,7 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt) ...@@ -8793,7 +8807,7 @@ int abort_and_upgrade_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
DBUG_RETURN(1); DBUG_RETURN(1);
} }
pthread_mutex_lock(&LOCK_open); 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); pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(0); DBUG_RETURN(0);
} }
......
...@@ -1172,7 +1172,8 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) ...@@ -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)) if (mdl_acquire_exclusive_locks(&thd->mdl_context))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
pthread_mutex_lock(&LOCK_open); 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); pthread_mutex_unlock(&LOCK_open);
} }
......
...@@ -139,7 +139,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) ...@@ -139,7 +139,8 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent)
pthread_mutex_lock(&LOCK_open); pthread_mutex_lock(&LOCK_open);
for (ren_table= table_list; ren_table; ren_table= ren_table->next_local) 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; error=0;
if ((ren_table=rename_tables(thd,table_list,0))) if ((ren_table=rename_tables(thd,table_list,0)))
......
...@@ -3075,6 +3075,56 @@ uint get_table_open_method(TABLE_LIST *tables, ...@@ -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 @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, ...@@ -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]; char db_name_buff[NAME_LEN + 1], table_name_buff[NAME_LEN + 1];
MDL_LOCK_DATA mdl_lock_data; MDL_LOCK_DATA mdl_lock_data;
char mdlkey[MAX_DBKEY_LENGTH]; char mdlkey[MAX_DBKEY_LENGTH];
bool retry;
bzero((char*) &table_list, sizeof(TABLE_LIST)); bzero((char*) &table_list, sizeof(TABLE_LIST));
bzero((char*) &tbl, sizeof(TABLE)); bzero((char*) &tbl, sizeof(TABLE));
...@@ -3133,32 +3182,20 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table, ...@@ -3133,32 +3182,20 @@ static int fill_schema_table_from_frm(THD *thd,TABLE *table,
table_list.db= db_name->str; 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 TODO: investigate if in this particular situation we can get by
simply obtaining internal lock of data-dictionary (ATM it simply obtaining internal lock of data-dictionary (ATM it
is LOCK_open) instead of obtaning full-blown metadata lock. is LOCK_open) instead of obtaning full-blown metadata lock.
*/ */
while (1) if (acquire_high_prio_shared_mdl_lock(thd, &mdl_lock_data, mdlkey,
&table_list))
{ {
if (mdl_acquire_shared_lock(&thd->mdl_context, &mdl_lock_data, &retry)) /*
{ Some error occured (most probably we have been killed while
if (!retry || mdl_wait_for_locks(&thd->mdl_context)) waiting for conflicting locks to go away), let the caller to
{ handle the situation.
/* */
Some error occured or we have been killed while waiting return 1;
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); key_length= create_table_def_key(thd, key, &table_list, 0);
......
...@@ -1897,7 +1897,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists, ...@@ -1897,7 +1897,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
DBUG_RETURN(1); DBUG_RETURN(1);
pthread_mutex_lock(&LOCK_open); pthread_mutex_lock(&LOCK_open);
for (table= tables; table; table= table->next_local) 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); pthread_mutex_unlock(&LOCK_open);
} }
else else
...@@ -4345,7 +4345,7 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table, ...@@ -4345,7 +4345,7 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
if (mdl_acquire_exclusive_locks(&thd->mdl_context)) if (mdl_acquire_exclusive_locks(&thd->mdl_context))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
pthread_mutex_lock(&LOCK_open); 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); pthread_mutex_unlock(&LOCK_open);
if (my_copy(src_path, dst_path, MYF(MY_WME))) 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) ...@@ -469,7 +469,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
goto end; goto end;
pthread_mutex_lock(&LOCK_open); 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)) if (reopen_name_locked_table(thd, tables))
goto end_unlock; 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