diff options
author | unknown <dlenev@mockturtle.local> | 2007-05-19 10:58:01 +0400 |
---|---|---|
committer | unknown <dlenev@mockturtle.local> | 2007-05-19 10:58:01 +0400 |
commit | 3d01594f349a540068943b1ba7ddea2ec2e448ef (patch) | |
tree | 7c5a80278b1be15a8b7766f9fae0fe02f4dae781 | |
parent | ad4da53510fe17b7b20912753232719fd8d3e033 (diff) | |
parent | 1a60685cbaf6bcd919189ac19f01f65c50d79b54 (diff) | |
download | mariadb-git-3d01594f349a540068943b1ba7ddea2ec2e448ef.tar.gz |
Merge bk-internal.mysql.com:/home/bk/mysql-5.1-runtime
into mockturtle.local:/home/dlenev/src/mysql-5.1-alter
sql/sql_base.cc:
Auto merged
-rw-r--r-- | mysql-test/include/mix1.inc | 29 | ||||
-rw-r--r-- | mysql-test/r/alter_table-big.result | 41 | ||||
-rw-r--r-- | mysql-test/r/alter_table.result | 53 | ||||
-rw-r--r-- | mysql-test/r/innodb_mysql.result | 24 | ||||
-rw-r--r-- | mysql-test/t/alter_table-big.test | 90 | ||||
-rw-r--r-- | mysql-test/t/alter_table.test | 53 | ||||
-rw-r--r-- | sql/mysql_priv.h | 5 | ||||
-rw-r--r-- | sql/sql_base.cc | 49 | ||||
-rw-r--r-- | sql/sql_table.cc | 228 |
9 files changed, 399 insertions, 173 deletions
diff --git a/mysql-test/include/mix1.inc b/mysql-test/include/mix1.inc index 3919e4918c8..bf21c6fad09 100644 --- a/mysql-test/include/mix1.inc +++ b/mysql-test/include/mix1.inc @@ -762,4 +762,33 @@ alter table t2 modify i int default 4, rename t1; unlock tables; drop table t1; + +# +# Some more tests for ALTER TABLE and LOCK TABLES for transactional tables. +# +# Table which is altered under LOCK TABLES should stay in list of locked +# tables and be available after alter takes place unless ALTER contains +# RENAME clause. We should see the new definition of table, of course. +# Before 5.1 this behavior was inconsistent across the platforms and +# different engines. See also tests in alter_table.test +# +--disable_warnings +drop table if exists t1; +--enable_warnings +create table t1 (i int); +insert into t1 values (); +lock table t1 write; +# Example of so-called 'fast' ALTER TABLE +alter table t1 modify i int default 1; +insert into t1 values (); +select * from t1; +# And now full-blown ALTER TABLE +alter table t1 change i c char(10) default "Two"; +insert into t1 values (); +select * from t1; +unlock tables; +select * from t1; +drop tables t1; + + --echo End of 5.1 tests diff --git a/mysql-test/r/alter_table-big.result b/mysql-test/r/alter_table-big.result index a9d0515d6bb..9761754a02f 100644 --- a/mysql-test/r/alter_table-big.result +++ b/mysql-test/r/alter_table-big.result @@ -5,14 +5,53 @@ key (n2, n3, n1), key (n3, n1, n2)); create table t2 (i int); alter table t1 disable keys; +insert into t1 values (RAND()*1000, RAND()*1000, RAND()*1000); reset master; +set session debug="+d,sleep_alter_enable_indexes"; alter table t1 enable keys;; insert into t2 values (1); insert into t1 values (1, 1, 1); -show binlog events in 'master-bin.000001' from 102; +set session debug="-d,sleep_alter_enable_indexes"; +show binlog events in 'master-bin.000001' from 106; Log_name Pos Event_type Server_id End_log_pos Info master-bin.000001 # Query 1 # use `test`; insert into t2 values (1) master-bin.000001 # Query 1 # use `test`; alter table t1 enable keys master-bin.000001 # Query 1 # use `test`; insert into t1 values (1, 1, 1) drop tables t1, t2; End of 5.0 tests +drop table if exists t1, t2, t3; +create table t1 (i int); +reset master; +set session debug="+d,sleep_alter_before_main_binlog"; +alter table t1 change i c char(10) default 'Test1';; +insert into t1 values (); +select * from t1; +c +Test1 +alter table t1 change c vc varchar(100) default 'Test2';; +rename table t1 to t2; +drop table t2; +create table t1 (i int); +alter table t1 change i c char(10) default 'Test3', rename to t2;; +insert into t2 values (); +select * from t2; +c +Test3 +alter table t2 change c vc varchar(100) default 'Test2', rename to t1;; +rename table t1 to t3; +drop table t3; +set session debug="-d,sleep_alter_before_main_binlog"; +show binlog events in 'master-bin.000001' from 106; +Log_name Pos Event_type Server_id End_log_pos Info +master-bin.000001 # Query 1 # use `test`; alter table t1 change i c char(10) default 'Test1' +master-bin.000001 # Query 1 # use `test`; insert into t1 values () +master-bin.000001 # Query 1 # use `test`; alter table t1 change c vc varchar(100) default 'Test2' +master-bin.000001 # Query 1 # use `test`; rename table t1 to t2 +master-bin.000001 # Query 1 # use `test`; drop table t2 +master-bin.000001 # Query 1 # use `test`; create table t1 (i int) +master-bin.000001 # Query 1 # use `test`; alter table t1 change i c char(10) default 'Test3', rename to t2 +master-bin.000001 # Query 1 # use `test`; insert into t2 values () +master-bin.000001 # Query 1 # use `test`; alter table t2 change c vc varchar(100) default 'Test2', rename to t1 +master-bin.000001 # Query 1 # use `test`; rename table t1 to t3 +master-bin.000001 # Query 1 # use `test`; drop table t3 +End of 5.1 tests diff --git a/mysql-test/r/alter_table.result b/mysql-test/r/alter_table.result index 4481b56791f..63dc13a4b6d 100644 --- a/mysql-test/r/alter_table.result +++ b/mysql-test/r/alter_table.result @@ -977,6 +977,59 @@ SELECT * FROM t1; v b abc 5 DROP TABLE t1; +End of 5.0 tests +drop table if exists t1, t2, t3; +create table t1 (i int); +create table t3 (j int); +insert into t1 values (); +insert into t3 values (); +lock table t1 write, t3 read; +alter table t1 modify i int default 1; +insert into t1 values (); +select * from t1; +i +NULL +1 +alter table t1 change i c char(10) default "Two"; +insert into t1 values (); +select * from t1; +c +NULL +1 +Two +alter table t1 modify c char(10) default "Three", rename to t2; +select * from t1; +ERROR HY000: Table 't1' was not locked with LOCK TABLES +select * from t2; +ERROR HY000: Table 't2' was not locked with LOCK TABLES +select * from t3; +j +NULL +unlock tables; +insert into t2 values (); +select * from t2; +c +NULL +1 +Three +lock table t2 write, t3 read; +alter table t2 change c vc varchar(100) default "Four", rename to t1; +select * from t1; +ERROR HY000: Table 't1' was not locked with LOCK TABLES +select * from t2; +ERROR HY000: Table 't2' was not locked with LOCK TABLES +select * from t3; +j +NULL +unlock tables; +insert into t1 values (); +select * from t1; +vc +NULL +1 +Three +Four +drop tables t1, t3; DROP TABLE IF EXISTS `t+1`, `t+2`; CREATE TABLE `t+1` (c1 INT); ALTER TABLE `t+1` RENAME `t+2`; diff --git a/mysql-test/r/innodb_mysql.result b/mysql-test/r/innodb_mysql.result index dc9564b21a2..c8aea5ebb61 100644 --- a/mysql-test/r/innodb_mysql.result +++ b/mysql-test/r/innodb_mysql.result @@ -796,4 +796,28 @@ lock table t2 write; alter table t2 modify i int default 4, rename t1; unlock tables; drop table t1; +drop table if exists t1; +create table t1 (i int); +insert into t1 values (); +lock table t1 write; +alter table t1 modify i int default 1; +insert into t1 values (); +select * from t1; +i +NULL +1 +alter table t1 change i c char(10) default "Two"; +insert into t1 values (); +select * from t1; +c +NULL +1 +Two +unlock tables; +select * from t1; +c +NULL +1 +Two +drop tables t1; End of 5.1 tests diff --git a/mysql-test/t/alter_table-big.test b/mysql-test/t/alter_table-big.test index befe6e14977..5d2c0ba0bb6 100644 --- a/mysql-test/t/alter_table-big.test +++ b/mysql-test/t/alter_table-big.test @@ -1,7 +1,12 @@ -# In order to be more or less robust test for bug#25044 has to take -# significant time (e.g. about 9 seconds on my (Dmitri's) computer) -# so we probably want execute it only in --big-test mode. +# +# Tests for various concurrency-related aspects of ALTER TABLE implemetation +# +# This test takes rather long time so let us run it only in --big-test mode --source include/big_test.inc +# We are using some debug-only features in this test +--source include/have_debug.inc +# Also we are using SBR to check that statements are executed +# in proper order. --source include/have_binlog_format_mixed_or_statement.inc @@ -22,27 +27,20 @@ create table t1 (n1 int, n2 int, n3 int, key (n3, n1, n2)); create table t2 (i int); -# Populating 't1' table with keys disabled, so ALTER TABLE .. ENABLE KEYS -# will run for some time +# Starting from 5.1 we have runtime settable @@debug variable, +# which can be used for introducing delays at certain points of +# statement execution, so we don't need many rows in 't1' to make +# this test repeatable. alter table t1 disable keys; ---disable_query_log -insert into t1 values (RAND()*1000,RAND()*1000,RAND()*1000); -let $1=19; -while ($1) -{ - eval insert into t1 select RAND()*1000,RAND()*1000,RAND()*1000 from t1; - dec $1; -} ---enable_query_log +insert into t1 values (RAND()*1000, RAND()*1000, RAND()*1000); # Later we use binlog to check the order in which statements are # executed so let us reset it first. reset master; +set session debug="+d,sleep_alter_enable_indexes"; --send alter table t1 enable keys; connection addconroot; -let $show_type= PROCESSLIST; -let $show_pattern= '%Repair by sorting%alter table t1 enable keys%'; ---source include/wait_show_pattern.inc +--sleep 2 # This statement should not be blocked by in-flight ALTER and therefore # should be executed and written to binlog before ALTER TABLE ... ENABLE KEYS # finishes. @@ -51,12 +49,68 @@ insert into t2 values (1); insert into t1 values (1, 1, 1); connection default; --reap +set session debug="-d,sleep_alter_enable_indexes"; # Check that statements were executed/binlogged in correct order. --replace_column 2 # 5 # -show binlog events in 'master-bin.000001' from 102; +show binlog events in 'master-bin.000001' from 106; # Clean up drop tables t1, t2; --echo End of 5.0 tests + +# +# Additional coverage for the main ALTER TABLE case +# +# We should be sure that table being altered is properly +# locked during statement execution and in particular that +# no DDL or DML statement can sneak in and get access to +# the table when real operation has already taken place +# but this fact has not been noted in binary log yet. +--disable_warnings +drop table if exists t1, t2, t3; +--enable_warnings +create table t1 (i int); +# We are going to check that statements are logged in correct order +reset master; +set session debug="+d,sleep_alter_before_main_binlog"; +--send alter table t1 change i c char(10) default 'Test1'; +connection addconroot; +--sleep 2 +insert into t1 values (); +select * from t1; +connection default; +--reap +--send alter table t1 change c vc varchar(100) default 'Test2'; +connection addconroot; +--sleep 2 +rename table t1 to t2; +connection default; +--reap +drop table t2; +# And now tests for ALTER TABLE with RENAME clause. In this +# case target table name should be properly locked as well. +create table t1 (i int); +--send alter table t1 change i c char(10) default 'Test3', rename to t2; +connection addconroot; +--sleep 2 +insert into t2 values (); +select * from t2; +connection default; +--reap +--send alter table t2 change c vc varchar(100) default 'Test2', rename to t1; +connection addconroot; +--sleep 2 +rename table t1 to t3; +connection default; +--reap +drop table t3; +set session debug="-d,sleep_alter_before_main_binlog"; + +# Check that all statements were logged in correct order +--replace_column 2 # 5 # +show binlog events in 'master-bin.000001' from 106; + + +--echo End of 5.1 tests diff --git a/mysql-test/t/alter_table.test b/mysql-test/t/alter_table.test index 965528642bf..7d3e9bba533 100644 --- a/mysql-test/t/alter_table.test +++ b/mysql-test/t/alter_table.test @@ -727,7 +727,58 @@ ALTER TABLE t1 MODIFY COLUMN v VARCHAR(4); SELECT * FROM t1; DROP TABLE t1; -# End of 5.0 tests +--echo End of 5.0 tests + +# +# Extended test coverage for ALTER TABLE behaviour under LOCK TABLES +# It should be consistent across all platforms and for all engines +# (Before 5.1 this was not true as behavior was different between +# Unix/Windows and transactional/non-transactional tables). +# See also innodb_mysql.test +# +--disable_warnings +drop table if exists t1, t2, t3; +--enable_warnings +create table t1 (i int); +create table t3 (j int); +insert into t1 values (); +insert into t3 values (); +# Table which is altered under LOCK TABLES it should stay in list of locked +# tables and be available after alter takes place unless ALTER contains RENAME +# clause. We should see the new definition of table, of course. +lock table t1 write, t3 read; +# Example of so-called 'fast' ALTER TABLE +alter table t1 modify i int default 1; +insert into t1 values (); +select * from t1; +# And now full-blown ALTER TABLE +alter table t1 change i c char(10) default "Two"; +insert into t1 values (); +select * from t1; +# If table is renamed then it should be removed from the list +# of locked tables. 'Fast' ALTER TABLE with RENAME clause: +alter table t1 modify c char(10) default "Three", rename to t2; +--error ER_TABLE_NOT_LOCKED +select * from t1; +--error ER_TABLE_NOT_LOCKED +select * from t2; +select * from t3; +unlock tables; +insert into t2 values (); +select * from t2; +lock table t2 write, t3 read; +# Full ALTER TABLE with RENAME +alter table t2 change c vc varchar(100) default "Four", rename to t1; +--error ER_TABLE_NOT_LOCKED +select * from t1; +--error ER_TABLE_NOT_LOCKED +select * from t2; +select * from t3; +unlock tables; +insert into t1 values (); +select * from t1; +drop tables t1, t3; + # # Bug#18775 - Temporary table from alter table visible to other threads diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index b9d5d9f9b34..e5e70f81bff 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -1032,8 +1032,11 @@ TABLE *table_cache_insert_placeholder(THD *thd, const char *key, bool lock_table_name_if_not_cached(THD *thd, const char *db, const char *table_name, TABLE **table); 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); -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); +void close_handle_and_leave_table_as_lock(TABLE *table); bool wait_for_tables(THD *thd); bool table_is_used(TABLE *table, bool wait_for_name_lock); TABLE *drop_locked_tables(THD *thd,const char *db, const char *table_name); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index c86ebf9ea41..a97d285810e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -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; @@ -2712,7 +2711,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; @@ -2795,27 +2794,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; } diff --git a/sql/sql_table.cc b/sql/sql_table.cc index df336545460..149c746a1de 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -5406,7 +5406,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, HA_CREATE_INFO *create_info; frm_type_enum frm_type; uint need_copy_table= 0; - bool no_table_reopen= FALSE, varchar= FALSE; + bool varchar= FALSE; #ifdef WITH_PARTITION_STORAGE_ENGINE uint fast_alter_partition= 0; bool partition_changed= FALSE; @@ -5665,6 +5665,7 @@ view_err: 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; @@ -6580,9 +6581,19 @@ view_err: } /* - Data is copied. Now we rename the old table to a temp name, - rename the new one to the old name, remove all entries about the old table - from the cache, free all locks, close the old table and remove it. + Data is copied. Now 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. */ thd->proc_info="rename result table"; @@ -6591,38 +6602,8 @@ view_err: if (lower_case_table_names) my_casedn_str(files_charset_info, old_name); -#if !defined( __WIN__) - if (table->file->has_transactions()) -#endif - { - /* - Win32 and InnoDB can't drop a table that is in use, so we must - close the original table before doing the rename - */ - close_cached_table(thd, table); - table=0; // Marker that table is closed - no_table_reopen= TRUE; - } -#if !defined( __WIN__) - else - table->file->extra(HA_EXTRA_FORCE_REOPEN); // Don't use this file anymore -#endif - - if (new_name != table_name || new_db != db) - { - /* - Check that there is no table with target name. See the - comment describing code for 'simple' ALTER TABLE ... RENAME. - */ - if (!access(new_name_buff,F_OK)) - { - error=1; - my_error(ER_TABLE_EXISTS_ERROR, MYF(0), new_name_buff); - VOID(quick_rm_table(new_db_type, new_db, tmp_name, FN_IS_TMP)); - VOID(pthread_mutex_unlock(&LOCK_open)); - goto err; - } - } + wait_while_table_is_used(thd, table, HA_EXTRA_PREPARE_FOR_DELETE); + close_data_files_and_morph_locks(thd, db, table_name); error=0; save_old_db_type= old_db_type; @@ -6667,121 +6648,64 @@ view_err: if (error) { - /* - This shouldn't happen. We solve this the safe way by - closing the locked table. - */ - if (table) - { - close_cached_table(thd,table); - } - VOID(pthread_mutex_unlock(&LOCK_open)); - goto err; + /* This shouldn't happen. But let us play it safe. */ + goto err_with_placeholders; } + if (! need_copy_table) { - bool needs_unlink= FALSE; - if (! table) - { - if (new_name != table_name || new_db != db) - { - table_list->alias= new_name; - table_list->table_name= new_name; - table_list->table_name_length= strlen(new_name); - table_list->db= new_db; - table_list->db_length= strlen(new_db); - } - else - { - /* - TODO: Creation of name-lock placeholder here is a temporary - work-around. Long term we should change close_cached_table() call - which we invoke before table renaming operation in such way that - it will leave placeholders for table in table cache/THD::open_tables - list. By doing this we will be able easily reopen and relock these - tables later and therefore behave under LOCK TABLES in the same way - on all platforms. - */ - char key[MAX_DBKEY_LENGTH]; - uint key_length; - key_length= create_table_def_key(thd, key, table_list, 0); - if (!(name_lock= table_cache_insert_placeholder(thd, key, - key_length))) - { - VOID(pthread_mutex_unlock(&LOCK_open)); - goto err; - } - name_lock->next= thd->open_tables; - thd->open_tables= name_lock; - } + /* + Now we have to inform handler that new .FRM file is in place. + To do this we need to obtain a handler object for it. + */ + TABLE *t_table; + if (new_name != table_name || new_db != db) + { + table_list->alias= new_name; + table_list->table_name= new_name; + 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)) - { - VOID(pthread_mutex_unlock(&LOCK_open)); - goto err; - } - table= table_list->table; - /* - We can't rely on later close_cached_table() calls to close - this instance of the table since it was not properly locked. - */ - needs_unlink= TRUE; + goto err_with_placeholders; + t_table= table_list->table; } - /* Tell the handler that a new frm file is in place. */ - if (table->file->create_handler_files(path, NULL, CHF_INDEX_FLAG, - create_info)) + else { - VOID(pthread_mutex_unlock(&LOCK_open)); - goto err; + if (reopen_table(table)) + goto err_with_placeholders; + t_table= table; } - if (needs_unlink) + /* Tell the handler that a new frm file is in place. */ + 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) { - unlink_open_table(thd, table, FALSE); - table= name_lock= 0; + /* + 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 (thd->lock || new_name != table_name || no_table_reopen) // True if WIN32 - { - /* - Not table locking or alter table with rename. - Free locks and remove old table - */ - if (table) - { - close_cached_table(thd,table); - } - VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP)); - } - else + VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP)); + + if (thd->locked_tables && new_name == table_name && new_db == db) { - /* - Using LOCK TABLES without rename. - This code is never executed on WIN32! - Remove old renamed table, reopen table and get new locks - */ - if (table) - { - VOID(table->file->extra(HA_EXTRA_FORCE_REOPEN)); // Use new file - /* Mark in-use copies old */ - remove_table_from_cache(thd,db,table_name,RTFC_NO_FLAG); - /* end threads waiting on lock */ - mysql_lock_abort(thd,table, TRUE); - } - VOID(quick_rm_table(old_db_type, db, old_name, FN_IS_TMP)); - if (close_data_tables(thd,db,table_name) || - reopen_tables(thd,1,0)) - { // This shouldn't happen - if (table) - { - close_cached_table(thd,table); // Remove lock for table - } - VOID(pthread_mutex_unlock(&LOCK_open)); - goto err; - } + thd->in_lock_tables= 1; + error= reopen_tables(thd, 1, 0); + thd->in_lock_tables= 0; + if (error) + goto err_with_placeholders; } VOID(pthread_mutex_unlock(&LOCK_open)); - broadcast_refresh(); + /* The ALTER TABLE is always in its own transaction. Commit must not be called while LOCK_open is locked. It could call @@ -6798,6 +6722,8 @@ view_err: } thd->proc_info="end"; + DBUG_EXECUTE_IF("sleep_alter_before_main_binlog", my_sleep(6000000);); + ha_binlog_log_query(thd, create_info->db_type, LOGCOM_ALTER_TABLE, thd->query, thd->query_length, db, table_name); @@ -6815,12 +6741,13 @@ view_err: shutdown. */ char path[FN_REFLEN]; + TABLE *t_table; build_table_filename(path, sizeof(path), new_db, table_name, "", 0); - table=open_temporary_table(thd, path, new_db, tmp_name,0); - if (table) + t_table= open_temporary_table(thd, path, new_db, tmp_name, 0); + if (t_table) { - intern_close_table(table); - my_free((char*) table, MYF(0)); + intern_close_table(t_table); + my_free((char*) t_table, MYF(0)); } else sql_print_warning("Could not open table %s.%s after rename\n", @@ -6830,9 +6757,16 @@ view_err: table_list->table=0; // For query cache query_cache_invalidate3(thd, table_list, 0); - if (name_lock) + if (thd->locked_tables && (new_name != table_name || new_db != db)) { + /* + 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); } @@ -6863,6 +6797,18 @@ err: pthread_mutex_unlock(&LOCK_open); } DBUG_RETURN(TRUE); + +err_with_placeholders: + /* + An error happened while we were holding exclusive name-lock on table + being altered. To be safe under LOCK TABLES we should remove 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)); + DBUG_RETURN(TRUE); } /* mysql_alter_table */ |