From 75f8e86f57f66b68e4a3b36188e24ba294764e25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 10 Sep 2018 14:59:58 +0300 Subject: [PATCH] MDEV-17158 TRUNCATE is not atomic after MDEV-13564 It turned out that ha_innobase::truncate() would prematurely commit the transaction already before the completion of the ha_innobase::create(). All of this must be atomic. innodb.truncate_crash: Use the correct DEBUG_SYNC point, and tolerate non-truncation of the table, because the redo log for the TRUNCATE transaction commit might be flushed due to some InnoDB background activity. dict_build_tablespace_for_table(): Merge to the function dict_build_table_def_step(). dict_build_table_def_step(): If a table is being created during an already started data dictionary transaction (such as TRUNCATE), persistently write the table_id to the undo log header before creating any file. In this way, the recovery of TRUNCATE will be able to delete the new file before rolling back the rename of the original table. dict_table_rename_in_cache(): Add the parameter replace_new_file, used as part of rolling back a TRUNCATE operation. fil_rename_tablespace_check(): Add the parameter replace_new. If the parameter is set and a file identified by new_path exists, remove a possible tablespace and also the file. create_table_info_t::create_table_def(): Remove some debug assertions that no longer hold. During TRUNCATE, the transaction will already have been started (and performed a rename operation) before the table is created. Also, remove a call to dict_build_tablespace_for_table(). create_table_info_t::create_table(): Add the parameter create_fk=true. During TRUNCATE TABLE, do not add FOREIGN KEY constraints to the InnoDB data dictionary, because they will also not be removed. row_table_add_foreign_constraints(): If trx=NULL, do not modify the InnoDB data dictionary, but only load the FOREIGN KEY constraints from the data dictionary. ha_innobase::create(): Lock the InnoDB data dictionary cache only if no transaction was passed by the caller. Unlock it in any case. innobase_rename_table(): Add the parameter commit = true. If !commit, do not lock or unlock the data dictionary cache. ha_innobase::truncate(): Lock the data dictionary before invoking rename or create, and let ha_innobase::create() unlock it and also commit or roll back the transaction. trx_undo_mark_as_dict(): Renamed from trx_undo_mark_as_dict_operation() and declared global instead of static. row_undo_ins_parse_undo_rec(): If table_id is set, this must be rolling back the rename operation in TRUNCATE TABLE, and therefore replace_new_file=true. --- .../suite/innodb/r/truncate_crash.result | 10 +- mysql-test/suite/innodb/t/truncate_crash.test | 7 +- storage/innobase/dict/dict0crea.cc | 83 ++++------- storage/innobase/dict/dict0dict.cc | 10 +- storage/innobase/fil/fil0fil.cc | 38 ++++- storage/innobase/handler/ha_innodb.cc | 135 ++++++++---------- storage/innobase/handler/ha_innodb.h | 5 +- storage/innobase/handler/handler0alter.cc | 4 +- storage/innobase/include/dict0crea.h | 9 -- storage/innobase/include/dict0dict.h | 6 +- storage/innobase/include/fil0fil.h | 4 +- storage/innobase/include/row0mysql.h | 9 +- storage/innobase/include/trx0undo.h | 5 + storage/innobase/row/row0mysql.cc | 31 ++-- storage/innobase/row/row0uins.cc | 3 +- storage/innobase/trx/trx0trx.cc | 4 +- storage/innobase/trx/trx0undo.cc | 22 ++- 17 files changed, 199 insertions(+), 186 deletions(-) diff --git a/mysql-test/suite/innodb/r/truncate_crash.result b/mysql-test/suite/innodb/r/truncate_crash.result index 6c20da46cf8..10ce8e92ab6 100644 --- a/mysql-test/suite/innodb/r/truncate_crash.result +++ b/mysql-test/suite/innodb/r/truncate_crash.result @@ -1,14 +1,14 @@ FLUSH TABLES; CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB; -INSERT INTO t1 SET a=1; +INSERT INTO t1 VALUES (1),(2); connect wait,localhost,root,,test; -SET DEBUG_SYNC='after_trx_committed_in_memory SIGNAL c WAIT_FOR ever'; +SET DEBUG_SYNC='before_trx_state_committed_in_memory SIGNAL c WAIT_FOR ever'; TRUNCATE TABLE t1; connection default; SET DEBUG_SYNC='now WAIT_FOR c'; disconnect wait; -SELECT * FROM t1; -a -1 +SELECT COUNT(*) FROM t1; +COUNT(*) +0 TRUNCATE TABLE t1; DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/truncate_crash.test b/mysql-test/suite/innodb/t/truncate_crash.test index 15ba475e0e1..5cb39c745dc 100644 --- a/mysql-test/suite/innodb/t/truncate_crash.test +++ b/mysql-test/suite/innodb/t/truncate_crash.test @@ -5,10 +5,10 @@ FLUSH TABLES; CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB; -INSERT INTO t1 SET a=1; +INSERT INTO t1 VALUES (1),(2); connect (wait,localhost,root,,test); -SET DEBUG_SYNC='after_trx_committed_in_memory SIGNAL c WAIT_FOR ever'; +SET DEBUG_SYNC='before_trx_state_committed_in_memory SIGNAL c WAIT_FOR ever'; send TRUNCATE TABLE t1; connection default; @@ -17,6 +17,7 @@ SET DEBUG_SYNC='now WAIT_FOR c'; --source include/restart_mysqld.inc disconnect wait; -SELECT * FROM t1; +--replace_result 2 0 +SELECT COUNT(*) FROM t1; TRUNCATE TABLE t1; DROP TABLE t1; diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index 91d979d4ee5..34cd627cdfd 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -38,6 +38,7 @@ Created 1/8/1996 Heikki Tuuri #include "row0mysql.h" #include "pars0pars.h" #include "trx0roll.h" +#include "trx0undo.h" #include "ut0vec.h" #include "dict0priv.h" #include "fts0priv.h" @@ -352,61 +353,43 @@ dict_build_table_def_step( tab_node_t* node) /*!< in: table create node */ { dict_table_t* table; - dtuple_t* row; - dberr_t err = DB_SUCCESS; table = node->table; + ut_ad(!dict_table_is_temporary(table)); trx_t* trx = thr_get_trx(thr); dict_table_assign_new_id(table, trx); - err = dict_build_tablespace_for_table(table, node); - - if (err != DB_SUCCESS) { - return(err); - } - - row = dict_create_sys_tables_tuple(table, node->heap); - - ins_node_set_new_row(node->tab_def, row); - - return(err); -} - -/** Builds a tablespace to contain a table, using file-per-table=1. -@param[in,out] table Table to build in its own tablespace. -@param[in] node Table create node -@return DB_SUCCESS or error code */ -dberr_t -dict_build_tablespace_for_table( - dict_table_t* table, - tab_node_t* node) -{ - dberr_t err = DB_SUCCESS; - mtr_t mtr; - ulint space = 0; - bool needs_file_per_table; - char* filepath; - ut_ad(mutex_own(&dict_sys->mutex)); - needs_file_per_table - = DICT_TF2_FLAG_IS_SET(table, DICT_TF2_USE_FILE_PER_TABLE); - /* Always set this bit for all new created tables */ DICT_TF2_FLAG_SET(table, DICT_TF2_FTS_AUX_HEX_NAME); DBUG_EXECUTE_IF("innodb_test_wrong_fts_aux_table_name", DICT_TF2_FLAG_UNSET(table, DICT_TF2_FTS_AUX_HEX_NAME);); - if (needs_file_per_table) { - ut_ad(!dict_table_is_temporary(table)); + if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_USE_FILE_PER_TABLE)) { /* This table will need a new tablespace. */ ut_ad(dict_table_get_format(table) <= UNIV_FORMAT_MAX); ut_ad(DICT_TF_GET_ZIP_SSIZE(table->flags) == 0 || dict_table_get_format(table) >= UNIV_FORMAT_B); - + ut_ad(trx->table_id); + mtr_t mtr; + trx_undo_t* undo = trx->rsegs.m_redo.insert_undo; + if (undo && !undo->table_id + && trx_get_dict_operation(trx) == TRX_DICT_OP_TABLE) { + /* This must be a TRUNCATE operation where + the empty table is created after the old table + was renamed. Be sure to mark the transaction + associated with the new empty table, so that + we can remove it on recovery. */ + mtr.start(); + trx_undo_mark_as_dict(trx, undo, &mtr); + mtr.commit(); + log_write_up_to(mtr.commit_lsn(), true); + } + ulint space; /* Get a new tablespace ID */ dict_hdr_get_new_id(NULL, NULL, &space, table, false); @@ -416,13 +399,14 @@ dict_build_tablespace_for_table( ); if (space == ULINT_UNDEFINED) { - return(DB_ERROR); + return DB_ERROR; } - table->space = static_cast(space); + table->space = unsigned(space); /* Determine the tablespace flags. */ bool has_data_dir = DICT_TF_HAS_DATA_DIR(table->flags); ulint fsp_flags = dict_tf_to_fsp_flags(table->flags); + char* filepath; if (has_data_dir) { ut_ad(table->data_dir_path); @@ -445,7 +429,7 @@ dict_build_tablespace_for_table( - page 3 will contain the root of the clustered index of the table we create here. */ - err = fil_ibd_create( + dberr_t err = fil_ibd_create( space, table->name.m_name, filepath, fsp_flags, FIL_IBD_FILE_INITIAL_SIZE, node ? node->mode : FIL_ENCRYPTION_DEFAULT, @@ -454,30 +438,25 @@ dict_build_tablespace_for_table( ut_free(filepath); if (err != DB_SUCCESS) { - - return(err); + return err; } - mtr_start(&mtr); + mtr.start(); mtr.set_named_space(table->space); fsp_header_init(table->space, FIL_IBD_FILE_INITIAL_SIZE, &mtr); - mtr_commit(&mtr); + mtr.commit(); } else { ut_ad(dict_tf_get_rec_format(table->flags) != REC_FORMAT_COMPRESSED); - if (dict_table_is_temporary(table)) { - table->space = SRV_TMP_SPACE_ID; - } else { - ut_ad(table->space == srv_sys_space.space_id()); - } - - DBUG_EXECUTE_IF("ib_ddl_crash_during_tablespace_alloc", - DBUG_SUICIDE();); + ut_ad(table->space == srv_sys_space.space_id()); } - return(DB_SUCCESS); + ins_node_set_new_row(node->tab_def, + dict_create_sys_tables_tuple(table, node->heap)); + + return DB_SUCCESS; } /***************************************************************//** diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 8f23bb4a54f..57cc98593ad 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -1581,9 +1581,14 @@ dict_table_rename_in_cache( /*=======================*/ dict_table_t* table, /*!< in/out: table */ const char* new_name, /*!< in: new name */ - ibool rename_also_foreigns)/*!< in: in ALTER TABLE we want + bool rename_also_foreigns, + /*!< in: in ALTER TABLE we want to preserve the original table name in constraints which reference it */ + bool replace_new_file) + /*!< in: whether to replace the + file with the new name + (as part of rolling back TRUNCATE) */ { dberr_t err; dict_foreign_t* foreign; @@ -1685,7 +1690,8 @@ dict_table_rename_in_cache( /* New filepath must not exist. */ err = fil_rename_tablespace_check( - table->space, old_path, new_path, false); + table->space, old_path, new_path, false, + replace_new_file); if (err != DB_SUCCESS) { ut_free(old_path); ut_free(new_path); diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index d42402b322d..28874154c16 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -3307,13 +3307,15 @@ if that the old filepath exists and the new filepath does not exist. @param[in] old_path old filepath @param[in] new_path new filepath @param[in] is_discarded whether the tablespace is discarded +@param[in] replace_new whether to ignore the existence of new_path @return innodb error code */ dberr_t fil_rename_tablespace_check( ulint space_id, const char* old_path, const char* new_path, - bool is_discarded) + bool is_discarded, + bool replace_new) { bool exists = false; os_file_type_t ftype; @@ -3330,7 +3332,11 @@ fil_rename_tablespace_check( } exists = false; - if (!os_file_status(new_path, &exists, &ftype) || exists) { + if (os_file_status(new_path, &exists, &ftype) && !exists) { + return DB_SUCCESS; + } + + if (!replace_new) { ib::error() << "Cannot rename '" << old_path << "' to '" << new_path << "' for space ID " << space_id @@ -3339,6 +3345,34 @@ fil_rename_tablespace_check( return(DB_TABLESPACE_EXISTS); } + /* This must be during the ROLLBACK of TRUNCATE TABLE. + Because InnoDB only allows at most one data dictionary + transaction at a time, and because this incomplete TRUNCATE + would have created a new tablespace file, we must remove + a possibly existing tablespace that is associated with the + new tablespace file. */ +retry: + mutex_enter(&fil_system->mutex); + for (fil_space_t* space = UT_LIST_GET_FIRST(fil_system->space_list); + space; space = UT_LIST_GET_NEXT(space_list, space)) { + ulint id = space->id; + if (id && id < SRV_LOG_SPACE_FIRST_ID + && space->purpose == FIL_TYPE_TABLESPACE + && !strcmp(new_path, + UT_LIST_GET_FIRST(space->chain)->name)) { + ib::info() << "TRUNCATE rollback: " << id + << "," << new_path; + mutex_exit(&fil_system->mutex); + dberr_t err = fil_delete_tablespace(id); + if (err != DB_SUCCESS) { + return err; + } + goto retry; + } + } + mutex_exit(&fil_system->mutex); + fil_delete_file(new_path); + return(DB_SUCCESS); } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index e29e1b0ed4b..725bbf86ca1 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -11013,8 +11013,6 @@ create_table_info_t::create_table_def() } } - ut_ad(trx_state_eq(m_trx, TRX_STATE_NOT_STARTED)); - /* Check whether there already exists a FTS_DOC_ID column */ if (create_table_check_doc_id_col(m_trx, m_form, &doc_id_col)){ @@ -11106,9 +11104,6 @@ create_table_info_t::create_table_def() mem_heap_free(heap); dict_mem_table_free(table); - ut_ad(trx_state_eq( - m_trx, TRX_STATE_NOT_STARTED)); - DBUG_RETURN(ER_CANT_CREATE_TABLE); } } @@ -11141,7 +11136,6 @@ create_table_info_t::create_table_def() err_col: dict_mem_table_free(table); mem_heap_free(heap); - ut_ad(trx_state_eq(m_trx, TRX_STATE_NOT_STARTED)); err = DB_ERROR; goto error_ret; @@ -11223,8 +11217,6 @@ err_col: fts_add_doc_id_column(table, heap); } - ut_ad(trx_state_eq(m_trx, TRX_STATE_NOT_STARTED)); - /* If temp table, then we avoid creation of entries in SYSTEM TABLES. Given that temp table lifetime is limited to connection/server lifetime on re-start we don't need to restore temp-table and so no entry is @@ -11232,25 +11224,18 @@ err_col: if (dict_table_is_temporary(table)) { /* Get a new table ID */ dict_table_assign_new_id(table, m_trx); + table->space = SRV_TMP_SPACE_ID; - /* Create temp tablespace if configured. */ - err = dict_build_tablespace_for_table(table, NULL); + /* Temp-table are maintained in memory and so + can_be_evicted is FALSE. */ + mem_heap_t* temp_table_heap = mem_heap_create(256); - if (err == DB_SUCCESS) { - /* Temp-table are maintained in memory and so - can_be_evicted is FALSE. */ - mem_heap_t* temp_table_heap; + dict_table_add_to_cache(table, FALSE, temp_table_heap); - temp_table_heap = mem_heap_create(256); + DBUG_EXECUTE_IF("ib_ddl_crash_during_create2", + DBUG_SUICIDE();); - dict_table_add_to_cache( - table, FALSE, temp_table_heap); - - DBUG_EXECUTE_IF("ib_ddl_crash_during_create2", - DBUG_SUICIDE();); - - mem_heap_free(temp_table_heap); - } + mem_heap_free(temp_table_heap); } else { if (err == DB_SUCCESS) { err = row_create_table_for_mysql( @@ -12538,10 +12523,9 @@ create_table_info_t::prepare_create_table( DBUG_RETURN(parse_table_name(name)); } -/** Create a new table to an InnoDB database. -@return error number */ -int -create_table_info_t::create_table() +/** Create the internal innodb table. +@param create_fk whether to add FOREIGN KEY constraints */ +int create_table_info_t::create_table(bool create_fk) { int error; int primary_key_no; @@ -12678,7 +12662,7 @@ create_table_info_t::create_table() if (stmt) { dberr_t err = row_table_add_foreign_constraints( - m_trx, stmt, stmt_len, m_table_name, + create_fk ? m_trx : NULL, stmt, stmt_len, m_table_name, m_create_info->options & HA_LEX_CREATE_TMP_TABLE); switch (err) { @@ -12866,56 +12850,56 @@ ha_innobase::create( remote_path, file_per_table, trx); - /* Initialize the object. */ - if ((error = info.initialize())) { + if ((error = info.initialize()) + || (error = info.prepare_create_table(name))) { + if (trx) { + trx_rollback_for_mysql(trx); + row_mysql_unlock_data_dictionary(trx); + } DBUG_RETURN(error); } - /* Prepare for create and validate options. */ - if ((error = info.prepare_create_table(name))) { - DBUG_RETURN(error); - } + const bool own_trx = !trx; - bool own_trx = !trx; if (own_trx) { info.allocate_trx(); trx = info.trx(); + /* Latch the InnoDB data dictionary exclusively so that no deadlocks + or lock waits can happen in it during a table create operation. + Drop table etc. do this latching in row0mysql.cc. */ + row_mysql_lock_data_dictionary(trx); + DBUG_ASSERT(trx_state_eq(trx, TRX_STATE_NOT_STARTED)); } - /* Latch the InnoDB data dictionary exclusively so that no deadlocks - or lock waits can happen in it during a table create operation. - Drop table etc. do this latching in row0mysql.cc. */ - row_mysql_lock_data_dictionary(trx); - - if ((error = info.create_table())) { - if (own_trx) { - trx_rollback_for_mysql(trx); - } + if ((error = info.create_table(own_trx))) { + trx_rollback_for_mysql(trx); row_mysql_unlock_data_dictionary(trx); - goto func_exit; + if (own_trx) { + trx_free_for_mysql(trx); + DBUG_RETURN(error); + } } + innobase_commit_low(trx); + row_mysql_unlock_data_dictionary(trx); + if (own_trx) { - innobase_commit_low(trx); + trx_free_for_mysql(trx); } - ut_ad(!srv_read_only_mode); - row_mysql_unlock_data_dictionary(trx); /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync if the user runs with innodb_flush_log_at_trx_commit = 0 */ log_buffer_flush_to_disk(); + ut_ad(!srv_read_only_mode); + error = info.create_table_update_dict(); /* Tell the InnoDB server that there might be work for utility threads: */ srv_active_wake_master_thread(); -func_exit: - if (own_trx) { - trx_free_for_mysql(trx); - } DBUG_RETURN(error); } @@ -13322,16 +13306,19 @@ innobase_drop_database( trx_free_for_mysql(trx); } -/*********************************************************************//** -Renames an InnoDB table. +/** Rename an InnoDB table. +@param[in,out] trx InnoDB data dictionary transaction +@param[in] from old table name +@param[in] to new table name +@param[in] commit whether to commit trx @return DB_SUCCESS or error code */ inline dberr_t innobase_rename_table( -/*==================*/ - trx_t* trx, /*!< in: transaction */ - const char* from, /*!< in: old name of the table */ - const char* to) /*!< in: new name of the table */ + trx_t* trx, + const char* from, + const char* to, + bool commit = true) { dberr_t error; char norm_to[FN_REFLEN]; @@ -13351,10 +13338,11 @@ innobase_rename_table( trx_start_if_not_started(trx, true); ut_ad(trx->will_lock > 0); - /* Serialize data dictionary operations with dictionary mutex: - no deadlocks can occur then in these operations. */ - - row_mysql_lock_data_dictionary(trx); + if (commit) { + /* Serialize data dictionary operations with dictionary mutex: + no deadlocks can occur then in these operations. */ + row_mysql_lock_data_dictionary(trx); + } dict_table_t* table = dict_table_open_on_name(norm_from, TRUE, FALSE, DICT_ERR_IGNORE_NONE); @@ -13382,8 +13370,7 @@ innobase_rename_table( /* FTS sync is in progress. We shall timeout this operation */ if (lock_wait_timeout < 0) { error = DB_LOCK_WAIT_TIMEOUT; - row_mysql_unlock_data_dictionary(trx); - DBUG_RETURN(error); + goto func_exit; } /* Transaction must be flagged as a locking transaction or it hasn't @@ -13391,7 +13378,7 @@ innobase_rename_table( ut_a(trx->will_lock > 0); - error = row_rename_table_for_mysql(norm_from, norm_to, trx, TRUE); + error = row_rename_table_for_mysql(norm_from, norm_to, trx, commit); if (error != DB_SUCCESS) { if (error == DB_TABLE_NOT_FOUND @@ -13440,7 +13427,10 @@ innobase_rename_table( } } - row_mysql_unlock_data_dictionary(trx); +func_exit: + if (commit) { + row_mysql_unlock_data_dictionary(trx); + } /* Flush the log to reduce probability that the .frm files and the InnoDB data dictionary get out-of-sync @@ -13492,19 +13482,18 @@ int ha_innobase::truncate() ++trx->will_lock; trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); + row_mysql_lock_data_dictionary(trx); int err = convert_error_code_to_mysql( - innobase_rename_table(trx, ib_table->name.m_name, temp_name), + innobase_rename_table(trx, ib_table->name.m_name, temp_name, false), ib_table->flags, m_user_thd); - if (!err) { + if (err) { + trx_rollback_for_mysql(trx); + row_mysql_unlock_data_dictionary(trx); + } else { err = create(name, table, &info, dict_table_is_file_per_table(ib_table), trx); } - if (err) { - trx_rollback_to_savepoint(trx, NULL); - } - - innobase_commit_low(trx); trx_free_for_mysql(trx); if (!err) { diff --git a/storage/innobase/handler/ha_innodb.h b/storage/innobase/handler/ha_innodb.h index 9ba7189a3bc..427f05a9a5c 100644 --- a/storage/innobase/handler/ha_innodb.h +++ b/storage/innobase/handler/ha_innodb.h @@ -671,8 +671,9 @@ public: /** Set m_tablespace_type. */ void set_tablespace_type(bool table_being_altered_is_file_per_table); - /** Create the internal innodb table. */ - int create_table(); + /** Create the internal innodb table. + @param create_fk whether to add FOREIGN KEY constraints */ + int create_table(bool create_fk = true); /** Update the internal data dictionary. */ int create_table_update_dict(); diff --git a/storage/innobase/handler/handler0alter.cc b/storage/innobase/handler/handler0alter.cc index 412438237fa..c55850d68cb 100644 --- a/storage/innobase/handler/handler0alter.cc +++ b/storage/innobase/handler/handler0alter.cc @@ -7927,11 +7927,11 @@ commit_cache_rebuild( /* We already committed and redo logged the renames, so this must succeed. */ error = dict_table_rename_in_cache( - ctx->old_table, ctx->tmp_name, FALSE); + ctx->old_table, ctx->tmp_name, false); ut_a(error == DB_SUCCESS); error = dict_table_rename_in_cache( - ctx->new_table, old_name, FALSE); + ctx->new_table, old_name, false); ut_a(error == DB_SUCCESS); DBUG_VOID_RETURN; diff --git a/storage/innobase/include/dict0crea.h b/storage/innobase/include/dict0crea.h index 12c78862261..e37ad66bc8c 100644 --- a/storage/innobase/include/dict0crea.h +++ b/storage/innobase/include/dict0crea.h @@ -68,15 +68,6 @@ dict_create_table_step( /*===================*/ que_thr_t* thr); /*!< in: query thread */ -/** Builds a tablespace to contain a table, using file-per-table=1. -@param[in,out] table Table to build in its own tablespace. -@param[in] node Table create node -@return DB_SUCCESS or error code */ -dberr_t -dict_build_tablespace_for_table( - dict_table_t* table, - tab_node_t* node); - /** Assign a new table ID and put it into the table cache and the transaction. @param[in,out] table Table that needs an ID @param[in,out] trx Transaction */ diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index b5f2b108959..bb4e15ea329 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -407,10 +407,14 @@ dict_table_rename_in_cache( /*=======================*/ dict_table_t* table, /*!< in/out: table */ const char* new_name, /*!< in: new name */ - ibool rename_also_foreigns) + bool rename_also_foreigns, /*!< in: in ALTER TABLE we want to preserve the original table name in constraints which reference it */ + bool replace_new_file = false) + /*!< in: whether to replace the + file with the new name + (as part of rolling back TRUNCATE) */ MY_ATTRIBUTE((nonnull)); /** Removes an index from the dictionary cache. diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index 1307598971b..c6b6ad92388 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -991,13 +991,15 @@ if that the old filepath exists and the new filepath does not exist. @param[in] old_path old filepath @param[in] new_path new filepath @param[in] is_discarded whether the tablespace is discarded +@param[in] replace_new whether to ignore the existence of new_path @return innodb error code */ dberr_t fil_rename_tablespace_check( ulint space_id, const char* old_path, const char* new_path, - bool is_discarded); + bool is_discarded, + bool replace_new = false); /** Rename a single-table tablespace. The tablespace must exist in the memory cache. diff --git a/storage/innobase/include/row0mysql.h b/storage/innobase/include/row0mysql.h index 3a53c35ba8b..50b0f75a689 100644 --- a/storage/innobase/include/row0mysql.h +++ b/storage/innobase/include/row0mysql.h @@ -386,7 +386,7 @@ Each foreign key constraint must be accompanied with indexes in bot participating tables. The indexes are allowed to contain more fields than mentioned in the constraint. -@param[in] trx transaction +@param[in] trx transaction (NULL if not adding to dictionary) @param[in] sql_string table create statement where foreign keys are declared like: FOREIGN KEY (a, b) REFERENCES table2(c, d), @@ -395,9 +395,8 @@ fields than mentioned in the constraint. database id the database of parameter name @param[in] sql_length length of sql_string @param[in] name table full name in normalized form -@param[in] reject_fks if TRUE, fail with error code - DB_CANNOT_ADD_CONSTRAINT if any - foreign keys are found. +@param[in] reject_fks whether to fail with DB_CANNOT_ADD_CONSTRAINT + if any foreign keys are found @return error code or DB_SUCCESS */ dberr_t row_table_add_foreign_constraints( @@ -405,7 +404,7 @@ row_table_add_foreign_constraints( const char* sql_string, size_t sql_length, const char* name, - ibool reject_fks) + bool reject_fks) MY_ATTRIBUTE((warn_unused_result)); /*********************************************************************//** diff --git a/storage/innobase/include/trx0undo.h b/storage/innobase/include/trx0undo.h index f738af4b454..0de4ef309a5 100644 --- a/storage/innobase/include/trx0undo.h +++ b/storage/innobase/include/trx0undo.h @@ -247,6 +247,11 @@ ulint trx_undo_lists_init( /*================*/ trx_rseg_t* rseg); /*!< in: rollback segment memory object */ +/** Mark that an undo log header belongs to a data dictionary transaction. +@param[in] trx dictionary transaction +@param[in,out] undo undo log +@param[in,out] mtr mini-transaction */ +void trx_undo_mark_as_dict(const trx_t* trx, trx_undo_t* undo, mtr_t* mtr); /** Assign an undo log for a transaction. A new undo log is created or a cached undo log reused. @param[in,out] trx transaction diff --git a/storage/innobase/row/row0mysql.cc b/storage/innobase/row/row0mysql.cc index 1460471b347..1491921bece 100644 --- a/storage/innobase/row/row0mysql.cc +++ b/storage/innobase/row/row0mysql.cc @@ -2527,7 +2527,7 @@ Each foreign key constraint must be accompanied with indexes in bot participating tables. The indexes are allowed to contain more fields than mentioned in the constraint. -@param[in] trx transaction +@param[in] trx transaction (NULL if not adding to dictionary) @param[in] sql_string table create statement where foreign keys are declared like: FOREIGN KEY (a, b) REFERENCES table2(c, d), @@ -2536,9 +2536,8 @@ fields than mentioned in the constraint. database id the database of parameter name @param[in] sql_length length of sql_string @param[in] name table full name in normalized form -@param[in] reject_fks if TRUE, fail with error code - DB_CANNOT_ADD_CONSTRAINT if any - foreign keys are found. +@param[in] reject_fks whether to fail with DB_CANNOT_ADD_CONSTRAINT + if any foreign keys are found @return error code or DB_SUCCESS */ dberr_t row_table_add_foreign_constraints( @@ -2546,7 +2545,7 @@ row_table_add_foreign_constraints( const char* sql_string, size_t sql_length, const char* name, - ibool reject_fks) + bool reject_fks) { dberr_t err; @@ -2556,19 +2555,23 @@ row_table_add_foreign_constraints( ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X)); ut_a(sql_string); - trx->op_info = "adding foreign keys"; + if (trx) { + trx->op_info = "adding foreign keys"; - trx_start_if_not_started_xa(trx, true); + trx_start_if_not_started_xa(trx, true); - trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); + trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); - err = dict_create_foreign_constraints( - trx, sql_string, sql_length, name, reject_fks); + err = dict_create_foreign_constraints( + trx, sql_string, sql_length, name, reject_fks); - DBUG_EXECUTE_IF("ib_table_add_foreign_fail", - err = DB_DUPLICATE_KEY;); + DBUG_EXECUTE_IF("ib_table_add_foreign_fail", + err = DB_DUPLICATE_KEY;); - DEBUG_SYNC_C("table_add_foreign_constraints"); + DEBUG_SYNC_C("table_add_foreign_constraints"); + } else { + err = DB_SUCCESS; + } if (err == DB_SUCCESS) { /* Check that also referencing constraints are ok */ @@ -2583,7 +2586,7 @@ row_table_add_foreign_constraints( } } - if (err != DB_SUCCESS) { + if (err != DB_SUCCESS && trx) { /* We have special error handling here */ trx->error_state = DB_SUCCESS; diff --git a/storage/innobase/row/row0uins.cc b/storage/innobase/row/row0uins.cc index 9b4a062b00f..22edb7faf89 100644 --- a/storage/innobase/row/row0uins.cc +++ b/storage/innobase/row/row0uins.cc @@ -363,7 +363,8 @@ row_undo_ins_parse_undo_rec( ptr[len] = 0; const char* name = reinterpret_cast(ptr); if (strcmp(table->name.m_name, name)) { - dict_table_rename_in_cache(table, name, false); + dict_table_rename_in_cache(table, name, false, + table_id != 0); } goto close_table; } diff --git a/storage/innobase/trx/trx0trx.cc b/storage/innobase/trx/trx0trx.cc index e4d9c0800c3..c3dd7e29c37 100644 --- a/storage/innobase/trx/trx0trx.cc +++ b/storage/innobase/trx/trx0trx.cc @@ -888,7 +888,9 @@ trx_resurrect_update( if (undo->dict_operation) { trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); - trx->table_id = undo->table_id; + if (!trx->table_id) { + trx->table_id = undo->table_id; + } } if (!undo->empty && undo->top_undo_no >= trx->undo_no) { diff --git a/storage/innobase/trx/trx0undo.cc b/storage/innobase/trx/trx0undo.cc index 2e069aa2937..33a38b915be 100644 --- a/storage/innobase/trx/trx0undo.cc +++ b/storage/innobase/trx/trx0undo.cc @@ -1526,20 +1526,16 @@ trx_undo_reuse_cached( return(undo); } -/**********************************************************************//** -Marks an undo log header as a header of a data dictionary operation -transaction. */ -static -void -trx_undo_mark_as_dict_operation( -/*============================*/ - trx_t* trx, /*!< in: dict op transaction */ - trx_undo_t* undo, /*!< in: assigned undo log */ - mtr_t* mtr) /*!< in: mtr */ +/** Mark that an undo log header belongs to a data dictionary transaction. +@param[in] trx dictionary transaction +@param[in,out] undo undo log +@param[in,out] mtr mini-transaction */ +void trx_undo_mark_as_dict(const trx_t* trx, trx_undo_t* undo, mtr_t* mtr) { - page_t* hdr_page; + ut_ad(undo == trx->rsegs.m_redo.insert_undo + || undo == trx->rsegs.m_redo.update_undo); - hdr_page = trx_undo_page_get( + page_t* hdr_page = trx_undo_page_get( page_id_t(undo->space, undo->hdr_page_no), mtr); switch (trx_get_dict_operation(trx)) { @@ -1627,7 +1623,7 @@ trx_undo_assign_undo( ? rseg->insert_undo_list : rseg->update_undo_list, *undo); if (trx_get_dict_operation(trx) != TRX_DICT_OP_NONE) { - trx_undo_mark_as_dict_operation(trx, *undo, &mtr); + trx_undo_mark_as_dict(trx, *undo, &mtr); } }