1
0
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:
unknown
2007-05-19 10:49:56 +04:00
parent d1c57f503f
commit 1a60685cba
9 changed files with 399 additions and 173 deletions

View File

@ -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;
}