diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 7721ca6bb5d..aa0ed07c205 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -628,15 +628,6 @@ static void backup_optimized_ddl_op(ulint space_id) pthread_mutex_unlock(&backup_mutex); } -/** Callback whenever MLOG_TRUNCATE happens. */ -static void backup_truncate_fail() -{ - msg("mariabackup: Incompatible TRUNCATE operation detected.%s\n", - opt_lock_ddl_per_table - ? "" - : " Use --lock-ddl-per-table to lock all tables before backup."); -} - /* ======== Date copying thread context ======== */ typedef struct { @@ -4179,7 +4170,6 @@ fail_before_log_copying_thread_start: log_copy_scanned_lsn = checkpoint_lsn_start; recv_sys->recovered_lsn = log_copy_scanned_lsn; log_optimized_ddl_op = backup_optimized_ddl_op; - log_truncate = backup_truncate_fail; if (xtrabackup_copy_logfile()) goto fail_before_log_copying_thread_start; diff --git a/mysql-test/suite/encryption/r/innodb_encrypt_log_corruption.result b/mysql-test/suite/encryption/r/innodb_encrypt_log_corruption.result index 8b58ff593de..59b28969559 100644 --- a/mysql-test/suite/encryption/r/innodb_encrypt_log_corruption.result +++ b/mysql-test/suite/encryption/r/innodb_encrypt_log_corruption.result @@ -125,13 +125,13 @@ WHERE engine = 'innodb' AND support IN ('YES', 'DEFAULT', 'ENABLED'); COUNT(*) 1 -FOUND 2 /InnoDB: Upgrading redo log:/ in mysqld.1.err +FOUND 3 /InnoDB: Upgrading redo log:/ in mysqld.1.err # Minimal MariaDB 10.1.21 encrypted redo log SELECT COUNT(*) `1` FROM INFORMATION_SCHEMA.ENGINES WHERE engine='innodb' AND support IN ('YES', 'DEFAULT', 'ENABLED'); 1 1 -FOUND 2 /InnoDB: Encrypting redo log/ in mysqld.1.err +FOUND 1 /InnoDB: Encrypting redo log/ in mysqld.1.err ib_buffer_pool ib_logfile0 ib_logfile1 diff --git a/mysql-test/suite/innodb/r/log_corruption.result b/mysql-test/suite/innodb/r/log_corruption.result index 6bc10c970fc..b174546c111 100644 --- a/mysql-test/suite/innodb/r/log_corruption.result +++ b/mysql-test/suite/innodb/r/log_corruption.result @@ -125,7 +125,7 @@ WHERE engine = 'innodb' AND support IN ('YES', 'DEFAULT', 'ENABLED'); COUNT(*) 1 -FOUND 2 /InnoDB: Upgrading redo log:/ in mysqld.1.err +FOUND 3 /InnoDB: Upgrading redo log:/ in mysqld.1.err # Minimal MariaDB 10.1.21 encrypted redo log SELECT * FROM INFORMATION_SCHEMA.ENGINES WHERE engine = 'innodb' diff --git a/storage/innobase/CMakeLists.txt b/storage/innobase/CMakeLists.txt index e0bc7006770..90a01540000 100644 --- a/storage/innobase/CMakeLists.txt +++ b/storage/innobase/CMakeLists.txt @@ -113,7 +113,6 @@ SET(INNOBASE_SOURCES row/row0purge.cc row/row0row.cc row/row0sel.cc - row/row0trunc.cc row/row0uins.cc row/row0umod.cc row/row0undo.cc @@ -179,7 +178,6 @@ IF(CMAKE_COMPILER_IS_GNUCXX AND CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") mtr/mtr0mtr.cc row/row0merge.cc row/row0mysql.cc - row/row0trunc.cc srv/srv0srv.cc COMPILE_FLAGS "-O0" ) diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 31e9a4cad1c..0d191cb9c64 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -438,7 +438,7 @@ btr_page_create( ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); if (page_zip) { - page_create_zip(block, index, level, 0, NULL, mtr); + page_create_zip(block, index, level, 0, mtr); } else { page_create(block, mtr, dict_table_is_comp(index->table), dict_index_is_spatial(index)); @@ -1179,21 +1179,18 @@ btr_free_root_check( /** Create the root node for a new index tree. @param[in] type type of the index -@param[in,out] space tablespace where created @param[in] index_id index id -@param[in] index index, or NULL when applying TRUNCATE -log record during recovery -@param[in] btr_redo_create_info used for applying TRUNCATE log -@param[in] mtr mini-transaction handle -record during recovery -@return page number of the created root, FIL_NULL if did not succeed */ +@param[in,out] space tablespace where created +@param[in] index index +@param[in,out] mtr mini-transaction +@return page number of the created root +@retval FIL_NULL if did not succeed */ ulint btr_create( ulint type, fil_space_t* space, index_id_t index_id, dict_index_t* index, - const btr_create_t* btr_redo_create_info, mtr_t* mtr) { buf_block_t* block; @@ -1208,7 +1205,7 @@ btr_create( (for an ibuf tree, not in the root, but on a separate ibuf header page) */ - if (type & DICT_IBUF) { + if (UNIV_UNLIKELY(type & DICT_IBUF)) { /* Allocate first the ibuf header page */ buf_block_t* ibuf_hdr_block = fseg_create( space, 0, @@ -1273,44 +1270,11 @@ btr_create( page_zip = buf_block_get_page_zip(block); if (page_zip) { - if (index != NULL) { - page = page_create_zip(block, index, 0, 0, NULL, mtr); - } else { - /* Create a compressed index page when applying - TRUNCATE log record during recovery */ - ut_ad(btr_redo_create_info != NULL); - - redo_page_compress_t page_comp_info; - - page_comp_info.type = type; - - page_comp_info.index_id = index_id; - - page_comp_info.n_fields = - btr_redo_create_info->n_fields; - - page_comp_info.field_len = - btr_redo_create_info->field_len; - - page_comp_info.fields = btr_redo_create_info->fields; - - page_comp_info.trx_id_pos = - btr_redo_create_info->trx_id_pos; - - page = page_create_zip(block, NULL, 0, 0, - &page_comp_info, mtr); - } + page = page_create_zip(block, index, 0, 0, mtr); } else { - if (index != NULL) { - page = page_create(block, mtr, - dict_table_is_comp(index->table), - dict_index_is_spatial(index)); - } else { - ut_ad(btr_redo_create_info != NULL); - page = page_create( - block, mtr, btr_redo_create_info->format_flags, - type == DICT_SPATIAL); - } + page = page_create(block, mtr, + dict_table_is_comp(index->table), + dict_index_is_spatial(index)); /* Set the level of the new index page */ btr_page_set_level(page, NULL, 0, mtr); } @@ -1322,18 +1286,14 @@ btr_create( btr_page_set_next(page, page_zip, FIL_NULL, mtr); btr_page_set_prev(page, page_zip, FIL_NULL, mtr); - /* We reset the free bits for the page to allow creation of several - trees in the same mtr, otherwise the latch on a bitmap page would - prevent it because of the latching order. - - index will be NULL if we are recreating the table during recovery - on behalf of TRUNCATE. + /* We reset the free bits for the page in a separate + mini-transaction to allow creation of several trees in the + same mtr, otherwise the latch on a bitmap page would prevent + it because of the latching order. Note: Insert Buffering is disabled for temporary tables given that most temporary tables are smaller in size and short-lived. */ - if (!(type & DICT_CLUSTERED) - && (index == NULL || !index->table->is_temporary())) { - + if (!(type & DICT_CLUSTERED) && !index->table->is_temporary()) { ibuf_reset_free_bits(block); } @@ -1675,7 +1635,7 @@ btr_page_reorganize_low( } if (page_zip - && !page_zip_compress(page_zip, page, index, z_level, NULL, mtr)) { + && !page_zip_compress(page_zip, page, index, z_level, mtr)) { /* Restore the old page and exit. */ #if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG @@ -1924,7 +1884,7 @@ btr_page_empty( : 0; if (page_zip) { - page_create_zip(block, index, level, autoinc, NULL, mtr); + page_create_zip(block, index, level, autoinc, mtr); } else { page_create(block, mtr, dict_table_is_comp(index->table), dict_index_is_spatial(index)); diff --git a/storage/innobase/btr/btr0bulk.cc b/storage/innobase/btr/btr0bulk.cc index 539ee522e2b..73b4335d265 100644 --- a/storage/innobase/btr/btr0bulk.cc +++ b/storage/innobase/btr/btr0bulk.cc @@ -94,7 +94,7 @@ PageBulk::init() if (new_page_zip) { page_create_zip(new_block, m_index, m_level, 0, - NULL, &m_mtr); + &m_mtr); memset(FIL_PAGE_PREV + new_page, 0xff, 8); page_zip_write_header(new_page_zip, FIL_PAGE_PREV + new_page, @@ -374,7 +374,7 @@ PageBulk::compress() ut_ad(m_page_zip != NULL); return(page_zip_compress(m_page_zip, m_page, m_index, - page_zip_level, NULL, &m_mtr)); + page_zip_level, &m_mtr)); } /** Get node pointer diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index bd64334c3ba..0f85761f297 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -6248,7 +6248,6 @@ database_corrupted: && !recv_no_ibuf_operations && (bpage->id.space() == 0 || !is_predefined_tablespace(bpage->id.space())) - && !srv_is_tablespace_truncated(bpage->id.space()) && fil_page_get_type(frame) == FIL_PAGE_INDEX && page_is_leaf(frame)) { diff --git a/storage/innobase/buf/buf0dblwr.cc b/storage/innobase/buf/buf0dblwr.cc index 043a5ffd426..c368bccf253 100644 --- a/storage/innobase/buf/buf0dblwr.cc +++ b/storage/innobase/buf/buf0dblwr.cc @@ -556,12 +556,9 @@ buf_dblwr_process() if (page_no >= space->size) { - /* Do not report the warning if the tablespace - is scheduled for truncation or was truncated - and we have parsed an MLOG_TRUNCATE record. */ - if (!srv_is_tablespace_truncated(space_id) - && !srv_was_tablespace_truncated(space) - && !srv_is_undo_tablespace(space_id)) { + /* Do not report the warning for undo + tablespaces, because they can be truncated in place. */ + if (!srv_is_undo_tablespace(space_id)) { ib::warn() << "A copy of page " << page_id << " in the doublewrite buffer slot " << page_no_dblwr diff --git a/storage/innobase/buf/buf0rea.cc b/storage/innobase/buf/buf0rea.cc index 14d1d3d4706..3c4d9d711c2 100644 --- a/storage/innobase/buf/buf0rea.cc +++ b/storage/innobase/buf/buf0rea.cc @@ -95,11 +95,9 @@ buffer buf_pool if it is not already there, in which case does nothing. Sets the io_fix flag and sets an exclusive lock on the buffer frame. The flag is cleared and the x-lock released by an i/o-handler thread. -@param[out] err DB_SUCCESS, DB_TABLESPACE_DELETED or - DB_TABLESPACE_TRUNCATED if we are trying - to read from a non-existent tablespace, a - tablespace which is just now being dropped, - or a tablespace which is truncated +@param[out] err DB_SUCCESS or DB_TABLESPACE_DELETED + if we are trying + to read from a non-existent tablespace @param[in] sync true if synchronous aio is desired @param[in] type IO type, SIMULATED, IGNORE_MISSING @param[in] mode BUF_READ_IBUF_PAGES_ONLY, ..., @@ -187,20 +185,8 @@ buf_read_page_low( } if (*err != DB_SUCCESS) { - if (*err == DB_TABLESPACE_TRUNCATED) { - /* Remove the page which is outside the - truncated tablespace bounds when recovering - from a crash happened during a truncation */ - buf_read_page_handle_error(bpage); - if (recv_recovery_on) { - mutex_enter(&recv_sys->mutex); - ut_ad(recv_sys->n_addrs > 0); - recv_sys->n_addrs--; - mutex_exit(&recv_sys->mutex); - } - return(0); - } else if (IORequest::ignore_missing(type) - || *err == DB_TABLESPACE_DELETED) { + if (IORequest::ignore_missing(type) + || *err == DB_TABLESPACE_DELETED) { buf_read_page_handle_error(bpage); return(0); } @@ -369,7 +355,6 @@ read_ahead: switch (err) { case DB_SUCCESS: - case DB_TABLESPACE_TRUNCATED: case DB_ERROR: break; case DB_TABLESPACE_DELETED: @@ -472,7 +457,6 @@ buf_read_page_background( switch (err) { case DB_SUCCESS: - case DB_TABLESPACE_TRUNCATED: case DB_ERROR: break; case DB_TABLESPACE_DELETED: @@ -755,7 +739,6 @@ buf_read_ahead_linear( switch (err) { case DB_SUCCESS: - case DB_TABLESPACE_TRUNCATED: case DB_TABLESPACE_DELETED: case DB_ERROR: break; @@ -853,7 +836,6 @@ tablespace_deleted: switch(err) { case DB_SUCCESS: - case DB_TABLESPACE_TRUNCATED: case DB_ERROR: break; case DB_TABLESPACE_DELETED: diff --git a/storage/innobase/dict/dict0boot.cc b/storage/innobase/dict/dict0boot.cc index cd4f6cab91c..5b176796644 100644 --- a/storage/innobase/dict/dict0boot.cc +++ b/storage/innobase/dict/dict0boot.cc @@ -214,7 +214,7 @@ dict_hdr_create( /*--------------------------*/ root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, fil_system.sys_space, DICT_TABLES_ID, - dict_ind_redundant, NULL, mtr); + dict_ind_redundant, mtr); if (root_page_no == FIL_NULL) { return(FALSE); @@ -225,7 +225,7 @@ dict_hdr_create( /*--------------------------*/ root_page_no = btr_create(DICT_UNIQUE, fil_system.sys_space, DICT_TABLE_IDS_ID, - dict_ind_redundant, NULL, mtr); + dict_ind_redundant, mtr); if (root_page_no == FIL_NULL) { return(FALSE); @@ -236,7 +236,7 @@ dict_hdr_create( /*--------------------------*/ root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, fil_system.sys_space, DICT_COLUMNS_ID, - dict_ind_redundant, NULL, mtr); + dict_ind_redundant, mtr); if (root_page_no == FIL_NULL) { return(FALSE); @@ -247,7 +247,7 @@ dict_hdr_create( /*--------------------------*/ root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, fil_system.sys_space, DICT_INDEXES_ID, - dict_ind_redundant, NULL, mtr); + dict_ind_redundant, mtr); if (root_page_no == FIL_NULL) { return(FALSE); @@ -258,7 +258,7 @@ dict_hdr_create( /*--------------------------*/ root_page_no = btr_create(DICT_CLUSTERED | DICT_UNIQUE, fil_system.sys_space, DICT_FIELDS_ID, - dict_ind_redundant, NULL, mtr); + dict_ind_redundant, mtr); if (root_page_no == FIL_NULL) { return(FALSE); diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index 7d83c183fdc..e145422c5bd 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -863,7 +863,7 @@ dict_create_index_tree_step( node->page_no = btr_create( index->type, index->table->space, - index->id, index, NULL, &mtr); + index->id, index, &mtr); if (node->page_no == FIL_NULL) { err = DB_OUT_OF_FILE_SPACE; @@ -909,7 +909,7 @@ dict_create_index_tree_in_mem( ut_ad(!(index->table->flags2 & DICT_TF2_DISCARDED)); index->page = btr_create(index->type, index->table->space, - index->id, index, NULL, &mtr); + index->id, index, &mtr); mtr_commit(&mtr); index->trx_id = trx->id; @@ -975,13 +975,6 @@ dict_drop_index_tree( return(false); } - /* If tablespace is scheduled for truncate, do not try to drop - the indexes in that tablespace. There is a truncate fixup action - which will take care of it. */ - if (srv_is_tablespace_truncated(space)) { - return(false); - } - btr_free_if_exists(page_id_t(space, root_page_no), page_size, mach_read_from_8(ptr), mtr); @@ -1057,7 +1050,7 @@ dict_recreate_index_tree( ulint root_page_no = (index->type & DICT_FTS) ? FIL_NULL : btr_create(type, table->space, - index_id, index, NULL, mtr); + index_id, index, mtr); index->page = unsigned(root_page_no); return root_page_no; } diff --git a/storage/innobase/fil/fil0fil.cc b/storage/innobase/fil/fil0fil.cc index 387d1dd64d6..9b42caf421f 100644 --- a/storage/innobase/fil/fil0fil.cc +++ b/storage/innobase/fil/fil0fil.cc @@ -45,7 +45,6 @@ Created 10/25/1995 Heikki Tuuri #include "os0file.h" #include "page0zip.h" #include "row0mysql.h" -#include "row0trunc.h" #include "srv0start.h" #include "trx0purge.h" #include "ut0new.h" @@ -553,8 +552,7 @@ fil_node_open_file( if (first_time_open || (space->purpose == FIL_TYPE_TABLESPACE && node == UT_LIST_GET_FIRST(space->chain) - && srv_startup_is_before_trx_rollback_phase - && !undo::Truncate::was_tablespace_truncated(space->id))) { + && srv_startup_is_before_trx_rollback_phase)) { /* We do not know the size of the file yet. First we open the file in the normal mode, no async I/O here, for simplicity. Then do some checks, and close the @@ -4241,7 +4239,7 @@ fil_report_invalid_page_access( @param[in] message message for aio handler if non-sync aio used, else ignored @param[in] ignore_missing_space true=ignore missing space duging read -@return DB_SUCCESS, DB_TABLESPACE_DELETED or DB_TABLESPACE_TRUNCATED +@return DB_SUCCESS, or DB_TABLESPACE_DELETED if we are trying to do i/o on a tablespace which does not exist */ dberr_t fil_io( @@ -4373,19 +4371,6 @@ fil_io( break; } else { - if (space->id != TRX_SYS_SPACE - && UT_LIST_GET_LEN(space->chain) == 1 - && (srv_is_tablespace_truncated(space->id) - || srv_was_tablespace_truncated(space)) - && req_type.is_read()) { - - /* Handle page which is outside the truncated - tablespace bounds when recovering from a crash - happened during a truncation */ - mutex_exit(&fil_system.mutex); - return(DB_TABLESPACE_TRUNCATED); - } - cur_page_no -= node->size; node = UT_LIST_GET_NEXT(chain, node); @@ -5126,116 +5111,6 @@ fil_names_clear( return(do_write); } -/** Truncate a single-table tablespace. The tablespace must be cached -in the memory cache. -@param space_id space id -@param dir_path directory path -@param tablename the table name in the usual - databasename/tablename format of InnoDB -@param flags tablespace flags -@param trunc_to_default truncate to default size if tablespace - is being newly re-initialized. -@return DB_SUCCESS or error */ -dberr_t -truncate_t::truncate( -/*=================*/ - ulint space_id, - const char* dir_path, - const char* tablename, - ulint flags, - bool trunc_to_default) -{ - dberr_t err = DB_SUCCESS; - char* path; - - ut_a(!is_system_tablespace(space_id)); - - if (FSP_FLAGS_HAS_DATA_DIR(flags)) { - ut_ad(dir_path != NULL); - path = fil_make_filepath(dir_path, tablename, IBD, true); - } else { - path = fil_make_filepath(NULL, tablename, IBD, false); - } - - if (path == NULL) { - return(DB_OUT_OF_MEMORY); - } - - mutex_enter(&fil_system.mutex); - - fil_space_t* space = fil_space_get_by_id(space_id); - - /* The following code must change when InnoDB supports - multiple datafiles per tablespace. */ - ut_a(UT_LIST_GET_LEN(space->chain) == 1); - - fil_node_t* node = UT_LIST_GET_FIRST(space->chain); - - if (trunc_to_default) { - space->size = node->size = FIL_IBD_FILE_INITIAL_SIZE; - } - - const bool already_open = node->is_open(); - - if (!already_open) { - - bool ret; - - node->handle = os_file_create_simple_no_error_handling( - innodb_data_file_key, path, OS_FILE_OPEN, - OS_FILE_READ_WRITE, - space->purpose != FIL_TYPE_TEMPORARY - && srv_read_only_mode, &ret); - - if (!ret) { - ib::error() << "Failed to open tablespace file " - << path << "."; - - ut_free(path); - - return(DB_ERROR); - } - - ut_a(node->is_open()); - } - - os_offset_t trunc_size = trunc_to_default - ? FIL_IBD_FILE_INITIAL_SIZE - : space->size; - - const bool success = os_file_truncate( - path, node->handle, trunc_size << srv_page_size_shift); - - if (!success) { - ib::error() << "Cannot truncate file " << path - << " in TRUNCATE TABLESPACE."; - err = DB_ERROR; - } - - space->stop_new_ops = false; - - /* If we opened the file in this function, close it. */ - if (!already_open) { - bool closed = os_file_close(node->handle); - - if (!closed) { - - ib::error() << "Failed to close tablespace file " - << path << "."; - - err = DB_ERROR; - } else { - node->handle = OS_FILE_CLOSED; - } - } - - mutex_exit(&fil_system.mutex); - - ut_free(path); - - return(err); -} - /* Unit Tests */ #ifdef UNIV_ENABLE_UNIT_TEST_MAKE_FILEPATH #define MF fil_make_filepath diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index 26502086d81..1ae32a62c87 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -624,8 +624,7 @@ fsp_space_modify_check( case MTR_LOG_NO_REDO: ut_ad(space->purpose == FIL_TYPE_TEMPORARY || space->purpose == FIL_TYPE_IMPORT - || my_atomic_loadlint(&space->redo_skipped_count) - || srv_is_tablespace_truncated(space->id)); + || my_atomic_loadlint(&space->redo_skipped_count)); return; case MTR_LOG_ALL: /* We may only write redo log for a persistent tablespace. */ @@ -1065,13 +1064,6 @@ fsp_fill_free_list( mtr_start(&ibuf_mtr); ibuf_mtr.set_named_space(space); - /* Avoid logging while truncate table - fix-up is active. */ - if (srv_is_tablespace_truncated(space->id)) { - mtr_set_log_mode( - &ibuf_mtr, MTR_LOG_NO_REDO); - } - const page_id_t page_id( space->id, i + FSP_IBUF_BITMAP_OFFSET); diff --git a/storage/innobase/gis/gis0rtree.cc b/storage/innobase/gis/gis0rtree.cc index 146d1616347..4bd3e1c232a 100644 --- a/storage/innobase/gis/gis0rtree.cc +++ b/storage/innobase/gis/gis0rtree.cc @@ -914,7 +914,7 @@ rtr_split_page_move_rec_list( mtr_set_log_mode(mtr, log_mode); if (!page_zip_compress(new_page_zip, new_page, index, - page_zip_level, NULL, mtr)) { + page_zip_level, mtr)) { ulint ret_pos; /* Before trying to reorganize the page, diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index f2ae8b53780..1eae180f9df 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -100,7 +100,6 @@ this program; if not, write to the Free Software Foundation, Inc., #include "row0mysql.h" #include "row0quiesce.h" #include "row0sel.h" -#include "row0trunc.h" #include "row0upd.h" #include "fil0crypt.h" #include "ut0timer.h" diff --git a/storage/innobase/ibuf/ibuf0ibuf.cc b/storage/innobase/ibuf/ibuf0ibuf.cc index 30e66c1110a..4bf21c0609b 100644 --- a/storage/innobase/ibuf/ibuf0ibuf.cc +++ b/storage/innobase/ibuf/ibuf0ibuf.cc @@ -916,10 +916,7 @@ ibuf_set_free_bits_func( ut_ad(0); break; case FIL_TYPE_TABLESPACE: - /* Avoid logging while fixing up truncate of table. */ - if (!srv_is_tablespace_truncated(block->page.id.space())) { - break; - } + break; /* fall through */ case FIL_TYPE_TEMPORARY: case FIL_TYPE_IMPORT: diff --git a/storage/innobase/include/btr0btr.h b/storage/innobase/include/btr0btr.h index 54f13a17c4c..1951d6f4eac 100644 --- a/storage/innobase/include/btr0btr.h +++ b/storage/innobase/include/btr0btr.h @@ -361,19 +361,16 @@ btr_node_ptr_get_child_page_no( @param[in] type type of the index @param[in,out] space tablespace where created @param[in] index_id index id -@param[in] index index, or NULL when applying TRUNCATE -log record during recovery -@param[in] btr_redo_create_info used for applying TRUNCATE log -@param[in] mtr mini-transaction handle -record during recovery -@return page number of the created root, FIL_NULL if did not succeed */ +@param[in] index index +@param[in,out] mtr mini-transaction +@return page number of the created root +@retval FIL_NULL if did not succeed */ ulint btr_create( ulint type, fil_space_t* space, index_id_t index_id, dict_index_t* index, - const btr_create_t* btr_redo_create_info, mtr_t* mtr); /** Free a persistent index tree if it exists. diff --git a/storage/innobase/include/btr0types.h b/storage/innobase/include/btr0types.h index 77d1b286fbb..615ad5e5ae0 100644 --- a/storage/innobase/include/btr0types.h +++ b/storage/innobase/include/btr0types.h @@ -1,6 +1,7 @@ /***************************************************************************** Copyright (c) 1996, 2015, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2018, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -58,35 +59,4 @@ in the index record. */ #define BTR_EXTERN_LOCAL_STORED_MAX_SIZE \ (BTR_EXTERN_FIELD_REF_SIZE * 2) -/** The information is used for creating a new index tree when -applying TRUNCATE log record during recovery */ -struct btr_create_t { - - explicit btr_create_t(const byte* const ptr) - : - format_flags(), - n_fields(), - field_len(), - fields(ptr), - trx_id_pos(ULINT_UNDEFINED) - { - /* Do nothing */ - } - - /** Page format */ - ulint format_flags; - - /** Numbr of index fields */ - ulint n_fields; - - /** The length of the encoded meta-data */ - ulint field_len; - - /** Field meta-data, encoded. */ - const byte* const fields; - - /** Position of trx-id column. */ - ulint trx_id_pos; -}; - #endif diff --git a/storage/innobase/include/db0err.h b/storage/innobase/include/db0err.h index ec8e29d458c..2110018b6d4 100644 --- a/storage/innobase/include/db0err.h +++ b/storage/innobase/include/db0err.h @@ -135,8 +135,6 @@ enum dberr_t { DB_FTS_TOO_MANY_WORDS_IN_PHRASE, /*< Too many words in a phrase */ - DB_TABLESPACE_TRUNCATED, /*!< tablespace was truncated */ - DB_DECRYPTION_FAILED, /* Tablespace encrypted and decrypt operation failed because of missing key management plugin, diff --git a/storage/innobase/include/fil0fil.h b/storage/innobase/include/fil0fil.h index d0dc02e6208..947d9d1c860 100644 --- a/storage/innobase/include/fil0fil.h +++ b/storage/innobase/include/fil0fil.h @@ -40,7 +40,6 @@ extern my_bool srv_use_doublewrite_buf; extern struct buf_dblwr_t* buf_dblwr; struct trx_t; class page_id_t; -class truncate_t; /** Structure containing encryption specification */ struct fil_space_crypt_t; @@ -1086,7 +1085,7 @@ fil_space_extend( @param[in] message message for aio handler if non-sync aio used, else ignored @param[in] ignore_missing_space true=ignore missing space during read -@return DB_SUCCESS, DB_TABLESPACE_DELETED or DB_TABLESPACE_TRUNCATED +@return DB_SUCCESS, or DB_TABLESPACE_DELETED if we are trying to do i/o on a tablespace which does not exist */ dberr_t fil_io( diff --git a/storage/innobase/include/log0recv.h b/storage/innobase/include/log0recv.h index d15ec19d86b..c18da743288 100644 --- a/storage/innobase/include/log0recv.h +++ b/storage/innobase/include/log0recv.h @@ -145,10 +145,6 @@ corresponding to MLOG_INDEX_LOAD. */ extern void (*log_optimized_ddl_op)(ulint space_id); -/** Report backup-unfriendly TRUNCATE operation (with separate log file), -corresponding to MLOG_TRUNCATE. */ -extern void (*log_truncate)(); - /** Report an operation to create, delete, or rename a file during backup. @param[in] space_id tablespace identifier @param[in] flags tablespace flags (NULL if not create) diff --git a/storage/innobase/include/mtr0types.h b/storage/innobase/include/mtr0types.h index eaf838aaa76..d88a31c96a3 100644 --- a/storage/innobase/include/mtr0types.h +++ b/storage/innobase/include/mtr0types.h @@ -216,7 +216,8 @@ enum mlog_id_t { /** initialize a file page */ MLOG_INIT_FILE_PAGE2 = 59, - /** Table is being truncated. (Marked only for file-per-table) */ + /** Table is being truncated. (Was used in 10.2 and 10.3; + not supported for crash-upgrade to 10.4 or later.) */ MLOG_TRUNCATE = 60, /** notify that an index tree is being loaded without writing diff --git a/storage/innobase/include/page0page.h b/storage/innobase/include/page0page.h index d3f6bd304a6..4132c24609b 100644 --- a/storage/innobase/include/page0page.h +++ b/storage/innobase/include/page0page.h @@ -1061,10 +1061,6 @@ page_create_zip( ulint level, /*!< in: the B-tree level of the page */ trx_id_t max_trx_id, /*!< in: PAGE_MAX_TRX_ID */ - const redo_page_compress_t* page_comp_info, - /*!< in: used for applying - TRUNCATE log - record during recovery */ mtr_t* mtr); /*!< in/out: mini-transaction handle */ /**********************************************************//** diff --git a/storage/innobase/include/page0types.h b/storage/innobase/include/page0types.h index fe56468c454..2fe433db959 100644 --- a/storage/innobase/include/page0types.h +++ b/storage/innobase/include/page0types.h @@ -85,18 +85,6 @@ enum page_cur_mode_t { PAGE_CUR_RTREE_GET_FATHER = 14 }; - -/** The information used for compressing a page when applying -TRUNCATE log record during recovery */ -struct redo_page_compress_t { - ulint type; /*!< index type */ - index_id_t index_id; /*!< index id */ - ulint n_fields; /*!< number of index fields */ - ulint field_len; /*!< the length of index field */ - const byte* fields; /*!< index field information */ - ulint trx_id_pos; /*!< position of trx-id column. */ -}; - /** Compressed page descriptor */ struct page_zip_des_t { diff --git a/storage/innobase/include/page0zip.h b/storage/innobase/include/page0zip.h index 6e0c097bbaf..cd999ab6891 100644 --- a/storage/innobase/include/page0zip.h +++ b/storage/innobase/include/page0zip.h @@ -2,7 +2,7 @@ Copyright (c) 2005, 2016, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2012, Facebook Inc. -Copyright (c) 2017, MariaDB Corporation. +Copyright (c) 2017, 2018, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software @@ -164,10 +164,6 @@ page_zip_compress( dict_index_t* index, /*!< in: index of the B-tree node */ ulint level, /*!< in: commpression level */ - const redo_page_compress_t* page_comp_info, - /*!< in: used for applying - TRUNCATE log - record during recovery */ mtr_t* mtr); /*!< in/out: mini-transaction, or NULL */ diff --git a/storage/innobase/include/page0zip.ic b/storage/innobase/include/page0zip.ic index b3ebc5dcf51..eefe7c8f0f7 100644 --- a/storage/innobase/include/page0zip.ic +++ b/storage/innobase/include/page0zip.ic @@ -414,7 +414,7 @@ page_zip_parse_compress_no_data( was successful. Crash in this case. */ if (page - && !page_zip_compress(page_zip, page, index, level, NULL, NULL)) { + && !page_zip_compress(page_zip, page, index, level, NULL)) { ut_error; } diff --git a/storage/innobase/include/row0trunc.h b/storage/innobase/include/row0trunc.h deleted file mode 100644 index 993dac295da..00000000000 --- a/storage/innobase/include/row0trunc.h +++ /dev/null @@ -1,417 +0,0 @@ -/***************************************************************************** - -Copyright (c) 2013, 2015, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2018, MariaDB Corporation. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; version 2 of the License. - -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 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 Street, Suite 500, Boston, MA 02110-1335 USA - -*****************************************************************************/ - -/**************************************************//** -@file include/row0trunc.h -TRUNCATE implementation - -Created 2013-04-25 Krunal Bauskar -*******************************************************/ - -#ifndef row0trunc_h -#define row0trunc_h - -#include "row0mysql.h" -#include "dict0boot.h" -#include "fil0fil.h" -#include "srv0start.h" -#include "ut0new.h" - -#include - -/** The information of TRUNCATE log record. -This class handles the recovery stage of TRUNCATE table. */ -class truncate_t { - -public: - /** - Constructor - - @param old_table_id old table id assigned to table before truncate - @param new_table_id new table id that will be assigned to table - after truncate - @param dir_path directory path */ - truncate_t( - table_id_t old_table_id, - table_id_t new_table_id, - const char* dir_path); - - /** - Constructor - - @param log_file_name parse the log file during recovery to populate - information related to table to truncate */ - truncate_t(const char* log_file_name); - - /** - Consturctor - - @param space_id space in which table reisde - @param name table name - @param tablespace_flags tablespace flags use for recreating tablespace - @param log_flags page format flag - @param recv_lsn lsn of redo log record. */ - truncate_t( - ulint space_id, - const char* name, - ulint tablespace_flags, - ulint log_flags, - lsn_t recv_lsn); - - /** Destructor */ - ~truncate_t(); - - /** The index information of MLOG_FILE_TRUNCATE redo record */ - struct index_t { - - /* Default copy constructor and destructor should be OK. */ - - index_t(); - - /** - Set the truncate log values for a compressed table. - @return DB_CORRUPTION or error code */ - dberr_t set(const dict_index_t* index); - - typedef std::vector > fields_t; - - /** Index id */ - index_id_t m_id; - - /** Index type */ - ulint m_type; - - /** Root Page Number */ - ulint m_root_page_no; - - /** New Root Page Number. - Note: This field is not persisted to TRUNCATE log but used - during truncate table fix-up for updating SYS_XXXX tables. */ - ulint m_new_root_page_no; - - /** Number of index fields */ - ulint m_n_fields; - - /** DATA_TRX_ID column position. */ - ulint m_trx_id_pos; - - /** Compressed table field meta data, encode by - page_zip_fields_encode. Empty for non-compressed tables. - Should be NUL terminated. */ - fields_t m_fields; - }; - - /** - @return the directory path, can be NULL */ - const char* get_dir_path() const - { - return(m_dir_path); - } - - /** - Register index information - - @param index index information logged as part of truncate log. */ - void add(index_t& index) - { - m_indexes.push_back(index); - } - - /** - Add table to truncate post recovery. - - @param ptr table information need to complete truncate of table. */ - static void add(truncate_t* ptr) - { - s_tables.push_back(ptr); - } - - /** - Clear registered index vector */ - void clear() - { - m_indexes.clear(); - } - - /** - @return old table id of the table to truncate */ - table_id_t old_table_id() const - { - return(m_old_table_id); - } - - /** - @return new table id of the table to truncate */ - table_id_t new_table_id() const - { - return(m_new_table_id); - } - - /** - Update root page number in SYS_XXXX tables. - - @param trx transaction object - @param table_id table id for which information needs to - be updated. - @param reserve_dict_mutex if TRUE, acquire/release - dict_sys->mutex around call to pars_sql. - @param mark_index_corrupted if true, then mark index corrupted - @return DB_SUCCESS or error code */ - dberr_t update_root_page_no( - trx_t* trx, - table_id_t table_id, - ibool reserve_dict_mutex, - bool mark_index_corrupted) const; - - /** Create an index for a table. - @param[in] table_name table name, for which to create - the index - @param[in,out] space tablespace - @param[in] index_type type of index to truncate - @param[in] index_id id of index to truncate - @param[in] btr_redo_create_info control info for ::btr_create() - @param[in,out] mtr mini-transaction covering the - create index - @return root page no or FIL_NULL on failure */ - inline ulint create_index( - const char* table_name, - fil_space_t* space, - ulint index_type, - index_id_t index_id, - const btr_create_t& btr_redo_create_info, - mtr_t* mtr) const; - - /** Create the indexes for a table - @param[in] table_name table name, for which to create the - indexes - @param[in,out] space tablespace - @param[in] format_flags page format flags - @return DB_SUCCESS or error code. */ - inline dberr_t create_indexes( - const char* table_name, - fil_space_t* space, - ulint format_flags); - - /** Check if index has been modified since TRUNCATE log snapshot - was recorded. - @param[in] space tablespace - @param[in] root_page_no index root page number - @return true if modified else false */ - inline bool is_index_modified_since_logged( - const fil_space_t* space, - ulint root_page_no) const; - - /** Drop indexes for a table. - @param[in,out] space tablespace - @return DB_SUCCESS or error code. */ - void drop_indexes(fil_space_t* space) const; - - /** - Parses log record during recovery - @param start_ptr buffer containing log body to parse - @param end_ptr buffer end - - @return DB_SUCCESS or error code */ - dberr_t parse( - byte* start_ptr, - const byte* end_ptr); - - /** Parse MLOG_TRUNCATE log record from REDO log file during recovery. - @param[in,out] start_ptr buffer containing log body to parse - @param[in] end_ptr buffer end - @param[in] space_id tablespace identifier - @return parsed upto or NULL. */ - static byte* parse_redo_entry( - byte* start_ptr, - const byte* end_ptr, - ulint space_id); - - /** - Write a log record for truncating a single-table tablespace. - - @param start_ptr buffer to write log record - @param end_ptr buffer end - @param space_id space id - @param tablename the table name in the usual - databasename/tablename format of InnoDB - @param flags tablespace flags - @param format_flags page format - @param lsn lsn while logging */ - dberr_t write( - byte* start_ptr, - byte* end_ptr, - ulint space_id, - const char* tablename, - ulint flags, - ulint format_flags, - lsn_t lsn) const; - - /** - @return number of indexes parsed from the truncate log record */ - size_t indexes() const; - - /** - Truncate a single-table tablespace. The tablespace must be cached - in the memory cache. - - Note: This is defined in fil0fil.cc because it needs to access some - types that are local to that file. - - @param space_id space id - @param dir_path directory path - @param tablename the table name in the usual - databasename/tablename format of InnoDB - @param flags tablespace flags - @param default_size if true, truncate to default size if tablespace - is being newly re-initialized. - @return DB_SUCCESS or error */ - static dberr_t truncate( - ulint space_id, - const char* dir_path, - const char* tablename, - ulint flags, - bool default_size); - - /** - Fix the table truncate by applying information parsed from TRUNCATE log. - Fix-up includes re-creating table (drop and re-create indexes) - @return error code or DB_SUCCESS */ - static dberr_t fixup_tables_in_system_tablespace(); - - /** - Fix the table truncate by applying information parsed from TRUNCATE log. - Fix-up includes re-creating tablespace. - @return error code or DB_SUCCESS */ - static dberr_t fixup_tables_in_non_system_tablespace(); - - /** - Check whether a tablespace was truncated during recovery - @param space_id tablespace id to check - @return true if the tablespace was truncated */ - static bool is_tablespace_truncated(ulint space_id); - - /** Was tablespace truncated (on crash before checkpoint). - If the MLOG_TRUNCATE redo-record is still available then tablespace - was truncated and checkpoint is yet to happen. - @param[in] space_id tablespace id to check. - @return true if tablespace was truncated. */ - static bool was_tablespace_truncated(ulint space_id); - - /** Get the lsn associated with space. - @param[in] space_id tablespace id to check. - @return associated lsn. */ - static lsn_t get_truncated_tablespace_init_lsn(ulint space_id); - -private: - typedef std::vector > indexes_t; - - /** Space ID of tablespace */ - ulint m_space_id; - - /** ID of table that is being truncated. */ - table_id_t m_old_table_id; - - /** New ID that will be assigned to table on truncation. */ - table_id_t m_new_table_id; - - /** Data dir path of tablespace */ - char* m_dir_path; - - /** Table name */ - char* m_tablename; - - /** Tablespace Flags */ - ulint m_tablespace_flags; - - /** Format flags (log flags; stored in page-no field of header) */ - ulint m_format_flags; - - /** Index meta-data */ - indexes_t m_indexes; - - /** LSN of TRUNCATE log record. */ - lsn_t m_log_lsn; - - /** Log file name. */ - char* m_log_file_name; - - /** Encryption information of the table */ - fil_encryption_t m_encryption; - uint32_t m_key_id; - - /** Vector of tables to truncate. */ - typedef std::vector > - tables_t; - - /** Information about tables to truncate post recovery */ - static tables_t s_tables; - - /** Information about truncated table - This is case when truncate is complete but checkpoint hasn't. */ - typedef std::map truncated_tables_t; - static truncated_tables_t s_truncated_tables; - -public: - /** If true then fix-up of table is active and so while creating - index instead of grabbing information from dict_index_t, grab it - from parsed truncate log record. */ - static bool s_fix_up_active; -}; - -/** -Parse truncate log file. */ -class TruncateLogParser { - -public: - - /** - Scan and Parse truncate log files. - - @param dir_path look for log directory in following path - @return DB_SUCCESS or error code. */ - static dberr_t scan_and_parse( - const char* dir_path); - -private: - typedef std::vector > - trunc_log_files_t; - -private: - /** - Scan to find out truncate log file from the given directory path. - - @param dir_path look for log directory in following path. - @param log_files cache to hold truncate log file name found. - @return DB_SUCCESS or error code. */ - static dberr_t scan( - const char* dir_path, - trunc_log_files_t& log_files); - - /** - Parse the log file and populate table to truncate information. - (Add this table to truncate information to central vector that is then - used by truncate fix-up routine to fix-up truncate action of the table.) - - @param log_file_name log file to parse - @return DB_SUCCESS or error code. */ - static dberr_t parse( - const char* log_file_name); -}; - -#endif /* row0trunc_h */ diff --git a/storage/innobase/include/srv0srv.h b/storage/innobase/include/srv0srv.h index 1f730842db1..25a71ffbafe 100644 --- a/storage/innobase/include/srv0srv.h +++ b/storage/innobase/include/srv0srv.h @@ -900,23 +900,6 @@ srv_purge_wakeup(); /** Shut down the purge threads. */ void srv_purge_shutdown(); -/** Check if tablespace is being truncated. -(Ignore system-tablespace as we don't re-create the tablespace -and so some of the action that are suppressed by this function -for independent tablespace are not applicable to system-tablespace). -@param space_id space_id to check for truncate action -@return true if being truncated, false if not being - truncated or tablespace is system-tablespace. */ -bool -srv_is_tablespace_truncated(ulint space_id); - -/** Check if tablespace was truncated. -@param[in] space space object to check for truncate action -@return true if tablespace was truncated and we still have an active -MLOG_TRUNCATE REDO log record. */ -bool -srv_was_tablespace_truncated(const fil_space_t* space); - #ifdef UNIV_DEBUG /** Disables master thread. It's used by: SET GLOBAL innodb_master_thread_disabled_debug = 1 (0). diff --git a/storage/innobase/include/trx0purge.h b/storage/innobase/include/trx0purge.h index 27807321212..b2f1797b706 100644 --- a/storage/innobase/include/trx0purge.h +++ b/storage/innobase/include/trx0purge.h @@ -133,202 +133,6 @@ private: TrxUndoRsegs::const_iterator m_iter; }; -/* Namespace to hold all the related functions and variables need for truncate -of undo tablespace. */ -namespace undo { - - typedef std::vector undo_spaces_t; - typedef std::vector rseg_for_trunc_t; - - /** Mark completion of undo truncate action by writing magic number to - the log file and then removing it from the disk. - If we are going to remove it from disk then why write magic number ? - This is to safeguard from unlink (file-system) anomalies that will keep - the link to the file even after unlink action is successfull and - ref-count = 0. - @param[in] space_id id of the undo tablespace to truncate.*/ - void done(ulint space_id); - - /** Check if TRUNCATE_DDL_LOG file exist. - @param[in] space_id id of the undo tablespace. - @return true if exist else false. */ - bool is_log_present(ulint space_id); - - /** Track UNDO tablespace mark for truncate. */ - class Truncate { - public: - void create() - { - m_undo_for_trunc = ULINT_UNDEFINED; - m_scan_start = 1; - m_purge_rseg_truncate_frequency = - ulint(srv_purge_rseg_truncate_frequency); - } - - /** Clear the cached rollback segment. Normally done - when purge is about to shutdown. */ - void clear() - { - reset(); - rseg_for_trunc_t temp; - m_rseg_for_trunc.swap(temp); - } - - /** Is tablespace selected for truncate. - @return true if undo tablespace is marked for truncate */ - bool is_marked() const - { - return(!(m_undo_for_trunc == ULINT_UNDEFINED)); - } - - /** Mark the tablespace for truncate. - @param[in] undo_id tablespace for truncate. */ - void mark(ulint undo_id) - { - m_undo_for_trunc = undo_id; - - m_scan_start = (undo_id + 1) - % (srv_undo_tablespaces_active + 1); - if (m_scan_start == 0) { - /* Note: UNDO tablespace ids starts from 1. */ - m_scan_start = 1; - } - - /* We found an UNDO-tablespace to truncate so set the - local purge rseg truncate frequency to 1. This will help - accelerate the purge action and in turn truncate. */ - m_purge_rseg_truncate_frequency = 1; - } - - /** Get the tablespace marked for truncate. - @return tablespace id marked for truncate. */ - ulint get_marked_space_id() const - { - return(m_undo_for_trunc); - } - - /** Add rseg to truncate vector. - @param[in,out] rseg rseg for truncate */ - void add_rseg_to_trunc(trx_rseg_t* rseg) - { - m_rseg_for_trunc.push_back(rseg); - } - - /** Get number of rsegs registered for truncate. - @return return number of rseg that belongs to tablespace mark - for truncate. */ - ulint rsegs_size() const - { - return(m_rseg_for_trunc.size()); - } - - /** Get ith registered rseg. - @param[in] id index of rseg to get. - @return reference to registered rseg. */ - trx_rseg_t* get_ith_rseg(ulint id) - { - ut_ad(id < m_rseg_for_trunc.size()); - return(m_rseg_for_trunc.at(id)); - } - - /** Reset for next rseg truncate. */ - void reset() - { - m_undo_for_trunc = ULINT_UNDEFINED; - m_rseg_for_trunc.clear(); - - /* Sync with global value as we are done with - truncate now. */ - m_purge_rseg_truncate_frequency = static_cast( - srv_purge_rseg_truncate_frequency); - } - - /** Get the tablespace id to start scanning from. - @return id of UNDO tablespace to start scanning from. */ - ulint get_scan_start() const - { - return(m_scan_start); - } - - /** Check if the tablespace needs fix-up (based on presence of - DDL truncate log) - @param space_id space id of the undo tablespace to check - @return true if fix up is needed else false */ - bool needs_fix_up(ulint space_id) const - { - return(is_log_present(space_id)); - } - - /** Add undo tablespace to truncate vector. - @param[in] space_id space id of tablespace to - truncate */ - static void add_space_to_trunc_list(ulint space_id) - { - s_spaces_to_truncate.push_back(space_id); - } - - /** Clear the truncate vector. */ - static void clear_trunc_list() - { - s_spaces_to_truncate.clear(); - } - - /** Is tablespace marked for truncate. - @param[in] space_id space id to check - @return true if marked for truncate, else false. */ - static bool is_tablespace_truncated(ulint space_id) - { - return(std::find(s_spaces_to_truncate.begin(), - s_spaces_to_truncate.end(), space_id) - != s_spaces_to_truncate.end()); - } - - /** Was a tablespace truncated at startup - @param[in] space_id space id to check - @return whether space_id was truncated at startup */ - static bool was_tablespace_truncated(ulint space_id) - { - return(std::find(s_fix_up_spaces.begin(), - s_fix_up_spaces.end(), - space_id) - != s_fix_up_spaces.end()); - } - - /** Get local rseg purge truncate frequency - @return rseg purge truncate frequency. */ - ulint get_rseg_truncate_frequency() const - { - return(m_purge_rseg_truncate_frequency); - } - - private: - /** UNDO tablespace is mark for truncate. */ - ulint m_undo_for_trunc; - - /** rseg that resides in UNDO tablespace is marked for - truncate. */ - rseg_for_trunc_t m_rseg_for_trunc; - - /** Start scanning for UNDO tablespace from this space_id. - This is to avoid bias selection of one tablespace always. */ - ulint m_scan_start; - - /** Rollback segment(s) purge frequency. This is local - value maintained along with global value. It is set to global - value on start but when tablespace is marked for truncate it - is updated to 1 and then minimum value among 2 is used by - purge action. */ - ulint m_purge_rseg_truncate_frequency; - - /** List of UNDO tablespace(s) to truncate. */ - static undo_spaces_t s_spaces_to_truncate; - public: - /** Undo tablespaces that were truncated at startup */ - static undo_spaces_t s_fix_up_spaces; - }; /* class Truncate */ - -}; /* namespace undo */ - /** The control structure used in the purge operation */ class purge_sys_t { @@ -410,9 +214,14 @@ public: by the pq_mutex */ PQMutex pq_mutex; /*!< Mutex protecting purge_queue */ - undo::Truncate undo_trunc; /*!< Track UNDO tablespace marked - for truncate. */ - + /** Undo tablespace file truncation (only accessed by the + srv_purge_coordinator_thread) */ + struct { + /** The undo tablespace that is currently being truncated */ + fil_space_t* current; + /** The undo tablespace that was last truncated */ + fil_space_t* last; + } truncate; /** Constructor. diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 8824d7d21d8..5669ad10553 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -54,7 +54,6 @@ Created 9/20/1997 Heikki Tuuri #include "fil0fil.h" #include "fsp0sysspace.h" #include "ut0new.h" -#include "row0trunc.h" #include "buf0rea.h" #include "srv0srv.h" #include "srv0start.h" @@ -203,10 +202,6 @@ corresponding to MLOG_INDEX_LOAD. */ void (*log_optimized_ddl_op)(ulint space_id); -/** Report backup-unfriendly TRUNCATE operation (with separate log file), -corresponding to MLOG_TRUNCATE. */ -void (*log_truncate)(); - /** Report an operation to create, delete, or rename a file during backup. @param[in] space_id tablespace identifier @param[in] flags tablespace flags (NULL if not create) @@ -1205,14 +1200,10 @@ recv_parse_or_apply_log_rec_body( } return(ptr + 8); case MLOG_TRUNCATE: - if (log_truncate) { - ut_ad(srv_operation != SRV_OPERATION_NORMAL); - log_truncate(); - recv_sys->found_corrupt_fs = true; - return NULL; - } - return(truncate_t::parse_redo_entry(ptr, end_ptr, space_id)); - + ib::error() << "Cannot crash-upgrade from " + "old-style TRUNCATE TABLE"; + recv_sys->found_corrupt_log = true; + return NULL; default: break; } @@ -1795,13 +1786,10 @@ recv_recover_page(bool just_read_in, buf_block_t* block) page_t* page; page_zip_des_t* page_zip; recv_addr_t* recv_addr; - recv_t* recv; - byte* buf; lsn_t start_lsn; lsn_t end_lsn; lsn_t page_lsn; lsn_t page_newest_lsn; - ibool modification_to_page; mtr_t mtr; mutex_enter(&(recv_sys->mutex)); @@ -1876,57 +1864,19 @@ recv_recover_page(bool just_read_in, buf_block_t* block) page_lsn = page_newest_lsn; } - modification_to_page = FALSE; start_lsn = end_lsn = 0; - recv = UT_LIST_GET_FIRST(recv_addr->rec_list); fil_space_t* space = fil_space_acquire(block->page.id.space()); - while (recv) { + for (recv_t* recv = UT_LIST_GET_FIRST(recv_addr->rec_list); + recv; recv = UT_LIST_GET_NEXT(rec_list, recv)) { end_lsn = recv->end_lsn; ut_ad(end_lsn <= log_sys.log.scanned_lsn); - if (recv->len > RECV_DATA_BLOCK_SIZE) { - /* We have to copy the record body to a separate - buffer */ - - buf = static_cast(ut_malloc_nokey(recv->len)); - - recv_data_copy_to_buf(buf, recv); - } else { - buf = ((byte*)(recv->data)) + sizeof(recv_data_t); - } - - /* If per-table tablespace was truncated and there exist REDO - records before truncate that are to be applied as part of - recovery (checkpoint didn't happen since truncate was done) - skip such records using lsn check as they may not stand valid - post truncate. - LSN at start of truncate is recorded and any redo record - with LSN less than recorded LSN is skipped. - Note: We can't skip complete recv_addr as same page may have - valid REDO records post truncate those needs to be applied. */ - - /* Ignore applying the redo logs for tablespace that is - truncated. Post recovery there is fixup action that will - restore the tablespace back to normal state. - Applying redo at this stage can result in error given that - redo will have action recorded on page before tablespace - was re-inited and that would lead to an error while applying - such action. */ - if (recv->start_lsn >= page_lsn - && !srv_is_tablespace_truncated(space->id) - && !(srv_was_tablespace_truncated(space) - && recv->start_lsn - < truncate_t::get_truncated_tablespace_init_lsn( - space->id))) { - - lsn_t end_lsn; - - if (!modification_to_page) { - - modification_to_page = TRUE; + ut_ad(recv->start_lsn); + if (recv->start_lsn >= page_lsn) { + if (!start_lsn) { start_lsn = recv->start_lsn; } @@ -1942,29 +1892,41 @@ recv_recover_page(bool just_read_in, buf_block_t* block) << " len " << recv->len << " page " << block->page.id); + byte* buf; + + if (recv->len > RECV_DATA_BLOCK_SIZE) { + /* We have to copy the record body to + a separate buffer */ + + buf = static_cast(ut_malloc_nokey( + recv->len)); + + recv_data_copy_to_buf(buf, recv); + } else { + buf = reinterpret_cast(recv->data) + + sizeof *recv->data; + } + recv_parse_or_apply_log_rec_body( recv->type, buf, buf + recv->len, block->page.id.space(), - block->page.id.page_no(), - true, block, &mtr); + block->page.id.page_no(), true, block, &mtr); - end_lsn = recv->start_lsn + recv->len; + lsn_t end_lsn = recv->start_lsn + recv->len; mach_write_to_8(FIL_PAGE_LSN + page, end_lsn); mach_write_to_8(srv_page_size - FIL_PAGE_END_LSN_OLD_CHKSUM + page, end_lsn); if (page_zip) { - mach_write_to_8(FIL_PAGE_LSN - + page_zip->data, end_lsn); + mach_write_to_8(FIL_PAGE_LSN + page_zip->data, + end_lsn); + } + + if (recv->len > RECV_DATA_BLOCK_SIZE) { + ut_free(buf); } } - - if (recv->len > RECV_DATA_BLOCK_SIZE) { - ut_free(buf); - } - - recv = UT_LIST_GET_NEXT(rec_list, recv); } space->release(); @@ -1978,9 +1940,7 @@ recv_recover_page(bool just_read_in, buf_block_t* block) } #endif /* UNIV_ZIP_DEBUG */ - if (modification_to_page) { - ut_a(block); - + if (start_lsn) { log_flush_order_mutex_enter(); buf_flush_recv_note_modification(block, start_lsn, end_lsn); log_flush_order_mutex_exit(); @@ -2095,6 +2055,17 @@ recv_apply_hashed_log_recs(bool last_batch) ut_d(recv_no_log_write = recv_no_ibuf_operations); if (ulint n = recv_sys->n_addrs) { + if (!log_sys.log.subformat && !srv_force_recovery + && srv_undo_tablespaces_open) { + ib::error() << "Recovery of separately logged" + " TRUNCATE operations is no longer supported." + " Set innodb_force_recovery=1" + " if no *trunc.log files exist"; + recv_sys->found_corrupt_log = true; + mutex_exit(&recv_sys->mutex); + return; + } + const char* msg = last_batch ? "Starting final batch to recover " : "Starting a batch to recover "; @@ -2120,15 +2091,6 @@ recv_apply_hashed_log_recs(bool last_batch) recv_addr = static_cast( HASH_GET_NEXT(addr_hash, recv_addr))) { - if (srv_is_tablespace_truncated(recv_addr->space)) { - /* Avoid applying REDO log for the tablespace - that is schedule for TRUNCATE. */ - ut_a(recv_sys->n_addrs); - recv_addr->state = RECV_DISCARDED; - recv_sys->n_addrs--; - continue; - } - if (recv_addr->state == RECV_DISCARDED) { ut_a(recv_sys->n_addrs); recv_sys->n_addrs--; diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index 215bfbaf42c..aba40c445ad 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -32,7 +32,6 @@ Created 11/26/1995 Heikki Tuuri #include "page0types.h" #include "mtr0log.h" #include "log0log.h" -#include "row0trunc.h" #include "log0recv.h" @@ -695,8 +694,7 @@ mtr_t::x_lock_space(ulint space_id, const char* file, unsigned line) ut_ad(get_log_mode() != MTR_LOG_NO_REDO || space->purpose == FIL_TYPE_TEMPORARY || space->purpose == FIL_TYPE_IMPORT - || my_atomic_loadlint(&space->redo_skipped_count) > 0 - || srv_is_tablespace_truncated(space->id)); + || my_atomic_loadlint(&space->redo_skipped_count) > 0); } ut_ad(space); diff --git a/storage/innobase/page/page0cur.cc b/storage/innobase/page/page0cur.cc index fb8f62f5fe3..7f3328bc723 100644 --- a/storage/innobase/page/page0cur.cc +++ b/storage/innobase/page/page0cur.cc @@ -1566,7 +1566,7 @@ page_cur_insert_rec_zip( get rid of the modification log. */ page_create_zip(page_cur_get_block(cursor), index, page_header_get_field(page, PAGE_LEVEL), - 0, NULL, mtr); + 0, mtr); ut_ad(!page_header_get_ptr(page, PAGE_FREE)); if (page_zip_available( @@ -1641,7 +1641,7 @@ page_cur_insert_rec_zip( if (!log_compressed) { if (page_zip_compress( page_zip, page, index, - level, NULL, NULL)) { + level, NULL)) { page_cur_insert_rec_write_log( insert_rec, rec_size, cursor->rec, index, mtr); diff --git a/storage/innobase/page/page0page.cc b/storage/innobase/page/page0page.cc index d438e4b64e0..43a097404e9 100644 --- a/storage/innobase/page/page0page.cc +++ b/storage/innobase/page/page0page.cc @@ -30,7 +30,6 @@ Created 2/2/1994 Heikki Tuuri #include "page0zip.h" #include "buf0buf.h" #include "btr0btr.h" -#include "row0trunc.h" #include "srv0srv.h" #include "lock0lock.h" #include "fut0lst.h" @@ -454,22 +453,15 @@ page_create_zip( ulint level, /*!< in: the B-tree level of the page */ trx_id_t max_trx_id, /*!< in: PAGE_MAX_TRX_ID */ - const redo_page_compress_t* page_comp_info, - /*!< in: used for applying - TRUNCATE log - record during recovery */ mtr_t* mtr) /*!< in/out: mini-transaction handle */ { page_t* page; page_zip_des_t* page_zip = buf_block_get_page_zip(block); - bool is_spatial; ut_ad(block); ut_ad(page_zip); - ut_ad(index == NULL || dict_table_is_comp(index->table)); - is_spatial = index ? dict_index_is_spatial(index) - : page_comp_info->type & DICT_SPATIAL; + ut_ad(dict_table_is_comp(index->table)); /* PAGE_MAX_TRX_ID or PAGE_ROOT_AUTO_INC are always 0 for temporary tables. */ @@ -487,22 +479,11 @@ page_create_zip( || !dict_index_is_sec_or_ibuf(index) || index->table->is_temporary()); - page = page_create_low(block, TRUE, is_spatial); + page = page_create_low(block, TRUE, dict_index_is_spatial(index)); mach_write_to_2(PAGE_HEADER + PAGE_LEVEL + page, level); mach_write_to_8(PAGE_HEADER + PAGE_MAX_TRX_ID + page, max_trx_id); - if (truncate_t::s_fix_up_active) { - /* Compress the index page created when applying - TRUNCATE log during recovery */ - if (!page_zip_compress(page_zip, page, index, page_zip_level, - page_comp_info, NULL)) { - /* The compression of a newly created - page should always succeed. */ - ut_error; - } - - } else if (!page_zip_compress(page_zip, page, index, - page_zip_level, NULL, mtr)) { + if (!page_zip_compress(page_zip, page, index, page_zip_level, mtr)) { /* The compression of a newly created page should always succeed. */ ut_error; @@ -546,7 +527,7 @@ page_create_empty( ut_ad(!index->table->is_temporary()); page_create_zip(block, index, page_header_get_field(page, PAGE_LEVEL), - max_trx_id, NULL, mtr); + max_trx_id, mtr); } else { page_create(block, mtr, page_is_comp(page), dict_index_is_spatial(index)); @@ -721,11 +702,8 @@ page_copy_rec_list_end( if (new_page_zip) { mtr_set_log_mode(mtr, log_mode); - if (!page_zip_compress(new_page_zip, - new_page, - index, - page_zip_level, - NULL, mtr)) { + if (!page_zip_compress(new_page_zip, new_page, index, + page_zip_level, mtr)) { /* Before trying to reorganize the page, store the number of preceding records on the page. */ ulint ret_pos @@ -887,7 +865,7 @@ page_copy_rec_list_start( goto zip_reorganize;); if (!page_zip_compress(new_page_zip, new_page, index, - page_zip_level, NULL, mtr)) { + page_zip_level, mtr)) { ulint ret_pos; #ifndef DBUG_OFF zip_reorganize: diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc index 4b611baefae..d0845a7f640 100644 --- a/storage/innobase/page/page0zip.cc +++ b/storage/innobase/page/page0zip.cc @@ -46,7 +46,6 @@ const byte field_ref_zero[FIELD_REF_SIZE] = { #include "page0types.h" #include "log0recv.h" #include "row0row.h" -#include "row0trunc.h" #include "zlib.h" #include "buf0buf.h" #include "buf0types.h" @@ -1248,17 +1247,11 @@ page_zip_compress( dict_index_t* index, /*!< in: index of the B-tree node */ ulint level, /*!< in: commpression level */ - const redo_page_compress_t* page_comp_info, - /*!< in: used for applying - TRUNCATE log - record during recovery */ mtr_t* mtr) /*!< in/out: mini-transaction, or NULL */ { z_stream c_stream; int err; - ulint n_fields; /* number of index fields - needed */ byte* fields; /*!< index field information */ byte* buf; /*!< compressed payload of the page */ @@ -1273,7 +1266,6 @@ page_zip_compress( ulint n_blobs = 0; byte* storage; /* storage of uncompressed columns */ - index_id_t ind_id; uintmax_t usec = ut_time_us(NULL); #ifdef PAGE_ZIP_COMPRESS_DBG FILE* logfile = NULL; @@ -1288,10 +1280,8 @@ page_zip_compress( ut_a(fil_page_index_page_check(page)); ut_ad(page_simple_validate_new((page_t*) page)); ut_ad(page_zip_simple_validate(page_zip)); - ut_ad(!index - || (index - && dict_table_is_comp(index->table) - && !dict_index_is_ibuf(index))); + ut_ad(dict_table_is_comp(index->table)); + ut_ad(!dict_index_is_ibuf(index)); UNIV_MEM_ASSERT_RW(page, srv_page_size); @@ -1311,18 +1301,10 @@ page_zip_compress( == PAGE_NEW_SUPREMUM); } - if (truncate_t::s_fix_up_active) { - ut_ad(page_comp_info != NULL); - n_fields = page_comp_info->n_fields; - ind_id = page_comp_info->index_id; - } else { - if (page_is_leaf(page)) { - n_fields = dict_index_get_n_fields(index); - } else { - n_fields = dict_index_get_n_unique_in_tree_nonleaf(index); - } - ind_id = index->id; - } + const ulint n_fields = page_is_leaf(page) + ? dict_index_get_n_fields(index) + : dict_index_get_n_unique_in_tree_nonleaf(index); + index_id_t ind_id = index->id; /* The dense directory excludes the infimum and supremum records. */ n_dense = ulint(page_dir_get_n_heap(page)) - PAGE_HEAP_NO_USER_LOW; @@ -1433,20 +1415,11 @@ page_zip_compress( /* Dense page directory and uncompressed columns, if any */ if (page_is_leaf(page)) { - if ((index && dict_index_is_clust(index)) - || (page_comp_info - && (page_comp_info->type & DICT_CLUSTERED))) { - - if (index) { - trx_id_col = dict_index_get_sys_col_pos( - index, DATA_TRX_ID); - ut_ad(trx_id_col > 0); - ut_ad(trx_id_col != ULINT_UNDEFINED); - } else if (page_comp_info - && (page_comp_info->type - & DICT_CLUSTERED)) { - trx_id_col = page_comp_info->trx_id_pos; - } + if (dict_index_is_clust(index)) { + trx_id_col = dict_index_get_sys_col_pos( + index, DATA_TRX_ID); + ut_ad(trx_id_col > 0); + ut_ad(trx_id_col != ULINT_UNDEFINED); slot_size = PAGE_ZIP_DIR_SLOT_SIZE + DATA_TRX_ID_LEN + DATA_ROLL_PTR_LEN; @@ -1454,10 +1427,8 @@ page_zip_compress( } else { /* Signal the absence of trx_id in page_zip_fields_encode() */ - if (index) { - ut_ad(dict_index_get_sys_col_pos( - index, DATA_TRX_ID) == ULINT_UNDEFINED); - } + ut_ad(dict_index_get_sys_col_pos( + index, DATA_TRX_ID) == ULINT_UNDEFINED); trx_id_col = 0; slot_size = PAGE_ZIP_DIR_SLOT_SIZE; } @@ -1471,19 +1442,9 @@ page_zip_compress( goto zlib_error; } - c_stream.avail_out -= static_cast(n_dense * slot_size); - if (truncate_t::s_fix_up_active) { - ut_ad(page_comp_info != NULL); - c_stream.avail_in = static_cast( - page_comp_info->field_len); - for (ulint i = 0; i < page_comp_info->field_len; i++) { - fields[i] = page_comp_info->fields[i]; - } - } else { - c_stream.avail_in = static_cast( - page_zip_fields_encode( - n_fields, index, trx_id_col, fields)); - } + c_stream.avail_out -= uInt(n_dense * slot_size); + c_stream.avail_in = uInt(page_zip_fields_encode(n_fields, index, + trx_id_col, fields)); c_stream.next_in = fields; if (UNIV_LIKELY(!trx_id_col)) { @@ -1637,7 +1598,7 @@ err_exit: mutex_exit(&page_zip_stat_per_index_mutex); } - if (page_is_leaf(page) && !truncate_t::s_fix_up_active) { + if (page_is_leaf(page)) { dict_index_zip_success(index); } @@ -4807,9 +4768,7 @@ page_zip_reorganize( /* Restore logging. */ mtr_set_log_mode(mtr, log_mode); - if (!page_zip_compress(page_zip, page, index, - page_zip_level, NULL, mtr)) { - + if (!page_zip_compress(page_zip, page, index, page_zip_level, mtr)) { buf_block_free(temp_block); return(FALSE); } diff --git a/storage/innobase/row/row0trunc.cc b/storage/innobase/row/row0trunc.cc deleted file mode 100644 index 39487d2749c..00000000000 --- a/storage/innobase/row/row0trunc.cc +++ /dev/null @@ -1,1966 +0,0 @@ -/***************************************************************************** - -Copyright (c) 2013, 2018, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2017, 2018, MariaDB Corporation. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; version 2 of the License. - -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 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 Street, Suite 500, Boston, MA 02110-1335 USA - -*****************************************************************************/ - -/**************************************************//** -@file row/row0trunc.cc -TRUNCATE implementation - -Created 2013-04-12 Sunny Bains -*******************************************************/ - -#include "row0trunc.h" -#include "btr0sea.h" -#include "pars0pars.h" -#include "dict0crea.h" -#include "dict0stats.h" -#include "dict0stats_bg.h" -#include "lock0lock.h" -#include "fts0fts.h" -#include "fsp0sysspace.h" -#include "ibuf0ibuf.h" -#include "os0file.h" -#include "que0que.h" -#include "trx0undo.h" - -/* FIXME: For temporary tables, use a simple approach of btr_free() -and btr_create() of each index tree. */ - -/* FIXME: For persistent tables, remove this code in MDEV-11655 -and use a combination of the transactional DDL log to make atomic the -low-level operations ha_innobase::delete_table(), ha_innobase::create(). */ - -bool truncate_t::s_fix_up_active = false; -truncate_t::tables_t truncate_t::s_tables; -truncate_t::truncated_tables_t truncate_t::s_truncated_tables; - -/** -Iterator over the the raw records in an index, doesn't support MVCC. */ -class IndexIterator { - -public: - /** - Iterate over an indexes records - @param index index to iterate over */ - explicit IndexIterator(dict_index_t* index) - : - m_index(index) - { - /* Do nothing */ - } - - /** - Search for key. Position the cursor on a record GE key. - @return DB_SUCCESS or error code. */ - dberr_t search(dtuple_t& key, bool noredo) - { - mtr_start(&m_mtr); - - if (noredo) { - mtr_set_log_mode(&m_mtr, MTR_LOG_NO_REDO); - } - - btr_pcur_open_on_user_rec( - m_index, - &key, - PAGE_CUR_GE, - BTR_MODIFY_LEAF, - &m_pcur, &m_mtr); - - return(DB_SUCCESS); - } - - /** - Iterate over all the records - @return DB_SUCCESS or error code */ - template - dberr_t for_each(Callback& callback) - { - dberr_t err = DB_SUCCESS; - - for (;;) { - - if (!btr_pcur_is_on_user_rec(&m_pcur) - || !callback.match(&m_pcur)) { - - /* The end of of the index has been reached. */ - err = DB_END_OF_INDEX; - break; - } - - rec_t* rec = btr_pcur_get_rec(&m_pcur); - - if (!rec_get_deleted_flag(rec, FALSE)) { - - err = callback(&m_mtr, &m_pcur); - - if (err != DB_SUCCESS) { - break; - } - } - - btr_pcur_move_to_next_user_rec(&m_pcur, &m_mtr); - } - - btr_pcur_close(&m_pcur); - mtr_commit(&m_mtr); - - return(err == DB_END_OF_INDEX ? DB_SUCCESS : err); - } - -private: - // Disable copying - IndexIterator(const IndexIterator&); - IndexIterator& operator=(const IndexIterator&); - -private: - mtr_t m_mtr; - btr_pcur_t m_pcur; - dict_index_t* m_index; -}; - -/** SysIndex table iterator, iterate over records for a table. */ -class SysIndexIterator { - -public: - /** - Iterate over all the records that match the table id. - @return DB_SUCCESS or error code */ - template - dberr_t for_each(Callback& callback) const - { - dict_index_t* sys_index; - byte buf[DTUPLE_EST_ALLOC(1)]; - dtuple_t* tuple = - dtuple_create_from_mem(buf, sizeof(buf), 1, 0); - dfield_t* dfield = dtuple_get_nth_field(tuple, 0); - - dfield_set_data( - dfield, - callback.table_id(), - sizeof(*callback.table_id())); - - sys_index = dict_table_get_first_index(dict_sys->sys_indexes); - - dict_index_copy_types(tuple, sys_index, 1); - - IndexIterator iterator(sys_index); - - /* Search on the table id and position the cursor - on GE table_id. */ - iterator.search(*tuple, callback.get_logging_status()); - - return(iterator.for_each(callback)); - } -}; - -/** Generic callback abstract class. */ -class Callback -{ - -public: - /** - Constructor - @param table_id id of the table being operated. - @param noredo if true turn off logging. */ - Callback(table_id_t table_id, bool noredo) - : - m_id(), - m_noredo(noredo) - { - /* Convert to storage byte order. */ - mach_write_to_8(&m_id, table_id); - } - - /** - Destructor */ - virtual ~Callback() - { - /* Do nothing */ - } - - /** - @param pcur persistent cursor used for iteration - @return true if the table id column matches. */ - bool match(btr_pcur_t* pcur) const - { - ulint len; - const byte* field; - rec_t* rec = btr_pcur_get_rec(pcur); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_INDEXES__TABLE_ID, &len); - - ut_ad(len == 8); - - return(memcmp(&m_id, field, len) == 0); - } - - /** - @return pointer to table id storage format buffer */ - const table_id_t* table_id() const - { - return(&m_id); - } - - /** - @return return if logging needs to be turned off. */ - bool get_logging_status() const - { - return(m_noredo); - } - -protected: - // Disably copying - Callback(const Callback&); - Callback& operator=(const Callback&); - -protected: - /** Table id in storage format */ - table_id_t m_id; - - /** Turn off logging. */ - const bool m_noredo; -}; - -/** -Scan to find out truncate log file from the given directory path. - -@param dir_path look for log directory in following path. -@param log_files cache to hold truncate log file name found. -@return DB_SUCCESS or error code. */ -dberr_t -TruncateLogParser::scan( - const char* dir_path, - trunc_log_files_t& log_files) -{ - os_file_dir_t dir; - os_file_stat_t fileinfo; - dberr_t err = DB_SUCCESS; - const ulint dir_len = strlen(dir_path); - - /* Scan and look out for the truncate log files. */ - dir = os_file_opendir(dir_path, true); - if (dir == NULL) { - return(DB_IO_ERROR); - } - - while (fil_file_readdir_next_file( - &err, dir_path, dir, &fileinfo) == 0) { - - ulint nm_len = strlen(fileinfo.name); - - if (fileinfo.type == OS_FILE_TYPE_FILE - && nm_len > sizeof "ib_trunc.log" - && (0 == strncmp(fileinfo.name + nm_len - - ((sizeof "trunc.log") - 1), - "trunc.log", (sizeof "trunc.log") - 1)) - && (0 == strncmp(fileinfo.name, "ib_", 3))) { - - if (fileinfo.size == 0) { - /* Truncate log not written. Remove the file. */ - os_file_delete( - innodb_log_file_key, fileinfo.name); - continue; - } - - /* Construct file name by appending directory path */ - ulint sz = dir_len + 22 + 22 + sizeof "ib_trunc.log"; - char* log_file_name = UT_NEW_ARRAY_NOKEY(char, sz); - if (log_file_name == NULL) { - err = DB_OUT_OF_MEMORY; - break; - } - memset(log_file_name, 0, sz); - - strncpy(log_file_name, dir_path, dir_len); - ulint log_file_name_len = strlen(log_file_name); - if (log_file_name[log_file_name_len - 1] - != OS_PATH_SEPARATOR) { - - log_file_name[log_file_name_len] - = OS_PATH_SEPARATOR; - log_file_name_len = strlen(log_file_name); - } - strcat(log_file_name, fileinfo.name); - log_files.push_back(log_file_name); - } - } - - os_file_closedir(dir); - - return(err); -} - -/** -Parse the log file and populate table to truncate information. -(Add this table to truncate information to central vector that is then - used by truncate fix-up routine to fix-up truncate action of the table.) - -@param log_file_name log file to parse -@return DB_SUCCESS or error code. */ -dberr_t -TruncateLogParser::parse( - const char* log_file_name) -{ - dberr_t err = DB_SUCCESS; - truncate_t* truncate = NULL; - - /* Open the file and read magic-number to findout if truncate action - was completed. */ - bool ret; - os_file_t handle = os_file_create_simple( - innodb_log_file_key, log_file_name, - OS_FILE_OPEN, OS_FILE_READ_ONLY, srv_read_only_mode, &ret); - if (!ret) { - ib::error() << "Error opening truncate log file: " - << log_file_name; - return(DB_IO_ERROR); - } - - ulint sz = srv_page_size; - void* buf = ut_zalloc_nokey(sz + srv_page_size); - if (buf == 0) { - os_file_close(handle); - return(DB_OUT_OF_MEMORY); - } - - IORequest request(IORequest::READ); - - /* Align the memory for file i/o if we might have O_DIRECT set*/ - byte* log_buf = static_cast(ut_align(buf, srv_page_size)); - - do { - err = os_file_read(request, handle, log_buf, 0, sz); - - if (err != DB_SUCCESS) { - os_file_close(handle); - break; - } - - if (mach_read_from_4(log_buf) == 32743712) { - - /* Truncate action completed. Avoid parsing the file. */ - os_file_close(handle); - - os_file_delete(innodb_log_file_key, log_file_name); - break; - } - - if (truncate == NULL) { - truncate = UT_NEW_NOKEY(truncate_t(log_file_name)); - if (truncate == NULL) { - os_file_close(handle); - err = DB_OUT_OF_MEMORY; - break; - } - } - - err = truncate->parse(log_buf + 4, log_buf + sz - 4); - - if (err != DB_SUCCESS) { - - ut_ad(err == DB_FAIL); - - ut_free(buf); - buf = 0; - - sz *= 2; - - buf = ut_zalloc_nokey(sz + srv_page_size); - - if (buf == 0) { - os_file_close(handle); - err = DB_OUT_OF_MEMORY; - UT_DELETE(truncate); - truncate = NULL; - break; - } - - log_buf = static_cast( - ut_align(buf, srv_page_size)); - } - } while (err != DB_SUCCESS); - - ut_free(buf); - - if (err == DB_SUCCESS && truncate != NULL) { - truncate_t::add(truncate); - os_file_close(handle); - } - - return(err); -} - -/** -Scan and Parse truncate log files. - -@param dir_path look for log directory in following path -@return DB_SUCCESS or error code. */ -dberr_t -TruncateLogParser::scan_and_parse( - const char* dir_path) -{ - dberr_t err; - trunc_log_files_t log_files; - - /* Scan and trace all the truncate log files. */ - err = TruncateLogParser::scan(dir_path, log_files); - - /* Parse truncate lof files if scan was successful. */ - if (err == DB_SUCCESS) { - - for (ulint i = 0; - i < log_files.size() && err == DB_SUCCESS; - i++) { - err = TruncateLogParser::parse(log_files[i]); - } - } - - trunc_log_files_t::const_iterator end = log_files.end(); - for (trunc_log_files_t::const_iterator it = log_files.begin(); - it != end; - ++it) { - if (*it != NULL) { - UT_DELETE_ARRAY(*it); - } - } - log_files.clear(); - - return(err); -} - -/** Callback to drop indexes during TRUNCATE */ -class DropIndex : public Callback { - -public: - /** - Constructor - - @param[in,out] table Table to truncate - @param[in] noredo whether to disable redo logging */ - DropIndex(dict_table_t* table, bool noredo) - : - Callback(table->id, noredo), - m_table(table) - { - /* No op */ - } - - /** - @param mtr mini-transaction covering the read - @param pcur persistent cursor used for reading - @return DB_SUCCESS or error code */ - dberr_t operator()(mtr_t* mtr, btr_pcur_t* pcur) const; - -private: - /** Table to be truncated */ - dict_table_t* m_table; -}; - -/** Callback to create the indexes during TRUNCATE */ -class CreateIndex : public Callback { - -public: - /** - Constructor - - @param[in,out] table Table to truncate - @param[in] noredo whether to disable redo logging */ - CreateIndex(dict_table_t* table, bool noredo) - : - Callback(table->id, noredo), - m_table(table) - { - /* No op */ - } - - /** - Create the new index and update the root page number in the - SysIndex table. - - @param mtr mini-transaction covering the read - @param pcur persistent cursor used for reading - @return DB_SUCCESS or error code */ - dberr_t operator()(mtr_t* mtr, btr_pcur_t* pcur) const; - -private: - // Disably copying - CreateIndex(const CreateIndex&); - CreateIndex& operator=(const CreateIndex&); - -private: - /** Table to be truncated */ - dict_table_t* m_table; -}; - -/** Check for presence of table-id in SYS_XXXX tables. */ -class TableLocator : public Callback { - -public: - /** - Constructor - @param table_id table_id to look for */ - explicit TableLocator(table_id_t table_id) - : - Callback(table_id, false), - m_table_found() - { - /* No op */ - } - - /** - @return true if table is found */ - bool is_table_found() const - { - return(m_table_found); - } - - /** - Look for table-id in SYS_XXXX tables without loading the table. - - @param pcur persistent cursor used for reading - @return DB_SUCCESS */ - dberr_t operator()(mtr_t*, btr_pcur_t*) - { - m_table_found = true; - return(DB_SUCCESS); - } - -private: - /** Set to true if table is present */ - bool m_table_found; -}; - -/** -Drop an index in the table. - -@param mtr mini-transaction covering the read -@param pcur persistent cursor used for reading -@return DB_SUCCESS or error code */ -dberr_t -DropIndex::operator()(mtr_t* mtr, btr_pcur_t* pcur) const -{ - rec_t* rec = btr_pcur_get_rec(pcur); - - bool freed = dict_drop_index_tree(rec, pcur, mtr); - -#ifdef UNIV_DEBUG - { - ulint len; - const byte* field; - ulint index_type; - - field = rec_get_nth_field_old( - btr_pcur_get_rec(pcur), DICT_FLD__SYS_INDEXES__TYPE, - &len); - ut_ad(len == 4); - - index_type = mach_read_from_4(field); - - if (index_type & DICT_CLUSTERED) { - /* Clustered index */ - DBUG_EXECUTE_IF("ib_trunc_crash_on_drop_of_clust_index", - log_buffer_flush_to_disk(); - os_thread_sleep(2000000); - DBUG_SUICIDE();); - } else if (index_type & DICT_UNIQUE) { - /* Unique index */ - DBUG_EXECUTE_IF("ib_trunc_crash_on_drop_of_uniq_index", - log_buffer_flush_to_disk(); - os_thread_sleep(2000000); - DBUG_SUICIDE();); - } else if (index_type == 0) { - /* Secondary index */ - DBUG_EXECUTE_IF("ib_trunc_crash_on_drop_of_sec_index", - log_buffer_flush_to_disk(); - os_thread_sleep(2000000); - DBUG_SUICIDE();); - } - } -#endif /* UNIV_DEBUG */ - - DBUG_EXECUTE_IF("ib_err_trunc_drop_index", return DB_ERROR;); - - if (freed) { - - /* We will need to commit and restart the - mini-transaction in order to avoid deadlocks. - The dict_drop_index_tree() call has freed - a page in this mini-transaction, and the rest - of this loop could latch another index page.*/ - const mtr_log_t log_mode = mtr->get_log_mode(); - mtr_commit(mtr); - - mtr_start(mtr); - mtr->set_log_mode(log_mode); - - btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr); - } else { - if (!m_table->space) { - return DB_ERROR; - } - } - - return(DB_SUCCESS); -} - -/** -Create the new index and update the root page number in the -SysIndex table. - -@param mtr mini-transaction covering the read -@param pcur persistent cursor used for reading -@return DB_SUCCESS or error code */ -dberr_t -CreateIndex::operator()(mtr_t* mtr, btr_pcur_t* pcur) const -{ - ulint root_page_no; - - root_page_no = dict_recreate_index_tree(m_table, pcur, mtr); - -#ifdef UNIV_DEBUG - { - ulint len; - const byte* field; - ulint index_type; - - field = rec_get_nth_field_old( - btr_pcur_get_rec(pcur), DICT_FLD__SYS_INDEXES__TYPE, - &len); - ut_ad(len == 4); - - index_type = mach_read_from_4(field); - - if (index_type & DICT_CLUSTERED) { - /* Clustered index */ - DBUG_EXECUTE_IF( - "ib_trunc_crash_on_create_of_clust_index", - log_buffer_flush_to_disk(); - os_thread_sleep(2000000); - DBUG_SUICIDE();); - } else if (index_type & DICT_UNIQUE) { - /* Unique index */ - DBUG_EXECUTE_IF( - "ib_trunc_crash_on_create_of_uniq_index", - log_buffer_flush_to_disk(); - os_thread_sleep(2000000); - DBUG_SUICIDE();); - } else if (index_type == 0) { - /* Secondary index */ - DBUG_EXECUTE_IF( - "ib_trunc_crash_on_create_of_sec_index", - log_buffer_flush_to_disk(); - os_thread_sleep(2000000); - DBUG_SUICIDE();); - } - } -#endif /* UNIV_DEBUG */ - - DBUG_EXECUTE_IF("ib_err_trunc_create_index", return DB_ERROR;); - - if (root_page_no != FIL_NULL) { - - rec_t* rec = btr_pcur_get_rec(pcur); - - page_rec_write_field( - rec, DICT_FLD__SYS_INDEXES__PAGE_NO, - root_page_no, mtr); - - /* We will need to commit and restart the - mini-transaction in order to avoid deadlocks. - The dict_create_index_tree() call has allocated - a page in this mini-transaction, and the rest of - this loop could latch another index page. */ - mtr_commit(mtr); - - mtr_start(mtr); - - btr_pcur_restore_position(BTR_MODIFY_LEAF, pcur, mtr); - - } else { - if (!m_table->space) { - return(DB_ERROR); - } - } - - return(DB_SUCCESS); -} - -/** -Update system table to reflect new table id. -@param old_table_id old table id -@param new_table_id new table id -@param reserve_dict_mutex if TRUE, acquire/release - dict_sys->mutex around call to pars_sql. -@param trx transaction -@return error code or DB_SUCCESS */ -static MY_ATTRIBUTE((warn_unused_result)) -dberr_t -row_truncate_update_table_id( - table_id_t old_table_id, - table_id_t new_table_id, - ibool reserve_dict_mutex, - trx_t* trx) -{ - pars_info_t* info = NULL; - dberr_t err = DB_SUCCESS; - - /* Scan the SYS_XXXX table and update to reflect new table-id. */ - info = pars_info_create(); - pars_info_add_ull_literal(info, "old_id", old_table_id); - pars_info_add_ull_literal(info, "new_id", new_table_id); - - err = que_eval_sql( - info, - "PROCEDURE RENUMBER_TABLE_ID_PROC () IS\n" - "BEGIN\n" - "UPDATE SYS_TABLES" - " SET ID = :new_id\n" - " WHERE ID = :old_id;\n" - "UPDATE SYS_COLUMNS SET TABLE_ID = :new_id\n" - " WHERE TABLE_ID = :old_id;\n" - "UPDATE SYS_INDEXES" - " SET TABLE_ID = :new_id\n" - " WHERE TABLE_ID = :old_id;\n" - "UPDATE SYS_VIRTUAL" - " SET TABLE_ID = :new_id\n" - " WHERE TABLE_ID = :old_id;\n" - "END;\n", reserve_dict_mutex, trx); - - return(err); -} - -/** -Get the table id to truncate. -@param truncate_t old/new table id of table to truncate -@return table_id_t table_id to use in SYS_XXXX table update. */ -static MY_ATTRIBUTE((warn_unused_result)) -table_id_t -row_truncate_get_trunc_table_id( - const truncate_t& truncate) -{ - TableLocator tableLocator(truncate.old_table_id()); - - SysIndexIterator().for_each(tableLocator); - - return(tableLocator.is_table_found() ? - truncate.old_table_id(): truncate.new_table_id()); -} - -/** -Update system table to reflect new table id and root page number. -@param truncate_t old/new table id of table to truncate - and updated root_page_no of indexes. -@param new_table_id new table id -@param reserve_dict_mutex if TRUE, acquire/release - dict_sys->mutex around call to pars_sql. -@param mark_index_corrupted if true, then mark index corrupted. -@return error code or DB_SUCCESS */ -static MY_ATTRIBUTE((warn_unused_result)) -dberr_t -row_truncate_update_sys_tables_during_fix_up( - const truncate_t& truncate, - table_id_t new_table_id, - ibool reserve_dict_mutex, - bool mark_index_corrupted) -{ - trx_t* trx = trx_create(); - - trx_set_dict_operation(trx, TRX_DICT_OP_TABLE); - - table_id_t table_id = row_truncate_get_trunc_table_id(truncate); - - /* Step-1: Update the root-page-no */ - - dberr_t err; - - err = truncate.update_root_page_no( - trx, table_id, reserve_dict_mutex, mark_index_corrupted); - - if (err != DB_SUCCESS) { - return(err); - } - - /* Step-2: Update table-id. */ - - err = row_truncate_update_table_id( - table_id, new_table_id, reserve_dict_mutex, trx); - - if (err == DB_SUCCESS) { - dict_mutex_enter_for_mysql(); - - /* Remove the table with old table_id from cache. */ - dict_table_t* old_table = dict_table_open_on_id( - table_id, true, DICT_TABLE_OP_NORMAL); - - if (old_table != NULL) { - dict_table_close(old_table, true, false); - dict_table_remove_from_cache(old_table); - } - - /* Open table with new table_id and set table as - corrupted if it has FTS index. */ - - dict_table_t* table = dict_table_open_on_id( - new_table_id, true, DICT_TABLE_OP_NORMAL); - ut_ad(table->id == new_table_id); - - bool has_internal_doc_id = - dict_table_has_fts_index(table) - || DICT_TF2_FLAG_IS_SET( - table, DICT_TF2_FTS_HAS_DOC_ID); - - if (has_internal_doc_id) { - trx->dict_operation_lock_mode = RW_X_LATCH; - fts_check_corrupt(table, trx); - trx->dict_operation_lock_mode = 0; - } - - dict_table_close(table, true, false); - dict_mutex_exit_for_mysql(); - } - - trx_commit_for_mysql(trx); - trx_free(trx); - - return(err); -} - -/********************************************************//** -Recreates table indexes by applying -TRUNCATE log record during recovery. -@return DB_SUCCESS or error code */ -static -dberr_t -fil_recreate_table( -/*===============*/ - ulint format_flags, /*!< in: page format */ - const char* name, /*!< in: table name */ - truncate_t& truncate) /*!< in: The information of - TRUNCATE log record */ -{ - ut_ad(!truncate_t::s_fix_up_active); - truncate_t::s_fix_up_active = true; - - /* Step-1: Scan for active indexes from REDO logs and drop - all the indexes using low level function that take root_page_no - and space-id. */ - truncate.drop_indexes(fil_system.sys_space); - - /* Step-2: Scan for active indexes and re-create them. */ - dberr_t err = truncate.create_indexes( - name, fil_system.sys_space, format_flags); - if (err != DB_SUCCESS) { - ib::info() << "Recovery failed for TRUNCATE TABLE '" - << name << "' within the system tablespace"; - } - - truncate_t::s_fix_up_active = false; - - return(err); -} - -/********************************************************//** -Recreates the tablespace and table indexes by applying -TRUNCATE log record during recovery. -@return DB_SUCCESS or error code */ -static -dberr_t -fil_recreate_tablespace( -/*====================*/ - ulint space_id, /*!< in: space id */ - ulint format_flags, /*!< in: page format */ - ulint flags, /*!< in: tablespace flags */ - const char* name, /*!< in: table name */ - truncate_t& truncate, /*!< in: The information of - TRUNCATE log record */ - lsn_t recv_lsn) /*!< in: the end LSN of - the log record */ -{ - dberr_t err = DB_SUCCESS; - mtr_t mtr; - - ut_ad(!truncate_t::s_fix_up_active); - truncate_t::s_fix_up_active = true; - - /* Step-1: Invalidate buffer pool pages belonging to the tablespace - to re-create. */ - buf_LRU_flush_or_remove_pages(space_id, NULL); - - /* Remove all insert buffer entries for the tablespace */ - ibuf_delete_for_discarded_space(space_id); - - /* Step-2: truncate tablespace (reset the size back to original or - default size) of tablespace. */ - err = truncate.truncate( - space_id, truncate.get_dir_path(), name, flags, true); - - if (err != DB_SUCCESS) { - - ib::info() << "Cannot access .ibd file for table '" - << name << "' with tablespace " << space_id - << " while truncating"; - return(DB_ERROR); - } - - fil_space_t* space = fil_space_acquire(space_id); - if (!space) { - ib::info() << "Missing .ibd file for table '" << name - << "' with tablespace " << space_id; - return(DB_ERROR); - } - - const page_size_t page_size(space->flags); - - /* Step-3: Initialize Header. */ - if (page_size.is_compressed()) { - byte* buf; - page_t* page; - - buf = static_cast( - ut_zalloc_nokey(3U << srv_page_size_shift)); - - /* Align the memory for file i/o */ - page = static_cast(ut_align(buf, srv_page_size)); - - flags |= FSP_FLAGS_PAGE_SSIZE(); - - fsp_header_init_fields(page, space_id, flags); - - mach_write_to_4( - page + FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID, space_id); - - page_zip_des_t page_zip; - page_zip_set_size(&page_zip, page_size.physical()); - page_zip.data = page + srv_page_size; - -#ifdef UNIV_DEBUG - page_zip.m_start = -#endif /* UNIV_DEBUG */ - page_zip.m_end = page_zip.m_nonempty = page_zip.n_blobs = 0; - buf_flush_init_for_writing(NULL, page, &page_zip, 0); - - err = fil_io(IORequestWrite, true, page_id_t(space_id, 0), - page_size, 0, page_size.physical(), page_zip.data, - NULL); - - ut_free(buf); - - if (err != DB_SUCCESS) { - ib::info() << "Failed to clean header of the" - " table '" << name << "' with tablespace " - << space_id; - goto func_exit; - } - } - - mtr_start(&mtr); - /* Don't log the operation while fixing up table truncate operation - as crash at this level can still be sustained with recovery restarting - from last checkpoint. */ - mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); - - /* Initialize the first extent descriptor page and - the second bitmap page for the new tablespace. */ - fsp_header_init(space, FIL_IBD_FILE_INITIAL_SIZE, &mtr); - mtr_commit(&mtr); - - /* Step-4: Re-Create Indexes to newly re-created tablespace. - This operation will restore tablespace back to what it was - when it was created during CREATE TABLE. */ - err = truncate.create_indexes(name, space, format_flags); - if (err != DB_SUCCESS) { - goto func_exit; - } - - /* Step-5: Write new created pages into ibd file handle and - flush it to disk for the tablespace, in case i/o-handler thread - deletes the bitmap page from buffer. */ - mtr_start(&mtr); - - mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); - - for (ulint page_no = 0; - page_no < UT_LIST_GET_FIRST(space->chain)->size; ++page_no) { - - const page_id_t cur_page_id(space_id, page_no); - - buf_block_t* block = buf_page_get(cur_page_id, page_size, - RW_X_LATCH, &mtr); - - byte* page = buf_block_get_frame(block); - - if (!FSP_FLAGS_GET_ZIP_SSIZE(flags)) { - ut_ad(!page_size.is_compressed()); - - buf_flush_init_for_writing( - block, page, NULL, recv_lsn); - - err = fil_io(IORequestWrite, true, cur_page_id, - page_size, 0, srv_page_size, page, NULL); - } else { - ut_ad(page_size.is_compressed()); - - /* We don't want to rewrite empty pages. */ - - if (fil_page_get_type(page) != 0) { - page_zip_des_t* page_zip = - buf_block_get_page_zip(block); - - buf_flush_init_for_writing( - block, page, page_zip, recv_lsn); - - err = fil_io(IORequestWrite, true, - cur_page_id, - page_size, 0, - page_size.physical(), - page_zip->data, NULL); - } else { -#ifdef UNIV_DEBUG - const byte* data = block->page.zip.data; - - /* Make sure that the page is really empty */ - for (ulint i = 0; - i < page_size.physical(); - ++i) { - - ut_a(data[i] == 0); - } -#endif /* UNIV_DEBUG */ - } - } - - if (err != DB_SUCCESS) { - ib::info() << "Cannot write page " << page_no - << " into a .ibd file for table '" - << name << "' with tablespace " << space_id; - } - } - - mtr_commit(&mtr); - - truncate_t::s_fix_up_active = false; -func_exit: - space->release(); - return(err); -} - -/** -Fix the table truncate by applying information parsed from TRUNCATE log. -Fix-up includes re-creating table (drop and re-create indexes) -@return error code or DB_SUCCESS */ -dberr_t -truncate_t::fixup_tables_in_system_tablespace() -{ - dberr_t err = DB_SUCCESS; - - /* Using the info cached during REDO log scan phase fix the - table truncate. */ - - for (tables_t::iterator it = s_tables.begin(); - it != s_tables.end();) { - - if ((*it)->m_space_id == TRX_SYS_SPACE) { - /* Step-1: Drop and re-create indexes. */ - ib::info() << "Completing truncate for table with " - "id (" << (*it)->m_old_table_id << ") " - "residing in the system tablespace."; - - err = fil_recreate_table( - (*it)->m_format_flags, - (*it)->m_tablename, - **it); - - /* Step-2: Update the SYS_XXXX tables to reflect - this new table_id and root_page_no. */ - table_id_t new_id; - - dict_hdr_get_new_id(&new_id, NULL, NULL, NULL, true); - - err = row_truncate_update_sys_tables_during_fix_up( - **it, new_id, TRUE, - (err == DB_SUCCESS) ? false : true); - - if (err != DB_SUCCESS) { - break; - } - - os_file_delete( - innodb_log_file_key, (*it)->m_log_file_name); - UT_DELETE(*it); - it = s_tables.erase(it); - } else { - ++it; - } - } - - /* Also clear the map used to track tablespace truncated. */ - s_truncated_tables.clear(); - - return(err); -} - -/** -Fix the table truncate by applying information parsed from TRUNCATE log. -Fix-up includes re-creating tablespace. -@return error code or DB_SUCCESS */ -dberr_t -truncate_t::fixup_tables_in_non_system_tablespace() -{ - dberr_t err = DB_SUCCESS; - - /* Using the info cached during REDO log scan phase fix the - table truncate. */ - tables_t::iterator end = s_tables.end(); - - for (tables_t::iterator it = s_tables.begin(); it != end; ++it) { - - /* All tables in the system tablespace have already been - done and erased from this list. */ - ut_a((*it)->m_space_id != TRX_SYS_SPACE); - - /* Drop tablespace, drop indexes and re-create indexes. */ - - ib::info() << "Completing truncate for table with " - "id (" << (*it)->m_old_table_id << ") " - "residing in file-per-table tablespace with " - "id (" << (*it)->m_space_id << ")"; - - fil_space_t* space = fil_space_get((*it)->m_space_id); - - if (!space) { - /* Create the database directory for name, - if it does not exist yet */ - fil_create_directory_for_tablename( - (*it)->m_tablename); - - space = fil_ibd_create((*it)->m_space_id, - (*it)->m_tablename, - (*it)->m_dir_path, - (*it)->m_tablespace_flags, - FIL_IBD_FILE_INITIAL_SIZE, - (*it)->m_encryption, - (*it)->m_key_id, &err); - if (!space) { - /* If checkpoint is not yet done - and table is dropped and then we might - still have REDO entries for this table - which are INVALID. Ignore them. */ - ib::warn() << "Failed to create" - " tablespace for " - << (*it)->m_space_id - << " space-id"; - err = DB_ERROR; - break; - } - } - - err = fil_recreate_tablespace( - (*it)->m_space_id, - (*it)->m_format_flags, - (*it)->m_tablespace_flags, - (*it)->m_tablename, - **it, log_get_lsn()); - - /* Step-2: Update the SYS_XXXX tables to reflect new - table-id and root_page_no. */ - table_id_t new_id; - - dict_hdr_get_new_id(&new_id, NULL, NULL, NULL, true); - - err = row_truncate_update_sys_tables_during_fix_up( - **it, new_id, TRUE, (err == DB_SUCCESS) ? false : true); - - if (err != DB_SUCCESS) { - break; - } - } - - if (err == DB_SUCCESS && s_tables.size() > 0) { - - log_make_checkpoint_at(LSN_MAX, TRUE); - } - - for (ulint i = 0; i < s_tables.size(); ++i) { - os_file_delete( - innodb_log_file_key, s_tables[i]->m_log_file_name); - UT_DELETE(s_tables[i]); - } - - s_tables.clear(); - - return(err); -} - -/** -Constructor - -@param old_table_id old table id assigned to table before truncate -@param new_table_id new table id that will be assigned to table - after truncate -@param dir_path directory path */ - -truncate_t::truncate_t( - table_id_t old_table_id, - table_id_t new_table_id, - const char* dir_path) - : - m_space_id(), - m_old_table_id(old_table_id), - m_new_table_id(new_table_id), - m_dir_path(), - m_tablename(), - m_tablespace_flags(), - m_format_flags(), - m_indexes(), - m_log_lsn(), - m_log_file_name(), - /* JAN: TODO: Encryption */ - m_encryption(FIL_ENCRYPTION_DEFAULT), - m_key_id(FIL_DEFAULT_ENCRYPTION_KEY) -{ - if (dir_path != NULL) { - m_dir_path = mem_strdup(dir_path); - } -} - -/** -Consturctor - -@param log_file_name parse the log file during recovery to populate - information related to table to truncate */ -truncate_t::truncate_t( - const char* log_file_name) - : - m_space_id(), - m_old_table_id(), - m_new_table_id(), - m_dir_path(), - m_tablename(), - m_tablespace_flags(), - m_format_flags(), - m_indexes(), - m_log_lsn(), - m_log_file_name(), - /* JAN: TODO: Encryption */ - m_encryption(FIL_ENCRYPTION_DEFAULT), - m_key_id(FIL_DEFAULT_ENCRYPTION_KEY) - -{ - m_log_file_name = mem_strdup(log_file_name); - if (m_log_file_name == NULL) { - ib::fatal() << "Failed creating truncate_t; out of memory"; - } -} - -/** Constructor */ - -truncate_t::index_t::index_t() - : - m_id(), - m_type(), - m_root_page_no(FIL_NULL), - m_new_root_page_no(FIL_NULL), - m_n_fields(), - m_trx_id_pos(ULINT_UNDEFINED), - m_fields() -{ - /* Do nothing */ -} - -/** Destructor */ - -truncate_t::~truncate_t() -{ - if (m_dir_path != NULL) { - ut_free(m_dir_path); - m_dir_path = NULL; - } - - if (m_tablename != NULL) { - ut_free(m_tablename); - m_tablename = NULL; - } - - if (m_log_file_name != NULL) { - ut_free(m_log_file_name); - m_log_file_name = NULL; - } - - m_indexes.clear(); -} - -/** -@return number of indexes parsed from the log record */ - -size_t -truncate_t::indexes() const -{ - return(m_indexes.size()); -} - -/** -Update root page number in SYS_XXXX tables. - -@param trx transaction object -@param table_id table id for which information needs to - be updated. -@param reserve_dict_mutex if TRUE, acquire/release - dict_sys->mutex around call to pars_sql. -@param mark_index_corrupted if true, then mark index corrupted. -@return DB_SUCCESS or error code */ - -dberr_t -truncate_t::update_root_page_no( - trx_t* trx, - table_id_t table_id, - ibool reserve_dict_mutex, - bool mark_index_corrupted) const -{ - indexes_t::const_iterator end = m_indexes.end(); - - dberr_t err = DB_SUCCESS; - - for (indexes_t::const_iterator it = m_indexes.begin(); - it != end; - ++it) { - - pars_info_t* info = pars_info_create(); - - pars_info_add_int4_literal( - info, "page_no", it->m_new_root_page_no); - - pars_info_add_ull_literal(info, "table_id", table_id); - - pars_info_add_ull_literal( - info, "index_id", - (mark_index_corrupted ? IB_ID_MAX : it->m_id)); - - err = que_eval_sql( - info, - "PROCEDURE RENUMBER_IDX_PAGE_NO_PROC () IS\n" - "BEGIN\n" - "UPDATE SYS_INDEXES" - " SET PAGE_NO = :page_no\n" - " WHERE TABLE_ID = :table_id" - " AND ID = :index_id;\n" - "END;\n", reserve_dict_mutex, trx); - - if (err != DB_SUCCESS) { - break; - } - } - - return(err); -} - -/** -Check whether a tablespace was truncated during recovery -@param space_id tablespace id to check -@return true if the tablespace was truncated */ - -bool -truncate_t::is_tablespace_truncated(ulint space_id) -{ - tables_t::iterator end = s_tables.end(); - - for (tables_t::iterator it = s_tables.begin(); it != end; ++it) { - - if ((*it)->m_space_id == space_id) { - - return(true); - } - } - - return(false); -} - -/** Was tablespace truncated (on crash before checkpoint). -If the MLOG_TRUNCATE redo-record is still available then tablespace -was truncated and checkpoint is yet to happen. -@param[in] space_id tablespace id to check. -@return true if tablespace is was truncated. */ -bool -truncate_t::was_tablespace_truncated(ulint space_id) -{ - return(s_truncated_tables.find(space_id) != s_truncated_tables.end()); -} - -/** Get the lsn associated with space. -@param[in] space_id tablespace id to check. -@return associated lsn. */ -lsn_t -truncate_t::get_truncated_tablespace_init_lsn(ulint space_id) -{ - ut_ad(was_tablespace_truncated(space_id)); - - return(s_truncated_tables.find(space_id)->second); -} - -/** -Parses log record during recovery -@param start_ptr buffer containing log body to parse -@param end_ptr buffer end - -@return DB_SUCCESS or error code */ - -dberr_t -truncate_t::parse( - byte* start_ptr, - const byte* end_ptr) -{ - /* Parse lsn, space-id, format-flags and tablespace-flags. */ - if (end_ptr < start_ptr + (8 + 4 + 4 + 4)) { - return(DB_FAIL); - } - - m_log_lsn = mach_read_from_8(start_ptr); - start_ptr += 8; - - m_space_id = mach_read_from_4(start_ptr); - start_ptr += 4; - - m_format_flags = mach_read_from_4(start_ptr); - start_ptr += 4; - - m_tablespace_flags = mach_read_from_4(start_ptr); - start_ptr += 4; - - /* Parse table-name. */ - if (end_ptr < start_ptr + (2)) { - return(DB_FAIL); - } - - ulint n_tablename_len = mach_read_from_2(start_ptr); - start_ptr += 2; - - if (n_tablename_len > 0) { - if (end_ptr < start_ptr + n_tablename_len) { - return(DB_FAIL); - } - m_tablename = mem_strdup(reinterpret_cast(start_ptr)); - ut_ad(m_tablename[n_tablename_len - 1] == 0); - start_ptr += n_tablename_len; - } - - - /* Parse and read old/new table-id, number of indexes */ - if (end_ptr < start_ptr + (8 + 8 + 2 + 2)) { - return(DB_FAIL); - } - - ut_ad(m_indexes.empty()); - - m_old_table_id = mach_read_from_8(start_ptr); - start_ptr += 8; - - m_new_table_id = mach_read_from_8(start_ptr); - start_ptr += 8; - - ulint n_indexes = mach_read_from_2(start_ptr); - start_ptr += 2; - - /* Parse the remote directory from TRUNCATE log record */ - { - ulint n_tabledirpath_len = mach_read_from_2(start_ptr); - start_ptr += 2; - - if (end_ptr < start_ptr + n_tabledirpath_len) { - return(DB_FAIL); - } - - if (n_tabledirpath_len > 0) { - - m_dir_path = mem_strdup(reinterpret_cast(start_ptr)); - ut_ad(m_dir_path[n_tabledirpath_len - 1] == 0); - start_ptr += n_tabledirpath_len; - } - } - - /* Parse index ids and types from TRUNCATE log record */ - for (ulint i = 0; i < n_indexes; ++i) { - index_t index; - - if (end_ptr < start_ptr + (8 + 4 + 4 + 4)) { - return(DB_FAIL); - } - - index.m_id = mach_read_from_8(start_ptr); - start_ptr += 8; - - index.m_type = mach_read_from_4(start_ptr); - start_ptr += 4; - - index.m_root_page_no = mach_read_from_4(start_ptr); - start_ptr += 4; - - index.m_trx_id_pos = mach_read_from_4(start_ptr); - start_ptr += 4; - - if (!(index.m_type & DICT_FTS)) { - m_indexes.push_back(index); - } - } - - ut_ad(!m_indexes.empty()); - - if (FSP_FLAGS_GET_ZIP_SSIZE(m_tablespace_flags)) { - - /* Parse the number of index fields from TRUNCATE log record */ - for (ulint i = 0; i < m_indexes.size(); ++i) { - - if (end_ptr < start_ptr + (2 + 2)) { - return(DB_FAIL); - } - - m_indexes[i].m_n_fields = mach_read_from_2(start_ptr); - start_ptr += 2; - - ulint len = mach_read_from_2(start_ptr); - start_ptr += 2; - - if (end_ptr < start_ptr + len) { - return(DB_FAIL); - } - - index_t& index = m_indexes[i]; - - /* Should be NUL terminated. */ - ut_ad((start_ptr)[len - 1] == 0); - - index_t::fields_t::iterator end; - - end = index.m_fields.end(); - - index.m_fields.insert( - end, start_ptr, &(start_ptr)[len]); - - start_ptr += len; - } - } - - return(DB_SUCCESS); -} - -/** Parse log record from REDO log file during recovery. -@param[in,out] start_ptr buffer containing log body to parse -@param[in] end_ptr buffer end -@param[in] space_id tablespace identifier -@return parsed upto or NULL. */ -byte* -truncate_t::parse_redo_entry( - byte* start_ptr, - const byte* end_ptr, - ulint space_id) -{ - lsn_t lsn; - - /* Parse space-id, lsn */ - if (end_ptr < (start_ptr + 8)) { - return(NULL); - } - - lsn = mach_read_from_8(start_ptr); - start_ptr += 8; - - /* Tablespace can't exist in both state. - (scheduled-for-truncate, was-truncated). */ - if (!is_tablespace_truncated(space_id)) { - - truncated_tables_t::iterator it = - s_truncated_tables.find(space_id); - - if (it == s_truncated_tables.end()) { - s_truncated_tables.insert( - std::pair(space_id, lsn)); - } else { - it->second = lsn; - } - } - - return(start_ptr); -} - -/** -Set the truncate log values for a compressed table. -@param index index from which recreate infoormation needs to be extracted -@return DB_SUCCESS or error code */ - -dberr_t -truncate_t::index_t::set( - const dict_index_t* index) -{ - /* Get trx-id column position (set only for clustered index) */ - if (dict_index_is_clust(index)) { - m_trx_id_pos = dict_index_get_sys_col_pos(index, DATA_TRX_ID); - ut_ad(m_trx_id_pos > 0); - ut_ad(m_trx_id_pos != ULINT_UNDEFINED); - } else { - m_trx_id_pos = 0; - } - - /* Original logic set this field differently if page is not leaf. - For truncate case this being first page to get created it is - always a leaf page and so we don't need that condition here. */ - m_n_fields = dict_index_get_n_fields(index); - - /* See requirements of page_zip_fields_encode for size. */ - ulint encoded_buf_size = (m_n_fields + 1) * 2; - byte* encoded_buf = UT_NEW_ARRAY_NOKEY(byte, encoded_buf_size); - - if (encoded_buf == NULL) { - return(DB_OUT_OF_MEMORY); - } - - ulint len = page_zip_fields_encode( - m_n_fields, index, m_trx_id_pos, encoded_buf); - ut_a(len <= encoded_buf_size); - - /* Append the encoded fields data. */ - m_fields.insert(m_fields.end(), &encoded_buf[0], &encoded_buf[len]); - - /* NUL terminate the encoded data */ - m_fields.push_back(0); - - UT_DELETE_ARRAY(encoded_buf); - - return(DB_SUCCESS); -} - -/** Create an index for a table. -@param[in] table_name table name, for which to create -the index -@param[in] space tablespace -@param[in] page_size page size of the .ibd file -@param[in] index_type type of index to truncate -@param[in] index_id id of index to truncate -@param[in] btr_redo_create_info control info for ::btr_create() -@param[in,out] mtr mini-transaction covering the -create index -@return root page no or FIL_NULL on failure */ -inline ulint -truncate_t::create_index( - const char* table_name, - fil_space_t* space, - ulint index_type, - index_id_t index_id, - const btr_create_t& btr_redo_create_info, - mtr_t* mtr) const -{ - ulint root_page_no = btr_create( - index_type, space, index_id, - NULL, &btr_redo_create_info, mtr); - - if (root_page_no == FIL_NULL) { - - ib::info() << "innodb_force_recovery was set to " - << srv_force_recovery << ". Continuing crash recovery" - " even though we failed to create index " << index_id - << " for compressed table '" << table_name << "' with" - " file " << space->chain.start->name; - } - - return(root_page_no); -} - -/** Check if index has been modified since TRUNCATE log snapshot -was recorded. -@param[in] space tablespace -@param[in] root_page_no index root page number -@return true if modified else false */ -inline -bool -truncate_t::is_index_modified_since_logged( - const fil_space_t* space, - ulint root_page_no) const -{ - dberr_t err; - mtr_t mtr; - - mtr_start(&mtr); - - /* Root page could be in free state if truncate crashed after drop_index - and page was not allocated for any other object. */ - buf_block_t* block= buf_page_get_gen( - page_id_t(space->id, root_page_no), page_size_t(space->flags), - RW_X_LATCH, NULL, - BUF_GET_POSSIBLY_FREED, __FILE__, __LINE__, &mtr, &err); - if (!block) return true; - - page_t* root = buf_block_get_frame(block); - -#ifdef UNIV_DEBUG - /* If the root page has been freed as part of truncate drop_index action - and not yet allocated for any object still the pagelsn > snapshot lsn */ - if (block->page.file_page_was_freed) { - ut_ad(mach_read_from_8(root + FIL_PAGE_LSN) > m_log_lsn); - } -#endif /* UNIV_DEBUG */ - - lsn_t page_lsn = mach_read_from_8(root + FIL_PAGE_LSN); - - mtr_commit(&mtr); - - if (page_lsn > m_log_lsn) { - return(true); - } - - return(false); -} - -/** Drop indexes for a table. -@param[in,out] space tablespace */ -void truncate_t::drop_indexes(fil_space_t* space) const -{ - mtr_t mtr; - - indexes_t::const_iterator end = m_indexes.end(); - const page_size_t page_size(space->flags); - - for (indexes_t::const_iterator it = m_indexes.begin(); - it != end; - ++it) { - - ulint root_page_no = it->m_root_page_no; - - if (is_index_modified_since_logged(space, root_page_no)) { - /* Page has been modified since TRUNCATE log snapshot - was recorded so not safe to drop the index. */ - continue; - } - - mtr_start(&mtr); - - if (space->id != TRX_SYS_SPACE) { - /* Do not log changes for single-table - tablespaces, we are in recovery mode. */ - mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); - } - - if (root_page_no != FIL_NULL) { - const page_id_t root_page_id(space->id, root_page_no); - - btr_free_if_exists( - root_page_id, page_size, it->m_id, &mtr); - } - - /* If tree is already freed then we might return immediately - in which case we need to release the lock we have acquired - on root_page. */ - mtr_commit(&mtr); - } -} - - -/** Create the indexes for a table -@param[in] table_name table name, for which to create the indexes -@param[in,out] space tablespace -@param[in] format_flags page format flags -@return DB_SUCCESS or error code. */ -inline dberr_t -truncate_t::create_indexes( - const char* table_name, - fil_space_t* space, - ulint format_flags) -{ - mtr_t mtr; - - mtr_start(&mtr); - - if (space->id != TRX_SYS_SPACE) { - /* Do not log changes for single-table tablespaces, we - are in recovery mode. */ - mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); - } - - /* Create all new index trees with table format, index ids, index - types, number of index fields and index field information taken - out from the TRUNCATE log record. */ - - ulint root_page_no = FIL_NULL; - indexes_t::iterator end = m_indexes.end(); - for (indexes_t::iterator it = m_indexes.begin(); - it != end; - ++it) { - - btr_create_t btr_redo_create_info( - FSP_FLAGS_GET_ZIP_SSIZE(space->flags) - ? &it->m_fields[0] : NULL); - - btr_redo_create_info.format_flags = format_flags; - - if (FSP_FLAGS_GET_ZIP_SSIZE(space->flags)) { - - btr_redo_create_info.n_fields = it->m_n_fields; - /* Skip the NUL appended field */ - btr_redo_create_info.field_len = - it->m_fields.size() - 1; - btr_redo_create_info.trx_id_pos = it->m_trx_id_pos; - } - - root_page_no = create_index( - table_name, space, it->m_type, it->m_id, - btr_redo_create_info, &mtr); - - if (root_page_no == FIL_NULL) { - break; - } - - it->m_new_root_page_no = root_page_no; - } - - mtr_commit(&mtr); - - return(root_page_no == FIL_NULL ? DB_ERROR : DB_SUCCESS); -} - -/** -Write a TRUNCATE log record for fixing up table if truncate crashes. -@param start_ptr buffer to write log record -@param end_ptr buffer end -@param space_id space id -@param tablename the table name in the usual databasename/tablename - format of InnoDB -@param flags tablespace flags -@param format_flags page format -@param lsn lsn while logging -@return DB_SUCCESS or error code */ - -dberr_t -truncate_t::write( - byte* start_ptr, - byte* end_ptr, - ulint space_id, - const char* tablename, - ulint flags, - ulint format_flags, - lsn_t lsn) const -{ - if (end_ptr < start_ptr) { - return(DB_FAIL); - } - - /* LSN, Type, Space-ID, format-flag (also know as log_flag. - Stored in page_no field), tablespace flags */ - if (end_ptr < (start_ptr + (8 + 4 + 4 + 4))) { - return(DB_FAIL); - } - - mach_write_to_8(start_ptr, lsn); - start_ptr += 8; - - mach_write_to_4(start_ptr, space_id); - start_ptr += 4; - - mach_write_to_4(start_ptr, format_flags); - start_ptr += 4; - - mach_write_to_4(start_ptr, flags); - start_ptr += 4; - - /* Name of the table. */ - /* Include the NUL in the log record. */ - ulint len = strlen(tablename) + 1; - if (end_ptr < (start_ptr + (len + 2))) { - return(DB_FAIL); - } - - mach_write_to_2(start_ptr, len); - start_ptr += 2; - - memcpy(start_ptr, tablename, len - 1); - start_ptr += len; - - DBUG_EXECUTE_IF("ib_trunc_crash_while_writing_redo_log", - DBUG_SUICIDE();); - - /* Old/New Table-ID, Number of Indexes and Tablespace dir-path-name. */ - /* Write the remote directory of the table into mtr log */ - len = m_dir_path != NULL ? strlen(m_dir_path) + 1 : 0; - if (end_ptr < (start_ptr + (len + 8 + 8 + 2 + 2))) { - return(DB_FAIL); - } - - /* Write out old-table-id. */ - mach_write_to_8(start_ptr, m_old_table_id); - start_ptr += 8; - - /* Write out new-table-id. */ - mach_write_to_8(start_ptr, m_new_table_id); - start_ptr += 8; - - /* Write out the number of indexes. */ - mach_write_to_2(start_ptr, m_indexes.size()); - start_ptr += 2; - - /* Write the length (NUL included) of the .ibd path. */ - mach_write_to_2(start_ptr, len); - start_ptr += 2; - - if (m_dir_path != NULL) { - memcpy(start_ptr, m_dir_path, len - 1); - start_ptr += len; - } - - /* Indexes information (id, type) */ - /* Write index ids, type, root-page-no into mtr log */ - for (ulint i = 0; i < m_indexes.size(); ++i) { - - if (end_ptr < (start_ptr + (8 + 4 + 4 + 4))) { - return(DB_FAIL); - } - - mach_write_to_8(start_ptr, m_indexes[i].m_id); - start_ptr += 8; - - mach_write_to_4(start_ptr, m_indexes[i].m_type); - start_ptr += 4; - - mach_write_to_4(start_ptr, m_indexes[i].m_root_page_no); - start_ptr += 4; - - mach_write_to_4(start_ptr, m_indexes[i].m_trx_id_pos); - start_ptr += 4; - } - - /* If tablespace compressed then field info of each index. */ - if (FSP_FLAGS_GET_ZIP_SSIZE(flags)) { - - for (ulint i = 0; i < m_indexes.size(); ++i) { - - ulint len = m_indexes[i].m_fields.size(); - if (end_ptr < (start_ptr + (len + 2 + 2))) { - return(DB_FAIL); - } - - mach_write_to_2( - start_ptr, m_indexes[i].m_n_fields); - start_ptr += 2; - - mach_write_to_2(start_ptr, len); - start_ptr += 2; - - const byte* ptr = &m_indexes[i].m_fields[0]; - memcpy(start_ptr, ptr, len - 1); - start_ptr += len; - } - } - - return(DB_SUCCESS); -} diff --git a/storage/innobase/srv/srv0srv.cc b/storage/innobase/srv/srv0srv.cc index 8f674f47b4c..d25a705c2ef 100644 --- a/storage/innobase/srv/srv0srv.cc +++ b/storage/innobase/srv/srv0srv.cc @@ -62,7 +62,6 @@ Created 10/8/1995 Heikki Tuuri #include "pars0pars.h" #include "que0que.h" #include "row0mysql.h" -#include "row0trunc.h" #include "row0log.h" #include "srv0mon.h" #include "srv0srv.h" @@ -2585,16 +2584,10 @@ srv_do_purge(ulint* n_total_purged) break; } - ulint undo_trunc_freq = - purge_sys.undo_trunc.get_rseg_truncate_frequency(); - - ulint rseg_truncate_frequency = ut_min( - static_cast(srv_purge_rseg_truncate_frequency), - undo_trunc_freq); - n_pages_purged = trx_purge( n_use_threads, - (++count % rseg_truncate_frequency) == 0); + !(++count % srv_purge_rseg_truncate_frequency) + || purge_sys.truncate.current); *n_total_purged += n_pages_purged; } while (n_pages_purged > 0 && !purge_sys.paused() @@ -2729,11 +2722,6 @@ DECLARE_THREAD(srv_purge_coordinator_thread)( /* Note that we are shutting down. */ rw_lock_x_lock(&purge_sys.latch); purge_sys.coordinator_shutdown(); - - /* If there are any pending undo-tablespace truncate then clear - it off as we plan to shutdown the purge thread. */ - purge_sys.undo_trunc.clear(); - /* Ensure that the wait in purge_sys_t::stop() will terminate. */ os_event_set(purge_sys.event); @@ -2840,38 +2828,3 @@ void srv_purge_shutdown() srv_purge_wakeup(); } while (srv_sys.sys_threads[SRV_PURGE_SLOT].in_use); } - -/** Check if tablespace is being truncated. -(Ignore system-tablespace as we don't re-create the tablespace -and so some of the action that are suppressed by this function -for independent tablespace are not applicable to system-tablespace). -@param space_id space_id to check for truncate action -@return true if being truncated, false if not being - truncated or tablespace is system-tablespace. */ -bool -srv_is_tablespace_truncated(ulint space_id) -{ - if (is_system_tablespace(space_id)) { - return(false); - } - - return(truncate_t::is_tablespace_truncated(space_id) - || undo::Truncate::is_tablespace_truncated(space_id)); - -} - -/** Check if tablespace was truncated. -@param[in] space space object to check for truncate action -@return true if tablespace was truncated and we still have an active -MLOG_TRUNCATE REDO log record. */ -bool -srv_was_tablespace_truncated(const fil_space_t* space) -{ - if (space == NULL) { - ut_ad(0); - return(false); - } - - return (!is_system_tablespace(space->id) - && truncate_t::was_tablespace_truncated(space->id)); -} diff --git a/storage/innobase/srv/srv0start.cc b/storage/innobase/srv/srv0start.cc index 638daeaadf0..f87fd2c370c 100644 --- a/storage/innobase/srv/srv0start.cc +++ b/storage/innobase/srv/srv0start.cc @@ -77,7 +77,6 @@ Created 2/16/1996 Heikki Tuuri #include "srv0srv.h" #include "btr0defragment.h" #include "fsp0sysspace.h" -#include "row0trunc.h" #include "mysql/service_wsrep.h" /* wsrep_recovery */ #include "trx0rseg.h" #include "os0proc.h" @@ -100,7 +99,6 @@ Created 2/16/1996 Heikki Tuuri #include "row0upd.h" #include "row0row.h" #include "row0mysql.h" -#include "row0trunc.h" #include "btr0pcur.h" #include "os0event.h" #include "zlib.h" @@ -815,8 +813,6 @@ srv_check_undo_redo_logs_exists() return(DB_SUCCESS); } -undo::undo_spaces_t undo::Truncate::s_fix_up_spaces; - /** Open the configured number of dedicated undo tablespaces. @param[in] create_new_db whether the database is being initialized @return DB_SUCCESS or error code */ @@ -898,46 +894,8 @@ srv_undo_tablespaces_init(bool create_new_db) prev_space_id = srv_undo_space_id_start - 1; break; case SRV_OPERATION_NORMAL: - if (create_new_db) { - break; - } - /* fall through */ case SRV_OPERATION_RESTORE: case SRV_OPERATION_RESTORE_EXPORT: - ut_ad(!create_new_db); - - /* Check if any of the UNDO tablespace needs fix-up because - server crashed while truncate was active on UNDO tablespace.*/ - for (i = 0; i < n_undo_tablespaces; ++i) { - - undo::Truncate undo_trunc; - - if (undo_trunc.needs_fix_up(undo_tablespace_ids[i])) { - - char name[OS_FILE_MAX_PATH]; - - snprintf(name, sizeof(name), - "%s%cundo%03zu", - srv_undo_dir, OS_PATH_SEPARATOR, - undo_tablespace_ids[i]); - - os_file_delete(innodb_data_file_key, name); - - err = srv_undo_tablespace_create( - name, - SRV_UNDO_TABLESPACE_SIZE_IN_PAGES); - - if (err != DB_SUCCESS) { - ib::error() << "Could not fix-up undo " - " tablespace truncate '" - << name << "'."; - return(err); - } - - undo::Truncate::s_fix_up_spaces.push_back( - undo_tablespace_ids[i]); - } - } break; } @@ -1044,64 +1002,6 @@ srv_undo_tablespaces_init(bool create_new_db) } } - if (!undo::Truncate::s_fix_up_spaces.empty()) { - - /* Step-1: Initialize the tablespace header and rsegs header. */ - mtr_t mtr; - - mtr_start(&mtr); - /* Turn off REDO logging. We are in server start mode and fixing - UNDO tablespace even before REDO log is read. Let's say we - do REDO logging here then this REDO log record will be applied - as part of the current recovery process. We surely don't need - that as this is fix-up action parallel to REDO logging. */ - mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); - buf_block_t* sys_header = trx_sysf_get(&mtr); - if (!sys_header) { - mtr.commit(); - return DB_CORRUPTION; - } - - for (undo::undo_spaces_t::const_iterator it - = undo::Truncate::s_fix_up_spaces.begin(); - it != undo::Truncate::s_fix_up_spaces.end(); - ++it) { - - undo::Truncate::add_space_to_trunc_list(*it); - - fil_space_t* space = fil_space_get(*it); - - fsp_header_init(space, - SRV_UNDO_TABLESPACE_SIZE_IN_PAGES, - &mtr); - - for (ulint i = 0; i < TRX_SYS_N_RSEGS; i++) { - if (trx_sysf_rseg_get_space(sys_header, i) - == *it) { - trx_rseg_header_create( - space, i, sys_header, &mtr); - } - } - - undo::Truncate::clear_trunc_list(); - } - mtr_commit(&mtr); - - /* Step-2: Flush the dirty pages from the buffer pool. */ - for (undo::undo_spaces_t::const_iterator it - = undo::Truncate::s_fix_up_spaces.begin(); - it != undo::Truncate::s_fix_up_spaces.end(); - ++it) { - FlushObserver dummy(fil_system.sys_space, NULL, NULL); - buf_LRU_flush_or_remove_pages(TRX_SYS_SPACE, &dummy); - FlushObserver dummy2(fil_space_get(*it), NULL, NULL); - buf_LRU_flush_or_remove_pages(*it, &dummy2); - - /* Remove the truncate redo log file. */ - undo::done(*it); - } - } - return(DB_SUCCESS); } @@ -1943,7 +1843,7 @@ files_checked: ulint ibuf_root = btr_create( DICT_CLUSTERED | DICT_IBUF, fil_system.sys_space, - DICT_IBUF_ID_MIN, dict_ind_redundant, NULL, &mtr); + DICT_IBUF_ID_MIN, dict_ind_redundant, &mtr); mtr_commit(&mtr); @@ -1982,22 +1882,6 @@ files_checked: return(srv_init_abort(err)); } } else { - /* Invalidate the buffer pool to ensure that we reread - the page that we read above, during recovery. - Note that this is not as heavy weight as it seems. At - this point there will be only ONE page in the buf_LRU - and there must be no page in the buf_flush list. */ - buf_pool_invalidate(); - - /* Scan and locate truncate log files. Parsed located files - and add table to truncate information to central vector for - truncate fix-up action post recovery. */ - err = TruncateLogParser::scan_and_parse(srv_log_group_home_dir); - if (err != DB_SUCCESS) { - - return(srv_init_abort(DB_ERROR)); - } - /* We always try to do a recovery, even if the database had been shut down normally: this is the normal startup path */ @@ -2276,14 +2160,6 @@ files_checked: trx_rollback_recovered(false); } - /* Fix-up truncate of tables in the system tablespace - if server crashed while truncate was active. The non- - system tables are done after tablespace discovery. Do - this now because this procedure assumes that no pages - have changed since redo recovery. Tablespace discovery - can do updates to pages in the system tablespace.*/ - err = truncate_t::fixup_tables_in_system_tablespace(); - if (srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE) { /* Open or Create SYS_TABLESPACES and SYS_DATAFILES so that tablespace names and other metadata can be @@ -2321,10 +2197,6 @@ files_checked: dict_check_tablespaces_and_store_max_id(validate); } - /* Fix-up truncate of table if server crashed while truncate - was active. */ - err = truncate_t::fixup_tables_in_non_system_tablespace(); - if (err != DB_SUCCESS) { return(srv_init_abort(err)); } diff --git a/storage/innobase/trx/trx0purge.cc b/storage/innobase/trx/trx0purge.cc index da4084f49d9..652734c4a31 100644 --- a/storage/innobase/trx/trx0purge.cc +++ b/storage/innobase/trx/trx0purge.cc @@ -177,7 +177,8 @@ void purge_sys_t::create() hdr_offset= 0; rw_lock_create(trx_purge_latch_key, &latch, SYNC_PURGE_LATCH); mutex_create(LATCH_ID_PURGE_SYS_PQ, &pq_mutex); - undo_trunc.create(); + truncate.current= NULL; + truncate.last= NULL; } /** Close the purge subsystem on shutdown. */ @@ -513,309 +514,22 @@ func_exit: goto loop; } -/** UNDO log truncate logger. Needed to track state of truncate during crash. -An auxiliary redo log file undo__trunc.log will created while the -truncate of the UNDO is in progress. This file is required during recovery -to complete the truncate. */ - -namespace undo { - /** Magic Number to indicate truncate action is complete. */ - static const ib_uint32_t s_magic = 76845412; - - /** Populate log file name based on space_id - @param[in] space_id id of the undo tablespace. - @return DB_SUCCESS or error code */ - static dberr_t populate_log_file_name( - ulint space_id, - char*& log_file_name) - { - static const char s_log_prefix[] = "undo_"; - static const char s_log_ext[] = "trunc.log"; - - ulint log_file_name_sz = strlen(srv_log_group_home_dir) - + (22 - 1 /* NUL */ - + sizeof s_log_prefix + sizeof s_log_ext); - - log_file_name = new (std::nothrow) char[log_file_name_sz]; - if (log_file_name == 0) { - return(DB_OUT_OF_MEMORY); - } - - memset(log_file_name, 0, log_file_name_sz); - - strcpy(log_file_name, srv_log_group_home_dir); - ulint log_file_name_len = strlen(log_file_name); - - if (log_file_name[log_file_name_len - 1] - != OS_PATH_SEPARATOR) { - - log_file_name[log_file_name_len] - = OS_PATH_SEPARATOR; - log_file_name_len = strlen(log_file_name); - } - - snprintf(log_file_name + log_file_name_len, - log_file_name_sz - log_file_name_len, - "%s" ULINTPF "_%s", s_log_prefix, - space_id, s_log_ext); - - return(DB_SUCCESS); - } - - /** Mark completion of undo truncate action by writing magic number to - the log file and then removing it from the disk. - If we are going to remove it from disk then why write magic number ? - This is to safeguard from unlink (file-system) anomalies that will keep - the link to the file even after unlink action is successfull and - ref-count = 0. - @param[in] space_id id of the undo tablespace to truncate.*/ - void done( - ulint space_id) - { - dberr_t err; - char* log_file_name; - - /* Step-1: Create the log file name using the pre-decided - prefix/suffix and table id of undo tablepsace to truncate. */ - err = populate_log_file_name(space_id, log_file_name); - if (err != DB_SUCCESS) { - return; - } - - /* Step-2: Open log file and write magic number to - indicate done phase. */ - bool ret; - os_file_t handle = - os_file_create_simple_no_error_handling( - innodb_log_file_key, log_file_name, - OS_FILE_OPEN, OS_FILE_READ_WRITE, - srv_read_only_mode, &ret); - - if (!ret) { - os_file_delete(innodb_log_file_key, log_file_name); - delete[] log_file_name; - return; - } - - ulint sz = srv_page_size; - void* buf = ut_zalloc_nokey(sz + srv_page_size); - if (buf == NULL) { - os_file_close(handle); - os_file_delete(innodb_log_file_key, log_file_name); - delete[] log_file_name; - return; - } - - byte* log_buf = static_cast( - ut_align(buf, srv_page_size)); - - mach_write_to_4(log_buf, undo::s_magic); - - IORequest request(IORequest::WRITE); - - err = os_file_write( - request, log_file_name, handle, log_buf, 0, sz); - - ut_ad(err == DB_SUCCESS); - - os_file_flush(handle); - os_file_close(handle); - - ut_free(buf); - os_file_delete(innodb_log_file_key, log_file_name); - delete[] log_file_name; - } - - /** Check if TRUNCATE_DDL_LOG file exist. - @param[in] space_id id of the undo tablespace. - @return true if exist else false. */ - bool is_log_present( - ulint space_id) - { - dberr_t err; - char* log_file_name; - - /* Step-1: Populate log file name. */ - err = populate_log_file_name(space_id, log_file_name); - if (err != DB_SUCCESS) { - return(false); - } - - /* Step-2: Check for existence of the file. */ - bool exist; - os_file_type_t type; - os_file_status(log_file_name, &exist, &type); - - /* Step-3: If file exists, check it for presence of magic - number. If found, then delete the file and report file - doesn't exist as presence of magic number suggest that - truncate action was complete. */ - - if (exist) { - bool ret; - os_file_t handle = - os_file_create_simple_no_error_handling( - innodb_log_file_key, log_file_name, - OS_FILE_OPEN, OS_FILE_READ_WRITE, - srv_read_only_mode, &ret); - if (!ret) { - os_file_delete(innodb_log_file_key, - log_file_name); - delete[] log_file_name; - return(false); - } - - ulint sz = srv_page_size; - void* buf = ut_zalloc_nokey(sz + srv_page_size); - if (buf == NULL) { - os_file_close(handle); - os_file_delete(innodb_log_file_key, - log_file_name); - delete[] log_file_name; - return(false); - } - - byte* log_buf = static_cast( - ut_align(buf, srv_page_size)); - - IORequest request(IORequest::READ); - - dberr_t err; - - err = os_file_read(request, handle, log_buf, 0, sz); - - os_file_close(handle); - - if (err != DB_SUCCESS) { - - ib::info() - << "Unable to read '" - << log_file_name << "' : " - << ut_strerr(err); - - os_file_delete( - innodb_log_file_key, log_file_name); - - ut_free(buf); - - delete[] log_file_name; - - return(false); - } - - ulint magic_no = mach_read_from_4(log_buf); - - ut_free(buf); - - if (magic_no == undo::s_magic) { - /* Found magic number. */ - os_file_delete(innodb_log_file_key, - log_file_name); - delete[] log_file_name; - return(false); - } - } - - delete[] log_file_name; - - return(exist); - } -}; - -/** Iterate over all the UNDO tablespaces and check if any of the UNDO -tablespace qualifies for TRUNCATE (size > threshold). -@param[in,out] undo_trunc undo truncate tracker */ -static -void -trx_purge_mark_undo_for_truncate( - undo::Truncate* undo_trunc) -{ - /* Step-1: If UNDO Tablespace - - already marked for truncate (OR) - - truncate disabled - return immediately else search for qualifying tablespace. */ - if (undo_trunc->is_marked() || !srv_undo_log_truncate) { - return; - } - - /* Step-2: Validation/Qualification checks - a. At-least 2 UNDO tablespaces so even if one UNDO tablespace - is being truncated server can continue to operate. - b. At-least 2 persistent UNDO logs (besides the default rseg-0) - b. At-least 1 UNDO tablespace size > threshold. */ - if (srv_undo_tablespaces_active < 2 || srv_undo_logs < 3) { - return; - } - - /* Avoid bias selection and so start the scan from immediate next - of last selected UNDO tablespace for truncate. */ - ulint space_id = undo_trunc->get_scan_start(); - - for (ulint i = 1; i <= srv_undo_tablespaces_active; i++) { - - if (fil_space_get_size(space_id) - > (srv_max_undo_log_size >> srv_page_size_shift)) { - /* Tablespace qualifies for truncate. */ - undo_trunc->mark(space_id); - undo::Truncate::add_space_to_trunc_list(space_id); - break; - } - - space_id = ((space_id + 1) % (srv_undo_tablespaces_active + 1)); - if (space_id == 0) { - /* Note: UNDO tablespace ids starts from 1. */ - ++space_id; - } - } - - /* Couldn't make any selection. */ - if (!undo_trunc->is_marked()) { - return; - } - - DBUG_LOG("undo", - "marking for truncate UNDO tablespace " - << undo_trunc->get_marked_space_id()); - - /* Step-3: Iterate over all the rsegs of selected UNDO tablespace - and mark them temporarily unavailable for allocation.*/ - for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) { - if (trx_rseg_t* rseg = trx_sys.rseg_array[i]) { - ut_ad(rseg->is_persistent()); - if (rseg->space->id - == undo_trunc->get_marked_space_id()) { - - /* Once set this rseg will not be allocated - to new booting transaction but we will wait - for existing active transaction to finish. */ - rseg->skip_allocation = true; - undo_trunc->add_rseg_to_trunc(rseg); - } - } - } -} - -undo::undo_spaces_t undo::Truncate::s_spaces_to_truncate; - /** Cleanse purge queue to remove the rseg that reside in undo-tablespace marked for truncate. -@param[in,out] undo_trunc undo truncate tracker */ -static -void -trx_purge_cleanse_purge_queue( - undo::Truncate* undo_trunc) +@param[in] space undo tablespace being truncated */ +static void trx_purge_cleanse_purge_queue(const fil_space_t& space) { - mutex_enter(&purge_sys.pq_mutex); typedef std::vector purge_elem_list_t; purge_elem_list_t purge_elem_list; + mutex_enter(&purge_sys.pq_mutex); + /* Remove rseg instances that are in the purge queue before we start truncate of corresponding UNDO truncate. */ while (!purge_sys.purge_queue.empty()) { purge_elem_list.push_back(purge_sys.purge_queue.top()); purge_sys.purge_queue.pop(); } - ut_ad(purge_sys.purge_queue.empty()); for (purge_elem_list_t::iterator it = purge_elem_list.begin(); it != purge_elem_list.end(); @@ -824,9 +538,7 @@ trx_purge_cleanse_purge_queue( for (TrxUndoRsegs::iterator it2 = it->begin(); it2 != it->end(); ++it2) { - - if ((*it2)->space->id - == undo_trunc->get_marked_space_id()) { + if ((*it2)->space == &space) { it->erase(it2); break; } @@ -836,251 +548,10 @@ trx_purge_cleanse_purge_queue( purge_sys.purge_queue.push(*it); } } + mutex_exit(&purge_sys.pq_mutex); } -/** Iterate over selected UNDO tablespace and check if all the rsegs -that resides in the tablespace are free. -@param[in] limit truncate_limit -@param[in,out] undo_trunc undo truncate tracker */ -static -void -trx_purge_initiate_truncate( - const purge_sys_t::iterator& limit, - undo::Truncate* undo_trunc) -{ - /* Step-1: Early check to findout if any of the the UNDO tablespace - is marked for truncate. */ - if (!undo_trunc->is_marked()) { - /* No tablespace marked for truncate yet. */ - return; - } - - /* Step-2: Scan over each rseg and ensure that it doesn't hold any - active undo records. */ - bool all_free = true; - - for (ulint i = 0; i < undo_trunc->rsegs_size() && all_free; ++i) { - - trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i); - - mutex_enter(&rseg->mutex); - - if (rseg->trx_ref_count > 0) { - /* This rseg is still being held by an active - transaction. */ - all_free = false; - mutex_exit(&rseg->mutex); - continue; - } - - ut_ad(rseg->trx_ref_count == 0); - ut_ad(rseg->skip_allocation); - - ulint size_of_rsegs = rseg->curr_size; - - if (size_of_rsegs == 1) { - mutex_exit(&rseg->mutex); - continue; - } else { - - /* There could be cached undo segment. Check if records - in these segments can be purged. Normal purge history - will not touch these cached segment. */ - ulint cached_undo_size = 0; - - for (trx_undo_t* undo = - UT_LIST_GET_FIRST(rseg->undo_cached); - undo != NULL && all_free; - undo = UT_LIST_GET_NEXT(undo_list, undo)) { - - if (limit.trx_no() < undo->trx_id) { - all_free = false; - } else { - cached_undo_size += undo->size; - } - } - - ut_ad(size_of_rsegs >= (cached_undo_size + 1)); - - if (size_of_rsegs > (cached_undo_size + 1)) { - /* There are pages besides cached pages that - still hold active data. */ - all_free = false; - } - } - - mutex_exit(&rseg->mutex); - } - - if (!all_free) { - /* rseg still holds active data.*/ - return; - } - - - /* Step-3: Start the actual truncate. - a. Remove rseg instance if added to purge queue before we - initiate truncate. - b. Execute actual truncate */ - - const ulint space_id = undo_trunc->get_marked_space_id(); - - ib::info() << "Truncating UNDO tablespace " << space_id; - - trx_purge_cleanse_purge_queue(undo_trunc); - - ut_a(srv_is_undo_tablespace(space_id)); - - fil_space_t* space = fil_space_get(space_id); - - if (!space) { -not_found: - ib::error() << "Failed to find UNDO tablespace " << space_id; - return; - } - - /* Flush all to-be-discarded pages of the tablespace. - - During truncation, we do not want any writes to the - to-be-discarded area, because we must set the space->size - early in order to have deterministic page allocation. - - If a log checkpoint was completed at LSN earlier than our - mini-transaction commit and the server was killed, then - discarding the to-be-trimmed pages without flushing would - break crash recovery. So, we cannot avoid the write. */ - { - FlushObserver observer( - space, - UT_LIST_GET_FIRST(purge_sys.query->thrs)->graph->trx, - NULL); - buf_LRU_flush_or_remove_pages(space_id, &observer); - } - - log_free_check(); - - /* Adjust the tablespace metadata. */ - space = fil_truncate_prepare(space_id); - - if (!space) { - goto not_found; - } - - /* Undo tablespace always are a single file. */ - ut_a(UT_LIST_GET_LEN(space->chain) == 1); - fil_node_t* file = UT_LIST_GET_FIRST(space->chain); - /* The undo tablespace files are never closed. */ - ut_ad(file->is_open()); - - /* Re-initialize tablespace, in a single mini-transaction. */ - mtr_t mtr; - const ulint size = SRV_UNDO_TABLESPACE_SIZE_IN_PAGES; - mtr.start(); - mtr_x_lock(&space->latch, &mtr); - fil_truncate_log(space, size, &mtr); - fsp_header_init(space, size, &mtr); - mutex_enter(&fil_system.mutex); - space->size = file->size = size; - mutex_exit(&fil_system.mutex); - - buf_block_t* sys_header = trx_sysf_get(&mtr); - - for (ulint i = 0; i < undo_trunc->rsegs_size(); ++i) { - trx_rsegf_t* rseg_header; - - trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i); - - rseg->page_no = trx_rseg_header_create( - space, rseg->id, sys_header, &mtr); - - rseg_header = trx_rsegf_get_new( - space_id, rseg->page_no, &mtr); - - /* Before re-initialization ensure that we free the existing - structure. There can't be any active transactions. */ - ut_a(UT_LIST_GET_LEN(rseg->undo_list) == 0); - ut_a(UT_LIST_GET_LEN(rseg->old_insert_list) == 0); - - trx_undo_t* next_undo; - - for (trx_undo_t* undo = UT_LIST_GET_FIRST(rseg->undo_cached); - undo != NULL; - undo = next_undo) { - - next_undo = UT_LIST_GET_NEXT(undo_list, undo); - UT_LIST_REMOVE(rseg->undo_cached, undo); - MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED); - ut_free(undo); - } - - UT_LIST_INIT(rseg->undo_list, &trx_undo_t::undo_list); - UT_LIST_INIT(rseg->undo_cached, &trx_undo_t::undo_list); - UT_LIST_INIT(rseg->old_insert_list, &trx_undo_t::undo_list); - - /* These were written by trx_rseg_header_create(). */ - ut_ad(!mach_read_from_4(rseg_header + TRX_RSEG_FORMAT)); - ut_ad(!mach_read_from_4(rseg_header + TRX_RSEG_HISTORY_SIZE)); - - /* Initialize the undo log lists according to the rseg header */ - rseg->curr_size = 1; - rseg->trx_ref_count = 0; - rseg->last_page_no = FIL_NULL; - rseg->last_offset = 0; - rseg->last_commit = 0; - rseg->needs_purge = false; - } - - mtr.commit(); - /* Write-ahead the redo log record. */ - log_write_up_to(mtr.commit_lsn(), true); - - /* Trim the file size. */ - os_file_truncate(file->name, file->handle, - os_offset_t(size) << srv_page_size_shift, true); - - /* This is only executed by the srv_coordinator_thread. */ - export_vars.innodb_undo_truncations++; - - /* TODO: PUNCH_HOLE the garbage (with write-ahead logging) */ - - mutex_enter(&fil_system.mutex); - ut_ad(space->stop_new_ops); - ut_ad(space->is_being_truncated); - space->stop_new_ops = false; - space->is_being_truncated = false; - mutex_exit(&fil_system.mutex); - - if (purge_sys.rseg != NULL - && purge_sys.rseg->last_page_no == FIL_NULL) { - /* If purge_sys.rseg is pointing to rseg that was recently - truncated then move to next rseg element. - Note: Ideally purge_sys.rseg should be NULL because purge - should complete processing of all the records but there is - purge_batch_size that can force the purge loop to exit before - all the records are purged and in this case purge_sys.rseg - could point to a valid rseg waiting for next purge cycle. */ - purge_sys.next_stored = false; - purge_sys.rseg = NULL; - } - - DBUG_EXECUTE_IF("ib_undo_trunc", - ib::info() << "ib_undo_trunc"; - log_write_up_to(LSN_MAX, true); - DBUG_SUICIDE();); - - /* Completed truncate. Now it is safe to re-use the tablespace. */ - for (ulint i = 0; i < undo_trunc->rsegs_size(); ++i) { - trx_rseg_t* rseg = undo_trunc->get_ith_rseg(i); - rseg->skip_allocation = false; - } - - ib::info() << "Truncated UNDO tablespace " << space_id; - - undo_trunc->reset(); - undo::Truncate::clear_trunc_list(); -} - /** Removes unnecessary history data from rollback segments. NOTE that when this function is called, the caller must not have any latches on undo log pages! @@ -1104,12 +575,258 @@ static void trx_purge_truncate_history() } } - /* UNDO tablespace truncate. We will try to truncate as much as we - can (greedy approach). This will ensure when the server is idle we - try and truncate all the UNDO tablespaces. */ - for (ulint i = srv_undo_tablespaces_active; i--; ) { - trx_purge_mark_undo_for_truncate(&purge_sys.undo_trunc); - trx_purge_initiate_truncate(head, &purge_sys.undo_trunc); + if (srv_undo_tablespaces_active < 2) { + return; + } + + while (srv_undo_log_truncate && srv_undo_logs >= 3) { + if (!purge_sys.truncate.current) { + const ulint threshold = ulint(srv_max_undo_log_size + >> srv_page_size_shift); + for (ulint i = purge_sys.truncate.last + ? purge_sys.truncate.last->id + - srv_undo_space_id_start + : 0, j = i;; ) { + ulint space_id = srv_undo_space_id_start + i; + ut_ad(srv_is_undo_tablespace(space_id)); + + if (fil_space_get_size(space_id) + > threshold) { + purge_sys.truncate.current + = fil_space_get(space_id); + break; + } + + ++i; + i %= srv_undo_tablespaces_active; + if (i == j) { + break; + } + } + } + + if (!purge_sys.truncate.current) { + return; + } + + const fil_space_t& space = *purge_sys.truncate.current; + /* Undo tablespace always are a single file. */ + ut_a(UT_LIST_GET_LEN(space.chain) == 1); + fil_node_t* file = UT_LIST_GET_FIRST(space.chain); + /* The undo tablespace files are never closed. */ + ut_ad(file->is_open()); + + DBUG_LOG("undo", "marking for truncate: " << file->name); + + for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) { + if (trx_rseg_t* rseg = trx_sys.rseg_array[i]) { + ut_ad(rseg->is_persistent()); + if (rseg->space == &space) { + /* Once set, this rseg will + not be allocated to subsequent + transactions, but we will wait + for existing active + transactions to finish. */ + rseg->skip_allocation = true; + } + } + } + + for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) { + trx_rseg_t* rseg = trx_sys.rseg_array[i]; + if (!rseg || rseg->space != &space) { + continue; + } + mutex_enter(&rseg->mutex); + ut_ad(rseg->skip_allocation); + if (rseg->trx_ref_count) { +not_free: + mutex_exit(&rseg->mutex); + return; + } + + if (rseg->curr_size != 1) { + /* Check if all segments are + cached and safe to remove. */ + ulint cached = 0; + + for (trx_undo_t* undo = UT_LIST_GET_FIRST( + rseg->undo_cached); + undo; + undo = UT_LIST_GET_NEXT(undo_list, + undo)) { + if (head.trx_no() < undo->trx_id) { + goto not_free; + } else { + cached += undo->size; + } + } + + ut_ad(rseg->curr_size > cached); + + if (rseg->curr_size > cached + 1) { + goto not_free; + } + } + + mutex_exit(&rseg->mutex); + } + + ib::info() << "Truncating " << file->name; + trx_purge_cleanse_purge_queue(space); + + /* Flush all to-be-discarded pages of the tablespace. + + During truncation, we do not want any writes to the + to-be-discarded area, because we must set the space.size + early in order to have deterministic page allocation. + + If a log checkpoint was completed at LSN earlier than our + mini-transaction commit and the server was killed, then + discarding the to-be-trimmed pages without flushing would + break crash recovery. So, we cannot avoid the write. */ + { + FlushObserver observer( + purge_sys.truncate.current, + UT_LIST_GET_FIRST(purge_sys.query->thrs) + ->graph->trx, + NULL); + buf_LRU_flush_or_remove_pages(space.id, &observer); + } + + log_free_check(); + + /* Adjust the tablespace metadata. */ + if (!fil_truncate_prepare(space.id)) { + ib::error() << "Failed to find UNDO tablespace " + << file->name; + return; + } + + /* Re-initialize tablespace, in a single mini-transaction. */ + mtr_t mtr; + const ulint size = SRV_UNDO_TABLESPACE_SIZE_IN_PAGES; + mtr.start(); + mtr_x_lock(&purge_sys.truncate.current->latch, &mtr); + fil_truncate_log(purge_sys.truncate.current, size, &mtr); + fsp_header_init(purge_sys.truncate.current, size, &mtr); + mutex_enter(&fil_system.mutex); + purge_sys.truncate.current->size = file->size = size; + mutex_exit(&fil_system.mutex); + + buf_block_t* sys_header = trx_sysf_get(&mtr); + + for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) { + trx_rseg_t* rseg = trx_sys.rseg_array[i]; + if (!rseg || rseg->space != &space) { + continue; + } + + ut_ad(rseg->is_persistent()); + ut_d(const ulint old_page = rseg->page_no); + + rseg->page_no = trx_rseg_header_create( + purge_sys.truncate.current, + rseg->id, sys_header, &mtr); + ut_ad(old_page == rseg->page_no); + + trx_rsegf_t* rseg_header = trx_rsegf_get_new( + space.id, rseg->page_no, &mtr); + + /* Before re-initialization ensure that we + free the existing structure. There can't be + any active transactions. */ + ut_a(UT_LIST_GET_LEN(rseg->undo_list) == 0); + ut_a(UT_LIST_GET_LEN(rseg->old_insert_list) == 0); + + trx_undo_t* next_undo; + + for (trx_undo_t* undo = UT_LIST_GET_FIRST( + rseg->undo_cached); + undo; undo = next_undo) { + + next_undo = UT_LIST_GET_NEXT(undo_list, undo); + UT_LIST_REMOVE(rseg->undo_cached, undo); + MONITOR_DEC(MONITOR_NUM_UNDO_SLOT_CACHED); + ut_free(undo); + } + + UT_LIST_INIT(rseg->undo_list, + &trx_undo_t::undo_list); + UT_LIST_INIT(rseg->undo_cached, + &trx_undo_t::undo_list); + UT_LIST_INIT(rseg->old_insert_list, + &trx_undo_t::undo_list); + + /* These were written by trx_rseg_header_create(). */ + ut_ad(!mach_read_from_4(rseg_header + + TRX_RSEG_FORMAT)); + ut_ad(!mach_read_from_4(rseg_header + + TRX_RSEG_HISTORY_SIZE)); + + /* Initialize the undo log lists according to + the rseg header */ + rseg->curr_size = 1; + rseg->trx_ref_count = 0; + rseg->last_page_no = FIL_NULL; + rseg->last_offset = 0; + rseg->last_commit = 0; + rseg->needs_purge = false; + } + + mtr.commit(); + /* Write-ahead the redo log record. */ + log_write_up_to(mtr.commit_lsn(), true); + + /* Trim the file size. */ + os_file_truncate(file->name, file->handle, + os_offset_t(size) << srv_page_size_shift, + true); + + /* This is only executed by the srv_coordinator_thread. */ + export_vars.innodb_undo_truncations++; + + /* TODO: PUNCH_HOLE the garbage (with write-ahead logging) */ + mutex_enter(&fil_system.mutex); + ut_ad(&space == purge_sys.truncate.current); + ut_ad(space.stop_new_ops); + ut_ad(space.is_being_truncated); + purge_sys.truncate.current->stop_new_ops = false; + purge_sys.truncate.current->is_being_truncated = false; + mutex_exit(&fil_system.mutex); + + if (purge_sys.rseg != NULL + && purge_sys.rseg->last_page_no == FIL_NULL) { + /* If purge_sys.rseg is pointing to rseg that + was recently truncated then move to next rseg + element. Note: Ideally purge_sys.rseg should + be NULL because purge should complete + processing of all the records but there is + purge_batch_size that can force the purge loop + to exit before all the records are purged and + in this case purge_sys.rseg could point to a + valid rseg waiting for next purge cycle. */ + purge_sys.next_stored = false; + purge_sys.rseg = NULL; + } + + DBUG_EXECUTE_IF("ib_undo_trunc", + ib::info() << "ib_undo_trunc"; + log_write_up_to(LSN_MAX, true); + DBUG_SUICIDE();); + + for (ulint i = 0; i < TRX_SYS_N_RSEGS; ++i) { + if (trx_rseg_t* rseg = trx_sys.rseg_array[i]) { + ut_ad(rseg->is_persistent()); + if (rseg->space == &space) { + rseg->skip_allocation = false; + } + } + } + + ib::info() << "Truncated " << file->name; + purge_sys.truncate.last = purge_sys.truncate.current; + purge_sys.truncate.current = NULL; } } diff --git a/storage/innobase/ut/ut0new.cc b/storage/innobase/ut/ut0new.cc index 35d49073678..3a17b5ebf30 100644 --- a/storage/innobase/ut/ut0new.cc +++ b/storage/innobase/ut/ut0new.cc @@ -150,7 +150,6 @@ ut_new_boot() "row0merge", "row0mysql", "row0sel", - "row0trunc", "srv0conc", "srv0srv", "srv0start", diff --git a/storage/innobase/ut/ut0ut.cc b/storage/innobase/ut/ut0ut.cc index 39fb037aa28..adda0b960c9 100644 --- a/storage/innobase/ut/ut0ut.cc +++ b/storage/innobase/ut/ut0ut.cc @@ -589,8 +589,6 @@ ut_strerr( return("Tablespace already exists"); case DB_TABLESPACE_DELETED: return("Tablespace deleted or being deleted"); - case DB_TABLESPACE_TRUNCATED: - return("Tablespace was truncated"); case DB_TABLESPACE_NOT_FOUND: return("Tablespace not found"); case DB_LOCK_TABLE_FULL: