summaryrefslogtreecommitdiff
path: root/storage
diff options
context:
space:
mode:
authorAnnamalai Gurusami <annamalai.gurusami@oracle.com>2012-11-09 19:04:01 +0530
committerAnnamalai Gurusami <annamalai.gurusami@oracle.com>2012-11-09 19:04:01 +0530
commit2ad007dfd658e250c176e19ae5deabc845bf7ae3 (patch)
tree905675ce9bb8f291f1c7ee4e7c7cf172cd4df688 /storage
parent14dfe6fcc8d4e862484eb5c6e9561c28fbe421df (diff)
downloadmariadb-git-2ad007dfd658e250c176e19ae5deabc845bf7ae3.tar.gz
Bug #14669848 CRASH DURING ALTER MAKES ORIGINAL TABLE INACCESSIBLE
When a new primary key is added to an InnoDB table, then the following steps are taken by InnoDB plugin: . let t1 be the original table. . a temporary table t1@00231 will be created by cloning t1. . all data will be copied from t1 to t1@00231. . rename t1 to t1@00232. . rename t1@00231 to t1. . drop t1@00232. The rename and drop operations involve file operations. But file operations cannot be rolled back. So in row_merge_rename_tables(), just after doing data dictionary update and before doing any file operations, generate redo logs for file operations and commit the transaction. This will ensure that any crash after this commit, the table is still recoverable by moving .ibd and .frm files. Manual recovery is required. During recovery, the rename file operation redo logs are processed. Previously this was being ignored. rb://1460 approved by Marko Makela.
Diffstat (limited to 'storage')
-rw-r--r--storage/innodb_plugin/fil/fil0fil.c27
-rw-r--r--storage/innodb_plugin/include/fil0fil.h15
-rw-r--r--storage/innodb_plugin/log/log0recv.c15
-rw-r--r--storage/innodb_plugin/row/row0merge.c22
4 files changed, 74 insertions, 5 deletions
diff --git a/storage/innodb_plugin/fil/fil0fil.c b/storage/innodb_plugin/fil/fil0fil.c
index 6f2ab938042..d5770baafea 100644
--- a/storage/innodb_plugin/fil/fil0fil.c
+++ b/storage/innodb_plugin/fil/fil0fil.c
@@ -2619,7 +2619,7 @@ retry:
mutex_exit(&fil_system->mutex);
#ifndef UNIV_HOTBACKUP
- if (success) {
+ if (success && !recv_recovery_on) {
mtr_t mtr;
mtr_start(&mtr);
@@ -4853,3 +4853,28 @@ fil_close(void)
fil_system = NULL;
}
+
+/****************************************************************//**
+Generate redo logs for swapping two .ibd files */
+UNIV_INTERN
+void
+fil_mtr_rename_log(
+/*===============*/
+ ulint old_space_id, /*!< in: tablespace id of the old
+ table. */
+ const char* old_name, /*!< in: old table name */
+ ulint new_space_id, /*!< in: tablespace id of the new
+ table */
+ const char* new_name, /*!< in: new table name */
+ const char* tmp_name) /*!< in: temp table name used while
+ swapping */
+{
+ mtr_t mtr;
+ mtr_start(&mtr);
+ fil_op_write_log(MLOG_FILE_RENAME, old_space_id,
+ 0, 0, old_name, tmp_name, &mtr);
+ fil_op_write_log(MLOG_FILE_RENAME, new_space_id,
+ 0, 0, new_name, old_name, &mtr);
+ mtr_commit(&mtr);
+}
+
diff --git a/storage/innodb_plugin/include/fil0fil.h b/storage/innodb_plugin/include/fil0fil.h
index 05217168764..09e6e37b223 100644
--- a/storage/innodb_plugin/include/fil0fil.h
+++ b/storage/innodb_plugin/include/fil0fil.h
@@ -726,6 +726,21 @@ fil_tablespace_is_being_deleted(
/*============================*/
ulint id); /*!< in: space id */
+/****************************************************************//**
+Generate redo logs for swapping two .ibd files */
+UNIV_INTERN
+void
+fil_mtr_rename_log(
+/*===============*/
+ ulint old_space_id, /*!< in: tablespace id of the old
+ table. */
+ const char* old_name, /*!< in: old table name */
+ ulint new_space_id, /*!< in: tablespace id of the new
+ table */
+ const char* new_name, /*!< in: new table name */
+ const char* tmp_name); /*!< in: temp table name used while
+ swapping */
+
typedef struct fil_space_struct fil_space_t;
#endif
diff --git a/storage/innodb_plugin/log/log0recv.c b/storage/innodb_plugin/log/log0recv.c
index 677ada9fbde..96d63144366 100644
--- a/storage/innodb_plugin/log/log0recv.c
+++ b/storage/innodb_plugin/log/log0recv.c
@@ -955,8 +955,11 @@ recv_parse_or_apply_log_rec_body(
not NULL, then the log record is
applied to the page, and the log
record should be complete then */
- mtr_t* mtr) /*!< in: mtr or NULL; should be non-NULL
+ mtr_t* mtr, /*!< in: mtr or NULL; should be non-NULL
if and only if block is non-NULL */
+ ulint space_id)
+ /*!< in: tablespace id obtained by
+ parsing initial log record */
{
dict_index_t* index = NULL;
page_t* page;
@@ -1228,8 +1231,11 @@ recv_parse_or_apply_log_rec_body(
ut_ad(!page || page_type != FIL_PAGE_TYPE_ALLOCATED);
ptr = mlog_parse_string(ptr, end_ptr, page, page_zip);
break;
- case MLOG_FILE_CREATE:
case MLOG_FILE_RENAME:
+ ptr = fil_op_log_parse_or_replay(ptr, end_ptr, type,
+ space_id, 0);
+ break;
+ case MLOG_FILE_CREATE:
case MLOG_FILE_DELETE:
case MLOG_FILE_CREATE2:
ptr = fil_op_log_parse_or_replay(ptr, end_ptr, type, 0, 0);
@@ -1601,7 +1607,8 @@ recv_recover_page_func(
recv_parse_or_apply_log_rec_body(recv->type, buf,
buf + recv->len,
- block, &mtr);
+ block, &mtr,
+ recv_addr->space);
end_lsn = recv->start_lsn + recv->len;
mach_write_ull(FIL_PAGE_LSN + page, end_lsn);
@@ -2067,7 +2074,7 @@ recv_parse_log_rec(
#endif /* UNIV_LOG_LSN_DEBUG */
new_ptr = recv_parse_or_apply_log_rec_body(*type, new_ptr, end_ptr,
- NULL, NULL);
+ NULL, NULL, *space);
if (UNIV_UNLIKELY(new_ptr == NULL)) {
return(0);
diff --git a/storage/innodb_plugin/row/row0merge.c b/storage/innodb_plugin/row/row0merge.c
index 5da2a4b8534..342d81256f2 100644
--- a/storage/innodb_plugin/row/row0merge.c
+++ b/storage/innodb_plugin/row/row0merge.c
@@ -2418,6 +2418,28 @@ row_merge_rename_tables(
goto err_exit;
}
+ /* Generate the redo logs for file operations */
+ fil_mtr_rename_log(old_table->space, old_name,
+ new_table->space, new_table->name, tmp_name);
+
+ /* What if the redo logs are flushed to disk here? This is
+ tested with following crash point */
+ DBUG_EXECUTE_IF("bug14669848_precommit", log_buffer_flush_to_disk();
+ DBUG_SUICIDE(););
+
+ /* File operations cannot be rolled back. So, before proceeding
+ with file operations, commit the dictionary changes.*/
+ trx_commit_for_mysql(trx);
+
+ /* If server crashes here, the dictionary in InnoDB and MySQL
+ will differ. The .ibd files and the .frm files must be swapped
+ manually by the administrator. No loss of data. */
+ DBUG_EXECUTE_IF("bug14669848", DBUG_SUICIDE(););
+
+ /* Ensure that the redo logs are flushed to disk. The config
+ innodb_flush_log_at_trx_commit must not affect this. */
+ log_buffer_flush_to_disk();
+
/* The following calls will also rename the .ibd data files if
the tables are stored in a single-table tablespace */