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