Commit 1b5f84db authored by Mattias Jonsson's avatar Mattias Jonsson

Bug#56172: Server crashes in ha_partition::reset on

           REBUILD PARTITION under LOCK TABLE

Collapsed patch including updates from the reviews.

In case of failure in ALTER ... PARTITION under LOCK TABLE
the server could crash, due to it had modified the locked
table object, which was not reverted in case of failure,
resulting in a bad table definition used after the failed
command.

Solved by instead of altering the locked table object and
its partition_info struct, creating an internal temporary
intermediate table object used for altering,
just like the non partitioned mysql_alter_table.
So if an error occur before the alter operation is complete,
the original table is not modified at all.
But if the alter operation have succeeded so far that it
must be completed as whole,
the table is properly closed and reopened.
(The completion on failure is done by the ddl_log.)

mysql-test/suite/parts/inc/partition_fail.inc:
  Added tests under LOCK TABLE
mysql-test/suite/parts/r/partition_debug_innodb.result:
  Updated results
mysql-test/suite/parts/r/partition_debug_myisam.result:
  Updated results
mysql-test/suite/parts/r/partition_special_innodb.result:
  updated result
mysql-test/suite/parts/t/partition_special_innodb.test:
  changing comment, since this patch also fixes this.
sql/sql_partition.cc:
  Added TODO, to use DBUG_SUICIDE() instead of abort()
  to avoid core-files on expected crashes.
  Removed unused arguments to fast_end_partition.
  Opening a intermediate table in prep_alter_part_table, instead of altering
  (a possible locked) normally opened table.
  That way we do not have to do anything more than close
  the intermediate table on error,
  leaving the ordinary table opened and locked.
  Also making sure that the intermediate table are
  closed/destroyed on failure. If no error occur
  it is later destroyed in the end of fast_alter_partition_table.
  Added ha_external_lock to make sure MyISAM flushed the index file
  after copying the partitions.
  This also leads to removal of the special close and removal from
  the table cache for other instances of the table.
sql/sql_partition.h:
  Changed the arguments for prep_alter_part_table and
  fast_alter_partition_table to use an intermediate table
  instead of altering a (possibly locked) normal table.
sql/sql_table.cc:
  Using an intermediate table created in prep_alter_part_table
  to be used in fast_alter_partition_table, also closing/destroying
  it on failure.
parent fdb40d42
...@@ -16,3 +16,23 @@ SHOW CREATE TABLE t1; ...@@ -16,3 +16,23 @@ SHOW CREATE TABLE t1;
--sorted_result --sorted_result
SELECT * FROM t1; SELECT * FROM t1;
DROP TABLE t1; DROP TABLE t1;
--echo # Same test under LOCK TABLE
--eval $create_statement
--eval $insert_statement
--echo # State before failure
--list_files $DATADIR/test
SHOW CREATE TABLE t1;
--sorted_result
SELECT * FROM t1;
LOCK TABLE t1 WRITE;
--disable_abort_on_error
--eval $fail_statement
--enable_abort_on_error
--echo # State after failure
--list_files $DATADIR/test
SHOW CREATE TABLE t1;
--sorted_result
SELECT * FROM t1;
UNLOCK TABLES;
DROP TABLE t1;
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -238,11 +238,10 @@ LOCK TABLE t1 READ; ...@@ -238,11 +238,10 @@ LOCK TABLE t1 READ;
# Third attempt: says that the table does not exist # Third attempt: says that the table does not exist
ALTER TABLE t1 ADD PARTITION PARTITIONS 2; ALTER TABLE t1 ADD PARTITION PARTITIONS 2;
ERROR HY000: Lock wait timeout exceeded; try restarting transaction ERROR HY000: Lock wait timeout exceeded; try restarting transaction
# Check table returns the same # Check table returns the same (not after fixing bug#56172!)
CHECK TABLE t1; CHECK TABLE t1;
Table Op Msg_type Msg_text Table Op Msg_type Msg_text
test.t1 check Error Lock wait timeout exceeded; try restarting transaction test.t1 check status OK
test.t1 check error Corrupt
UNLOCK TABLES; UNLOCK TABLES;
DROP TABLE t1; DROP TABLE t1;
CREATE TABLE t2 ( i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, f INT ) CREATE TABLE t2 ( i INT NOT NULL AUTO_INCREMENT PRIMARY KEY, f INT )
......
...@@ -111,7 +111,7 @@ LOCK TABLE t1 READ; ...@@ -111,7 +111,7 @@ LOCK TABLE t1 READ;
--echo # Third attempt: says that the table does not exist --echo # Third attempt: says that the table does not exist
--error ER_LOCK_WAIT_TIMEOUT --error ER_LOCK_WAIT_TIMEOUT
ALTER TABLE t1 ADD PARTITION PARTITIONS 2; ALTER TABLE t1 ADD PARTITION PARTITIONS 2;
--echo # Check table returns the same --echo # Check table returns the same (not after fixing bug#56172!)
CHECK TABLE t1; CHECK TABLE t1;
--connection con1 --connection con1
......
This diff is collapsed.
...@@ -54,6 +54,7 @@ typedef struct st_lock_param_type ...@@ -54,6 +54,7 @@ typedef struct st_lock_param_type
HA_CREATE_INFO *create_info; HA_CREATE_INFO *create_info;
Alter_info *alter_info; Alter_info *alter_info;
TABLE *table; TABLE *table;
TABLE *old_table;
KEY *key_info_buffer; KEY *key_info_buffer;
const char *db; const char *db;
const char *table_name; const char *table_name;
...@@ -252,14 +253,17 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, ...@@ -252,14 +253,17 @@ uint fast_alter_partition_table(THD *thd, TABLE *table,
TABLE_LIST *table_list, TABLE_LIST *table_list,
char *db, char *db,
const char *table_name, const char *table_name,
uint fast_alter_partition); TABLE *fast_alter_table);
uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info, uint set_part_state(Alter_info *alter_info, partition_info *tab_part_info,
enum partition_state part_state); enum partition_state part_state);
uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info, uint prep_alter_part_table(THD *thd, TABLE *table, Alter_info *alter_info,
HA_CREATE_INFO *create_info, HA_CREATE_INFO *create_info,
handlerton *old_db_type, handlerton *old_db_type,
bool *partition_changed, bool *partition_changed,
uint *fast_alter_partition); char *db,
const char *table_name,
const char *path,
TABLE **fast_alter_table);
char *generate_partition_syntax(partition_info *part_info, char *generate_partition_syntax(partition_info *part_info,
uint *buf_length, bool use_sql_alloc, uint *buf_length, bool use_sql_alloc,
bool show_partition_options, bool show_partition_options,
......
...@@ -1725,8 +1725,6 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags) ...@@ -1725,8 +1725,6 @@ bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags)
CHF_DELETE_FLAG, NULL) || CHF_DELETE_FLAG, NULL) ||
deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) || deactivate_ddl_log_entry(part_info->frm_log_entry->entry_pos) ||
(sync_ddl_log(), FALSE) || (sync_ddl_log(), FALSE) ||
#endif
#ifdef WITH_PARTITION_STORAGE_ENGINE
mysql_file_rename(key_file_frm, mysql_file_rename(key_file_frm,
shadow_frm_name, frm_name, MYF(MY_WME)) || shadow_frm_name, frm_name, MYF(MY_WME)) ||
lpt->table->file->ha_create_handler_files(path, shadow_path, lpt->table->file->ha_create_handler_files(path, shadow_path,
...@@ -5599,7 +5597,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -5599,7 +5597,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
handlerton *old_db_type, *new_db_type, *save_old_db_type; handlerton *old_db_type, *new_db_type, *save_old_db_type;
enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY; enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
uint fast_alter_partition= 0; TABLE *table_for_fast_alter_partition= NULL;
bool partition_changed= FALSE; bool partition_changed= FALSE;
#endif #endif
bool need_lock_for_indexes= TRUE; bool need_lock_for_indexes= TRUE;
...@@ -5968,7 +5966,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -5968,7 +5966,9 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type, if (prep_alter_part_table(thd, table, alter_info, create_info, old_db_type,
&partition_changed, &fast_alter_partition)) &partition_changed,
db, table_name, path,
&table_for_fast_alter_partition))
goto err; goto err;
#endif #endif
/* /*
...@@ -6201,12 +6201,12 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, ...@@ -6201,12 +6201,12 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
create_info->frm_only= 1; create_info->frm_only= 1;
#ifdef WITH_PARTITION_STORAGE_ENGINE #ifdef WITH_PARTITION_STORAGE_ENGINE
if (fast_alter_partition) if (table_for_fast_alter_partition)
{ {
DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info, DBUG_RETURN(fast_alter_partition_table(thd, table, alter_info,
create_info, table_list, create_info, table_list,
db, table_name, db, table_name,
fast_alter_partition)); table_for_fast_alter_partition));
} }
#endif #endif
...@@ -6686,6 +6686,11 @@ err_new_table_cleanup: ...@@ -6686,6 +6686,11 @@ err_new_table_cleanup:
create_info->frm_only ? FN_IS_TMP | FRM_ONLY : FN_IS_TMP); create_info->frm_only ? FN_IS_TMP | FRM_ONLY : FN_IS_TMP);
err: err:
#ifdef WITH_PARTITION_STORAGE_ENGINE
/* If prep_alter_part_table created an intermediate table, destroy it. */
if (table_for_fast_alter_partition)
close_temporary(table_for_fast_alter_partition, 1, 0);
#endif /* WITH_PARTITION_STORAGE_ENGINE */
/* /*
No default value was provided for a DATE/DATETIME field, the No default value was provided for a DATE/DATETIME field, the
current sql_mode doesn't allow the '0000-00-00' value and current sql_mode doesn't allow the '0000-00-00' value and
......
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