mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
Patch changing how ALTER TABLE implementation handles table locking
and invalidation in the most general case (non-temporary table and not simple RENAME or ENABLE/DISABLE KEYS or partitioning command). See comment for sql/sql_table.cc for more information. These changes are prerequisite for 5.1 version of fix for bug #23667 "CREATE TABLE LIKE is not isolated from alteration by other connections" mysql-test/include/mix1.inc: Extended coverage for behavior of ALTER TABLE statement under LOCK TABLES, which should be consistent across all platforms and for all engines. mysql-test/r/alter_table-big.result: Changed test for bug #25044 to use @@debug and injected sleeps infrastructure. Extended test coverage for ALTER TABLE's behavior under concurrency. mysql-test/r/alter_table.result: Extended coverage for behavior of ALTER TABLE statement under LOCK TABLES, which should be consistent across all platforms and for all engines. mysql-test/r/innodb_mysql.result: Extended coverage for behavior of ALTER TABLE statement under LOCK TABLES, which should be consistent across all platforms and for all engines. mysql-test/t/alter_table-big.test: Changed test for bug #25044 to use @@debug and injected sleeps infrastructure. Extended test coverage for ALTER TABLE's behavior under concurrency. mysql-test/t/alter_table.test: Extended coverage for behavior of ALTER TABLE statement under LOCK TABLES, which should be consistent across all platforms and for all engines. sql/mysql_priv.h: Made functions reopen_table() and close_handle_and_leave_table_as_lock() available outside of sql_base.cc file. Changed close_data_tables() in such way that after closing handler for the table it leaves TABLE object for it in table cache not as placeholder for ordinary name-lock but as placeholder for an exclusive name-lock. Renamed this routine to close_data_files_and_morph_locks(). sql/sql_base.cc: Made functions reopen_table() and close_handle_and_leave_table_as_lock() available outside of sql_base.cc file. Changed close_data_tables() in such way that after closing handler for the table it leaves TABLE object for it in table cache not as placeholder for ordinary name-lock but as placeholder for an exclusive name-lock. Renamed this routine to close_data_files_and_morph_locks(). Also adjusted it so it can work properly not only in LOCK TABLES mode. sql/sql_table.cc: Changed the way in which ALTER TABLE implementation handles table locking and invalidation in the most general case (non-temporary table and not simple RENAME or ENABLE/DISABLE KEYS or partitioning command) Now after preparing new version of the table we: 1) Wait until all other threads close old version of table. 2) Close instances of table open by this thread and replace them with exclusive name-locks. 3) Rename the old table to a temp name, rename the new one to the old name. 4) If we are under LOCK TABLES and don't do ALTER TABLE ... RENAME we reopen new version of table. 5) Write statement to the binary log. 6) If we are under LOCK TABLES and do ALTER TABLE ... RENAME we remove name-locks from list of open tables and table cache. 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. Such approach: a) Eliminates possibility for concurrent statement to sneak in and get access to the new version of the table before ALTER TABLE gets logged into binary log. b) Ensures that ALTER TABLE behaves under LOCK TABLES in the same way on all platforms and for all engines (in 5.0 this was not true) c) Preserves nice invariant that if table is open in some connection there is a guarantee that .FRM file for this table exists and is properly named.
This commit is contained in:
@ -99,7 +99,6 @@ static bool open_new_frm(THD *thd, TABLE_SHARE *share, const char *alias,
|
||||
TABLE_LIST *table_desc, MEM_ROOT *mem_root);
|
||||
static void close_old_data_files(THD *thd, TABLE *table, bool morph_locks,
|
||||
bool send_refresh);
|
||||
static bool reopen_table(TABLE *table);
|
||||
static bool
|
||||
has_two_write_locked_tables_with_auto_increment(TABLE_LIST *tables);
|
||||
|
||||
@ -681,7 +680,7 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name)
|
||||
*/
|
||||
|
||||
|
||||
static void close_handle_and_leave_table_as_lock(TABLE *table)
|
||||
void close_handle_and_leave_table_as_lock(TABLE *table)
|
||||
{
|
||||
TABLE_SHARE *share, *old_share= table->s;
|
||||
char *key_buff;
|
||||
@ -2705,7 +2704,7 @@ TABLE *find_locked_table(THD *thd, const char *db,const char *table_name)
|
||||
1 error. The old table object is not changed.
|
||||
*/
|
||||
|
||||
static bool reopen_table(TABLE *table)
|
||||
bool reopen_table(TABLE *table)
|
||||
{
|
||||
TABLE tmp;
|
||||
bool error= 1;
|
||||
@ -2788,27 +2787,55 @@ static bool reopen_table(TABLE *table)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Used with ALTER TABLE:
|
||||
Close all instanses of table when LOCK TABLES is in used;
|
||||
Close first all instances of table and then reopen them
|
||||
/**
|
||||
@brief Close all instances of a table open by this thread and replace
|
||||
them with exclusive name-locks.
|
||||
|
||||
@param thd Thread context
|
||||
@param db Database name for the table to be closed
|
||||
@param table_name Name of the table to be closed
|
||||
|
||||
@note This function assumes that if we are not under LOCK TABLES,
|
||||
then there is only one table open and locked. This means that
|
||||
the function probably has to be adjusted before it can be used
|
||||
anywhere outside ALTER TABLE.
|
||||
*/
|
||||
|
||||
bool close_data_tables(THD *thd,const char *db, const char *table_name)
|
||||
void close_data_files_and_morph_locks(THD *thd, const char *db,
|
||||
const char *table_name)
|
||||
{
|
||||
TABLE *table;
|
||||
DBUG_ENTER("close_data_tables");
|
||||
DBUG_ENTER("close_data_files_and_morph_locks");
|
||||
|
||||
safe_mutex_assert_owner(&LOCK_open);
|
||||
|
||||
if (thd->lock)
|
||||
{
|
||||
/*
|
||||
If we are not under LOCK TABLES we should have only one table
|
||||
open and locked so it makes sense to remove the lock at once.
|
||||
*/
|
||||
mysql_unlock_tables(thd, thd->lock);
|
||||
thd->lock= 0;
|
||||
}
|
||||
|
||||
/*
|
||||
Note that open table list may contain a name-lock placeholder
|
||||
for target table name if we process ALTER TABLE ... RENAME.
|
||||
So loop below makes sense even if we are not under LOCK TABLES.
|
||||
*/
|
||||
for (table=thd->open_tables; table ; table=table->next)
|
||||
{
|
||||
if (!strcmp(table->s->table_name.str, table_name) &&
|
||||
!strcmp(table->s->db.str, db))
|
||||
{
|
||||
mysql_lock_remove(thd, thd->locked_tables,table);
|
||||
if (thd->locked_tables)
|
||||
mysql_lock_remove(thd, thd->locked_tables, table);
|
||||
table->open_placeholder= 1;
|
||||
close_handle_and_leave_table_as_lock(table);
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(0); // For the future
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user