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. sql/sql_base.cc: Adjusted comments to use proper terminology. sql/sql_delete.cc: Adjusted comments to use proper terminology. sql/sql_handler.cc: Adjusted comments to use proper terminology. sql/sql_partition.cc: Adjusted comments to use proper terminology also fixed one comment to correspond to what really happens in code. sql/sql_show.cc: Adjusted comments to use proper terminology. sql/sql_table.cc: Adjusted comments to use proper terminology, brought one of them up-to-date with current situation. sql/sql_trigger.cc: Adjusted comments to use proper terminology. sql/table.h: Removed two unused members of TABLE_SHARE struct.
This commit is contained in:
parent
6d5dd31584
commit
1480dafe2f
@ -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)
|
|||||||
|
|
||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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.
|
longer used and rename those added to their real new names.
|
||||||
8) Close all partitions opened by this thread, but retain name lock.
|
10) Install the shadow frm file
|
||||||
9) Write bin log
|
11) Reopen the table if under lock tables
|
||||||
10) Prepare handlers for rename and delete of partitions
|
12) Complete query
|
||||||
11) Rename and drop the reorged partitions such that they are no
|
|
||||||
longer used and rename those added to their real new names.
|
|
||||||
12) Install the shadow frm file
|
|
||||||
13) Reopen the table if under lock tables
|
|
||||||
14) 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)
|
|||||||
/*
|
/*
|
||||||
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)
|
|||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
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, ¬_used, 0))
|
if (open_tables(thd, &src_table, ¬_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,
|
|||||||
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:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
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:
|
|||||||
/*
|
/*
|
||||||
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:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
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:
|
|||||||
|
|
||||||
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:
|
|||||||
/*
|
/*
|
||||||
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,
|
|||||||
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,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
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
|
|||||||
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;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user