/***************************************************************************** Copyright (c) 2017, 2019, Oracle and/or its affiliates. All Rights Reserved. Portions of this file contain modifications contributed and copyrighted by Google, Inc. Those modifications are gratefully acknowledged and are described briefly in the InnoDB documentation. The contributions by Google are incorporated with their permission, and subject to the conditions contained in the file COPYING.Google. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is also distributed with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have included with MySQL. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *****************************************************************************/ /** @file log/log0ddl.cc DDL log Created 12/1/2016 Shaohua Wang *******************************************************/ #include #include "ha_prototypes.h" #include #include #include "btr0sea.h" #include "dict0dd.h" #include "dict0mem.h" #include "dict0stats.h" #include "fil0purge.h" #include "ha_innodb.h" #include "log0ddl.h" #include "mysql/plugin.h" #include "pars0pars.h" #include "que0que.h" #include "row0ins.h" #include "row0row.h" #include "row0sel.h" #include "trx0trx.h" #include "lizard0data0types.h" #include "lizard0undo.h" #include "lizard0dict.h" /** Object to handle Log_DDL */ Log_DDL *log_ddl = nullptr; /** Whether replaying DDL log Note: we should not write DDL log when replaying DDL log. */ thread_local bool thread_local_ddl_log_replay = false; /** Whether in recover(replay) DDL log in startup. */ bool Log_DDL::s_in_recovery = false; #ifdef UNIV_DEBUG /** Used by SET GLOBAL innodb_ddl_log_crash_counter_reset_debug = 1; */ bool innodb_ddl_log_crash_reset_debug; /** Below counters are only used for four types of DDL log: 1. FREE TREE 2. DELETE SPACE 3. RENAME SPACE 4. DROP 5. PURGE FILE Other RENAME_TABLE and REMOVE CACHE doesn't touch the data files at all, so would be skipped */ /** Crash injection counter used before writing FREE TREE log */ static uint32_t crash_before_free_tree_log_counter = 1; /** Crash injection counter used after writing FREE TREE log */ static uint32_t crash_after_free_tree_log_counter = 1; /** Crash injection counter used after deleting FREE TREE log */ static uint32_t crash_after_free_tree_delete_counter = 1; /** Crash injection counter used before writing DELETE SPACE log */ static uint32_t crash_before_delete_space_log_counter = 1; /** Crash injection counter used after writing DELETE SPACE log */ static uint32_t crash_after_delete_space_log_counter = 1; /** Crash injection counter used after deleting DELETE SPACE log */ static uint32_t crash_after_delete_space_delete_counter = 1; /** Crash injection counter used before writing RENAME SPACE log */ static uint32_t crash_before_rename_space_log_counter = 1; /** Crash injection counter used after writing RENAME SPACE log */ static uint32_t crash_after_rename_space_log_counter = 1; /** Crash injection counter used after deleting RENAME SPACE log */ static uint32_t crash_after_rename_space_delete_counter = 1; /** Crash injection counter used before writing DROP log */ static uint32_t crash_before_drop_log_counter = 1; /** Crash injection counter used after writing DROP log */ static uint32_t crash_after_drop_log_counter = 1; /** Crash injection counter used after any replay */ static uint32_t crash_after_replay_counter = 1; /** Crash injection counter used before writing ALTER ENCRYPT TABLESPACE log */ static uint32_t crash_before_alter_encrypt_space_log_counter = 1; /** Crash injection counter used after writing ALTER ENCRYPT TABLESPACE log */ static uint32_t crash_after_alter_encrypt_space_log_counter = 1; /** Crash injection counter used before writing PURGE_FILE log */ static uint32_t crash_before_purge_file_log_counter = 1; /** Crash injection counter used after writing PURGE_FILE log */ static uint32_t crash_after_purge_file_log_counter = 1; void ddl_log_crash_reset(THD *thd, SYS_VAR *var, void *var_ptr, const void *save) { const bool reset = *static_cast(save); innodb_ddl_log_crash_reset_debug = reset; if (reset) { crash_before_free_tree_log_counter = 1; crash_after_free_tree_log_counter = 1; crash_after_free_tree_delete_counter = 1; crash_before_delete_space_log_counter = 1; crash_after_delete_space_log_counter = 1; crash_after_delete_space_delete_counter = 1; crash_before_rename_space_log_counter = 1; crash_after_rename_space_log_counter = 1; crash_after_rename_space_delete_counter = 1; crash_before_drop_log_counter = 1; crash_after_drop_log_counter = 1; crash_after_replay_counter = 1; crash_before_purge_file_log_counter = 1; crash_after_purge_file_log_counter = 1; } } #endif /* UNIV_DEBUG */ DDL_Record::DDL_Record() : m_id(ULINT_UNDEFINED), m_thread_id(ULINT_UNDEFINED), m_space_id(SPACE_UNKNOWN), m_page_no(FIL_NULL), m_index_id(ULINT_UNDEFINED), m_table_id(ULINT_UNDEFINED), m_old_file_path(nullptr), m_new_file_path(nullptr), m_heap(nullptr), m_deletable(true), m_removable(true) {} DDL_Record::~DDL_Record() { if (m_heap != nullptr) { mem_heap_free(m_heap); } } void DDL_Record::set_old_file_path(const char *name) { ulint len = strlen(name); if (m_heap == nullptr) { m_heap = mem_heap_create(FN_REFLEN + 1); } m_old_file_path = mem_heap_strdupl(m_heap, name, len); } void DDL_Record::set_old_file_path(const byte *data, ulint len) { if (m_heap == nullptr) { m_heap = mem_heap_create(FN_REFLEN + 1); } m_old_file_path = static_cast(mem_heap_dup(m_heap, data, len + 1)); m_old_file_path[len] = '\0'; } void DDL_Record::set_new_file_path(const char *name) { ulint len = strlen(name); if (m_heap == nullptr) { m_heap = mem_heap_create(FN_REFLEN + 1); } m_new_file_path = mem_heap_strdupl(m_heap, name, len); } void DDL_Record::set_new_file_path(const byte *data, ulint len) { if (m_heap == nullptr) { m_heap = mem_heap_create(FN_REFLEN + 1); } m_new_file_path = static_cast(mem_heap_dup(m_heap, data, len + 1)); m_new_file_path[len] = '\0'; } std::ostream &DDL_Record::print(std::ostream &out) const { ut_ad(m_type >= Log_Type::SMALLEST_LOG); ut_ad(m_type <= Log_Type::BIGGEST_LOG); bool printed = false; out << "[DDL record: "; switch (m_type) { case Log_Type::FREE_TREE_LOG: out << "FREE"; break; case Log_Type::DELETE_SPACE_LOG: out << "DELETE SPACE"; break; case Log_Type::RENAME_SPACE_LOG: out << "RENAME SPACE"; break; case Log_Type::DROP_LOG: out << "DROP"; break; case Log_Type::RENAME_TABLE_LOG: out << "RENAME TABLE"; break; case Log_Type::REMOVE_CACHE_LOG: out << "REMOVE CACHE"; break; case Log_Type::ALTER_ENCRYPT_TABLESPACE_LOG: out << "ALTER ENCRYPT TABLESPACE"; break; default: ut_ad(0); } out << ","; if (m_id != ULINT_UNDEFINED) { out << " id=" << m_id; printed = true; } if (m_thread_id != ULINT_UNDEFINED) { if (printed) { out << ","; } out << " thread_id=" << m_thread_id; printed = true; } if (m_space_id != SPACE_UNKNOWN) { if (printed) { out << ","; } out << " space_id=" << m_space_id; printed = true; } if (m_table_id != ULINT_UNDEFINED) { if (printed) { out << ","; } out << " table_id=" << m_table_id; printed = true; } if (m_index_id != ULINT_UNDEFINED) { if (printed) { out << ","; } out << " index_id=" << m_index_id; printed = true; } if (m_page_no != FIL_NULL) { if (printed) { out << ","; } out << " page_no=" << m_page_no; printed = true; } if (m_old_file_path != nullptr) { if (printed) { out << ","; } out << " old_file_path=" << m_old_file_path; printed = true; } if (m_new_file_path != nullptr) { if (printed) { out << ","; } out << " new_file_path=" << m_new_file_path; } out << "]"; return (out); } /** Display a DDL record @param[in,out] o output stream @param[in] record DDL record to display @return the output stream */ std::ostream &operator<<(std::ostream &o, const DDL_Record &record) { return (record.print(o)); } DDL_Log_Table::DDL_Log_Table() : DDL_Log_Table(nullptr) {} DDL_Log_Table::DDL_Log_Table(trx_t *trx) : m_table(dict_sys->ddl_log), m_tuple(nullptr), m_trx(trx), m_thr(nullptr) { ut_ad(m_trx == nullptr || m_trx->ddl_operation); m_heap = mem_heap_create(1000); if (m_trx != nullptr) { start_query_thread(); } } DDL_Log_Table::~DDL_Log_Table() { stop_query_thread(); mem_heap_free(m_heap); } void DDL_Log_Table::start_query_thread() { que_t *graph = static_cast(que_node_get_parent( pars_complete_graph_for_exec(NULL, m_trx, m_heap, NULL))); m_thr = que_fork_start_command(graph); ut_ad(m_trx->lock.n_active_thrs == 1); } void DDL_Log_Table::stop_query_thread() { if (m_thr != nullptr) { que_thr_stop_for_mysql_no_error(m_thr, m_trx); } } void DDL_Log_Table::create_tuple(const DDL_Record &record) { const dict_col_t *col; dfield_t *dfield; byte *buf; byte *undo_buf; byte *scn_buf; byte *gcn_buf; m_tuple = dtuple_create(m_heap, m_table->get_n_cols()); dict_table_copy_types(m_tuple, m_table); buf = static_cast(mem_heap_alloc(m_heap, 8)); memset(buf, 0xFF, 8); col = m_table->get_sys_col(DATA_ROW_ID); dfield = dtuple_get_nth_field(m_tuple, dict_col_get_no(col)); dfield_set_data(dfield, buf, DATA_ROW_ID_LEN); col = m_table->get_sys_col(DATA_ROLL_PTR); dfield = dtuple_get_nth_field(m_tuple, dict_col_get_no(col)); dfield_set_data(dfield, buf, DATA_ROLL_PTR_LEN); buf = static_cast(mem_heap_alloc(m_heap, DATA_TRX_ID_LEN)); mach_write_to_6(buf, m_trx->id); col = m_table->get_sys_col(DATA_TRX_ID); dfield = dtuple_get_nth_field(m_tuple, dict_col_get_no(col)); dfield_set_data(dfield, buf, DATA_TRX_ID_LEN); /** SCN_LOG_DDL and UNDO_PTR_LOG_DDL are only for a short time, and the right SCN and UBA are set straight away */ scn_buf = static_cast(mem_heap_alloc(m_heap, DATA_SCN_ID_LEN)); lizard::trx_write_scn(scn_buf, lizard::TXN_DESC_LD.cmmt.scn); col = m_table->get_sys_col(DATA_SCN_ID); dfield = dtuple_get_nth_field(m_tuple, dict_col_get_no(col)); dfield_set_data(dfield, scn_buf, DATA_SCN_ID_LEN); /** Fill UNDO_PTR */ undo_buf = static_cast(mem_heap_alloc(m_heap, DATA_UNDO_PTR_LEN)); lizard::trx_write_undo_ptr(undo_buf, lizard::TXN_DESC_LD.undo_ptr); col = m_table->get_sys_col(DATA_UNDO_PTR); dfield = dtuple_get_nth_field(m_tuple, dict_col_get_no(col)); dfield_set_data(dfield, undo_buf, DATA_UNDO_PTR_LEN); /** Fill GCN */ gcn_buf = static_cast(mem_heap_alloc(m_heap, DATA_GCN_ID_LEN)); lizard::trx_write_gcn(gcn_buf, lizard::TXN_DESC_LD.cmmt.gcn); col = m_table->get_sys_col(DATA_GCN_ID); dfield = dtuple_get_nth_field(m_tuple, dict_col_get_no(col)); dfield_set_data(dfield, gcn_buf, DATA_GCN_ID_LEN); const ulint rec_id = record.get_id(); if (rec_id != ULINT_UNDEFINED) { buf = static_cast(mem_heap_alloc(m_heap, s_id_col_len)); mach_write_to_8(buf, rec_id); dfield = dtuple_get_nth_field(m_tuple, s_id_col_no); dfield_set_data(dfield, buf, s_id_col_len); } if (record.get_thread_id() != ULINT_UNDEFINED) { buf = static_cast(mem_heap_alloc(m_heap, s_thread_id_col_len)); mach_write_to_8(buf, record.get_thread_id()); dfield = dtuple_get_nth_field(m_tuple, s_thread_id_col_no); dfield_set_data(dfield, buf, s_thread_id_col_len); } ut_ad(record.get_type() >= Log_Type::SMALLEST_LOG); ut_ad(record.get_type() <= Log_Type::BIGGEST_LOG); buf = static_cast(mem_heap_alloc(m_heap, s_type_col_len)); mach_write_to_4(buf, static_cast::type>( record.get_type())); dfield = dtuple_get_nth_field(m_tuple, s_type_col_no); dfield_set_data(dfield, buf, s_type_col_len); if (record.get_space_id() != SPACE_UNKNOWN) { buf = static_cast(mem_heap_alloc(m_heap, s_space_id_col_len)); mach_write_to_4(buf, record.get_space_id()); dfield = dtuple_get_nth_field(m_tuple, s_space_id_col_no); dfield_set_data(dfield, buf, s_space_id_col_len); } if (record.get_page_no() != FIL_NULL) { buf = static_cast(mem_heap_alloc(m_heap, s_page_no_col_len)); mach_write_to_4(buf, record.get_page_no()); dfield = dtuple_get_nth_field(m_tuple, s_page_no_col_no); dfield_set_data(dfield, buf, s_page_no_col_len); } if (record.get_index_id() != ULINT_UNDEFINED) { buf = static_cast(mem_heap_alloc(m_heap, s_index_id_col_len)); mach_write_to_8(buf, record.get_index_id()); dfield = dtuple_get_nth_field(m_tuple, s_index_id_col_no); dfield_set_data(dfield, buf, s_index_id_col_len); } if (record.get_table_id() != ULINT_UNDEFINED) { buf = static_cast(mem_heap_alloc(m_heap, s_table_id_col_len)); mach_write_to_8(buf, record.get_table_id()); dfield = dtuple_get_nth_field(m_tuple, s_table_id_col_no); dfield_set_data(dfield, buf, s_table_id_col_len); } if (record.get_old_file_path() != nullptr) { ulint m_len = strlen(record.get_old_file_path()) + 1; dfield = dtuple_get_nth_field(m_tuple, s_old_file_path_col_no); dfield_set_data(dfield, record.get_old_file_path(), m_len); } if (record.get_new_file_path() != nullptr) { ulint m_len = strlen(record.get_new_file_path()) + 1; dfield = dtuple_get_nth_field(m_tuple, s_new_file_path_col_no); dfield_set_data(dfield, record.get_new_file_path(), m_len); } } void DDL_Log_Table::create_tuple(ulint id, const dict_index_t *index) { ut_ad(id != ULINT_UNDEFINED); dfield_t *dfield; ulint len; ulint table_col_offset; ulint index_col_offset; m_tuple = dtuple_create(m_heap, 1); dict_index_copy_types(m_tuple, index, 1); if (index->is_clustered()) { len = s_id_col_len; table_col_offset = s_id_col_no; } else { len = s_thread_id_col_len; table_col_offset = s_thread_id_col_no; } index_col_offset = index->get_col_pos(table_col_offset); byte *buf = static_cast(mem_heap_alloc(m_heap, len)); mach_write_to_8(buf, id); dfield = dtuple_get_nth_field(m_tuple, index_col_offset); dfield_set_data(dfield, buf, len); } dberr_t DDL_Log_Table::insert(const DDL_Record &record) { dberr_t error; dict_index_t *index = m_table->first_index(); dtuple_t *entry; ulint flags = BTR_NO_LOCKING_FLAG; mem_heap_t *offsets_heap = mem_heap_create(1000); static std::atomic count(0); if (count++ % 64 == 0) { log_free_check(); } assert_lizard_dict_index_check(index); assert_lizard_dict_table_check(index->table); create_tuple(record); entry = row_build_index_entry(m_tuple, NULL, index, m_heap); error = row_ins_clust_index_entry_low(flags, BTR_MODIFY_LEAF, index, index->n_uniq, entry, 0, m_thr, false); if (error == DB_FAIL) { error = row_ins_clust_index_entry_low( flags, BTR_MODIFY_TREE, index, index->n_uniq, entry, 0, m_thr, false); ut_ad(error == DB_SUCCESS); } index = index->next(); entry = row_build_index_entry(m_tuple, NULL, index, m_heap); error = row_ins_sec_index_entry_low(flags, BTR_MODIFY_LEAF, index, offsets_heap, m_heap, entry, m_trx->id, m_thr, false); if (error == DB_FAIL) { error = row_ins_sec_index_entry_low(flags, BTR_MODIFY_TREE, index, offsets_heap, m_heap, entry, m_trx->id, m_thr, false); } mem_heap_free(offsets_heap); ut_ad(error == DB_SUCCESS); return (error); } void DDL_Log_Table::convert_to_ddl_record(bool is_clustered, rec_t *rec, const ulint *offsets, DDL_Record &record) { if (is_clustered) { for (ulint i = 0; i < rec_offs_n_fields(offsets); i++) { const byte *data; ulint len; if (i == DATA_ROLL_PTR || i == DATA_TRX_ID || i == DATA_SCN_ID || i == DATA_UNDO_PTR || i == DATA_GCN_ID) { continue; } data = rec_get_nth_field(rec, offsets, i, &len); if (len != UNIV_SQL_NULL) { set_field(data, i, len, record); } } } else { /* For secondary index, only the ID would be stored */ record.set_id(parse_id(m_table->first_index()->next(), rec, offsets)); } } ulint DDL_Log_Table::parse_id(const dict_index_t *index, rec_t *rec, const ulint *offsets) { ulint len; ulint index_offset = index->get_col_pos(s_id_col_no); const byte *data = rec_get_nth_field(rec, offsets, index_offset, &len); ut_ad(len == s_id_col_len); return (mach_read_from_8(data)); } void DDL_Log_Table::set_field(const byte *data, ulint index_offset, ulint len, DDL_Record &record) { dict_index_t *index = dict_sys->ddl_log->first_index(); ulint col_offset = index->get_col_no(index_offset); if (col_offset == s_new_file_path_col_no) { record.set_new_file_path(data, len); return; } if (col_offset == s_old_file_path_col_no) { record.set_old_file_path(data, len); return; } ulint value = fetch_value(data, col_offset); switch (col_offset) { case s_id_col_no: record.set_id(value); break; case s_thread_id_col_no: record.set_thread_id(value); break; case s_type_col_no: record.set_type(static_cast(value)); break; case s_space_id_col_no: record.set_space_id(static_cast(value)); break; case s_page_no_col_no: record.set_page_no(static_cast(value)); break; case s_index_id_col_no: record.set_index_id(value); break; case s_table_id_col_no: record.set_table_id(value); break; case s_old_file_path_col_no: case s_new_file_path_col_no: default: ut_ad(0); } } ulint DDL_Log_Table::fetch_value(const byte *data, ulint offset) { ulint value = 0; switch (offset) { case s_id_col_no: case s_thread_id_col_no: case s_index_id_col_no: case s_table_id_col_no: value = mach_read_from_8(data); return (value); case s_type_col_no: case s_space_id_col_no: case s_page_no_col_no: value = mach_read_from_4(data); return (value); case s_new_file_path_col_no: case s_old_file_path_col_no: default: ut_ad(0); break; } return (value); } dberr_t DDL_Log_Table::search_all(DDL_Records &records) { mtr_t mtr; btr_pcur_t pcur; rec_t *rec; bool move = true; ulint *offsets; dict_index_t *index = m_table->first_index(); dberr_t error = DB_SUCCESS; mtr_start(&mtr); /** Scan the index in decreasing order. */ btr_pcur_open_at_index_side(false, index, BTR_SEARCH_LEAF, &pcur, true, 0, &mtr); for (; move == true; move = btr_pcur_move_to_prev(&pcur, &mtr)) { rec = btr_pcur_get_rec(&pcur); if (page_rec_is_infimum(rec) || page_rec_is_supremum(rec)) { continue; } offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &m_heap); if (rec_get_deleted_flag(rec, dict_table_is_comp(m_table))) { continue; } DDL_Record *record = UT_NEW_NOKEY(DDL_Record()); convert_to_ddl_record(index->is_clustered(), rec, offsets, *record); records.push_back(record); } btr_pcur_close(&pcur); mtr_commit(&mtr); return (error); } dberr_t DDL_Log_Table::search(ulint thread_id, DDL_Records &records) { dberr_t error; DDL_Records records_of_thread_id; error = search_by_id(thread_id, m_table->first_index()->next(), records_of_thread_id); ut_ad(error == DB_SUCCESS); for (auto it = records_of_thread_id.rbegin(); it != records_of_thread_id.rend(); ++it) { error = search_by_id((*it)->get_id(), m_table->first_index(), records); ut_ad(error == DB_SUCCESS); } for (auto record : records_of_thread_id) { UT_DELETE(record); } return (error); } dberr_t DDL_Log_Table::search_by_id(ulint id, dict_index_t *index, DDL_Records &records) { mtr_t mtr; btr_pcur_t pcur; rec_t *rec; bool move = true; ulint *offsets; dberr_t error = DB_SUCCESS; mtr_start(&mtr); create_tuple(id, index); btr_pcur_open_with_no_init(index, m_tuple, PAGE_CUR_GE, BTR_SEARCH_LEAF, &pcur, 0, &mtr); for (; move == true; move = btr_pcur_move_to_next(&pcur, &mtr)) { rec = btr_pcur_get_rec(&pcur); if (page_rec_is_infimum(rec) || page_rec_is_supremum(rec)) { continue; } offsets = rec_get_offsets(rec, index, NULL, ULINT_UNDEFINED, &m_heap); if (cmp_dtuple_rec(m_tuple, rec, index, offsets) != 0) { break; } if (rec_get_deleted_flag(rec, dict_table_is_comp(m_table))) { continue; } DDL_Record *record = UT_NEW_NOKEY(DDL_Record()); convert_to_ddl_record(index->is_clustered(), rec, offsets, *record); records.push_back(record); } mtr_commit(&mtr); return (error); } dberr_t DDL_Log_Table::remove(ulint id) { mtr_t mtr; dict_index_t *clust_index = m_table->first_index(); btr_pcur_t pcur; ulint *offsets; rec_t *rec; dict_index_t *index; dtuple_t *row; btr_cur_t *btr_cur; dtuple_t *entry; dberr_t err = DB_SUCCESS; enum row_search_result search_result; ulint flags = BTR_NO_LOCKING_FLAG; static uint64_t count = 0; if (count++ % 64 == 0) { log_free_check(); } create_tuple(id, clust_index); mtr_start(&mtr); btr_pcur_open(clust_index, m_tuple, PAGE_CUR_LE, BTR_MODIFY_TREE | BTR_LATCH_FOR_DELETE, &pcur, &mtr); btr_cur = btr_pcur_get_btr_cur(&pcur); if (page_rec_is_infimum(btr_pcur_get_rec(&pcur)) || btr_pcur_get_low_match(&pcur) < clust_index->n_uniq) { btr_pcur_close(&pcur); mtr_commit(&mtr); return (DB_SUCCESS); } offsets = rec_get_offsets(btr_pcur_get_rec(&pcur), clust_index, NULL, ULINT_UNDEFINED, &m_heap); row = row_build(ROW_COPY_DATA, clust_index, btr_pcur_get_rec(&pcur), offsets, NULL, NULL, NULL, NULL, m_heap); rec = btr_cur_get_rec(btr_cur); if (!rec_get_deleted_flag(rec, dict_table_is_comp(m_table))) { err = btr_cur_del_mark_set_clust_rec(flags, btr_cur_get_block(btr_cur), rec, clust_index, offsets, m_thr, m_tuple, &mtr); } btr_pcur_close(&pcur); mtr_commit(&mtr); if (err != DB_SUCCESS) { return (err); } mtr_start(&mtr); index = clust_index->next(); entry = row_build_index_entry(row, NULL, index, m_heap); search_result = row_search_index_entry( index, entry, BTR_MODIFY_LEAF | BTR_DELETE_MARK, &pcur, &mtr); btr_cur = btr_pcur_get_btr_cur(&pcur); if (search_result == ROW_NOT_FOUND) { btr_pcur_close(&pcur); mtr_commit(&mtr); ut_ad(0); return (DB_CORRUPTION); } rec = btr_cur_get_rec(btr_cur); if (!rec_get_deleted_flag(rec, dict_table_is_comp(m_table))) { err = btr_cur_del_mark_set_sec_rec(flags, btr_cur, TRUE, m_thr, &mtr); } btr_pcur_close(&pcur); mtr_commit(&mtr); return (err); } dberr_t DDL_Log_Table::remove(const DDL_Records &records) { dberr_t ret = DB_SUCCESS; for (auto record : records) { if (record->get_deletable() && record->get_removable()) { dberr_t err = remove(record->get_id()); ut_ad(err == DB_SUCCESS || err == DB_TOO_MANY_CONCURRENT_TRXS); if (err != DB_SUCCESS) { ret = err; } } } return (ret); } Log_DDL::Log_DDL() { ut_ad(dict_sys->ddl_log != NULL); ut_ad(dict_table_has_autoinc_col(dict_sys->ddl_log)); } inline uint64_t Log_DDL::next_id() { uint64_t autoinc; dict_table_autoinc_lock(dict_sys->ddl_log); autoinc = dict_table_autoinc_read(dict_sys->ddl_log); ++autoinc; dict_table_autoinc_update_if_greater(dict_sys->ddl_log, autoinc); dict_table_autoinc_unlock(dict_sys->ddl_log); return (autoinc); } inline bool Log_DDL::skip(const dict_table_t *table, THD *thd) { return (recv_recovery_on || thread_local_ddl_log_replay || (table != nullptr && table->is_temporary()) || thd_is_bootstrap_thread(thd)); } dberr_t Log_DDL::write_free_tree_log(trx_t *trx, const dict_index_t *index, bool is_drop_table) { ut_ad(trx == thd_to_trx(current_thd)); if (skip(index->table, trx->mysql_thd)) { return (DB_SUCCESS); } if (index->type & DICT_FTS) { ut_ad(index->page == FIL_NULL); return (DB_SUCCESS); } if (dict_index_get_online_status(index) != ONLINE_INDEX_COMPLETE) { /* To skip any previously aborted index. This is because this kind of index should be already freed in previous post_ddl. It's inproper to log it and may free it again later, which may trigger some double free page problem. */ return (DB_SUCCESS); } uint64_t id = next_id(); ulint thread_id = thd_get_thread_id(trx->mysql_thd); dberr_t err; trx->ddl_operation = true; DBUG_INJECT_CRASH("ddl_log_crash_before_free_tree_log", crash_before_free_tree_log_counter++); if (is_drop_table) { /* Drop index case, if committed, will be redo only */ err = insert_free_tree_log(trx, index, id, thread_id); ut_ad(err == DB_SUCCESS); DBUG_INJECT_CRASH("ddl_log_crash_after_free_tree_log", crash_after_free_tree_log_counter++); } else { /* This is the case of building index during create table scenario. The index will be dropped if ddl is rolled back */ err = insert_free_tree_log(nullptr, index, id, thread_id); ut_ad(err == DB_SUCCESS); DBUG_INJECT_CRASH("ddl_log_crash_after_free_tree_log", crash_after_free_tree_log_counter++); DBUG_EXECUTE_IF("DDL_Log_remove_inject_error_1", srv_inject_too_many_concurrent_trxs = true;); /* Delete this operation if the create trx is committed */ err = delete_by_id(trx, id, false); ut_ad(err == DB_SUCCESS || err == DB_TOO_MANY_CONCURRENT_TRXS); DBUG_EXECUTE_IF("DDL_Log_remove_inject_error_1", srv_inject_too_many_concurrent_trxs = false;); DBUG_INJECT_CRASH("ddl_log_crash_after_free_tree_delete", crash_after_free_tree_delete_counter++); } return (err); } dberr_t Log_DDL::insert_free_tree_log(trx_t *trx, const dict_index_t *index, uint64_t id, ulint thread_id) { ut_ad(index->page != FIL_NULL); dberr_t error; bool has_dd_trx = (trx != nullptr); if (!has_dd_trx) { trx = trx_allocate_for_background(); trx_start_internal(trx); trx->ddl_operation = true; } else { trx_start_if_not_started(trx, true); } ut_ad(trx->ddl_operation); DDL_Record record; record.set_id(id); record.set_thread_id(thread_id); record.set_type(Log_Type::FREE_TREE_LOG); record.set_space_id(index->space); record.set_page_no(index->page); record.set_index_id(index->id); { DDL_Log_Table ddl_log(trx); error = ddl_log.insert(record); ut_ad(error == DB_SUCCESS); } if (!has_dd_trx) { trx_commit_for_mysql(trx); trx_free_for_background(trx); } if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_647) << "DDL log insert : " << record; } return (error); } dberr_t Log_DDL::write_delete_space_log(trx_t *trx, const dict_table_t *table, space_id_t space_id, const char *file_path, bool is_drop, bool dict_locked) { ut_ad(trx == thd_to_trx(current_thd)); ut_ad(table == nullptr || dict_table_is_file_per_table(table)); if (skip(table, trx->mysql_thd)) { return (DB_SUCCESS); } uint64_t id = next_id(); ulint thread_id = thd_get_thread_id(trx->mysql_thd); dberr_t err; trx->ddl_operation = true; DBUG_INJECT_CRASH("ddl_log_crash_before_delete_space_log", crash_before_delete_space_log_counter++); if (is_drop) { err = insert_delete_space_log(trx, id, thread_id, space_id, file_path, dict_locked); ut_ad(err == DB_SUCCESS); DBUG_INJECT_CRASH("ddl_log_crash_after_delete_space_log", crash_after_delete_space_log_counter++); } else { err = insert_delete_space_log(nullptr, id, thread_id, space_id, file_path, dict_locked); ut_ad(err == DB_SUCCESS); DBUG_INJECT_CRASH("ddl_log_crash_after_delete_space_log", crash_after_delete_space_log_counter++); DBUG_EXECUTE_IF("DDL_Log_remove_inject_error_2", srv_inject_too_many_concurrent_trxs = true;); err = delete_by_id(trx, id, dict_locked); ut_ad(err == DB_SUCCESS || err == DB_TOO_MANY_CONCURRENT_TRXS); DBUG_EXECUTE_IF("DDL_Log_remove_inject_error_2", srv_inject_too_many_concurrent_trxs = false;); DBUG_INJECT_CRASH("ddl_log_crash_after_delete_space_delete", crash_after_delete_space_delete_counter++); } return (err); } dberr_t Log_DDL::insert_delete_space_log(trx_t *trx, uint64_t id, ulint thread_id, space_id_t space_id, const char *file_path, bool dict_locked) { dberr_t error; bool has_dd_trx = (trx != nullptr); if (!has_dd_trx) { trx = trx_allocate_for_background(); trx_start_internal(trx); trx->ddl_operation = true; } else { trx_start_if_not_started(trx, true); } ut_ad(trx->ddl_operation); if (dict_locked) { mutex_exit(&dict_sys->mutex); } DDL_Record record; record.set_id(id); record.set_thread_id(thread_id); record.set_type(Log_Type::DELETE_SPACE_LOG); record.set_space_id(space_id); record.set_old_file_path(file_path); { DDL_Log_Table ddl_log(trx); error = ddl_log.insert(record); ut_ad(error == DB_SUCCESS); } if (dict_locked) { mutex_enter(&dict_sys->mutex); } if (!has_dd_trx) { trx_commit_for_mysql(trx); trx_free_for_background(trx); } if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_648) << "DDL log insert : " << record; } return (error); } dberr_t Log_DDL::write_rename_space_log(space_id_t space_id, const char *old_file_path, const char *new_file_path) { /* Missing current_thd, it happens during crash recovery */ if (!current_thd) { return (DB_SUCCESS); } trx_t *trx = thd_to_trx(current_thd); /* This is special case for fil_rename_tablespace during recovery */ if (trx == nullptr) { return (DB_SUCCESS); } if (skip(nullptr, trx->mysql_thd)) { return (DB_SUCCESS); } uint64_t id = next_id(); ulint thread_id = thd_get_thread_id(trx->mysql_thd); trx->ddl_operation = true; DBUG_INJECT_CRASH("ddl_log_crash_before_rename_space_log", crash_before_rename_space_log_counter++); dberr_t err = insert_rename_space_log(id, thread_id, space_id, old_file_path, new_file_path); ut_ad(err == DB_SUCCESS); DBUG_INJECT_CRASH("ddl_log_crash_after_rename_space_log", crash_after_rename_space_log_counter++); DBUG_EXECUTE_IF("DDL_Log_remove_inject_error_4", srv_inject_too_many_concurrent_trxs = true;); err = delete_by_id(trx, id, true); ut_ad(err == DB_SUCCESS || err == DB_TOO_MANY_CONCURRENT_TRXS); DBUG_EXECUTE_IF("DDL_Log_remove_inject_error_4", srv_inject_too_many_concurrent_trxs = false;); DBUG_INJECT_CRASH("ddl_log_crash_after_rename_space_delete", crash_after_rename_space_delete_counter++); return (err); } dberr_t Log_DDL::insert_rename_space_log(uint64_t id, ulint thread_id, space_id_t space_id, const char *old_file_path, const char *new_file_path) { dberr_t error; trx_t *trx = trx_allocate_for_background(); trx_start_internal(trx); trx->ddl_operation = true; ut_ad(mutex_own(&dict_sys->mutex)); mutex_exit(&dict_sys->mutex); DDL_Record record; record.set_id(id); record.set_thread_id(thread_id); record.set_type(Log_Type::RENAME_SPACE_LOG); record.set_space_id(space_id); record.set_old_file_path(old_file_path); record.set_new_file_path(new_file_path); { DDL_Log_Table ddl_log(trx); error = ddl_log.insert(record); ut_ad(error == DB_SUCCESS); } mutex_enter(&dict_sys->mutex); trx_commit_for_mysql(trx); trx_free_for_background(trx); if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_649) << "DDL log insert : " << record; } return (error); } dberr_t Log_DDL::write_purge_file_log(uint64_t *id, ulint thread_id, const char *file_path) { *id = next_id(); dberr_t err; err = insert_purge_file_log(*id, thread_id, file_path); ut_ad(err == DB_SUCCESS); return (err); } void Log_DDL::replay_purge_file_log(uint64_t id, ulint thread_id, const char *file_path) { char *path = mem_strdup(file_path); file_purge_sys->add_file(id, path, nullptr); } dberr_t Log_DDL::insert_purge_file_log(uint64_t id, ulint thread_id, const char *file_path) { dberr_t error; trx_t *trx = trx_allocate_for_background(); trx_start_internal(trx); trx->ddl_operation = true; DBUG_INJECT_CRASH("ddl_log_crash_before_purge_file_log_counter", crash_before_purge_file_log_counter++); DDL_Record record; record.set_id(id); record.set_thread_id(thread_id); record.set_type(Log_Type::PURGE_FILE_LOG); record.set_new_file_path(file_path); { DDL_Log_Table ddl_log(trx); error = ddl_log.insert(record); ut_ad(error == DB_SUCCESS); } trx_commit_for_mysql(trx); trx_free_for_background(trx); DBUG_INJECT_CRASH("ddl_log_crash_after_purge_file_log_counter", crash_after_purge_file_log_counter++); if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_649) << "DDL log insert : " << record; } return (error); } dberr_t Log_DDL::remove_by_id(uint64_t id) { dberr_t error; trx_t *trx = trx_allocate_for_background(); trx_start_internal(trx); trx->ddl_operation = true; { DDL_Log_Table ddl_log(trx); error = ddl_log.remove(id); ut_ad(error == DB_SUCCESS); } trx_commit_for_mysql(trx); trx_free_for_background(trx); return (error); } dberr_t Log_DDL::write_alter_encrypt_space_log(space_id_t space_id) { /* Missing current_thd, it happens during crash recovery */ if (!current_thd) { return (DB_SUCCESS); } trx_t *trx = thd_to_trx(current_thd); if (skip(nullptr, trx->mysql_thd)) { return (DB_SUCCESS); } uint64_t id = next_id(); ulint thread_id = thd_get_thread_id(trx->mysql_thd); trx->ddl_operation = true; DBUG_INJECT_CRASH("ddl_log_crash_before_alter_encrypt_space_log", crash_before_alter_encrypt_space_log_counter++); dberr_t err = insert_alter_encrypt_space_log(id, thread_id, space_id); ut_ad(err == DB_SUCCESS); DBUG_INJECT_CRASH("ddl_log_crash_after_alter_encrypt_space_log", crash_after_alter_encrypt_space_log_counter++); return (err); } dberr_t Log_DDL::insert_alter_encrypt_space_log(uint64_t id, ulint thread_id, space_id_t space_id) { dberr_t error; trx_t *trx = trx_allocate_for_background(); trx_start_internal(trx); trx->ddl_operation = true; ut_ad(mutex_own(&dict_sys->mutex)); mutex_exit(&dict_sys->mutex); DDL_Record record; record.set_id(id); record.set_thread_id(thread_id); record.set_type(Log_Type::ALTER_ENCRYPT_TABLESPACE_LOG); record.set_space_id(space_id); { DDL_Log_Table ddl_log(trx); error = ddl_log.insert(record); ut_ad(error == DB_SUCCESS); } mutex_enter(&dict_sys->mutex); trx_commit_for_mysql(trx); trx_free_for_background(trx); if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_1284) << "DDL log insert : " << record; } return (error); } dberr_t Log_DDL::write_drop_log(trx_t *trx, const table_id_t table_id) { if (skip(NULL, trx->mysql_thd)) { return (DB_SUCCESS); } trx->ddl_operation = true; uint64_t id = next_id(); ulint thread_id = thd_get_thread_id(trx->mysql_thd); DBUG_INJECT_CRASH("ddl_log_crash_before_drop_log", crash_before_drop_log_counter++); dberr_t err; err = insert_drop_log(trx, id, thread_id, table_id); ut_ad(err == DB_SUCCESS); DBUG_INJECT_CRASH("ddl_log_crash_after_drop_log", crash_after_drop_log_counter++); return (err); } dberr_t Log_DDL::insert_drop_log(trx_t *trx, uint64_t id, ulint thread_id, const table_id_t table_id) { ut_ad(trx->ddl_operation); ut_ad(mutex_own(&dict_sys->mutex)); trx_start_if_not_started(trx, true); mutex_exit(&dict_sys->mutex); dberr_t error; DDL_Record record; record.set_id(id); record.set_thread_id(thread_id); record.set_type(Log_Type::DROP_LOG); record.set_table_id(table_id); { DDL_Log_Table ddl_log(trx); error = ddl_log.insert(record); ut_ad(error == DB_SUCCESS); } mutex_enter(&dict_sys->mutex); if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_650) << "DDL log insert : " << record; } return (error); } dberr_t Log_DDL::write_rename_table_log(dict_table_t *table, const char *old_name, const char *new_name) { trx_t *trx = thd_to_trx(current_thd); if (skip(table, trx->mysql_thd)) { return (DB_SUCCESS); } uint64_t id = next_id(); ulint thread_id = thd_get_thread_id(trx->mysql_thd); trx->ddl_operation = true; dberr_t err = insert_rename_table_log(id, thread_id, table->id, old_name, new_name); ut_ad(err == DB_SUCCESS); DBUG_EXECUTE_IF("DDL_Log_remove_inject_error_5", srv_inject_too_many_concurrent_trxs = true;); err = delete_by_id(trx, id, true); ut_ad(err == DB_SUCCESS || err == DB_TOO_MANY_CONCURRENT_TRXS); DBUG_EXECUTE_IF("DDL_Log_remove_inject_error_5", srv_inject_too_many_concurrent_trxs = false;); return (err); } dberr_t Log_DDL::insert_rename_table_log(uint64_t id, ulint thread_id, table_id_t table_id, const char *old_name, const char *new_name) { dberr_t error; trx_t *trx = trx_allocate_for_background(); trx_start_internal(trx); trx->ddl_operation = true; ut_ad(mutex_own(&dict_sys->mutex)); mutex_exit(&dict_sys->mutex); DDL_Record record; record.set_id(id); record.set_thread_id(thread_id); record.set_type(Log_Type::RENAME_TABLE_LOG); record.set_table_id(table_id); record.set_old_file_path(old_name); record.set_new_file_path(new_name); { DDL_Log_Table ddl_log(trx); error = ddl_log.insert(record); ut_ad(error == DB_SUCCESS); } mutex_enter(&dict_sys->mutex); trx_commit_for_mysql(trx); trx_free_for_background(trx); if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_651) << "DDL log insert : " << record; } return (error); } dberr_t Log_DDL::write_remove_cache_log(trx_t *trx, dict_table_t *table) { ut_ad(trx == thd_to_trx(current_thd)); if (skip(table, trx->mysql_thd)) { return (DB_SUCCESS); } uint64_t id = next_id(); ulint thread_id = thd_get_thread_id(trx->mysql_thd); trx->ddl_operation = true; dberr_t err = insert_remove_cache_log(id, thread_id, table->id, table->name.m_name); ut_ad(err == DB_SUCCESS); DBUG_EXECUTE_IF("DDL_Log_remove_inject_error_3", srv_inject_too_many_concurrent_trxs = true;); err = delete_by_id(trx, id, false); ut_ad(err == DB_SUCCESS || err == DB_TOO_MANY_CONCURRENT_TRXS); DBUG_EXECUTE_IF("DDL_Log_remove_inject_error_3", srv_inject_too_many_concurrent_trxs = false;); return (err); } dberr_t Log_DDL::insert_remove_cache_log(uint64_t id, ulint thread_id, table_id_t table_id, const char *table_name) { dberr_t error; trx_t *trx = trx_allocate_for_background(); trx_start_internal(trx); trx->ddl_operation = true; DDL_Record record; record.set_id(id); record.set_thread_id(thread_id); record.set_type(Log_Type::REMOVE_CACHE_LOG); record.set_table_id(table_id); record.set_new_file_path(table_name); { DDL_Log_Table ddl_log(trx); error = ddl_log.insert(record); ut_ad(error == DB_SUCCESS); } trx_commit_for_mysql(trx); trx_free_for_background(trx); if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_652) << "DDL log insert : " << record; } return (error); } dberr_t Log_DDL::delete_by_id(trx_t *trx, uint64_t id, bool dict_locked) { dberr_t err; trx_start_if_not_started(trx, true); ut_ad(trx->ddl_operation); if (dict_locked) { mutex_exit(&dict_sys->mutex); } { DDL_Log_Table ddl_log(trx); err = ddl_log.remove(id); ut_ad(err == DB_SUCCESS || err == DB_TOO_MANY_CONCURRENT_TRXS); if (err == DB_TOO_MANY_CONCURRENT_TRXS) { ib::error(ER_IB_MSG_DDL_LOG_DELETE_BY_ID_TMCT); } } if (dict_locked) { mutex_enter(&dict_sys->mutex); } if (srv_print_ddl_logs && err == DB_SUCCESS) { ib::info(ER_IB_MSG_DDL_LOG_DELETE_BY_ID_OK) << "DDL log delete : " << id; } return (err); } dberr_t Log_DDL::replay_all() { ut_ad(is_in_recovery()); DDL_Log_Table ddl_log; DDL_Records records; DDL_Records purge_records; dberr_t err = ddl_log.search_all(records); ut_ad(err == DB_SUCCESS); for (auto record : records) { log_ddl->replay(*record); /* If this is alter tablespace encrypt entry, don't delete it yet. This is to handle crash during resume operation. This entry will be deleted once resume operation is finished. */ if (record->get_type() == Log_Type::ALTER_ENCRYPT_TABLESPACE_LOG) { ts_encrypt_ddl_records.push_back(record); record->set_deletable(false); } /* PURGE_FILE_LOG will be deleted by background thread, so front thread set its removable = false */ if (record->get_type() == Log_Type::PURGE_FILE_LOG) { record->set_removable(false); } } err = delete_by_ids(records); ut_ad(err == DB_SUCCESS || err == DB_TOO_MANY_CONCURRENT_TRXS); for (auto record : records) { if (record->get_deletable()) { UT_DELETE(record); } } return (err); } dberr_t Log_DDL::replay_by_thread_id(ulint thread_id) { DDL_Log_Table ddl_log; DDL_Records records; dberr_t err = ddl_log.search(thread_id, records); ut_ad(err == DB_SUCCESS); for (auto record : records) { log_ddl->replay(*record); } err = delete_by_ids(records); ut_ad(err == DB_SUCCESS || err == DB_TOO_MANY_CONCURRENT_TRXS); for (auto record : records) { if (record->get_deletable()) { UT_DELETE(record); } } return (err); } #define DELETE_IDS_RETRIES_MAX 10 dberr_t Log_DDL::delete_by_ids(DDL_Records &records) { dberr_t err = DB_SUCCESS; if (records.empty()) { return (err); } int t; for (t = DELETE_IDS_RETRIES_MAX; t > 0; t--) { trx_t *trx; trx = trx_allocate_for_background(); trx_start_if_not_started(trx, true); trx->ddl_operation = true; { DDL_Log_Table ddl_log(trx); err = ddl_log.remove(records); ut_ad(err == DB_SUCCESS || err == DB_TOO_MANY_CONCURRENT_TRXS); } trx_commit_for_mysql(trx); trx_free_for_background(trx); #ifdef UNIV_DEBUG if (srv_inject_too_many_concurrent_trxs) { srv_inject_too_many_concurrent_trxs = false; } #endif /* UNIV_DEBUG */ if (err != DB_TOO_MANY_CONCURRENT_TRXS) { break; } } return (err); } dberr_t Log_DDL::replay(DDL_Record &record) { dberr_t err = DB_SUCCESS; if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_654) << "DDL log replay : " << record; } switch (record.get_type()) { case Log_Type::FREE_TREE_LOG: replay_free_tree_log(record.get_space_id(), record.get_page_no(), record.get_index_id()); break; case Log_Type::DELETE_SPACE_LOG: replay_delete_space_log(record.get_space_id(), record.get_old_file_path()); break; case Log_Type::RENAME_SPACE_LOG: replay_rename_space_log(record.get_space_id(), record.get_old_file_path(), record.get_new_file_path()); break; case Log_Type::DROP_LOG: replay_drop_log(record.get_table_id()); break; case Log_Type::RENAME_TABLE_LOG: replay_rename_table_log(record.get_table_id(), record.get_old_file_path(), record.get_new_file_path()); break; case Log_Type::REMOVE_CACHE_LOG: replay_remove_cache_log(record.get_table_id(), record.get_new_file_path()); break; case Log_Type::ALTER_ENCRYPT_TABLESPACE_LOG: replay_alter_encrypt_space_log(record.get_space_id()); break; case Log_Type::PURGE_FILE_LOG: replay_purge_file_log(record.get_id(), record.get_thread_id(), record.get_new_file_path()); break; default: ut_error; } return (err); } void Log_DDL::replay_free_tree_log(space_id_t space_id, page_no_t page_no, ulint index_id) { ut_ad(space_id != SPACE_UNKNOWN); ut_ad(page_no != FIL_NULL); bool found; const page_size_t page_size(fil_space_get_page_size(space_id, &found)); /* Skip if it is a single table tablespace and the .ibd file is missing */ if (!found) { if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_655) << "DDL log replay : FREE tablespace " << space_id << " is missing."; } return; } /* This is required by dropping hash index afterwards. */ mutex_enter(&dict_sys->mutex); mtr_t mtr; mtr_start(&mtr); btr_free_if_exists(page_id_t(space_id, page_no), page_size, index_id, &mtr); mtr_commit(&mtr); mutex_exit(&dict_sys->mutex); DBUG_INJECT_CRASH("ddl_log_crash_after_replay", crash_after_replay_counter++); } void Log_DDL::replay_delete_space_log(space_id_t space_id, const char *file_path) { THD *thd = current_thd; if (fsp_is_undo_tablespace(space_id)) { /* If this is called during DROP UNDO TABLESPACE, then the undo_space is already gone. But if this is called at startup after a crash, that memory object might exist. If the crash occurred just before the file was deleted, then at startup it was opened in srv_undo_tablespaces_open(). Then in trx_rsegs_init(), any explicit undo tablespace that did not contain any undo logs was set to empty. That prevented any new undo logs to be added during the startup process up till now. So whether we are at runtime or startup, we assert that the undo tablespace is empty and delete the undo::Tablespace object if it exists. */ undo::spaces->x_lock(); space_id_t space_num = undo::id2num(space_id); undo::Tablespace *undo_space = undo::spaces->find(space_num); if (undo_space != nullptr) { ut_a(undo_space->is_empty()); undo::spaces->drop(undo_space); } undo::spaces->x_unlock(); } if (thd != nullptr) { /* For general tablespace, MDL on SDI tables is already acquired at innobase_drop_tablespace() and for file_per_table tablespace, MDL is acquired at row_drop_table_for_mysql() */ mutex_enter(&dict_sys->mutex); dict_sdi_remove_from_cache(space_id, NULL, true); mutex_exit(&dict_sys->mutex); } /* A master key rotation blocks all DDLs using backup_lock, so it is assured that during CREATE/DROP TABLE, master key will not change. */ DBUG_EXECUTE_IF("ddl_log_replay_delete_space_crash_before_drop", DBUG_SUICIDE();); row_drop_or_purge_single_table_tablespace(space_id, file_path); /* If this is an undo space_id, allow the undo number for it to be reused. */ if (fsp_is_undo_tablespace(space_id)) { undo::spaces->x_lock(); undo::unuse_space_id(space_id); undo::spaces->x_unlock(); } DBUG_INJECT_CRASH("ddl_log_crash_after_replay", crash_after_replay_counter++); } void Log_DDL::replay_rename_space_log(space_id_t space_id, const char *old_file_path, const char *new_file_path) { bool ret; page_id_t page_id(space_id, 0); ret = fil_op_replay_rename_for_ddl(page_id, old_file_path, new_file_path); if (!ret && srv_print_ddl_logs) { ib::info(ER_IB_MSG_656) << "DDL log replay : RENAME from " << old_file_path << " to " << new_file_path << " failed"; } DBUG_INJECT_CRASH("ddl_log_crash_after_replay", crash_after_replay_counter++); } void Log_DDL::replay_alter_encrypt_space_log(space_id_t space_id) { /* NOOP */ DBUG_INJECT_CRASH("ddl_log_crash_after_replay", crash_after_replay_counter++); } void Log_DDL::replay_drop_log(const table_id_t table_id) { mutex_enter(&dict_persist->mutex); ut_d(dberr_t error =) dict_persist->table_buffer->remove(table_id); ut_ad(error == DB_SUCCESS); mutex_exit(&dict_persist->mutex); DBUG_INJECT_CRASH("ddl_log_crash_after_replay", crash_after_replay_counter++); } void Log_DDL::replay_rename_table_log(table_id_t table_id, const char *old_name, const char *new_name) { if (is_in_recovery()) { if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_657) << "DDL log replay : in recovery," << " skip RENAME TABLE"; } return; } trx_t *trx; trx = trx_allocate_for_background(); trx->mysql_thd = current_thd; trx_start_if_not_started(trx, true); row_mysql_lock_data_dictionary(trx); trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); dberr_t err; err = row_rename_table_for_mysql(old_name, new_name, NULL, trx, true); dict_table_t *table; table = dd_table_open_on_name_in_mem(new_name, true); if (table != nullptr) { dict_table_ddl_release(table); dd_table_close(table, nullptr, nullptr, true); } row_mysql_unlock_data_dictionary(trx); trx_commit_for_mysql(trx); trx_free_for_background(trx); if (err != DB_SUCCESS) { if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_658) << "DDL log replay : rename table" << " in cache from " << old_name << " to " << new_name; } } else { /* TODO: Once we get rid of dict_operation_lock, we may consider to do this in row_rename_table_for_mysql, so no need to worry this rename here */ char errstr[512]; dict_stats_rename_table(old_name, new_name, errstr, sizeof(errstr)); } } void Log_DDL::replay_remove_cache_log(table_id_t table_id, const char *table_name) { if (is_in_recovery()) { if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_659) << "DDL log replay : in recovery," << " skip REMOVE CACHE"; } return; } dict_table_t *table; table = dd_table_open_on_id_in_mem(table_id, false); if (table != nullptr) { ut_ad(strcmp(table->name.m_name, table_name) == 0); mutex_enter(&dict_sys->mutex); dd_table_close(table, nullptr, nullptr, true); btr_drop_ahi_for_table(table); dict_table_remove_from_cache(table); mutex_exit(&dict_sys->mutex); } } dberr_t Log_DDL::post_ddl(THD *thd) { if (skip(nullptr, thd)) { return (DB_SUCCESS); } if (srv_read_only_mode || srv_force_recovery >= SRV_FORCE_NO_UNDO_LOG_SCAN) { return (DB_SUCCESS); } DEBUG_SYNC(thd, "innodb_ddl_log_before_enter"); DBUG_EXECUTE_IF("ddl_log_before_post_ddl", DBUG_SUICIDE();); /* If srv_force_recovery > 0, DROP TABLE is allowed, and here only DELETE and DROP log can be replayed. */ ulint thread_id = thd_get_thread_id(thd); if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_660) << "DDL log post ddl : begin for thread id : " << thread_id; } thread_local_ddl_log_replay = true; dberr_t err = replay_by_thread_id(thread_id); thread_local_ddl_log_replay = false; if (srv_print_ddl_logs) { ib::info(ER_IB_MSG_661) << "DDL log post ddl : end for thread id : " << thread_id; } return (err); } dberr_t Log_DDL::recover() { if (srv_read_only_mode || srv_force_recovery > 0) { return (DB_SUCCESS); } ib::info(ER_IB_MSG_662) << "DDL log recovery : begin"; thread_local_ddl_log_replay = true; s_in_recovery = true; dberr_t err = replay_all(); thread_local_ddl_log_replay = false; s_in_recovery = false; ib::info(ER_IB_MSG_663) << "DDL log recovery : end"; return (err); } dberr_t Log_DDL::post_ts_encryption(DDL_Records &records) { for (auto record : records) { record->set_deletable(true); } dberr_t err = Log_DDL::delete_by_ids(records); for (auto record : records) { UT_DELETE(record); } return (err); }