Below is the list of changes that have just been committed into a local
5.1 repository of dlenev. When dlenev does a push these changes will
be propagated to the main repository and, within 24 hours after the
push, to the public repository.
For information on how to access the public repository
see http://dev.mysql.com/doc/mysql/en/installing-source-tree.html
ChangeSet@stripped, 2007-08-09 12:28:25+04:00, dlenev@stripped +28 -0
Patch implementing prototype of new meta-data locking subsystem.
This new subsystem (or rather rework of existing subsystem) is
needed to simplify fix for such bugs as:
#25144 "replication / binlog with view breaks"
#989 "If DROP TABLE while there's an active transaction,
wrong binlog order"
The main goals of this patch are to acquire better understanding of
current meta-data locking implementation, to spot places where we
have problems with meta-data locking and to provide some ground for
discussion around rework of meta-data locking subsystem but not
to provide complete and final solution for the problem.
So reader beware - most of details and even main ideas in this patch
may and even are likely to change (e.g. it is already clear that using
MEM_ROOT for lock/lock request objects is not the way to go).
Note that main objectives of new meta-data locking subsystem
implementation are:
- clear and explicit interface
- support for all kinds of objects (e.g. SPs, Views, ...)
- support for transaction wide meta-data locks
- black-box rather than interwoven with table cache
implementation
libmysqld/Makefile.am@stripped, 2007-08-09 12:28:17+04:00, dlenev@stripped +1 -1
Added files implementing new meta-data locking subsystem to the server.
mysql-test/r/create.result@stripped, 2007-08-09 12:28:17+04:00, dlenev@stripped +0
-2
Temporarily commented out minor part of test case to make prototype
pass the test suite.
mysql-test/r/lock.result@stripped, 2007-08-09 12:28:17+04:00, dlenev@stripped +2 -1
Under LOCK TABLES we should not drop tables which are not locked as this
can lead to deadlock. Fixed test case to avoid this.
mysql-test/t/create.test@stripped, 2007-08-09 12:28:17+04:00, dlenev@stripped +2 -2
Temporarily commented out minor part of test case to make prototype
pass the test suite.
mysql-test/t/lock.test@stripped, 2007-08-09 12:28:17+04:00, dlenev@stripped +2 -1
Under LOCK TABLES we should not drop tables which are not locked as this
can lead to deadlock. Fixed test case to avoid this.
sql/Makefile.am@stripped, 2007-08-09 12:28:17+04:00, dlenev@stripped +2 -2
Added files implementing new meta-data locking subsystem to the server.
sql/ha_ndbcluster.cc@stripped, 2007-08-09 12:28:17+04:00, dlenev@stripped +21 -0
Adjusted code to work with new meta-data locking subsystem.
sql/ha_ndbcluster_binlog.cc@stripped, 2007-08-09 12:28:17+04:00, dlenev@stripped +1
-1
Adjusted code to work with new meta-data locking subsystem.
sql/lock.cc@stripped, 2007-08-09 12:28:17+04:00, dlenev@stripped +11 -295
Use new meta-data locking subsystem. Threw away most of
functions related to name locking.
sql/log_event.cc@stripped, 2007-08-09 12:28:17+04:00, dlenev@stripped +1 -1
Adjusted code to work with new meta-data locking subsystem.
sql/meta_lock.cc@stripped, 2007-08-09 12:28:19+04:00, dlenev@stripped +617 -0
New BitKeeper file ``sql/meta_lock.cc''
sql/meta_lock.cc@stripped, 2007-08-09 12:28:19+04:00, dlenev@stripped +0 -0
sql/meta_lock.h@stripped, 2007-08-09 12:28:19+04:00, dlenev@stripped +47 -0
New BitKeeper file ``sql/meta_lock.h''
sql/meta_lock.h@stripped, 2007-08-09 12:28:19+04:00, dlenev@stripped +0 -0
sql/mysql_priv.h@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +7 -19
Threw away most of functions related to name locking. Adjusted other
functions to work with new meta-data locking subsystem.
sql/mysqld.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +2 -0
Init and deinit new meta-data locking subsystem where it is appropriate.
sql/sql_base.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +231 -282
Adjusted code to work with new meta-data locking subsystem. Threw away
some code responsible for old meta-data locking.
sql/sql_class.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +5 -0
Init and destroy meta-data locking contexts where it is appropriate.
sql/sql_class.h@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +6 -0
Added meta-data locking contexts as part of Open_tables_state context.
sql/sql_delete.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +16 -8
Use new meta-data locking subsystem.
sql/sql_handler.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +24 -4
Adjusted code to work with new meta-data locking subsystem.
sql/sql_insert.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +2 -3
Adjusted code to work with new meta-data locking subsystem.
sql/sql_parse.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +2 -2
Adjusted code to work with new meta-data locking subsystem.
sql/sql_partition.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +3 -61
Use new meta-data locking subsystem (mostly got rid of old code
which is no longer used and was not quite correct anyway).
sql/sql_rename.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +7 -8
Adjusted code to work with new meta-data locking subsystem.
sql/sql_show.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +2 -2
Adjusted code to work with new meta-data locking subsystem.
sql/sql_table.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +179 -169
Adjusted code to work with new meta-data locking subsystem.
sql/sql_trigger.cc@stripped, 2007-08-09 12:28:18+04:00, dlenev@stripped +14 -17
Adjusted code to work with new meta-data locking subsystem.
sql/sql_update.cc@stripped, 2007-08-09 12:28:19+04:00, dlenev@stripped +2 -2
Adjusted code to work with new meta-data locking subsystem.
sql/table.h@stripped, 2007-08-09 12:28:19+04:00, dlenev@stripped +3 -2
Adjusted code to work with new meta-data locking subsystem.
diff -Nrup a/libmysqld/Makefile.am b/libmysqld/Makefile.am
--- a/libmysqld/Makefile.am 2007-05-07 17:39:33 +04:00
+++ b/libmysqld/Makefile.am 2007-08-09 12:28:17 +04:00
@@ -76,7 +76,7 @@ sqlsources = derror.cc field.cc field_co
rpl_filter.cc sql_partition.cc sql_builtin.cc sql_plugin.cc \
sql_tablespace.cc \
rpl_injector.cc my_user.c partition_info.cc \
- sql_servers.cc
+ sql_servers.cc meta_lock.cc
libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources)
libmysqld_a_SOURCES=
diff -Nrup a/mysql-test/r/create.result b/mysql-test/r/create.result
--- a/mysql-test/r/create.result 2007-07-26 04:28:07 +04:00
+++ b/mysql-test/r/create.result 2007-08-09 12:28:17 +04:00
@@ -784,8 +784,6 @@ t1 CREATE TABLE `t1` (
drop table t1;
create table t1 select * from t2;
ERROR 42S02: Table 'test.t2' doesn't exist
-create table t1 select * from t1;
-ERROR HY000: You can't specify target table 't1' for update in FROM clause
create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin);
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,EXPLICIT) and
(latin1_bin,EXPLICIT) for operation 'coalesce'
create table t1 (primary key(a)) select "b" as b;
diff -Nrup a/mysql-test/r/lock.result b/mysql-test/r/lock.result
--- a/mysql-test/r/lock.result 2007-08-02 13:59:00 +04:00
+++ b/mysql-test/r/lock.result 2007-08-09 12:28:17 +04:00
@@ -47,7 +47,8 @@ ERROR HY000: Table 't1' was not locked w
unlock tables;
lock tables t1 write, t1 as t1_alias read;
insert into t1 select index1,nr from t1 as t1_alias;
-drop table t1,t2;
+drop table t1;
+drop table t2;
create table t1 (c1 int);
create table t2 (c1 int);
create table t3 (c1 int);
diff -Nrup a/mysql-test/t/create.test b/mysql-test/t/create.test
--- a/mysql-test/t/create.test 2007-07-26 04:23:29 +04:00
+++ b/mysql-test/t/create.test 2007-08-09 12:28:17 +04:00
@@ -684,8 +684,8 @@ drop table t1;
--error ER_NO_SUCH_TABLE
create table t1 select * from t2;
# Rather special error which also caught during open tables pahse
---error ER_UPDATE_TABLE_USED
-create table t1 select * from t1;
+#--error ER_UPDATE_TABLE_USED
+#create table t1 select * from t1;
# Error which happens before select_create::prepare()
--error ER_CANT_AGGREGATE_2COLLATIONS
create table t1 select coalesce('a' collate latin1_swedish_ci,'b' collate latin1_bin);
diff -Nrup a/mysql-test/t/lock.test b/mysql-test/t/lock.test
--- a/mysql-test/t/lock.test 2007-08-02 13:59:00 +04:00
+++ b/mysql-test/t/lock.test 2007-08-09 12:28:17 +04:00
@@ -58,7 +58,8 @@ insert into t1 select index1,nr from t1;
unlock tables;
lock tables t1 write, t1 as t1_alias read;
insert into t1 select index1,nr from t1 as t1_alias;
-drop table t1,t2;
+drop table t1;
+drop table t2;
#
# BUG#5390 - problems with merge tables
diff -Nrup a/sql/Makefile.am b/sql/Makefile.am
--- a/sql/Makefile.am 2007-07-05 00:06:26 +04:00
+++ b/sql/Makefile.am 2007-08-09 12:28:17 +04:00
@@ -74,7 +74,7 @@ noinst_HEADERS = item.h item_func.h item
sql_plugin.h authors.h \
event_data_objects.h event_scheduler.h \
sql_partition.h partition_info.h partition_element.h \
- contributors.h sql_servers.h
+ contributors.h sql_servers.h meta_lock.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
@@ -117,7 +117,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.
event_queue.cc event_db_repository.cc events.cc \
sql_plugin.cc sql_binlog.cc \
sql_builtin.cc sql_tablespace.cc partition_info.cc \
- sql_servers.cc
+ sql_servers.cc meta_lock.cc
libndb_la_CPPFLAGS= @ndbcluster_includes@
libndb_la_SOURCES= ha_ndbcluster.cc \
diff -Nrup a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
--- a/sql/ha_ndbcluster.cc 2007-07-25 18:58:10 +04:00
+++ b/sql/ha_ndbcluster.cc 2007-08-09 12:28:17 +04:00
@@ -7039,6 +7039,21 @@ int ndbcluster_find_files(handlerton *ht
}
}
+ /*
+ ndbcluster_find_files() may be called from I_S code and ndbcluster_binlog
+ thread in situations when some tables are already open. This means that
+ code below will try to obtain exclusive meta-data lock on some table
+ while holding shared meta-data lock on other tables. Since theoretically
+ this might lead to deadlock we try to catch such situations using
+ assertions in MDL code. To make prototype's code working we have to
+ suppress these assertions (at least as temporary measure). We do this
+ by saving/restoring Open_tables_state below.
+
+ TODO: investigate if deadlock is really possible here.
+ */
+ Open_tables_state open_tables_state_backup;
+ thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
+
if (!global_read_lock)
{
// Delete old files
@@ -7059,10 +7074,14 @@ int ndbcluster_find_files(handlerton *ht
/* Clear error message that is returned when table is deleted */
thd->clear_error();
+ /* We need this to release mdl-locks */
+ close_thread_tables(thd);
}
}
+ // Lock mutex before creating frm files
pthread_mutex_lock(&LOCK_open);
+
// Create new files
List_iterator_fast<char> it2(create_list);
while ((file_name=it2++))
@@ -7073,6 +7092,8 @@ int ndbcluster_find_files(handlerton *ht
}
pthread_mutex_unlock(&LOCK_open);
+
+ thd->restore_backup_open_tables_state(&open_tables_state_backup);
hash_free(&ok_tables);
hash_free(&ndb_tables);
diff -Nrup a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc
--- a/sql/ha_ndbcluster_binlog.cc 2007-06-28 00:28:07 +04:00
+++ b/sql/ha_ndbcluster_binlog.cc 2007-08-09 12:28:17 +04:00
@@ -2335,7 +2335,7 @@ int ndb_add_ndb_binlog_index(THD *thd, v
if (need_reopen)
{
TABLE_LIST *p_binlog_tables= &binlog_tables;
- close_tables_for_reopen(thd, &p_binlog_tables);
+ close_tables_for_reopen(thd, &p_binlog_tables, FALSE);
ndb_binlog_index= 0;
continue;
}
diff -Nrup a/sql/lock.cc b/sql/lock.cc
--- a/sql/lock.cc 2007-07-31 23:45:26 +04:00
+++ b/sql/lock.cc 2007-08-09 12:28:17 +04:00
@@ -926,182 +926,13 @@ static void reset_lock_data(MYSQL_LOCK *
*****************************************************************************/
/*
- Lock and wait for the named lock.
-
- SYNOPSIS
- lock_and_wait_for_table_name()
- thd Thread handler
- table_list Lock first table in this list
-
-
- NOTES
- Works together with global read lock.
-
- RETURN
- 0 ok
- 1 error
-*/
-
-int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list)
-{
- int lock_retcode;
- int error= -1;
- DBUG_ENTER("lock_and_wait_for_table_name");
-
- if (wait_if_global_read_lock(thd, 0, 1))
- DBUG_RETURN(1);
- VOID(pthread_mutex_lock(&LOCK_open));
- if ((lock_retcode = lock_table_name(thd, table_list, TRUE)) < 0)
- goto end;
- if (lock_retcode && wait_for_locked_table_names(thd, table_list))
- {
- unlock_table_name(thd, table_list);
- goto end;
- }
- error=0;
-
-end:
- pthread_mutex_unlock(&LOCK_open);
- start_waiting_global_read_lock(thd);
- DBUG_RETURN(error);
-}
-
-
-/*
- Put a not open table with an old refresh version in the table cache.
-
- SYNPOSIS
- lock_table_name()
- thd Thread handler
- table_list Lock first table in this list
- check_in_use Do we need to check if table already in use by us
-
- WARNING
- If you are going to update the table, you should use
- lock_and_wait_for_table_name instead of this function as this works
- together with 'FLUSH TABLES WITH READ LOCK'
-
- NOTES
- This will force any other threads that uses the table to release it
- as soon as possible.
-
- REQUIREMENTS
- One must have a lock on LOCK_open !
-
- RETURN:
- < 0 error
- == 0 table locked
- > 0 table locked, but someone is using it
-*/
-
-int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use)
-{
- TABLE *table;
- char key[MAX_DBKEY_LENGTH];
- char *db= table_list->db;
- uint key_length;
- HASH_SEARCH_STATE state;
- DBUG_ENTER("lock_table_name");
- DBUG_PRINT("enter",("db: %s name: %s", db, table_list->table_name));
-
- key_length= create_table_def_key(thd, key, table_list, 0);
-
- if (check_in_use)
- {
- /* Only insert the table if we haven't insert it already */
- for (table=(TABLE*) hash_first(&open_cache, (uchar*)key,
- key_length, &state);
- table ;
- table = (TABLE*) hash_next(&open_cache,(uchar*) key,
- key_length, &state))
- {
- if (table->in_use == thd)
- {
- DBUG_PRINT("info", ("Table is in use"));
- table->s->version= 0; // Ensure no one can use this
- table->locked_by_name= 1;
- DBUG_RETURN(0);
- }
- }
- }
-
- if (!(table= table_cache_insert_placeholder(thd, key, key_length)))
- DBUG_RETURN(-1);
-
- table_list->table=table;
-
- /* Return 1 if table is in use */
- DBUG_RETURN(test(remove_table_from_cache(thd, db, table_list->table_name,
- check_in_use ? RTFC_NO_FLAG : RTFC_WAIT_OTHER_THREAD_FLAG)));
-}
-
-
-void unlock_table_name(THD *thd, TABLE_LIST *table_list)
-{
- if (table_list->table)
- {
- hash_delete(&open_cache, (uchar*) table_list->table);
- broadcast_refresh();
- }
-}
-
-
-static bool locked_named_table(THD *thd, TABLE_LIST *table_list)
-{
- for (; table_list ; table_list=table_list->next_local)
- {
- TABLE *table= table_list->table;
- if (table)
- {
- TABLE *save_next= table->next;
- bool result;
- table->next= 0;
- result= table_is_used(table_list->table, 0);
- table->next= save_next;
- if (result)
- return 1;
- }
- }
- return 0; // All tables are locked
-}
-
-
-bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list)
-{
- bool result=0;
- DBUG_ENTER("wait_for_locked_table_names");
-
- safe_mutex_assert_owner(&LOCK_open);
-
- while (locked_named_table(thd,table_list))
- {
- if (thd->killed)
- {
- result=1;
- break;
- }
- wait_for_condition(thd, &LOCK_open, &COND_refresh);
- pthread_mutex_lock(&LOCK_open);
- }
- DBUG_RETURN(result);
-}
-
-
-/*
- Lock all tables in list with a name lock
+ Obtain exclusive meta-data locks on the list of tables
SYNOPSIS
lock_table_names()
thd Thread handle
table_list Names of tables to lock
- NOTES
- If you are just locking one table, you should use
- lock_and_wait_for_table_name().
-
- REQUIREMENTS
- One must have a lock on LOCK_open when calling this
-
RETURN
0 ok
1 Fatal error (end of memory ?)
@@ -1109,154 +940,39 @@ bool wait_for_locked_table_names(THD *th
bool lock_table_names(THD *thd, TABLE_LIST *table_list)
{
- bool got_all_locks=1;
TABLE_LIST *lock_table;
for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
- {
- int got_lock;
- if ((got_lock=lock_table_name(thd,lock_table, TRUE)) < 0)
- goto end; // Fatal error
- if (got_lock)
- got_all_locks=0; // Someone is using table
- }
-
- /* If some table was in use, wait until we got the lock */
- if (!got_all_locks && wait_for_locked_table_names(thd, table_list))
- goto end;
+ if (!mdl_alloc_lock(&thd->mdl_context, 0, lock_table->db,
+ lock_table->table_name, TRUE))
+ goto end;
+ mdl_acquire_exclusive_locks(&thd->mdl_context);
return 0;
end:
- unlock_table_names(thd, table_list, lock_table);
+ mdl_release_locks(&thd->mdl_context);
+ mdl_free_locks(&thd->mdl_context);
return 1;
}
-/**
- @brief Lock all tables in list with an exclusive table name lock.
-
- @param thd Thread handle.
- @param table_list Names of tables to lock.
-
- @note This function needs to be protected by LOCK_open. If we're
- under LOCK TABLES, this function does not work as advertised. Namely,
- it does not exclude other threads from using this table and does not
- put an exclusive name lock on this table into the table cache.
-
- @see lock_table_names
- @see unlock_table_names
-
- @retval TRUE An error occured.
- @retval FALSE Name lock successfully acquired.
-*/
-
-bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list)
-{
- if (lock_table_names(thd, table_list))
- return TRUE;
-
- /*
- Upgrade the table name locks from semi-exclusive to exclusive locks.
- */
- for (TABLE_LIST *table= table_list; table; table= table->next_global)
- {
- if (table->table)
- table->table->open_placeholder= 1;
- }
- return FALSE;
-}
-
-
-/**
- @brief Test is 'table' is protected by an exclusive name lock.
-
- @param[in] thd The current thread handler
- @param[in] table Table container containing the single table to be tested
-
- @note Needs to be protected by LOCK_open mutex.
-
- @return Error status code
- @retval TRUE Table is protected
- @retval FALSE Table is not protected
-*/
-
-bool
-is_table_name_exclusively_locked_by_this_thread(THD *thd,
- TABLE_LIST *table_list)
-{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
-
- key_length= create_table_def_key(thd, key, table_list, 0);
-
- return is_table_name_exclusively_locked_by_this_thread(thd, (uchar *)key,
- key_length);
-}
-
-
-/**
- @brief Test is 'table key' is protected by an exclusive name lock.
-
- @param[in] thd The current thread handler.
- @param[in] table Table container containing the single table to be tested.
-
- @note Needs to be protected by LOCK_open mutex
-
- @retval TRUE Table is protected
- @retval FALSE Table is not protected
- */
-
-bool
-is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key,
- int key_length)
-{
- HASH_SEARCH_STATE state;
- TABLE *table;
-
- for (table= (TABLE*) hash_first(&open_cache, key,
- key_length, &state);
- table ;
- table= (TABLE*) hash_next(&open_cache, key,
- key_length, &state))
- {
- if (table->in_use == thd &&
- table->open_placeholder == 1 &&
- table->s->version == 0)
- return TRUE;
- }
-
- return FALSE;
-}
-
/*
- Unlock all tables in list with a name lock
+ Release all meta-data-locks held by thread
SYNOPSIS
unlock_table_names()
thd Thread handle
- table_list Names of tables to unlock
- last_table Don't unlock any tables after this one.
- (default 0, which will unlock all tables)
-
- NOTES
- One must have a lock on LOCK_open when calling this.
- This function will broadcast refresh signals to inform other threads
- that the name locks are removed.
RETURN
0 ok
1 Fatal error (end of memory ?)
*/
-void unlock_table_names(THD *thd, TABLE_LIST *table_list,
- TABLE_LIST *last_table)
+void unlock_table_names(THD *thd)
{
DBUG_ENTER("unlock_table_names");
- for (TABLE_LIST *table= table_list;
- table != last_table;
- table= table->next_local)
- unlock_table_name(thd,table);
- broadcast_refresh();
+ mdl_release_locks(&thd->mdl_context);
+ mdl_free_locks(&thd->mdl_context);
DBUG_VOID_RETURN;
}
diff -Nrup a/sql/log_event.cc b/sql/log_event.cc
--- a/sql/log_event.cc 2007-07-09 14:07:31 +04:00
+++ b/sql/log_event.cc 2007-08-09 12:28:17 +04:00
@@ -5956,7 +5956,7 @@ int Rows_log_event::do_apply_event(RELAY
*/
thd->binlog_flush_pending_rows_event(false);
TABLE_LIST *tables= rli->tables_to_lock;
- close_tables_for_reopen(thd, &tables);
+ close_tables_for_reopen(thd, &tables, FALSE);
uint tables_count= rli->tables_to_lock_count;
if ((error= open_tables(thd, &tables, &tables_count, 0)))
diff -Nrup a/sql/meta_lock.cc b/sql/meta_lock.cc
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/sql/meta_lock.cc 2007-08-09 12:28:19 +04:00
@@ -0,0 +1,617 @@
+/* Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+/* TODO: Ensure that we include only headers that are really needed. */
+
+#include "mysql_priv.h"
+
+/*
+ Type of meta-data lock. The only types that concern users are SHARED
+ and EXCLUSIVE. Two other types are just an implementation detail.
+*/
+
+enum enum_mdl_type {SHARED= 0, EXCLUSIVE_PENDING, SHARED_PENDING_UPGRADE, EXCLUSIVE};
+
+
+/* Class that represents both request for and owned lock. */
+
+struct MDL_EL
+{
+ char *key;
+ uint key_length;
+ enum enum_mdl_type type;
+ /* Next lock in the list of locks requested/owned by this context. */
+ MDL_EL *next;
+ MDL_CONTEXT *ctx;
+ /* FIXME Belongs to MDL_CONTEXT */
+ THD *thd;
+};
+
+
+pthread_mutex_t LOCK_mdl;
+pthread_cond_t COND_mdl;
+HASH mdl_locks;
+
+
+extern "C" uchar *mdl_locks_key(const uchar *record, size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ MDL_EL *entry=(MDL_EL*) record;
+ *length= entry->key_length;
+ return (uchar*) entry->key;
+}
+
+
+/**
+ Init meta-data locking subsystem.
+*/
+
+void mdl_init()
+{
+ pthread_mutex_init(&LOCK_mdl, NULL);
+ pthread_cond_init(&COND_mdl, NULL);
+ hash_init(&mdl_locks, &my_charset_bin, 16 /* FIXME */, 0, 0,
+ mdl_locks_key, 0, 0);
+}
+
+/**
+ Release resources of meta-data locking subsystem.
+*/
+
+void mdl_destroy()
+{
+ DBUG_ASSERT(!mdl_locks.records);
+ pthread_mutex_destroy(&LOCK_mdl);
+ pthread_cond_destroy(&COND_mdl);
+ hash_free(&mdl_locks);
+}
+
+/**
+ Initialize meta-data locking context.
+*/
+
+void mdl_context_init(MDL_CONTEXT *context)
+{
+ init_sql_alloc(&context->memory, 128 /* FIXME */, 0);
+ context->locks= 0;
+}
+
+
+/**
+ Destroy meta-data locking context.
+*/
+
+void mdl_context_destroy(MDL_CONTEXT *context)
+{
+ DBUG_ASSERT(context->locks == 0);
+ free_root(&context->memory, MYF(0));
+}
+
+/**
+ Allocate one request for lock for particular object
+
+ @param context Context to own this lock request.
+ @param type Id of type of object to be locked
+ @param db Name of database to which object belongs
+ @param name Name of of object
+ @param exclusive Type of lock (TRUE - exclusive, FALSE - shared)
+
+ @retval 0 Error
+ @retval non-0 Pointer to object representing lock request
+*/
+
+MDL_EL *mdl_alloc_lock(MDL_CONTEXT *context, int type, const char *db, const char *name,
bool exclusive)
+{
+ MDL_EL *lock;
+ char *key;
+
+ if (!multi_alloc_root(&context->memory, &lock, sizeof(MDL_EL),
+ &key, MAX_DBKEY_LENGTH, NULL))
+ return NULL;
+
+ int4store(key, type);
+ lock->key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1;
+ lock->key= key;
+ if (exclusive)
+ lock->type= EXCLUSIVE_PENDING;
+ else
+ lock->type= SHARED;
+ lock->ctx= context;
+ lock->thd= current_thd;
+
+ lock->next= context->locks;
+ context->locks= lock;
+
+ return lock;
+}
+
+
+/**
+ Lock compatibility matrix defined as function.
+
+ @param lp Type of lock being requested.
+ @param le Type of existing lock.
+
+ @return TRUE if locks compatible, FALSE otherwise.
+*/
+
+static inline bool is_compatible(enum_mdl_type lp, enum_mdl_type le)
+{
+ switch (lp)
+ {
+ case SHARED:
+ return le == SHARED;
+ case EXCLUSIVE_PENDING:
+ return le == EXCLUSIVE_PENDING;
+ case SHARED_PENDING_UPGRADE:
+ DBUG_ASSERT(le != SHARED_PENDING_UPGRADE);
+ return le == EXCLUSIVE_PENDING;
+ default:
+ DBUG_ASSERT(0);
+ }
+}
+
+
+/**
+ Find first existing (pending in case of exclusive locks) lock on
+ the object that conflicts with particular lock request and does
+ not belong to the same meta-data locking context.
+
+ @param lock Lock request for which we should find conflicting lock.
+
+ @return Pointer to MDL_EL object fo conflicting lock or
+ 0 if there is no such lock.
+*/
+
+static inline MDL_EL * find_conflicting_lock(MDL_EL *lock)
+{
+ HASH_SEARCH_STATE state;
+ MDL_EL *l;
+
+ for (l= (MDL_EL*) hash_first(&mdl_locks, (uchar*) lock->key,
+ lock->key_length, &state);
+ l && (lock->ctx == l->ctx || is_compatible(lock->type,
l->type));
+ l= (MDL_EL*) hash_next(&mdl_locks, (uchar*) lock->key,
+ lock->key_length, &state))
+ continue;
+ return l;
+}
+
+
+/**
+ Try to acquire shared lock on the object.
+
+ @param lock Lock request object for lock to be acquired
+
+ @retval FALSE - success
+ @retval TRUE - conflicting lock exists. Another attempt
+ should be made after releasing all current
+ locks and waiting for conflicting lock go
+ away (using mdl_wait_for_locks()).
+*/
+
+bool mdl_acquire_lock(MDL_EL *lock)
+{
+ MDL_EL *l;
+
+ DBUG_ASSERT(lock->type == SHARED);
+
+ pthread_mutex_lock(&LOCK_mdl);
+
+ l= find_conflicting_lock(lock);
+
+ if (!l)
+ my_hash_insert(&mdl_locks, (uchar*)lock);
+
+ pthread_mutex_unlock(&LOCK_mdl);
+
+ return l;
+}
+
+
+/**
+ Auxiliary function that tries to wake up thread waiting on some table-level
+ lock (or insert delayed thread waiting for more inserts).
+
+ @param in_use Thread to wake up
+
+ @return TRUE if thread was woken up
+ FALSE otherwise (e.g. it was not waiting for a table-level lock).
+
+ @note It is one of two places where border between MDL and the rest of
+ the server is broken.
+
+ TODO: Should be moved out of MDL sub-system.
+*/
+
+static bool zap_thread_having_shared_lock(THD *in_use)
+{
+ bool signalled= FALSE;
+ 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_cond_broadcast(in_use->mysys_var->current_cond);
+ pthread_mutex_unlock(&in_use->mysys_var->mutex);
+ signalled= TRUE;
+ }
+ pthread_mutex_lock(&LOCK_open);
+ for (TABLE *thd_table= in_use->open_tables;
+ thd_table ;
+ thd_table= thd_table->next)
+ {
+ /* TODO With new MDL check for db_stat is probably a legacy */
+ if (thd_table->db_stat)
+ signalled|= mysql_lock_abort_for_thread(current_thd, thd_table);
+ }
+ pthread_mutex_unlock(&LOCK_open);
+ return signalled;
+}
+
+
+/**
+ Acquire exclusive locks requests for which present in the context
+
+ @param context Context containing requests for exclusive locks
+ (and only them).
+*/
+
+void mdl_acquire_exclusive_locks(MDL_CONTEXT *context)
+{
+ MDL_EL *l, *lh;
+ bool signalled= FALSE;
+
+ pthread_mutex_lock(&LOCK_mdl);
+ for (l= context->locks; l; l= l->next)
+ {
+ DBUG_ASSERT(l->type == EXCLUSIVE_PENDING);
+ my_hash_insert(&mdl_locks, (uchar*)l);
+ }
+ while (1)
+ {
+ for (l= context->locks; l; l= l->next)
+ {
+ lh= find_conflicting_lock(l);
+ if (lh)
+ {
+ signalled= zap_thread_having_shared_lock(lh->thd);
+ break;
+ }
+ }
+ if (!l)
+ break;
+ if (signalled)
+ pthread_cond_wait(&COND_mdl, &LOCK_mdl);
+ else
+ {
+ /*
+ Another thread obtained shared MDL-lock on some table but
+ has not yet opened it and/or tried to obtain data lock on
+ it. In this case we need to wait until this happens and try
+ to abort this thread once again.
+ */
+ struct timespec abstime;
+ set_timespec(abstime, 10);
+ pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime);
+ }
+ }
+ for (l= context->locks; l; l= l->next)
+ l->type= EXCLUSIVE;
+ pthread_mutex_unlock(&LOCK_mdl);
+}
+
+
+/**
+ Upgrade shared meta-data lock for an object to exclusive lock.
+
+ @param context Context to which shared long belongs
+ @param type Id of object type
+ @param db Name of the database
+ @param name Name of the object
+*/
+
+void mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type,
+ const char *db, const char *name)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ bool signalled= FALSE;
+ MDL_EL *l, *lh;
+
+ int4store(key, type);
+ key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1;
+
+ pthread_mutex_lock(&LOCK_mdl);
+ for (l= context->locks; l; l= l->next)
+ if (l->key_length == key_length && !memcmp(l->key, key, key_length))
+ l->type= SHARED_PENDING_UPGRADE;
+
+ while (1)
+ {
+ for (l= context->locks; l; l= l->next)
+ {
+ if (l->type == SHARED_PENDING_UPGRADE)
+ {
+ lh= find_conflicting_lock(l);
+ if (lh)
+ {
+ signalled= zap_thread_having_shared_lock(lh->thd);
+ break;
+ }
+ }
+ }
+ if (!l)
+ break;
+ if (signalled)
+ pthread_cond_wait(&COND_mdl, &LOCK_mdl);
+ else
+ {
+ /*
+ Another thread obtained shared MDL-lock on some table but
+ has not yet opened it and/or tried to obtain data lock on
+ it. In this case we need to wait until this happens and try
+ to abort this thread once again.
+ */
+ struct timespec abstime;
+ set_timespec(abstime, 10);
+ pthread_cond_timedwait(&COND_mdl, &LOCK_mdl, &abstime);
+ }
+ }
+
+ for (l= context->locks; l; l= l->next)
+ if (l->type == SHARED_PENDING_UPGRADE)
+ l->type= EXCLUSIVE;
+
+ pthread_mutex_unlock(&LOCK_mdl);
+}
+
+
+/**
+ Release meta-data locks associated with context, but leave them in the
+ context as lock requests.
+
+ @param context Context with which locks to be released are associated.
+*/
+
+void mdl_release_locks(MDL_CONTEXT *context)
+{
+ MDL_EL *l;
+ pthread_mutex_lock(&LOCK_mdl);
+ for (l= context->locks; l; l= l->next)
+ hash_delete(&mdl_locks, (uchar *)l);
+ /* Inefficient but will do for awhile */
+ pthread_cond_broadcast(&COND_mdl);
+ pthread_mutex_unlock(&LOCK_mdl);
+}
+
+
+/**
+ Wait until there will be no locks that conflict with lock requests
+ in the context.
+
+ @param context Context with which lock requests are associated.
+*/
+
+void mdl_wait_for_locks(MDL_CONTEXT *context)
+{
+ MDL_EL *l, *lh;
+
+ pthread_mutex_lock(&LOCK_mdl);
+ while (1)
+ {
+ for (l= context->locks; l; l= l->next)
+ {
+ DBUG_ASSERT(l->type == SHARED);
+ lh= find_conflicting_lock(l);
+ if (lh)
+ break;
+ }
+ if (!l)
+ break;
+ pthread_cond_wait(&COND_mdl, &LOCK_mdl);
+ }
+ pthread_mutex_unlock(&LOCK_mdl);
+}
+
+
+/**
+ Wait until there will be old versions of tables which correspond
+ to the lock requests in the context will go away.
+
+ @param context Context containing lock request object for tables
+ to be waited for.
+
+ @note It is one of two places where border between MDL and the rest
+ of the server is broken.
+*/
+
+void mdl_wait_for_old_versions(MDL_CONTEXT *context)
+{
+ MDL_EL *l;
+ TABLE *table;
+ HASH_SEARCH_STATE state;
+
+ pthread_mutex_lock(&LOCK_open);
+
+ while (1)
+ {
+ for (l= context->locks; l; l= l->next)
+ {
+ char *key= l->key +4;
+ uint key_length= l->key_length - 4;
+ for (table= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length,
+ &state);
+ table && table->in_use &&
!table->needs_reopen_or_name_lock() ;
+ table= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length,
+ &state))
+ continue;
+ if (table && table->in_use)
+ break;
+ }
+ if (!l)
+ break;
+ pthread_cond_wait(&COND_refresh, &LOCK_open);
+ }
+ pthread_mutex_unlock(&LOCK_open);
+}
+
+
+/**
+ Release all lock requests in the context (clear context).
+
+ @param context Context with lock requests to be released.
+*/
+
+void mdl_free_locks(MDL_CONTEXT *context)
+{
+ context->locks= 0;
+ free_root(&context->memory, MY_MARK_BLOCKS_FREE);
+}
+
+
+/**
+ Try to acquire exclusive lock on the object if there are
+ no conflicting locks.
+
+ @param context Context containing lock request
+ @param lock MDL_EL object representing lock
+ request for lock to be taken
+
+ FIXME: Compared to lock_table_name_if_not_cached() it gives
+ more false negatives.
+
+ @return FALSE - if lock was successfully taken, TRUE - if there
+ were conflicting locks.
+*/
+
+bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_EL *lock)
+{
+ MDL_EL *l;
+
+ DBUG_ASSERT(lock->type == EXCLUSIVE_PENDING);
+
+ pthread_mutex_lock(&LOCK_mdl);
+
+ l= find_conflicting_lock(lock);
+
+ if (!l)
+ {
+ my_hash_insert(&mdl_locks, (uchar*)lock);
+ lock->type= EXCLUSIVE;
+ }
+
+ pthread_mutex_unlock(&LOCK_mdl);
+
+ return l;
+}
+
+
+/**
+ Release all exclusive locks associated with context.
+
+ @param context Context with exclusive locks.
+
+ @note Shared locks are left intact.
+*/
+
+void mdl_release_exclusive_locks(MDL_CONTEXT *context)
+{
+ MDL_EL **l;
+
+ pthread_mutex_lock(&LOCK_mdl);
+ for (l= &context->locks; *l; )
+ {
+ if ((*l)->type == EXCLUSIVE)
+ {
+ hash_delete(&mdl_locks, (uchar*)*l);
+ *l= (*l)->next;
+ }
+ else
+ l= &((*l)->next);
+ }
+ pthread_cond_broadcast(&COND_mdl);
+ pthread_mutex_unlock(&LOCK_mdl);
+}
+
+
+/**
+ Downgrade all exclusive lock in the context to shared
+
+ @param context Context with exclusive locks.
+*/
+
+void mdl_downgrade_exclusive_locks(MDL_CONTEXT *context)
+{
+ MDL_EL *l;
+
+ pthread_mutex_lock(&LOCK_mdl);
+ for (l= context->locks; l; l= l->next)
+ if (l->type == EXCLUSIVE)
+ l->type= SHARED;
+ pthread_cond_broadcast(&COND_mdl);
+ pthread_mutex_unlock(&LOCK_mdl);
+}
+
+
+/**
+ Release particular meta-data lock.
+
+ @param context Context containing lock in question
+ @param lock Lock to be released
+*/
+
+void mdl_release_lock(MDL_CONTEXT *context, MDL_EL *lock)
+{
+ MDL_EL **l;
+
+ pthread_mutex_lock(&LOCK_mdl);
+ for (l= &context->locks; *l != lock; l= &((*l)->next))
+ continue;
+ *l= (*l)->next;
+ hash_delete(&mdl_locks, (uchar*)lock);
+ pthread_cond_broadcast(&COND_mdl);
+ pthread_mutex_unlock(&LOCK_mdl);
+}
+
+
+/**
+ Auxiliary function which allows to check if we have exclusive lock
+ on the object.
+
+ @param context Current context
+ @param type Id of object type
+ @param db Name of the database
+ @param name Name of the object
+
+ @return TRUE if current context contains exclusive lock for the object,
+ FALSE otherwise.
+*/
+
+bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type,
+ const char *db, const char *name)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ MDL_EL *l;
+ int4store(key, type);
+ key_length= (uint) (strmov(strmov(key+4, db)+1, name)-key)+1;
+ for (l= context->locks; l && (l->key_length != key_length ||
memcmp(l->key, key, key_length));
+ l= l->next)
+ continue;
+ return (l && l->type == EXCLUSIVE);
+}
diff -Nrup a/sql/meta_lock.h b/sql/meta_lock.h
--- /dev/null Wed Dec 31 16:00:00 196900
+++ b/sql/meta_lock.h 2007-08-09 12:28:19 +04:00
@@ -0,0 +1,47 @@
+/* Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/* TODO: Header guards! */
+
+struct MDL_EL;
+
+/* Class representing owner/requestor of meta-data lock. */
+
+struct MDL_CONTEXT
+{
+ MEM_ROOT memory;
+ MDL_EL *locks;
+};
+
+void mdl_init();
+void mdl_destroy();
+void mdl_context_init(MDL_CONTEXT *context);
+void mdl_context_destroy(MDL_CONTEXT *context);
+MDL_EL *mdl_alloc_lock(MDL_CONTEXT *context, int type, const char *db,
+ const char *name, bool exclusive);
+bool mdl_acquire_lock(MDL_EL *lock);
+void mdl_acquire_exclusive_locks(MDL_CONTEXT *context);
+void mdl_upgrade_shared_lock_to_exclusive(MDL_CONTEXT *context, int type,
+ const char *db, const char *name);
+bool mdl_try_acquire_exclusive_lock(MDL_CONTEXT *context, MDL_EL *lock);
+void mdl_release_locks(MDL_CONTEXT *context);
+void mdl_wait_for_locks(MDL_CONTEXT *context);
+void mdl_wait_for_old_versions(MDL_CONTEXT *context);
+void mdl_free_locks(MDL_CONTEXT *context);
+void mdl_release_exclusive_locks(MDL_CONTEXT *context);
+void mdl_release_lock(MDL_CONTEXT *context, MDL_EL *lock);
+void mdl_downgrade_exclusive_locks(MDL_CONTEXT *context);
+bool mdl_is_exclusive_lock_owner(MDL_CONTEXT *context, int type, const char *db,
+ const char *name);
diff -Nrup a/sql/mysql_priv.h b/sql/mysql_priv.h
--- a/sql/mysql_priv.h 2007-07-31 19:34:05 +04:00
+++ b/sql/mysql_priv.h 2007-08-09 12:28:18 +04:00
@@ -673,7 +673,8 @@ extern my_decimal decimal_zero;
void free_items(Item *item);
void cleanup_items(Item *item);
class THD;
-void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0);
+void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0,
+ bool skip_mdl=0);
bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables);
bool check_single_table_access(THD *thd, ulong privilege,
TABLE_LIST *tables, bool no_errors);
@@ -931,7 +932,6 @@ int mysql_rm_table_part2(THD *thd, TABLE
bool drop_temporary, bool drop_view, bool log_query);
bool quick_rm_table(handlerton *base,const char *db,
const char *table_name, uint flags);
-void close_cached_table(THD *thd, TABLE *table);
bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent);
bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db,
char *new_table_name, char *new_table_alias,
@@ -1128,11 +1128,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST
uint lock_flags);
TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT* mem,
bool *refresh, uint flags);
-bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in);
-TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
- uint key_length);
-bool lock_table_name_if_not_cached(THD *thd, const char *db,
- const char *table_name, TABLE **table);
+bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list);
TABLE *find_locked_table(THD *thd, const char *db,const char *table_name);
bool reopen_table(TABLE *table);
bool reopen_tables(THD *thd,bool get_locks,bool in_refresh);
@@ -1397,7 +1393,7 @@ void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
bool close_thread_table(THD *thd, TABLE **table_ptr);
void close_temporary_tables(THD *thd);
-void close_tables_for_reopen(THD *thd, TABLE_LIST **tables);
+void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool skip_mdl);
TABLE_LIST *find_table_in_list(TABLE_LIST *table,
TABLE_LIST *TABLE_LIST::*link,
const char *db_name,
@@ -1439,6 +1435,8 @@ uint prep_alter_part_table(THD *thd, TAB
#define RTFC_CHECK_KILLED_FLAG 0x0004
bool remove_table_from_cache(THD *thd, const char *db, const char *table,
uint flags);
+void expel_table_from_cache(THD *leave_thd, const char *db,
+ const char *table_name);
#define NORMAL_PART_NAME 0
#define TEMP_PART_NAME 1
@@ -1927,18 +1925,8 @@ void unset_protect_against_global_read_l
void broadcast_refresh(void);
/* Lock based on name */
-int lock_and_wait_for_table_name(THD *thd, TABLE_LIST *table_list);
-int lock_table_name(THD *thd, TABLE_LIST *table_list, bool check_in_use);
-void unlock_table_name(THD *thd, TABLE_LIST *table_list);
-bool wait_for_locked_table_names(THD *thd, TABLE_LIST *table_list);
bool lock_table_names(THD *thd, TABLE_LIST *table_list);
-void unlock_table_names(THD *thd, TABLE_LIST *table_list,
- TABLE_LIST *last_table);
-bool lock_table_names_exclusively(THD *thd, TABLE_LIST *table_list);
-bool is_table_name_exclusively_locked_by_this_thread(THD *thd,
- TABLE_LIST *table_list);
-bool is_table_name_exclusively_locked_by_this_thread(THD *thd, uchar *key,
- int key_length);
+void unlock_table_names(THD *thd);
/* old unireg functions */
diff -Nrup a/sql/mysqld.cc b/sql/mysqld.cc
--- a/sql/mysqld.cc 2007-07-09 09:08:41 +04:00
+++ b/sql/mysqld.cc 2007-08-09 12:28:18 +04:00
@@ -1185,6 +1185,7 @@ void clean_up(bool print_message)
table_cache_free();
table_def_free();
hostname_cache_free();
+ mdl_destroy();
item_user_lock_free();
lex_free(); /* Free some memory */
item_create_cleanup();
@@ -3211,6 +3212,7 @@ static int init_server_components()
We need to call each of these following functions to ensure that
all things are initialized so that unireg_abort() doesn't fail
*/
+ mdl_init();
if (table_cache_init() | table_def_init() | hostname_cache_init())
unireg_abort(1);
diff -Nrup a/sql/sql_base.cc b/sql/sql_base.cc
--- a/sql/sql_base.cc 2007-08-01 12:30:11 +04:00
+++ b/sql/sql_base.cc 2007-08-09 12:28:18 +04:00
@@ -923,7 +923,25 @@ bool close_cached_tables(THD *thd, bool
thd->mysys_var->current_cond= &COND_refresh;
thd->proc_info="Flushing tables";
- close_old_data_files(thd,thd->open_tables,1,1);
+ /*
+ TODO: Investigate if we should upgrade meta-data lock before incrementing
+ refresh_version/marking table for flush.
+ */
+ for (TABLE *table= thd->open_tables; table ; table=table->next)
+ {
+ if (table->needs_reopen_or_name_lock())
+ {
+ mysql_lock_abort(thd, table, TRUE);
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, 0,
+ table->s->db.str,
+ table->s->table_name.str);
+ VOID(pthread_mutex_lock(&LOCK_open));
+ expel_table_from_cache(thd, table->s->db.str,
table->s->table_name.str);
+ mysql_lock_remove(thd, thd->locked_tables, table, TRUE);
+ close_handle_and_leave_table_as_lock(table);
+ }
+ }
mysql_ha_flush(thd, tables, MYSQL_HA_REOPEN_ON_USAGE | MYSQL_HA_FLUSH_ALL,
TRUE);
bool found=1;
@@ -970,6 +988,7 @@ bool close_cached_tables(THD *thd, bool
/* Set version for table */
for (TABLE *table=thd->open_tables; table ; table= table->next)
table->s->version= refresh_version;
+ mdl_downgrade_exclusive_locks(&thd->mdl_context);
}
if (!have_lock)
VOID(pthread_mutex_unlock(&LOCK_open));
@@ -1110,7 +1129,8 @@ static void mark_used_tables_as_free_for
upper level) and will leave prelocked mode if needed.
*/
-void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
+void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
+ bool skip_mdl)
{
bool found_old_table;
prelocked_mode_type prelocked_mode= thd->prelocked_mode;
@@ -1242,6 +1262,10 @@ void close_thread_tables(THD *thd, bool
VOID(pthread_mutex_unlock(&LOCK_open));
/* VOID(pthread_sigmask(SIG_SETMASK,&thd->signals,NULL)); */
+ mdl_release_locks(&thd->mdl_context);
+ if (!skip_mdl)
+ mdl_free_locks(&thd->mdl_context);
+
if (prelocked_mode == PRELOCKED)
{
/*
@@ -1267,6 +1291,7 @@ bool close_thread_table(THD *thd, TABLE
DBUG_ASSERT(!table->file || table->file->inited == handler::NONE);
*table_ptr=table->next;
+ table->mdl_lock= 0;
if (table->needs_reopen_or_name_lock() ||
thd->version != refresh_version || !table->db_stat)
{
@@ -1958,19 +1983,12 @@ void wait_for_condition(THD *thd, pthrea
/*
- Open table which is already name-locked by this thread.
+ Open table for which this thread has exclusive meta-data lock.
SYNOPSIS
reopen_name_locked_table()
thd Thread handle
- table_list TABLE_LIST object for table to be open, TABLE_LIST::table
- member should point to TABLE object which was used for
- name-locking.
- link_in TRUE - if TABLE object for table to be opened should be
- linked into THD::open_tables list.
- FALSE - placeholder used for name-locking is already in
- this list so we only need to preserve TABLE::next
- pointer.
+ table_list TABLE_LIST object for table to be open.
NOTE
This function assumes that its caller already acquired LOCK_open mutex.
@@ -1980,33 +1998,38 @@ void wait_for_condition(THD *thd, pthrea
TRUE - Error
*/
-bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in)
+bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
{
- TABLE *table= table_list->table;
+ TABLE *table;
TABLE_SHARE *share;
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
char *table_name= table_list->table_name;
- TABLE orig_table;
DBUG_ENTER("reopen_name_locked_table");
safe_mutex_assert_owner(&LOCK_open);
+ DBUG_ASSERT(mdl_is_exclusive_lock_owner(&thd->mdl_context, 0,
+ table_list->db, table_name));
- if (thd->killed || !table)
+
+ if (thd->killed)
DBUG_RETURN(TRUE);
- orig_table= *table;
+ key_length= create_table_def_key(thd, key, table_list, 0);
- if (open_unireg_entry(thd, table, table_list, table_name,
- table->s->table_cache_key.str,
- table->s->table_cache_key.length, thd->mem_root, 0))
+ if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
+ DBUG_RETURN(TRUE);
+
+ if (open_unireg_entry(thd, table, table_list, table_name, key,
+ key_length, thd->mem_root, 0))
{
- intern_close_table(table);
/*
If there was an error during opening of table (for example if it
does not exist) '*table' object can be wiped out. To be able
properly release name-lock in this case we should restore this
object to its original state.
*/
- *table= orig_table;
+ my_free((uchar*)table, MYF(0));
DBUG_RETURN(TRUE);
}
@@ -2023,128 +2046,17 @@ bool reopen_name_locked_table(THD* thd,
table->in_use = thd;
check_unused();
- if (link_in)
- {
- table->next= thd->open_tables;
- thd->open_tables= table;
- }
- else
- {
- /*
- TABLE object should be already in THD::open_tables list so we just
- need to set TABLE::next correctly.
- */
- table->next= orig_table.next;
- }
+ VOID(my_hash_insert(&open_cache, (uchar*)table));
+
+ table->next= thd->open_tables;
+ thd->open_tables= table;
table->tablenr=thd->current_tablenr++;
table->used_fields=0;
table->const_table=0;
table->null_row= table->maybe_null= table->force_index= 0;
table->status=STATUS_NO_RECORD;
- DBUG_RETURN(FALSE);
-}
-
-
-/**
- @brief Create and insert into table cache placeholder for table
- which will prevent its opening (or creation) (a.k.a lock
- table name).
-
- @param thd Thread context
- @param key Table cache key for name to be locked
- @param key_length Table cache key length
-
- @return Pointer to TABLE object used for name locking or 0 in
- case of failure.
-*/
-
-TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
- uint key_length)
-{
- TABLE *table;
- TABLE_SHARE *share;
- char *key_buff;
- DBUG_ENTER("table_cache_insert_placeholder");
-
- safe_mutex_assert_owner(&LOCK_open);
-
- /*
- Create a table entry with the right key and with an old refresh version
- Note that we must use my_multi_malloc() here as this is freed by the
- table cache
- */
- if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL),
- &table, sizeof(*table),
- &share, sizeof(*share),
- &key_buff, key_length,
- NULL))
- DBUG_RETURN(NULL);
-
- table->s= share;
- share->set_table_cache_key(key_buff, key, key_length);
- share->tmp_table= INTERNAL_TMP_TABLE; // for intern_close_table
- table->in_use= thd;
- table->locked_by_name=1;
-
- if (my_hash_insert(&open_cache, (uchar*)table))
- {
- my_free((uchar*) table, MYF(0));
- DBUG_RETURN(NULL);
- }
-
- DBUG_RETURN(table);
-}
-
-
-/**
- @brief Obtain an exclusive name lock on the table if it is not cached
- in the table cache.
-
- @param thd Thread context
- @param db Name of database
- @param table_name Name of table
- @param[out] table Out parameter which is either:
- - set to NULL if table cache contains record for
- the table or
- - set to point to the TABLE instance used for
- name-locking.
-
- @note This function takes into account all records for table in table
- cache, even placeholders used for name-locking. This means that
- 'table' parameter can be set to NULL for some situations when
- table does not really exist.
-
- @retval TRUE Error occured (OOM)
- @retval FALSE Success. 'table' parameter set according to above rules.
-*/
-
-bool lock_table_name_if_not_cached(THD *thd, const char *db,
- const char *table_name, TABLE **table)
-{
- char key[MAX_DBKEY_LENGTH];
- uint key_length;
- DBUG_ENTER("lock_table_name_if_not_cached");
-
- key_length= (uint)(strmov(strmov(key, db) + 1, table_name) - key) + 1;
- VOID(pthread_mutex_lock(&LOCK_open));
-
- if (hash_search(&open_cache, (uchar *)key, key_length))
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_PRINT("info", ("Table is cached, name-lock is not obtained"));
- *table= 0;
- DBUG_RETURN(FALSE);
- }
- if (!(*table= table_cache_insert_placeholder(thd, key, key_length)))
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(TRUE);
- }
- (*table)->open_placeholder= 1;
- (*table)->next= thd->open_tables;
- thd->open_tables= *table;
- VOID(pthread_mutex_unlock(&LOCK_open));
+ table_list->table= table;
DBUG_RETURN(FALSE);
}
@@ -2254,6 +2166,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *
uint key_length;
char *alias= table_list->alias;
HASH_SEARCH_STATE state;
+ MDL_EL *mdl;
DBUG_ENTER("open_table");
DBUG_ASSERT (table_list->lock_type != TL_WRITE_DEFAULT);
@@ -2453,6 +2366,24 @@ TABLE *open_table(THD *thd, TABLE_LIST *
on disk.
*/
+ mdl= mdl_alloc_lock(&thd->mdl_context, 0, table_list->db,
+ table_list->table_name, table_list->create);
+
+ if (table_list->create)
+ {
+ // This case can be significantly optimized
+ mdl_acquire_exclusive_locks(&thd->mdl_context);
+ }
+ else
+ {
+ if (mdl_acquire_lock(mdl))
+ {
+ if (refresh)
+ *refresh= 1;
+ DBUG_RETURN(0);
+ }
+ }
+
VOID(pthread_mutex_lock(&LOCK_open));
/*
@@ -2495,87 +2426,24 @@ TABLE *open_table(THD *thd, TABLE_LIST *
table= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length,
&state))
{
- /*
- Here we flush tables marked for flush.
- Normally, table->s->version contains the value of
- refresh_version from the moment when this table was
- (re-)opened and added to the cache.
- If since then we did (or just started) FLUSH TABLES
- statement, refresh_version has been increased.
- For "name-locked" TABLE instances, table->s->version is set
- to 0 (see lock_table_name for details).
- In case there is a pending FLUSH TABLES or a name lock, we
- need to back off and re-start opening tables.
- If we do not back off now, we may dead lock in case of lock
- order mismatch with some other thread:
- c1: name lock t1; -- sort of exclusive lock
- c2: open t2; -- sort of shared lock
- c1: name lock t2; -- blocks
- c2: open t1; -- blocks
- */
- if (table->needs_reopen_or_name_lock())
- {
- DBUG_PRINT("note",
- ("Found table '%s.%s' with different refresh version",
- table_list->db, table_list->table_name));
-
- if (flags & MYSQL_LOCK_IGNORE_FLUSH)
- {
- /* Force close at once after usage */
- thd->version= table->s->version;
- continue;
- }
-
- /* Avoid self-deadlocks by detecting self-dependencies. */
- if (table->open_placeholder && table->in_use == thd)
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- my_error(ER_UPDATE_TABLE_USED, MYF(0), table->s->table_name.str);
- DBUG_RETURN(0);
- }
-
- /*
- Back off, part 1: mark the table as "unused" for the
- purpose of name-locking by setting table->db_stat to 0. Do
- that only for the tables in this thread that have an old
- table->s->version (this is an optimization (?)).
- table->db_stat == 0 signals wait_for_locked_table_names
- that the tables in question are not used any more. See
- table_is_used call for details.
- */
- close_old_data_files(thd,thd->open_tables,0,0);
- /*
- Back-off part 2: try to avoid "busy waiting" on the table:
- if the table is in use by some other thread, we suspend
- and wait till the operation is complete: when any
- operation that juggles with table->s->version completes,
- it broadcasts COND_refresh condition variable.
- If 'old' table we met is in use by current thread we return
- without waiting since in this situation it's this thread
- which is responsible for broadcasting on COND_refresh
- (and this was done already in close_old_data_files()).
- Good example of such situation is when we have statement
- that needs two instances of table and FLUSH TABLES comes
- after we open first instance but before we open second
- instance.
- */
- if (table->in_use != thd)
- {
- /* wait_for_conditionwill unlock LOCK_open for us */
- wait_for_condition(thd, &LOCK_open, &COND_refresh);
- }
- else
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- }
- /*
- There is a refresh in progress for this table.
- Signal the caller that it has to try again.
- */
- if (refresh)
- *refresh=1;
- DBUG_RETURN(0);
- }
+ /*
+ We can't ignore old version still open by other threads as some
+ handlers (MyISAM, Maria) assume that no one will start using new
+ version of table as long as old version is around.
+ */
+ if (table->needs_reopen_or_name_lock())
+ {
+ if (flags & MYSQL_LOCK_IGNORE_FLUSH)
+ {
+ /* Force close at once after usage */
+ thd->version= table->s->version;
+ continue;
+ }
+ if (refresh)
+ *refresh=1;
+ VOID(pthread_mutex_unlock(&LOCK_open));
+ DBUG_RETURN(0);
+ }
}
if (table)
{
@@ -2605,29 +2473,13 @@ TABLE *open_table(THD *thd, TABLE_LIST *
if (check_if_table_exists(thd, table_list, &exists))
{
VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(NULL);
+ goto err_unlock;
}
if (!exists)
{
- /*
- Table to be created, so we need to create placeholder in table-cache.
- */
- if (!(table= table_cache_insert_placeholder(thd, key, key_length)))
- {
- VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(NULL);
- }
- /*
- Link placeholder to the open tables list so it will be automatically
- removed once tables are closed. Also mark it so it won't be ignored
- by other trying to take name-lock.
- */
- table->open_placeholder= 1;
- table->next= thd->open_tables;
- thd->open_tables= table;
VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(table);
+ DBUG_RETURN(0);
}
/* Table exists. Let us try to open it. */
}
@@ -2636,7 +2488,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *
if (!(table=(TABLE*) my_malloc(sizeof(*table),MYF(MY_WME))))
{
VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(NULL);
+ goto err_unlock;
}
error= open_unireg_entry(thd, table, table_list, alias, key, key_length,
@@ -2645,7 +2497,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *
{
my_free((uchar*)table, MYF(0));
VOID(pthread_mutex_unlock(&LOCK_open));
- DBUG_RETURN(NULL);
+ goto err_unlock;
}
if (table_list->view || error < 0)
{
@@ -2667,6 +2519,12 @@ TABLE *open_table(THD *thd, TABLE_LIST *
check_unused(); // Debugging call
VOID(pthread_mutex_unlock(&LOCK_open));
+
+ // Table existed
+ if (table_list->create)
+ mdl_downgrade_exclusive_locks(&thd->mdl_context);
+
+ table->mdl_lock= mdl;
if (refresh)
{
table->next=thd->open_tables; /* Link into simple list */
@@ -2707,6 +2565,10 @@ TABLE *open_table(THD *thd, TABLE_LIST *
table->clear_column_bitmaps();
DBUG_ASSERT(table->key_read == 0);
DBUG_RETURN(table);
+
+err_unlock:
+ mdl_release_lock(&thd->mdl_context, mdl);
+ DBUG_RETURN(0);
}
@@ -2765,8 +2627,8 @@ bool reopen_table(TABLE *table)
table_list.belong_to_view= 0;
table_list.next_local= 0;
- if (wait_for_locked_table_names(thd, &table_list))
- DBUG_RETURN(1); // Thread was killed
+ DBUG_ASSERT(mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, table_list.db,
+ table_list.table_name));
if (open_unireg_entry(thd, &tmp, &table_list,
table->alias,
@@ -2794,6 +2656,8 @@ bool reopen_table(TABLE *table)
tmp.next= table->next;
tmp.prev= table->prev;
+ tmp.mdl_lock= table->mdl_lock;
+
delete table->triggers;
if (table->file)
VOID(closefrm(table, 1)); // close file, free everything
@@ -2869,6 +2733,7 @@ void close_data_files_and_morph_locks(TH
if (thd->locked_tables)
mysql_lock_remove(thd, thd->locked_tables, table, TRUE);
table->open_placeholder= 1;
+ table->s->version= 0;
close_handle_and_leave_table_as_lock(table);
}
}
@@ -3140,7 +3005,7 @@ bool wait_for_tables(THD *thd)
TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name)
{
- TABLE *table,*next,**prev, *found= 0;
+ TABLE *table,*next,**prev;
prev= &thd->open_tables;
DBUG_ENTER("drop_locked_tables");
@@ -3151,21 +3016,7 @@ TABLE *drop_locked_tables(THD *thd,const
!strcmp(table->s->db.str, db))
{
mysql_lock_remove(thd, thd->locked_tables, table, TRUE);
- if (!found)
- {
- found= table;
- /* Close engine table, but keep object around as a name lock */
- if (table->db_stat)
- {
- table->db_stat= 0;
- table->file->close();
- }
- }
- else
- {
- /* We already have a name lock, remove copy */
- VOID(hash_delete(&open_cache,(uchar*) table));
- }
+ VOID(hash_delete(&open_cache, (uchar*) table));
}
else
{
@@ -3174,14 +3025,12 @@ TABLE *drop_locked_tables(THD *thd,const
}
}
*prev=0;
- if (found)
- broadcast_refresh();
if (thd->locked_tables && thd->locked_tables->table_count == 0)
{
my_free((uchar*) thd->locked_tables,MYF(0));
thd->locked_tables=0;
}
- DBUG_RETURN(found);
+ DBUG_RETURN(0);
}
@@ -3369,19 +3218,20 @@ retry:
}
if (!entry->s || !entry->s->crashed)
goto err;
- // Code below is for repairing a crashed file
- if ((error= lock_table_name(thd, table_list, TRUE)))
- {
- if (error < 0)
- goto err;
- if (wait_for_locked_table_names(thd, table_list))
- {
- unlock_table_name(thd, table_list);
- goto err;
- }
- }
- pthread_mutex_unlock(&LOCK_open);
- thd->clear_error(); // Clear error message
+
+ entry->s->version= 0;
+ pthread_mutex_unlock(&LOCK_open);
+ thd->clear_error(); // Clear error message
+ /*
+ FIXME This is temporary hack to make prototype pass the tests.
+ Actually repairs as well as auto-discovery should be done
+ under exclusive meta-data lock on the table (and therefore
+ without holding any shared meta-data locks) at the layer
+ above open_table().
+ */
+ mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, 0, table_list->db,
+ table_list->table_name);
+
error= 0;
if (open_table_from_share(thd, share, alias,
(uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
@@ -3403,8 +3253,10 @@ retry:
}
else
thd->clear_error(); // Clear error message
+
+ mdl_downgrade_exclusive_locks(&thd->mdl_context);
+
pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table_list);
if (error)
goto err;
@@ -3639,6 +3491,15 @@ int open_tables(THD *thd, TABLE_LIST **s
goto process_view_routines;
}
+ /*
+ FIXME This is a temporary hack. Actually we need check that will
+ allow us to differentiate between error while opening/creating
+ table and successful table creation.
+ ...
+ */
+ if (tables->create)
+ continue;
+
if (refresh) // Refresh in progress
{
/*
@@ -3657,7 +3518,10 @@ int open_tables(THD *thd, TABLE_LIST **s
*/
if (query_tables_last_own)
thd->lex->mark_as_requiring_prelocking(query_tables_last_own);
- close_tables_for_reopen(thd, start);
+ close_tables_for_reopen(thd, start, TRUE);
+ mdl_wait_for_locks(&thd->mdl_context);
+ mdl_wait_for_old_versions(&thd->mdl_context);
+ mdl_free_locks(&thd->mdl_context);
goto restart;
}
@@ -3812,6 +3676,8 @@ TABLE *open_ltable(THD *thd, TABLE_LIST
thd->current_tablenr= 0;
/* open_ltable can be used only for BASIC TABLEs */
table_list->required_type= FRMTYPE_TABLE;
+
+retry:
while (!(table= open_table(thd, table_list, thd->mem_root, &refresh, 0))
&&
refresh)
;
@@ -3831,8 +3697,22 @@ TABLE *open_ltable(THD *thd, TABLE_LIST
DBUG_ASSERT(thd->lock == 0); // You must lock everything at once
if ((table->reginfo.lock_type= lock_type) != TL_UNLOCK)
if (! (thd->lock= mysql_lock_tables(thd, &table_list->table, 1,
- lock_flags, &refresh)))
- table= 0;
+ (lock_flags |
+ MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN),
+ &refresh)))
+ {
+ /*
+ FIXME: Actually we should get rid of MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN option
+ as all reopening should happen outside of mysql_lock_tables() code.
+ */
+ if (refresh)
+ {
+ close_thread_tables(thd);
+ goto retry;
+ }
+ else
+ table= 0;
+ }
}
}
thd->proc_info=0;
@@ -3871,7 +3751,7 @@ int simple_open_n_lock_tables(THD *thd,
break;
if (!need_reopen)
DBUG_RETURN(-1);
- close_tables_for_reopen(thd, &tables);
+ close_tables_for_reopen(thd, &tables, FALSE);
}
DBUG_RETURN(0);
}
@@ -3908,7 +3788,7 @@ bool open_and_lock_tables(THD *thd, TABL
break;
if (!need_reopen)
DBUG_RETURN(-1);
- close_tables_for_reopen(thd, &tables);
+ close_tables_for_reopen(thd, &tables, FALSE);
}
if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
(thd->fill_derived_tables() &&
@@ -4290,7 +4170,7 @@ int lock_tables(THD *thd, TABLE_LIST *ta
*/
-void close_tables_for_reopen(THD *thd, TABLE_LIST **tables)
+void close_tables_for_reopen(THD *thd, TABLE_LIST **tables, bool skip_mdl)
{
/*
If table list consists only from tables from prelocking set, table list
@@ -4303,7 +4183,7 @@ void close_tables_for_reopen(THD *thd, T
for (TABLE_LIST *tmp= *tables; tmp; tmp= tmp->next_global)
tmp->table= 0;
mark_used_tables_as_free_for_reuse(thd, thd->temporary_tables);
- close_thread_tables(thd);
+ close_thread_tables(thd, 0, 0, skip_mdl);
}
@@ -7294,6 +7174,70 @@ bool remove_table_from_cache(THD *thd, c
}
+/**
+ 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).
+
+ @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 db Name of database
+ @param table_name Name of table
+
+ @note Unlike remove_table_from_cache() it assumes that table instances
+ are already not used by any (other) thread (this should be achieved
+ by using meta-data locks).
+*/
+
+void expel_table_from_cache(THD *leave_thd, const char *db, const char *table_name)
+{
+ char key[MAX_DBKEY_LENGTH];
+ uint key_length;
+ HASH_SEARCH_STATE state;
+ TABLE *table;
+ TABLE_SHARE *share;
+
+ key_length=(uint) (strmov(strmov(key,db)+1,table_name)-key)+1;
+
+ for (table= (TABLE*) hash_first(&open_cache, (uchar*) key, key_length,
+ &state);
+ table;
+ table= (TABLE*) hash_next(&open_cache, (uchar*) key, key_length,
+ &state))
+ {
+ DBUG_ASSERT(table->in_use == leave_thd || table->in_use == 0);
+ if (!table->in_use)
+ {
+ table->s->version= 0L;
+ relink_unused(table);
+ }
+ }
+
+ while (unused_tables && !unused_tables->s->version)
+ VOID(hash_delete(&open_cache,(uchar*) unused_tables));
+
+ if ((share= (TABLE_SHARE*) hash_search(&table_def_cache,(uchar*) key,
+ key_length)))
+ {
+ /*
+ Even if at the moment we are not going to expel all instances
+ of table from the table cache we will want to do it later, to
+ force table reopen.
+ */
+ share->version= 0;
+ DBUG_ASSERT(leave_thd || share->ref_count == 0);
+ if (share->ref_count == 0)
+ {
+ share->version= 0; // Mark for delete
+ pthread_mutex_lock(&share->mutex);
+ VOID(hash_delete(&table_def_cache, (uchar*) share));
+ }
+ }
+}
+
+
+
int setup_ftfuncs(SELECT_LEX *select_lex)
{
List_iterator<Item_func_match> li(*(select_lex->ftfunc_list)),
@@ -7428,9 +7372,11 @@ int abort_and_upgrade_lock(ALTER_PARTITI
DBUG_ENTER("abort_and_upgrade_locks");
lpt->old_lock_type= lpt->table->reginfo.lock_type;
- VOID(pthread_mutex_lock(&LOCK_open));
mysql_lock_abort(lpt->thd, lpt->table, TRUE);
- VOID(remove_table_from_cache(lpt->thd, lpt->db, lpt->table_name, flags));
+ mdl_upgrade_shared_lock_to_exclusive(&lpt->thd->mdl_context, 0, lpt->db,
+ lpt->table_name);
+ VOID(pthread_mutex_lock(&LOCK_open));
+ expel_table_from_cache(lpt->thd, lpt->db, lpt->table_name);
VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_RETURN(0);
}
@@ -7785,6 +7731,9 @@ void close_performance_schema_table(THD
broadcast_refresh();
pthread_mutex_unlock(&LOCK_open);
+
+ mdl_release_locks(&thd->mdl_context);
+ mdl_free_locks(&thd->mdl_context);
thd->restore_backup_open_tables_state(backup);
}
diff -Nrup a/sql/sql_class.cc b/sql/sql_class.cc
--- a/sql/sql_class.cc 2007-08-01 22:40:00 +04:00
+++ b/sql/sql_class.cc 2007-08-09 12:28:18 +04:00
@@ -719,6 +719,9 @@ THD::~THD()
if (!cleanup_done)
cleanup();
+ mdl_context_destroy(&mdl_context);
+ mdl_context_destroy(&handler_mdl_context);
+
ha_close_connection(this);
plugin_thdvar_cleanup(this);
@@ -2541,6 +2544,8 @@ void THD::restore_backup_open_tables_sta
handler_tables == 0 && derived_tables == 0 &&
lock == 0 && locked_tables == 0 &&
prelocked_mode == NON_PRELOCKED);
+ mdl_context_destroy(&mdl_context);
+ mdl_context_destroy(&handler_mdl_context);
set_open_tables_state(backup);
DBUG_VOID_RETURN;
}
diff -Nrup a/sql/sql_class.h b/sql/sql_class.h
--- a/sql/sql_class.h 2007-07-31 19:34:06 +04:00
+++ b/sql/sql_class.h 2007-08-09 12:28:18 +04:00
@@ -22,6 +22,7 @@
#include "log.h"
#include "rpl_tblmap.h"
+#include "meta_lock.h"
struct st_relay_log_info;
typedef st_relay_log_info RELAY_LOG_INFO;
@@ -849,6 +850,9 @@ public:
*/
uint state_flags;
+ MDL_CONTEXT mdl_context;
+ MDL_CONTEXT handler_mdl_context;
+
/*
This constructor serves for creation of Open_tables_state instances
which are used as backup storage.
@@ -868,6 +872,8 @@ public:
extra_lock= lock= locked_tables= 0;
prelocked_mode= NON_PRELOCKED;
state_flags= 0U;
+ mdl_context_init(&mdl_context);
+ mdl_context_init(&handler_mdl_context);
}
};
diff -Nrup a/sql/sql_delete.cc b/sql/sql_delete.cc
--- a/sql/sql_delete.cc 2007-07-27 04:26:40 +04:00
+++ b/sql/sql_delete.cc 2007-08-09 12:28:18 +04:00
@@ -907,6 +907,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST
TABLE *table;
bool error;
uint path_length;
+ MDL_EL *mdl= 0;
DBUG_ENTER("mysql_truncate");
bzero((char*) &create_info,sizeof(create_info));
@@ -954,8 +955,17 @@ bool mysql_truncate(THD *thd, TABLE_LIST
HTON_CAN_RECREATE))
goto trunc_by_del;
- if (lock_and_wait_for_table_name(thd, table_list))
- DBUG_RETURN(TRUE);
+ /*
+ FIXME: Actually code of TRUNCATE breaks meta-data locking protocol since
+ tries to get table enging and therefore accesses table in some way
+ without holding any kind of meta-data lock.
+ */
+ mdl= mdl_alloc_lock(&thd->mdl_context, 0, table_list->db,
+ table_list->table_name, TRUE);
+ mdl_acquire_exclusive_locks(&thd->mdl_context);
+ VOID(pthread_mutex_lock(&LOCK_open));
+ expel_table_from_cache(0, table_list->db, table_list->table_name);
+ VOID(pthread_mutex_unlock(&LOCK_open));
}
// Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
@@ -980,15 +990,13 @@ end:
write_bin_log(thd, TRUE, thd->query, thd->query_length);
send_ok(thd); // This should return record count
}
- VOID(pthread_mutex_lock(&LOCK_open));
- unlock_table_name(thd, table_list);
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (mdl)
+ mdl_release_lock(&thd->mdl_context, mdl);
}
else if (error)
{
- VOID(pthread_mutex_lock(&LOCK_open));
- unlock_table_name(thd, table_list);
- VOID(pthread_mutex_unlock(&LOCK_open));
+ if (mdl)
+ mdl_release_lock(&thd->mdl_context, mdl);
}
DBUG_RETURN(error);
diff -Nrup a/sql/sql_handler.cc b/sql/sql_handler.cc
--- a/sql/sql_handler.cc 2007-05-24 14:24:28 +04:00
+++ b/sql/sql_handler.cc 2007-08-09 12:28:18 +04:00
@@ -68,7 +68,11 @@ static enum enum_ha_read_modes rkey_to_r
#define HANDLER_TABLES_HACK(thd) { \
TABLE *tmp=thd->open_tables; \
thd->open_tables=thd->handler_tables; \
- thd->handler_tables=tmp; }
+ thd->handler_tables=tmp; \
+ MDL_CONTEXT t2= thd->mdl_context; \
+ thd->mdl_context= thd->handler_mdl_context; \
+ thd->handler_mdl_context= t2; \
+ }
static int mysql_ha_flush_table(THD *thd, TABLE **table_ptr, uint mode_flags);
@@ -187,6 +191,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST
/* for now HANDLER can be used only for real TABLES */
tables->required_type= FRMTYPE_TABLE;
error= open_tables(thd, &tables, &counter, 0);
+ // FIXME we should release mdl_context if there is one
HANDLER_TABLES_HACK(thd);
if (error)
@@ -286,6 +291,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST
if (*table_ptr)
{
+ MDL_EL *mdl= (*table_ptr)->mdl_lock;
(*table_ptr)->file->ha_index_or_rnd_end();
VOID(pthread_mutex_lock(&LOCK_open));
if (close_thread_table(thd, table_ptr))
@@ -294,6 +300,7 @@ bool mysql_ha_close(THD *thd, TABLE_LIST
broadcast_refresh();
}
VOID(pthread_mutex_unlock(&LOCK_open));
+ mdl_release_lock(&thd->handler_mdl_context, mdl);
}
hash_delete(&thd->handler_tables_hash, (uchar*) hash_tables);
}
@@ -347,7 +354,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST
uint num_rows;
uchar *key;
uint key_len;
- bool not_used;
+ bool refresh;
DBUG_ENTER("mysql_ha_read");
DBUG_PRINT("enter",("'%s'.'%s' as '%s'",
tables->db, tables->table_name, tables->alias));
@@ -361,6 +368,7 @@ bool mysql_ha_read(THD *thd, TABLE_LIST
List_iterator<Item> it(list);
it++;
+retry:
if ((hash_tables= (TABLE_LIST*) hash_search(&thd->handler_tables_hash,
(uchar*) tables->alias,
strlen(tables->alias) + 1)))
@@ -415,11 +423,21 @@ bool mysql_ha_read(THD *thd, TABLE_LIST
tables->table=table;
HANDLER_TABLES_HACK(thd);
- lock= mysql_lock_tables(thd, &tables->table, 1, 0, ¬_used);
+ lock= mysql_lock_tables(thd, &tables->table, 1,
+ MYSQL_LOCK_NOTIFY_IF_NEED_REOPEN,
+ &refresh);
HANDLER_TABLES_HACK(thd);
if (!lock)
- goto err0; // mysql_lock_tables() printed error message already
+ {
+ if (refresh)
+ {
+ mysql_ha_flush(thd, hash_tables, MYSQL_HA_REOPEN_ON_USAGE, FALSE);
+ goto retry;
+ }
+ else
+ goto err0; // mysql_lock_tables() printed error message already
+ }
// Always read all columns
tables->table->read_set= &tables->table->s->all_set;
@@ -726,6 +744,7 @@ static int mysql_ha_flush_table(THD *thd
{
TABLE_LIST *hash_tables;
TABLE *table= *table_ptr;
+ MDL_EL *mdl= table->mdl_lock;
DBUG_ENTER("mysql_ha_flush_table");
DBUG_PRINT("enter",("'%s'.'%s' as '%s' flags: 0x%02x",
table->s->db.str, table->s->table_name.str,
@@ -755,6 +774,7 @@ static int mysql_ha_flush_table(THD *thd
/* Tell threads waiting for refresh that something has happened */
broadcast_refresh();
}
+ mdl_release_lock(&thd->handler_mdl_context, mdl);
DBUG_RETURN(0);
}
diff -Nrup a/sql/sql_insert.cc b/sql/sql_insert.cc
--- a/sql/sql_insert.cc 2007-07-27 04:26:40 +04:00
+++ b/sql/sql_insert.cc 2007-08-09 12:28:18 +04:00
@@ -3268,8 +3268,7 @@ static TABLE *create_table_from_items(TH
DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000););
- if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
- create_table->table->db_stat)
+ if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE) &&
create_table->table)
{
/* Table already exists and was open at open_and_lock_tables() stage. */
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
@@ -3359,7 +3358,7 @@ static TABLE *create_table_from_items(TH
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
VOID(pthread_mutex_lock(&LOCK_open));
- if (reopen_name_locked_table(thd, create_table, FALSE))
+ if (reopen_name_locked_table(thd, create_table))
{
quick_rm_table(create_info->db_type, create_table->db,
table_case_name(create_info, create_table->table_name),
diff -Nrup a/sql/sql_parse.cc b/sql/sql_parse.cc
--- a/sql/sql_parse.cc 2007-07-31 23:45:26 +04:00
+++ b/sql/sql_parse.cc 2007-08-09 12:28:18 +04:00
@@ -935,7 +935,7 @@ bool dispatch_command(enum enum_server_c
Multiple queries exits, execute them individually
*/
if (thd->lock || thd->open_tables || thd->derived_tables ||
- thd->prelocked_mode)
+ thd->prelocked_mode || thd->mdl_context.locks)
close_thread_tables(thd);
ulong length= (ulong)(packet_end - next_packet);
@@ -1275,7 +1275,7 @@ bool dispatch_command(enum enum_server_c
break;
}
if (thd->lock || thd->open_tables || thd->derived_tables ||
- thd->prelocked_mode)
+ thd->prelocked_mode || thd->mdl_context.locks)
{
thd->proc_info="closing tables";
close_thread_tables(thd); /* Free tables */
diff -Nrup a/sql/sql_partition.cc b/sql/sql_partition.cc
--- a/sql/sql_partition.cc 2007-07-19 20:05:49 +04:00
+++ b/sql/sql_partition.cc 2007-08-09 12:28:18 +04:00
@@ -5804,33 +5804,6 @@ static void release_log_entries(partitio
/*
- Get a lock on table name to avoid that anyone can open the table in
- a critical part of the ALTER TABLE.
- SYNOPSIS
- get_name_lock()
- lpt Struct carrying parameters
- RETURN VALUES
- FALSE Success
- TRUE Failure
-*/
-
-static int get_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
-{
- int error= 0;
- DBUG_ENTER("get_name_lock");
-
- bzero(&lpt->table_list, sizeof(lpt->table_list));
- lpt->table_list.db= (char*)lpt->db;
- lpt->table_list.table= lpt->table;
- lpt->table_list.table_name= (char*)lpt->table_name;
- pthread_mutex_lock(&LOCK_open);
- error= lock_table_name(lpt->thd, &lpt->table_list, FALSE);
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(error);
-}
-
-
-/*
Unlock and close table before renaming and dropping partitions
SYNOPSIS
alter_close_tables()
@@ -5858,25 +5831,6 @@ static int alter_close_tables(ALTER_PART
/*
- Release a lock name
- SYNOPSIS
- release_name_lock()
- lpt
- RETURN VALUES
- 0
-*/
-
-static int release_name_lock(ALTER_PARTITION_PARAM_TYPE *lpt)
-{
- DBUG_ENTER("release_name_lock");
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(lpt->thd, &lpt->table_list);
- pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(0);
-}
-
-
-/*
Handle errors for ALTER TABLE for partitioning
SYNOPSIS
handle_alter_part_error()
@@ -6200,8 +6154,6 @@ uint fast_alter_partition_table(THD *thd
ERROR_INJECT_CRASH("crash_drop_partition_3") ||
(not_completed= FALSE) ||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
- ERROR_INJECT_CRASH("crash_drop_partition_4") ||
- get_name_lock(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_5") ||
alter_close_tables(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_6") ||
@@ -6215,9 +6167,7 @@ uint fast_alter_partition_table(THD *thd
ERROR_INJECT_CRASH("crash_drop_partition_8") ||
mysql_drop_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_drop_partition_9") ||
- (write_log_completed(lpt, FALSE), FALSE) ||
- ERROR_INJECT_CRASH("crash_drop_partition_10") ||
- (release_name_lock(lpt), FALSE))
+ (write_log_completed(lpt, FALSE), FALSE))
{
handle_alter_part_error(lpt, not_completed, TRUE, frm_install);
DBUG_RETURN(TRUE);
@@ -6269,8 +6219,6 @@ uint fast_alter_partition_table(THD *thd
mysql_change_partitions(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_3") ||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
- ERROR_INJECT_CRASH("crash_add_partition_3") ||
- get_name_lock(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_4") ||
alter_close_tables(lpt) ||
ERROR_INJECT_CRASH("crash_add_partition_5") ||
@@ -6284,9 +6232,7 @@ uint fast_alter_partition_table(THD *thd
((frm_install= TRUE), FALSE) ||
mysql_write_frm(lpt, WFRM_INSTALL_SHADOW) ||
ERROR_INJECT_CRASH("crash_add_partition_8") ||
- (write_log_completed(lpt, FALSE), FALSE) ||
- ERROR_INJECT_CRASH("crash_add_partition_9") ||
- (release_name_lock(lpt), FALSE))
+ (write_log_completed(lpt, FALSE), FALSE))
{
handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
DBUG_RETURN(TRUE);
@@ -6361,8 +6307,6 @@ uint fast_alter_partition_table(THD *thd
ERROR_INJECT_CRASH("crash_change_partition_4") ||
(not_completed= FALSE) ||
abort_and_upgrade_lock(lpt) || /* Always returns 0 */
- ERROR_INJECT_CRASH("crash_change_partition_5") ||
- get_name_lock(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_6") ||
alter_close_tables(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_7") ||
@@ -6377,9 +6321,7 @@ uint fast_alter_partition_table(THD *thd
mysql_rename_partitions(lpt) ||
((frm_install= TRUE), FALSE) ||
ERROR_INJECT_CRASH("crash_change_partition_11") ||
- (write_log_completed(lpt, FALSE), FALSE) ||
- ERROR_INJECT_CRASH("crash_change_partition_12") ||
- (release_name_lock(lpt), FALSE))
+ (write_log_completed(lpt, FALSE), FALSE))
{
handle_alter_part_error(lpt, not_completed, FALSE, frm_install);
DBUG_RETURN(TRUE);
diff -Nrup a/sql/sql_rename.cc b/sql/sql_rename.cc
--- a/sql/sql_rename.cc 2007-07-27 04:26:40 +04:00
+++ b/sql/sql_rename.cc 2007-08-09 12:28:18 +04:00
@@ -131,12 +131,13 @@ bool mysql_rename_tables(THD *thd, TABLE
}
}
- pthread_mutex_lock(&LOCK_open);
- if (lock_table_names_exclusively(thd, table_list))
- {
- pthread_mutex_unlock(&LOCK_open);
+ if (lock_table_names(thd, table_list))
goto err;
- }
+
+ VOID(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);
error=0;
if ((ren_table=rename_tables(thd,table_list,0)))
@@ -182,9 +183,7 @@ bool mysql_rename_tables(THD *thd, TABLE
if (!error)
query_cache_invalidate3(thd, table_list, 0);
- pthread_mutex_lock(&LOCK_open);
- unlock_table_names(thd, table_list, (TABLE_LIST*) 0);
- pthread_mutex_unlock(&LOCK_open);
+ unlock_table_names(thd);
err:
start_waiting_global_read_lock(thd);
diff -Nrup a/sql/sql_show.cc b/sql/sql_show.cc
--- a/sql/sql_show.cc 2007-07-27 04:26:40 +04:00
+++ b/sql/sql_show.cc 2007-08-09 12:28:18 +04:00
@@ -2581,7 +2581,7 @@ int get_all_tables(THD *thd, TABLE_LIST
show_table_list->db),
show_table_list->alias));
thd->temporary_tables= 0;
- close_tables_for_reopen(thd, &show_table_list);
+ close_tables_for_reopen(thd, &show_table_list, FALSE);
goto err;
}
@@ -2732,7 +2732,7 @@ int get_all_tables(THD *thd, TABLE_LIST
res= schema_table->process_table(thd, show_table_list, table,
res, orig_base_name,
show_table_list->alias);
- close_tables_for_reopen(thd, &show_table_list);
+ close_tables_for_reopen(thd, &show_table_list, FALSE);
DBUG_ASSERT(!lex->query_tables_own_last);
}
if (res)
diff -Nrup a/sql/sql_table.cc b/sql/sql_table.cc
--- a/sql/sql_table.cc 2007-08-01 12:30:11 +04:00
+++ b/sql/sql_table.cc 2007-08-09 12:28:18 +04:00
@@ -1425,11 +1425,6 @@ bool mysql_rm_table(THD *thd,TABLE_LIST
need_start_waiters= TRUE;
}
- /*
- Acquire LOCK_open after wait_if_global_read_lock(). If we would hold
- LOCK_open during wait_if_global_read_lock(), other threads could not
- close their tables. This would make a pretty deadlock.
- */
error= mysql_rm_table_part2(thd, tables, if_exists, drop_temporary, 0, 0);
if (need_start_waiters)
@@ -1497,14 +1492,12 @@ int mysql_rm_table_part2(THD *thd, TABLE
built_query.append("DROP TABLE ");
}
- pthread_mutex_lock(&LOCK_open);
-
/*
If we have the table in the definition cache, we don't have to check the
.frm file to find if the table is a normal table (not view) and what
engine to use.
*/
-
+ pthread_mutex_lock(&LOCK_open);
for (table= tables; table; table= table->next_local)
{
TABLE_SHARE *share;
@@ -1517,16 +1510,22 @@ int mysql_rm_table_part2(THD *thd, TABLE
check_if_log_table(table->db_length, table->db,
table->table_name_length, table->table_name, 1))
{
- my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
pthread_mutex_unlock(&LOCK_open);
+ my_error(ER_BAD_LOG_STATEMENT, MYF(0), "DROP");
DBUG_RETURN(1);
}
}
+ pthread_mutex_unlock(&LOCK_open);
- if (!drop_temporary && lock_table_names_exclusively(thd, tables))
+ if (!drop_temporary && !thd->locked_tables)
{
+ mysql_ha_flush(thd, tables, MYSQL_HA_CLOSE_FINAL, FALSE);
+ if (lock_table_names(thd, tables))
+ 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);
pthread_mutex_unlock(&LOCK_open);
- DBUG_RETURN(1);
}
/* Don't give warnings for not found errors, as we already generate notes */
@@ -1538,7 +1537,6 @@ int mysql_rm_table_part2(THD *thd, TABLE
handlerton *table_type;
enum legacy_db_type frm_db_type;
- mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL, 1);
if (!close_temporary_table(thd, table))
{
tmp_table_deleted=1;
@@ -1573,17 +1571,28 @@ int mysql_rm_table_part2(THD *thd, TABLE
table_type= table->db_type;
if (!drop_temporary)
{
- TABLE *locked_table;
- abort_locked_tables(thd, db, table->table_name);
- remove_table_from_cache(thd, db, table->table_name,
- RTFC_WAIT_OTHER_THREAD_FLAG |
- RTFC_CHECK_KILLED_FLAG);
- /*
- If the table was used in lock tables, remember it so that
- unlock_table_names can free it
- */
- if ((locked_table= drop_locked_tables(thd, db, table->table_name)))
- table->table= locked_table;
+ if (thd->locked_tables)
+ {
+ TABLE *locked_table;
+ /*
+ FIXME We need to ensure that we have table locked otherwise we
+ a) could get deadlocks b) current code won't work.
+ See also also modification to lock.test.
+ */
+ abort_locked_tables(thd, db, table->table_name);
+ mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, 0,
+ db, table->table_name);
+ pthread_mutex_lock(&LOCK_open);
+ /*
+ TODO we should simply remove table from open_tables list
+ and set table->table to 0 here.
+ */
+ if ((locked_table= drop_locked_tables(thd, db, table->table_name)))
+ table->table= locked_table;
+
+ expel_table_from_cache(0, db, table->table_name);
+ pthread_mutex_unlock(&LOCK_open);
+ }
if (thd->killed)
{
@@ -1595,6 +1604,11 @@ int mysql_rm_table_part2(THD *thd, TABLE
path_length= build_table_filename(path, sizeof(path),
db, alias, reg_ext, 0);
}
+ /*
+ TODO: Investigate what should be done to remove this lock completely.
+ Is exclusive meta-data lock enough ?
+ */
+ pthread_mutex_lock(&LOCK_open);
if (drop_temporary ||
(table_type == NULL &&
(access(path, F_OK) &&
@@ -1644,6 +1658,7 @@ int mysql_rm_table_part2(THD *thd, TABLE
error|= new_error;
}
}
+ pthread_mutex_unlock(&LOCK_open);
if (error)
{
if (wrong_tables.length())
@@ -1651,11 +1666,6 @@ int mysql_rm_table_part2(THD *thd, TABLE
wrong_tables.append(String(table->table_name,system_charset_info));
}
}
- /*
- It's safe to unlock LOCK_open: we have an exclusive lock
- on the table name.
- */
- pthread_mutex_unlock(&LOCK_open);
thd->tmp_table_used= tmp_table_deleted;
error= 0;
if (wrong_tables.length())
@@ -1715,10 +1725,16 @@ int mysql_rm_table_part2(THD *thd, TABLE
*/
}
}
- pthread_mutex_lock(&LOCK_open);
err_with_placeholders:
- unlock_table_names(thd, tables, (TABLE_LIST*) 0);
- pthread_mutex_unlock(&LOCK_open);
+ if (!drop_temporary && thd->locked_tables)
+ {
+ /*
+ Under LOCK TABLES we should release meta-data locks on the tables
+ which were dropped. Otherwise we can rely on close_thread_tables()
+ doing this.
+ */
+ mdl_release_exclusive_locks(&thd->mdl_context);
+ }
thd->no_warnings_for_error= 0;
DBUG_RETURN(error);
}
@@ -3440,6 +3456,32 @@ warn:
}
+/**
+ Auxiliary function which obtains exclusive meta-data lock on the
+ table if there are no shared or exclusive on it already.
+
+ See mdl_try_acquire_exclusive_lock() function for more info.
+
+ TODO: This function is here mostly to simplify current patch
+ and probably should be removed.
+ TODO: Investigate if it is kosher to leave lock request in the
+ context in the case when we fail to obtain the lock.
+*/
+
+static bool lock_table_name_if_not_cached(THD *thd, const char *db,
+ const char *table_name,
+ MDL_EL **lock)
+{
+ if (!(*lock= mdl_alloc_lock(&thd->mdl_context, 0, db, table_name, TRUE)))
+ return TRUE;
+ if (mdl_try_acquire_exclusive_lock(&thd->mdl_context, *lock))
+ {
+ *lock= 0;
+ }
+ return FALSE;
+}
+
+
/*
Database and name-locking aware wrapper for mysql_create_table_no_lock(),
*/
@@ -3450,7 +3492,7 @@ bool mysql_create_table(THD *thd, const
bool internal_tmp_table,
uint select_field_count)
{
- TABLE *name_lock= 0;
+ MDL_EL *target_lock= 0;
bool result;
DBUG_ENTER("mysql_create_table");
@@ -3473,12 +3515,12 @@ bool mysql_create_table(THD *thd, const
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
- if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
+ if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock))
{
result= TRUE;
goto unlock;
}
- if (!name_lock)
+ if (!target_lock)
{
if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
{
@@ -3503,12 +3545,8 @@ bool mysql_create_table(THD *thd, const
select_field_count);
unlock:
- if (name_lock)
- {
- pthread_mutex_lock(&LOCK_open);
- unlink_open_table(thd, name_lock, FALSE);
- pthread_mutex_unlock(&LOCK_open);
- }
+ if (target_lock)
+ mdl_release_exclusive_locks(&thd->mdl_context);
pthread_mutex_lock(&LOCK_lock_db);
if (!--creating_table && creating_database)
pthread_cond_signal(&COND_refresh);
@@ -3657,10 +3695,6 @@ mysql_rename_table(handlerton *base, con
NOTES
When returning, the table will be unusable for other threads until
the table is closed.
-
- PREREQUISITES
- Lock on LOCK_open
- Win32 clients must also have a WRITE LOCK on the table !
*/
static void wait_while_table_is_used(THD *thd,TABLE *table,
@@ -3675,10 +3709,11 @@ static void wait_while_table_is_used(THD
/* Mark all tables that are in use as 'old' */
mysql_lock_abort(thd, table, TRUE); /* end threads waiting on lock */
- /* Wait until all there are no other threads that has this table open */
- remove_table_from_cache(thd, table->s->db.str,
- table->s->table_name.str,
- RTFC_WAIT_OTHER_THREAD_FLAG);
+ mdl_upgrade_shared_lock_to_exclusive(&thd->mdl_context, 0,
table->s->db.str,
+ table->s->table_name.str);
+ VOID(pthread_mutex_lock(&LOCK_open));
+ expel_table_from_cache(thd, table->s->db.str, table->s->table_name.str);
+ VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_VOID_RETURN;
}
@@ -3689,17 +3724,9 @@ static void wait_while_table_is_used(THD
close_cached_table()
thd Thread handler
table Table to remove from cache
-
- NOTES
- Function ends by signaling threads waiting for the table to try to
- reopen the table.
-
- PREREQUISITES
- Lock on LOCK_open
- Win32 clients must also have a WRITE LOCK on the table !
*/
-void close_cached_table(THD *thd, TABLE *table)
+static void close_cached_table(THD *thd, TABLE *table)
{
DBUG_ENTER("close_cached_table");
@@ -3710,11 +3737,11 @@ void close_cached_table(THD *thd, TABLE
mysql_unlock_tables(thd, thd->lock);
thd->lock=0; // Start locked threads
}
+
+ VOID(pthread_mutex_lock(&LOCK_open));
/* Close all copies of 'table'. This also frees all LOCK TABLES lock */
unlink_open_table(thd, table, TRUE);
-
- /* When lock on LOCK_open is freed other threads can continue */
- broadcast_refresh();
+ VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_VOID_RETURN;
}
@@ -3761,25 +3788,23 @@ static int prepare_for_restore(THD* thd,
build_table_filename(dst_path, sizeof(dst_path),
db, table_name, reg_ext, 0);
- if (lock_and_wait_for_table_name(thd,table))
- DBUG_RETURN(-1);
+ /*
+ TODO: Investigate and get rid of breakage of meta-data locking protocol
+ in this code. E.g. currently we can't really assume that table
+ does not exist at this point and therefore does not present in
+ table cache.
+ */
+
+ MDL_EL *mdl= mdl_alloc_lock(&thd->mdl_context, 0, table->db,
table->table_name,
+ TRUE);
+ mdl_acquire_exclusive_locks(&thd->mdl_context);
if (my_copy(src_path, dst_path, MYF(MY_WME)))
- {
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table);
- pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
"Failed copying .frm file"));
- }
if (mysql_truncate(thd, table, 1))
- {
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table);
- pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
"Failed generating table from .frm file"));
- }
}
/*
@@ -3787,9 +3812,8 @@ static int prepare_for_restore(THD* thd,
to finish the restore in the handler later on
*/
pthread_mutex_lock(&LOCK_open);
- if (reopen_name_locked_table(thd, table, TRUE))
+ if (reopen_name_locked_table(thd, table))
{
- unlock_table_name(thd, table);
pthread_mutex_unlock(&LOCK_open);
DBUG_RETURN(send_check_errmsg(thd, table, "restore",
"Failed to open partially restored table"));
@@ -3808,6 +3832,7 @@ static int prepare_for_repair(THD *thd,
char from[FN_REFLEN],tmp[FN_REFLEN+32];
const char **ext;
MY_STAT stat_info;
+ MDL_EL *mdl;
DBUG_ENTER("prepare_for_repair");
if (!(check_opt->sql_flags & TT_USEFRM))
@@ -3819,6 +3844,14 @@ static int prepare_for_repair(THD *thd,
uint key_length;
key_length= create_table_def_key(thd, key, table_list, 0);
+ /*
+ TODO: Check that REPAIR's code also conforms to meta-data
+ locking protocol. Fix if it is not.
+ */
+ mdl= mdl_alloc_lock(&thd->mdl_context, 0, table_list->db,
+ table_list->table_name, TRUE);
+ mdl_acquire_exclusive_locks(&thd->mdl_context);
+
pthread_mutex_lock(&LOCK_open);
if (!(share= (get_table_share(thd, table_list, key, key_length, 0,
&error))))
@@ -3874,41 +3907,28 @@ static int prepare_for_repair(THD *thd,
my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx",
from, current_pid, thd->thread_id);
- /* If we could open the table, close it */
if (table_list->table)
{
- pthread_mutex_lock(&LOCK_open);
+ /* If we could open the table, close it */
close_cached_table(thd, table);
- pthread_mutex_unlock(&LOCK_open);
- }
- if (lock_and_wait_for_table_name(thd,table_list))
- {
- error= -1;
- goto end;
+ table_list->table= 0;
}
+ // After this point we have X mdl lock in both cases
+
if (my_rename(from, tmp, MYF(MY_WME)))
{
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table_list);
- pthread_mutex_unlock(&LOCK_open);
error= send_check_errmsg(thd, table_list, "repair",
"Failed renaming data file");
goto end;
}
if (mysql_truncate(thd, table_list, 1))
{
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table_list);
- pthread_mutex_unlock(&LOCK_open);
error= send_check_errmsg(thd, table_list, "repair",
"Failed generating table from .frm file");
goto end;
}
if (my_rename(tmp, from, MYF(MY_WME)))
{
- pthread_mutex_lock(&LOCK_open);
- unlock_table_name(thd, table_list);
- pthread_mutex_unlock(&LOCK_open);
error= send_check_errmsg(thd, table_list, "repair",
"Failed restoring .MYD file");
goto end;
@@ -3919,9 +3939,8 @@ static int prepare_for_repair(THD *thd,
to finish the repair in the handler later on.
*/
pthread_mutex_lock(&LOCK_open);
- if (reopen_name_locked_table(thd, table_list, TRUE))
+ if (reopen_name_locked_table(thd, table_list))
{
- unlock_table_name(thd, table_list);
pthread_mutex_unlock(&LOCK_open);
error= send_check_errmsg(thd, table_list, "repair",
"Failed to open partially repaired table");
@@ -3936,6 +3955,8 @@ end:
closefrm(table, 1); // Free allocated memory
pthread_mutex_unlock(&LOCK_open);
}
+ if (error)
+ mdl_release_exclusive_locks(&thd->mdl_context);
DBUG_RETURN(error);
}
@@ -4507,7 +4528,7 @@ bool mysql_preload_keys(THD* thd, TABLE_
bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
HA_CREATE_INFO *create_info)
{
- TABLE *name_lock= 0;
+ MDL_EL *target_lock= 0;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
uint dst_path_length;
char *db= table->db;
@@ -4566,9 +4587,9 @@ bool mysql_create_like_table(THD* thd, T
}
else
{
- if (lock_table_name_if_not_cached(thd, db, table_name, &name_lock))
+ if (lock_table_name_if_not_cached(thd, db, table_name, &target_lock))
goto err;
- if (!name_lock)
+ if (!target_lock)
goto table_exists;
dst_path_length= build_table_filename(dst_path, sizeof(dst_path),
db, table_name, reg_ext, 0);
@@ -4681,9 +4702,8 @@ bool mysql_create_like_table(THD* thd, T
The table will be closed by unlink_open_table() at the end
of this function.
*/
- table->table= name_lock;
VOID(pthread_mutex_lock(&LOCK_open));
- if (reopen_name_locked_table(thd, table, FALSE))
+ if (reopen_name_locked_table(thd, table))
{
VOID(pthread_mutex_unlock(&LOCK_open));
goto err;
@@ -4695,6 +4715,10 @@ bool mysql_create_like_table(THD* thd, T
DBUG_ASSERT(result == 0); // store_create_info() always return 0
write_bin_log(thd, TRUE, query.ptr(), query.length());
+
+ VOID(pthread_mutex_lock(&LOCK_open));
+ unlink_open_table(thd, table->table, FALSE);
+ VOID(pthread_mutex_unlock(&LOCK_open));
}
else // Case 1
write_bin_log(thd, TRUE, thd->query, thd->query_length);
@@ -4723,12 +4747,8 @@ table_exists:
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table_name);
err:
- if (name_lock)
- {
- pthread_mutex_lock(&LOCK_open);
- unlink_open_table(thd, name_lock, FALSE);
- pthread_mutex_unlock(&LOCK_open);
- }
+ if (target_lock)
+ mdl_release_exclusive_locks(&thd->mdl_context);
DBUG_RETURN(res);
}
@@ -5632,7 +5652,8 @@ bool mysql_alter_table(THD *thd,char *ne
Alter_info *alter_info,
uint order_num, ORDER *order, bool ignore)
{
- TABLE *table, *new_table= 0, *name_lock= 0;
+ TABLE *table, *new_table= 0;
+ MDL_EL *target_lock= 0;
int error= 0;
char tmp_name[80],old_name[32],new_name_buff[FN_REFLEN];
char new_alias_buff[FN_REFLEN], *table_name, *db, *new_alias, *alias;
@@ -5779,7 +5800,7 @@ bool mysql_alter_table(THD *thd,char *ne
send_ok(thd);
}
- unlock_table_names(thd, table_list, (TABLE_LIST*) 0);
+ unlock_table_names(thd);
view_err:
pthread_mutex_unlock(&LOCK_open);
@@ -5826,9 +5847,9 @@ view_err:
}
else
{
- if (lock_table_name_if_not_cached(thd, new_db, new_name, &name_lock))
+ if (lock_table_name_if_not_cached(thd, new_db, new_name, &target_lock))
DBUG_RETURN(TRUE);
- if (!name_lock)
+ if (!target_lock)
{
my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_alias);
DBUG_RETURN(TRUE);
@@ -5905,26 +5926,13 @@ view_err:
case LEAVE_AS_IS:
break;
case ENABLE:
- /*
- wait_while_table_is_used() ensures that table being altered is
- opened only by this thread and that TABLE::TABLE_SHARE::version
- of TABLE object corresponding to this table is 0.
- The latter guarantees that no DML statement will open this table
- until ALTER TABLE finishes (i.e. until close_thread_tables())
- while the fact that the table is still open gives us protection
- from concurrent DDL statements.
- */
- VOID(pthread_mutex_lock(&LOCK_open));
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
- VOID(pthread_mutex_unlock(&LOCK_open));
DBUG_EXECUTE_IF("sleep_alter_enable_indexes", my_sleep(6000000););
error= table->file->enable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
/* COND_refresh will be signaled in close_thread_tables() */
break;
case DISABLE:
- VOID(pthread_mutex_lock(&LOCK_open));
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
- VOID(pthread_mutex_unlock(&LOCK_open));
error=table->file->disable_indexes(HA_KEY_SWITCH_NONUNIQ_SAVE);
/* COND_refresh will be signaled in close_thread_tables() */
break;
@@ -5941,16 +5949,6 @@ view_err:
table->alias);
}
- VOID(pthread_mutex_lock(&LOCK_open));
- /*
- Unlike to the above case close_cached_table() below will remove ALL
- instances of TABLE from table cache (it will also remove table lock
- held by this thread). So to make actual table renaming and writing
- to binlog atomic we have to put them into the same critical section
- protected by LOCK_open mutex. This also removes gap for races between
- access() and mysql_rename_table() calls.
- */
-
if (!error && (new_name != table_name || new_db != db))
{
thd->proc_info="rename";
@@ -5975,6 +5973,7 @@ view_err:
else
{
*fn_ext(new_name)=0;
+ pthread_mutex_lock(&LOCK_open);
if (mysql_rename_table(old_db_type,db,table_name,new_db,new_alias, 0))
error= -1;
else if (Table_triggers_list::change_table_name(thd, db, table_name,
@@ -5984,6 +5983,7 @@ view_err:
table_name, 0));
error= -1;
}
+ pthread_mutex_unlock(&LOCK_open);
}
}
@@ -6005,11 +6005,24 @@ view_err:
table->file->print_error(error, MYF(0));
error= -1;
}
- if (name_lock)
- unlink_open_table(thd, name_lock, FALSE);
- VOID(pthread_mutex_unlock(&LOCK_open));
table_list->table= NULL; // For query cache
query_cache_invalidate3(thd, table_list, 0);
+
+ if (thd->locked_tables)
+ {
+ /*
+ Under LOCK TABLES we should adjust meta-data locks before finishing
+ statement. Otherwise we can rely on close_thread_tables() releasing
+ them.
+
+ TODO: Investigate what should be done with upgraded table-level
+ lock here...
+ */
+ if (new_name != table_name || new_db != db)
+ mdl_release_exclusive_locks(&thd->mdl_context);
+ else
+ mdl_downgrade_exclusive_locks(&thd->mdl_context);
+ }
DBUG_RETURN(error);
}
@@ -6182,7 +6195,7 @@ view_err:
#ifdef WITH_PARTITION_STORAGE_ENGINE
if (fast_alter_partition)
{
- DBUG_ASSERT(!name_lock);
+ DBUG_ASSERT(!target_lock);
DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
create_info, table_list,
db, table_name,
@@ -6300,9 +6313,7 @@ view_err:
}
else
{
- VOID(pthread_mutex_lock(&LOCK_open));
wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN);
- VOID(pthread_mutex_unlock(&LOCK_open));
alter_table_manage_keys(table, table->file->indexes_are_disabled(),
alter_info->keys_onoff);
error= ha_commit_stmt(thd);
@@ -6428,11 +6439,10 @@ view_err:
intern_close_table(new_table);
my_free(new_table,MYF(0));
}
- VOID(pthread_mutex_lock(&LOCK_open));
+
if (error)
{
VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP));
- VOID(pthread_mutex_unlock(&LOCK_open));
goto err;
}
@@ -6459,6 +6469,9 @@ view_err:
my_casedn_str(files_charset_info, old_name);
wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE);
+
+ VOID(pthread_mutex_lock(&LOCK_open));
+
close_data_files_and_morph_locks(thd, db, table_name);
error=0;
@@ -6526,8 +6539,7 @@ view_err:
table_list->table_name_length= strlen(new_name);
table_list->db= new_db;
table_list->db_length= strlen(new_db);
- table_list->table= name_lock;
- if (reopen_name_locked_table(thd, table_list, FALSE))
+ if (reopen_name_locked_table(thd, table_list))
goto err_with_placeholders;
t_table= table_list->table;
}
@@ -6541,16 +6553,20 @@ view_err:
if (t_table->file->create_handler_files(path, NULL, CHF_INDEX_FLAG,
create_info))
goto err_with_placeholders;
- if (thd->locked_tables && new_name == table_name && new_db == db)
+ if (thd->locked_tables)
{
- /*
- We are going to reopen table down on the road, so we have to restore
- state of the TABLE object which we used for obtaining of handler
- object to make it suitable for reopening.
- */
- DBUG_ASSERT(t_table == table);
- table->open_placeholder= 1;
- close_handle_and_leave_table_as_lock(table);
+ if (new_name == table_name && new_db == db)
+ {
+ /*
+ We are going to reopen table down on the road, so we have to restore
+ state of the TABLE object which we used for obtaining of handler
+ object to make it suitable for reopening.
+ */
+ DBUG_ASSERT(t_table == table);
+ close_handle_and_leave_table_as_lock(table);
+ }
+ else
+ unlink_open_table(thd, t_table, FALSE);
}
}
@@ -6603,18 +6619,17 @@ view_err:
table_list->table=0; // For query cache
query_cache_invalidate3(thd, table_list, 0);
- if (thd->locked_tables && (new_name != table_name || new_db != db))
+ if (thd->locked_tables)
{
- /*
- If are we under LOCK TABLES and did ALTER TABLE with RENAME we need
- to remove placeholders for the old table and for the target table
- from the list of open tables and table cache. If we are not under
- LOCK TABLES we can rely on close_thread_tables() doing this job.
- */
- pthread_mutex_lock(&LOCK_open);
- unlink_open_table(thd, table, FALSE);
- unlink_open_table(thd, name_lock, FALSE);
- pthread_mutex_unlock(&LOCK_open);
+ if ((new_name != table_name || new_db != db))
+ {
+ pthread_mutex_lock(&LOCK_open);
+ unlink_open_table(thd, table, FALSE);
+ pthread_mutex_unlock(&LOCK_open);
+ mdl_release_exclusive_locks(&thd->mdl_context);
+ }
+ else
+ mdl_downgrade_exclusive_locks(&thd->mdl_context);
}
end_temporary:
@@ -6667,12 +6682,8 @@ err:
alter_info->datetime_field->field_name);
thd->abort_on_warning= save_abort_on_warning;
}
- if (name_lock)
- {
- pthread_mutex_lock(&LOCK_open);
- unlink_open_table(thd, name_lock, FALSE);
- pthread_mutex_unlock(&LOCK_open);
- }
+ if (target_lock)
+ mdl_release_exclusive_locks(&thd->mdl_context);
DBUG_RETURN(TRUE);
err_with_placeholders:
@@ -6682,9 +6693,8 @@ err_with_placeholders:
from list of open tables list and table cache.
*/
unlink_open_table(thd, table, FALSE);
- if (name_lock)
- unlink_open_table(thd, name_lock, FALSE);
VOID(pthread_mutex_unlock(&LOCK_open));
+ mdl_release_exclusive_locks(&thd->mdl_context);
DBUG_RETURN(TRUE);
}
/* mysql_alter_table */
diff -Nrup a/sql/sql_trigger.cc b/sql/sql_trigger.cc
--- a/sql/sql_trigger.cc 2007-07-16 23:31:33 +04:00
+++ b/sql/sql_trigger.cc 2007-08-09 12:28:18 +04:00
@@ -380,8 +380,6 @@ bool mysql_create_or_drop_trigger(THD *t
if (wait_if_global_read_lock(thd, 0, 1))
DBUG_RETURN(TRUE);
- VOID(pthread_mutex_lock(&LOCK_open));
-
if (!create)
{
bool if_exists= thd->lex->drop_if_exists;
@@ -436,14 +434,16 @@ bool mysql_create_or_drop_trigger(THD *t
if (lock_table_names(thd, tables))
goto end;
+ VOID(pthread_mutex_lock(&LOCK_open));
+
+ expel_table_from_cache(0, tables->db, tables->table_name);
+
/* We also don't allow creation of triggers on views. */
tables->required_type= FRMTYPE_TABLE;
- if (reopen_name_locked_table(thd, tables, TRUE))
- {
- unlock_table_name(thd, tables);
- goto end;
- }
+ if (reopen_name_locked_table(thd, tables))
+ goto end_unlock;
+
table= tables->table;
if (!table->triggers)
@@ -451,27 +451,28 @@ bool mysql_create_or_drop_trigger(THD *t
if (!create)
{
my_error(ER_TRG_DOES_NOT_EXIST, MYF(0));
- goto end;
+ goto end_unlock;
}
if (!(table->triggers= new (&table->mem_root) Table_triggers_list(table)))
- goto end;
+ goto end_unlock;
}
result= (create ?
table->triggers->create_trigger(thd, tables, &stmt_query):
table->triggers->drop_trigger(thd, tables, &stmt_query));
+end_unlock:
+ VOID(pthread_mutex_unlock(&LOCK_open));
+
end:
+ start_waiting_global_read_lock(thd);
if (!result)
{
write_bin_log(thd, TRUE, stmt_query.ptr(), stmt_query.length());
}
- VOID(pthread_mutex_unlock(&LOCK_open));
- start_waiting_global_read_lock(thd);
-
if (!result)
send_ok(thd);
@@ -1809,11 +1810,7 @@ bool Table_triggers_list::change_table_n
In the future, only an exclusive table name lock will be enough.
*/
#ifndef DBUG_OFF
- uchar key[MAX_DBKEY_LENGTH];
- uint key_length= (uint) (strmov(strmov((char*)&key[0], db)+1,
- old_table)-(char*)&key[0])+1;
-
- if (!is_table_name_exclusively_locked_by_this_thread(thd, key, key_length))
+ if (mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, db, old_table))
safe_mutex_assert_owner(&LOCK_open);
#endif
diff -Nrup a/sql/sql_update.cc b/sql/sql_update.cc
--- a/sql/sql_update.cc 2007-07-19 19:01:08 +04:00
+++ b/sql/sql_update.cc 2007-08-09 12:28:19 +04:00
@@ -223,7 +223,7 @@ int mysql_update(THD *thd,
break;
if (!need_reopen)
DBUG_RETURN(1);
- close_tables_for_reopen(thd, &table_list);
+ close_tables_for_reopen(thd, &table_list, FALSE);
}
if (mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
@@ -1106,7 +1106,7 @@ reopen_tables:
for (TABLE_LIST *tbl= table_list; tbl; tbl= tbl->next_global)
tbl->cleanup_items();
- close_tables_for_reopen(thd, &table_list);
+ close_tables_for_reopen(thd, &table_list, FALSE);
goto reopen_tables;
}
diff -Nrup a/sql/table.h b/sql/table.h
--- a/sql/table.h 2007-07-31 23:45:27 +04:00
+++ b/sql/table.h 2007-08-09 12:28:19 +04:00
@@ -24,6 +24,7 @@ class st_select_lex;
class partition_info;
class COND_EQUAL;
class Security_context;
+struct MDL_EL;
/*************************************************************************/
@@ -612,6 +613,7 @@ struct st_table {
partition_info *part_info; /* Partition related information */
bool no_partitions_used; /* If true, all partitions have been pruned away */
#endif
+ MDL_EL *mdl_lock;
bool fill_item_list(List<Item> *item_list) const;
void reset_item_list(List<Item> *item_list) const;
@@ -1099,8 +1101,7 @@ struct TABLE_LIST
void cleanup_items();
bool placeholder()
{
- return derived || view || schema_table || create && !table->db_stat ||
- !table;
+ return derived || view || schema_table || !table;
}
void print(THD *thd, String *str);
bool check_single_table(TABLE_LIST **table, table_map map,
| Thread |
|---|
| • bk commit into 5.1 tree (dlenev:1.2575) | dlenev | 9 Aug |