Commit 386b95df authored by Konstantin Osipov's avatar Konstantin Osipov

Backport of:

------------------------------------------------------------
revno: 2630.8.3
committer: Dmitry Lenev <dlenev@mysql.com>
branch nick: mysql-6.0-3726-w4
timestamp: Thu 2008-06-05 10:48:36 +0400
message:
  WL#3726 "DDL locking for all metadata objects".

  After-review fixes in progress.

  Adjust some comments that were using old terminology
  (name locks instead of exclusive metadata locks), brought
  some of them up-to-date with current situation in code.
parent f198e8be
...@@ -869,7 +869,7 @@ void intern_close_table(TABLE *table) ...@@ -869,7 +869,7 @@ void intern_close_table(TABLE *table)
free_io_cache(table); free_io_cache(table);
delete table->triggers; delete table->triggers;
if (table->file) // Not true if name lock if (table->file) // Not true if placeholder
(void) closefrm(table, 1); // close file (void) closefrm(table, 1); // close file
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -2461,12 +2461,6 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) ...@@ -2461,12 +2461,6 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
if (reopen_table_entry(thd, table, table_list, table_name, key, key_length)) if (reopen_table_entry(thd, table, table_list, table_name, key, key_length))
{ {
/*
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.
*/
my_free((uchar*)table, MYF(0)); my_free((uchar*)table, MYF(0));
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
...@@ -2509,8 +2503,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list) ...@@ -2509,8 +2503,8 @@ bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
exists and to FALSE otherwise. exists and to FALSE otherwise.
@note This function assumes that caller owns LOCK_open mutex. @note This function assumes that caller owns LOCK_open mutex.
It also assumes that the fact that there are no name-locks It also assumes that the fact that there are no exclusive
on the table was checked beforehand. metadata locks on the table was checked beforehand.
@note If there is no .FRM file for the table but it exists in one @note If there is no .FRM file for the table but it exists in one
of engines (e.g. it was created on another node of NDB cluster) of engines (e.g. it was created on another node of NDB cluster)
...@@ -2605,10 +2599,10 @@ void table_share_release_hook(void *share) ...@@ -2605,10 +2599,10 @@ void table_share_release_hook(void *share)
IMPLEMENTATION IMPLEMENTATION
Uses a cache of open tables to find a table not in use. Uses a cache of open tables to find a table not in use.
If table list element for the table to be opened has "create" flag If table list element for the table to be opened has "open_type" set
set and table does not exist, this function will automatically insert to OPEN_OR_CREATE and table does not exist, this function will take
a placeholder for exclusive name lock into the open tables cache and exclusive metadata lock on the table, also it will do this if
will return the TABLE instance that corresponds to this placeholder. "open_type" is TAKE_EXCLUSIVE_MDL.
RETURN RETURN
NULL Open failed. If refresh is set then one should close NULL Open failed. If refresh is set then one should close
...@@ -4708,11 +4702,11 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags) ...@@ -4708,11 +4702,11 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
if (action) if (action)
{ {
/* /*
We have met name-locked or old version of table. Now we have We have met exclusive metadata lock or old version of table. Now we
to close all tables which are not up to date. We also have to have to close all tables which are not up to date/release metadata
throw away set of prelocked tables (and thus close tables from locks. We also have to throw away set of prelocked tables (and thus
this set that were open by now) since it possible that one of close tables from this set that were open by now) since it possible
tables which determined its content was changed. that one of tables which determined its content was changed.
Instead of implementing complex/non-robust logic mentioned Instead of implementing complex/non-robust logic mentioned
above we simply close and then reopen all tables. above we simply close and then reopen all tables.
......
...@@ -1079,7 +1079,8 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list) ...@@ -1079,7 +1079,8 @@ static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list)
normally can't safely do this. normally can't safely do this.
- We don't want an ok to be sent to the end user. - We don't want an ok to be sent to the end user.
- We don't want to log the truncate command - We don't want to log the truncate command
- If we want to have a name lock on the table on exit without errors. - If we want to keep exclusive metadata lock on the table (obtained by
caller) on exit without errors.
*/ */
bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok) bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
......
...@@ -282,7 +282,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen) ...@@ -282,7 +282,8 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
The thd->handler_tables list is kept as-is to avoid deadlocks if The thd->handler_tables list is kept as-is to avoid deadlocks if
open_table(), called by open_tables(), needs to back-off because open_table(), called by open_tables(), needs to back-off because
of a pending name-lock on the table being opened. of a pending exclusive metadata lock or flush for the table being
opened.
See open_table() back-off comments for more details. See open_table() back-off comments for more details.
*/ */
......
...@@ -6528,10 +6528,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ...@@ -6528,10 +6528,10 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
3) Lock the table in TL_WRITE_ONLY to ensure all other accesses to 3) Lock the table in TL_WRITE_ONLY to ensure all other accesses to
the table have completed. This ensures that other threads can not the table have completed. This ensures that other threads can not
execute on the table in parallel. execute on the table in parallel.
4) Get a name lock on the table. This ensures that we can release all 4) Get an exclusive metadata lock on the table. This ensures that we
locks on the table and since no one can open the table, there can can release all other locks on the table and since no one can open
be no new threads accessing the table. They will be hanging on the the table, there can be no new threads accessing the table. They
name lock. will be hanging on this exclusive lock.
5) Close all tables that have already been opened but didn't stumble on 5) Close all tables that have already been opened but didn't stumble on
the abort locked previously. This is done as part of the the abort locked previously. This is done as part of the
close_data_files_and_morph_locks call. close_data_files_and_morph_locks call.
...@@ -6606,14 +6606,15 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ...@@ -6606,14 +6606,15 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
3) Lock all partitions in TL_WRITE_ONLY to ensure that no users 3) Lock all partitions in TL_WRITE_ONLY to ensure that no users
are still using the old partitioning scheme. Wait until all are still using the old partitioning scheme. Wait until all
ongoing users have completed before progressing. ongoing users have completed before progressing.
4) Get a name lock on the table. This ensures that we can release all 4) Get an exclusive metadata lock on the table. This ensures that we
locks on the table and since no one can open the table, there can can release all other locks on the table and since no one can open
be no new threads accessing the table. They will be hanging on the the table, there can be no new threads accessing the table. They
name lock. will be hanging on this exclusive lock.
5) Close all tables that have already been opened but didn't stumble on 5) Close all tables that have already been opened but didn't stumble on
the abort locked previously. This is done as part of the the abort locked previously. This is done as part of the
close_data_files_and_morph_locks call. close_data_files_and_morph_locks call.
6) Close all table handlers and unlock all handlers but retain name lock 6) Close all table handlers and unlock all handlers but retain
metadata lock.
7) Write binlog 7) Write binlog
8) Now the change is completed except for the installation of the 8) Now the change is completed except for the installation of the
new frm file. We thus write an action in the log to change to new frm file. We thus write an action in the log to change to
...@@ -6694,23 +6695,18 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ...@@ -6694,23 +6695,18 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
Copy from the reorganised partitions to the new partitions Copy from the reorganised partitions to the new partitions
4) Log that operation is completed and log all complete actions 4) Log that operation is completed and log all complete actions
needed to complete operation from here needed to complete operation from here
5) Lock all partitions in TL_WRITE_ONLY to ensure that no users 5) Upgrade shared metadata lock on the table to an exclusive one.
are still using the old partitioning scheme. Wait until all After this we can be sure that there is no other connection
ongoing users have completed before progressing. using this table (they will be waiting for metadata lock).
6) Get a name lock of the table 6) Close all table instances opened by this thread, but retain
7) Close all tables opened but not yet locked, after this call we are exclusive metadata lock.
certain that no other thread is in the lock wait queue or has 7) Write bin log
opened the table. The name lock will ensure that they are blocked 8) Prepare handlers for rename and delete of partitions
on the open call. 9) Rename and drop the reorged partitions such that they are no
This is achieved also by close_data_files_and_morph_locks call.
8) Close all partitions opened by this thread, but retain name lock.
9) Write bin log
10) Prepare handlers for rename and delete of partitions
11) Rename and drop the reorged partitions such that they are no
longer used and rename those added to their real new names. longer used and rename those added to their real new names.
12) Install the shadow frm file 10) Install the shadow frm file
13) Reopen the table if under lock tables 11) Reopen the table if under lock tables
14) Complete query 12) Complete query
*/ */
if (write_log_add_change_partition(lpt) || if (write_log_add_change_partition(lpt) ||
ERROR_INJECT_CRASH("crash_change_partition_1") || ERROR_INJECT_CRASH("crash_change_partition_1") ||
......
...@@ -3271,7 +3271,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ...@@ -3271,7 +3271,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
/* /*
We should not introduce deadlocks even if we already have some We should not introduce deadlocks even if we already have some
tables open and locked, since we won't lock tables which we will tables open and locked, since we won't lock tables which we will
open and will ignore possible name-locks for these tables. open and will ignore pending exclusive metadata locks for these
tables by using high-priority requests for shared metadata locks.
*/ */
thd->reset_n_backup_open_tables_state(&open_tables_state_backup); thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
...@@ -7301,8 +7302,8 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name) ...@@ -7301,8 +7302,8 @@ bool show_create_trigger(THD *thd, const sp_name *trg_name)
Open the table by name in order to load Table_triggers_list object. Open the table by name in order to load Table_triggers_list object.
NOTE: there is race condition here -- the table can be dropped after NOTE: there is race condition here -- the table can be dropped after
LOCK_open is released. It will be fixed later by introducing LOCK_open is released. It will be fixed later by acquiring shared
acquire-shared-table-name-lock functionality. metadata lock on trigger or table name.
*/ */
uint num_tables; /* NOTE: unused, only to pass to open_tables(). */ uint num_tables; /* NOTE: unused, only to pass to open_tables(). */
......
...@@ -3674,9 +3674,9 @@ static inline void write_create_table_bin_log(THD *thd, ...@@ -3674,9 +3674,9 @@ static inline void write_create_table_bin_log(THD *thd,
If one creates a temporary table, this is automatically opened If one creates a temporary table, this is automatically opened
Note that this function assumes that caller already have taken Note that this function assumes that caller already have taken
name-lock on table being created or used some other way to ensure exclusive metadata lock on table being created or used some other
that concurrent operations won't intervene. mysql_create_table() way to ensure that concurrent operations won't intervene.
is a wrapper that can be used for this. mysql_create_table() is a wrapper that can be used for this.
no_log is needed for the case of CREATE ... SELECT, no_log is needed for the case of CREATE ... SELECT,
as the logging will be done later in sql_insert.cc as the logging will be done later in sql_insert.cc
...@@ -5279,13 +5279,13 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, ...@@ -5279,13 +5279,13 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
/* /*
By opening source table we guarantee that it exists and no concurrent By opening source table and thus acquiring shared metadata lock on it
DDL operation will mess with it. Later we also take an exclusive we guarantee that it exists and no concurrent DDL operation will mess
name-lock on target table name, which makes copying of .frm file, with it. Later we also take an exclusive metadata lock on target table
call to ha_create_table() and binlogging atomic against concurrent DML name, which makes copying of .frm file, call to ha_create_table() and
and DDL operations on target table. Thus by holding both these "locks" binlogging atomic against concurrent DML and DDL operations on target
we ensure that our statement is properly isolated from all concurrent table. Thus by holding both these "locks" we ensure that our statement
operations which matter. is properly isolated from all concurrent operations which matter.
*/ */
if (open_tables(thd, &src_table, &not_used, 0)) if (open_tables(thd, &src_table, &not_used, 0))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -5338,15 +5338,13 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table, ...@@ -5338,15 +5338,13 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
Create a new table by copying from source table Create a new table by copying from source table
and sync the new table if the flag MY_SYNC is set and sync the new table if the flag MY_SYNC is set
Altough exclusive name-lock on target table protects us from concurrent TODO: Obtaining LOCK_open mutex here is actually a legacy from the
DML and DDL operations on it we still want to wrap .FRM creation and call times when some operations (e.g. I_S implementation) ignored
to ha_create_table() in critical section protected by LOCK_open in order exclusive metadata lock on target table. Also some engines
to provide minimal atomicity against operations which disregard name-locks, (e.g. NDB cluster) require that LOCK_open should be held
like I_S implementation, for example. This is a temporary and should not during the call to ha_create_table() (See bug #28614 for more
be copied. Instead we should fix our code to always honor name-locks. info). So we should double check and probably fix this code
to not acquire this mutex.
Also some engines (e.g. NDB cluster) require that LOCK_open should be held
during the call to ha_create_table(). See bug #28614 for more info.
*/ */
pthread_mutex_lock(&LOCK_open); pthread_mutex_lock(&LOCK_open);
if (src_table->schema_table) if (src_table->schema_table)
...@@ -5461,9 +5459,9 @@ binlog: ...@@ -5461,9 +5459,9 @@ binlog:
/* /*
Here we open the destination table, on which we already have Here we open the destination table, on which we already have
name-lock. This is needed for store_create_info() to work. exclusive metada lock. This is needed for store_create_info()
The table will be closed by unlink_open_table() at the end to work. The table will be closed by unlink_open_table() at
of this function. the end of this function.
*/ */
pthread_mutex_lock(&LOCK_open); pthread_mutex_lock(&LOCK_open);
if (reopen_name_locked_table(thd, table)) if (reopen_name_locked_table(thd, table))
...@@ -6794,9 +6792,9 @@ view_err: ...@@ -6794,9 +6792,9 @@ view_err:
/* /*
Then, we want check once again that target table does not exist. Then, we want check once again that target table does not exist.
Actually the order of these two steps does not matter since Actually the order of these two steps does not matter since
earlier we took name-lock on the target table, so we do them earlier we took exclusive metadata lock on the target table, so
in this particular order only to be consistent with 5.0, in which we do them in this particular order only to be consistent with 5.0,
we don't take this name-lock and where this order really matters. in which we don't take this lock and where this order really matters.
TODO: Investigate if we need this access() check at all. TODO: Investigate if we need this access() check at all.
*/ */
if (!access(new_name_buff,F_OK)) if (!access(new_name_buff,F_OK))
...@@ -7354,18 +7352,19 @@ view_err: ...@@ -7354,18 +7352,19 @@ view_err:
/* /*
Data is copied. Now we: Data is copied. Now we:
1) Wait until all other threads close old version of table. 1) Wait until all other threads will stop using old version of table
by upgrading shared metadata lock to exclusive one.
2) Close instances of table open by this thread and replace them 2) Close instances of table open by this thread and replace them
with exclusive name-locks. with placeholders to simplify reopen process.
3) Rename the old table to a temp name, rename the new one to the 3) Rename the old table to a temp name, rename the new one to the
old name. old name.
4) If we are under LOCK TABLES and don't do ALTER TABLE ... RENAME 4) If we are under LOCK TABLES and don't do ALTER TABLE ... RENAME
we reopen new version of table. we reopen new version of table.
5) Write statement to the binary log. 5) Write statement to the binary log.
6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we 6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we
remove name-locks from list of open tables and table cache. remove placeholders and release metadata locks.
7) If we are not not under LOCK TABLES we rely on close_thread_tables() 7) If we are not not under LOCK TABLES we rely on close_thread_tables()
call to remove name-locks from table cache and list of open table. call to remove placeholders and releasing metadata locks.
*/ */
thd_proc_info(thd, "rename result table"); thd_proc_info(thd, "rename result table");
...@@ -7602,9 +7601,9 @@ err: ...@@ -7602,9 +7601,9 @@ err:
err_with_placeholders: err_with_placeholders:
/* /*
An error happened while we were holding exclusive name-lock on table An error happened while we were holding exclusive name metadata lock
being altered. To be safe under LOCK TABLES we should remove placeholders on table being altered. To be safe under LOCK TABLES we should remove
from list of open tables list and table cache. placeholders from the list of open tables and relese metadata lock.
*/ */
unlink_open_table(thd, table, FALSE); unlink_open_table(thd, table, FALSE);
pthread_mutex_unlock(&LOCK_open); pthread_mutex_unlock(&LOCK_open);
......
...@@ -525,7 +525,7 @@ end: ...@@ -525,7 +525,7 @@ end:
/* /*
If we are under LOCK TABLES we should restore original state of meta-data If we are under LOCK TABLES we should restore original state of meta-data
locks. Otherwise call to close_thread_tables() will take care about both locks. Otherwise call to close_thread_tables() will take care about both
TABLE instance created by reopen_name_locked_table() and meta-data lock. TABLE instance created by reopen_name_locked_table() and metadata lock.
*/ */
if (thd->locked_tables && tables && tables->table) if (thd->locked_tables && tables && tables->table)
mdl_downgrade_exclusive_lock(&thd->mdl_context, mdl_downgrade_exclusive_lock(&thd->mdl_context,
...@@ -1872,7 +1872,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *old_db_name, ...@@ -1872,7 +1872,7 @@ Table_triggers_list::change_table_name_in_trignames(const char *old_db_name,
i.e. it either will complete successfully, or will fail leaving files i.e. it either will complete successfully, or will fail leaving files
in their initial state. in their initial state.
Also this method assumes that subject table is not renamed to itself. Also this method assumes that subject table is not renamed to itself.
This method needs to be called under an exclusive table name lock. This method needs to be called under an exclusive table metadata lock.
@retval FALSE Success @retval FALSE Success
@retval TRUE Error @retval TRUE Error
...@@ -1894,8 +1894,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, ...@@ -1894,8 +1894,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db,
/* /*
This method interfaces the mysql server code protected by This method interfaces the mysql server code protected by
either LOCK_open mutex or with an exclusive table name lock. either LOCK_open mutex or with an exclusive metadata lock.
In the future, only an exclusive table name lock will be enough. In the future, only an exclusive metadata lock will be enough.
*/ */
#ifndef DBUG_OFF #ifndef DBUG_OFF
if (mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, db, old_table)) if (mdl_is_exclusive_lock_owner(&thd->mdl_context, 0, db, old_table))
......
...@@ -408,7 +408,6 @@ struct TABLE_SHARE ...@@ -408,7 +408,6 @@ struct TABLE_SHARE
bool db_low_byte_first; /* Portable row format */ bool db_low_byte_first; /* Portable row format */
bool crashed; bool crashed;
bool is_view; bool is_view;
bool name_lock, replace_with_name_lock;
ulong table_map_id; /* for row-based replication */ ulong table_map_id; /* for row-based replication */
ulonglong table_map_version; ulonglong table_map_version;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment