diff options
Diffstat (limited to 'storage/innobase/row/row0import.cc')
-rw-r--r-- | storage/innobase/row/row0import.cc | 409 |
1 files changed, 276 insertions, 133 deletions
diff --git a/storage/innobase/row/row0import.cc b/storage/innobase/row/row0import.cc index 6194e9c3..5febd6df 100644 --- a/storage/innobase/row/row0import.cc +++ b/storage/innobase/row/row0import.cc @@ -293,6 +293,7 @@ struct fil_iterator_t { byte* io_buffer; /*!< Buffer to use for IO */ fil_space_crypt_t *crypt_data; /*!< Crypt data (if encrypted) */ byte* crypt_io_buffer; /*!< IO buffer when encrypted */ + byte* crypt_tmp_buffer; /*!< Temporary buffer for crypt use */ }; /** Use the page cursor to iterate over records in a block. */ @@ -739,7 +740,8 @@ struct FetchIndexRootPages : public AbstractCallback { /** Constructor @param trx covering (user) transaction @param table table definition in server .*/ - FetchIndexRootPages(const dict_table_t* table, trx_t* trx) + FetchIndexRootPages(const dict_table_t* table = nullptr, + trx_t* trx = nullptr) : AbstractCallback(trx, UINT32_MAX), m_table(table), m_index(0, 0) UNIV_NOTHROW { } @@ -754,18 +756,46 @@ struct FetchIndexRootPages : public AbstractCallback { dberr_t run(const fil_iterator_t& iter, buf_block_t* block) UNIV_NOTHROW override; - /** Called for each block as it is read from the file. + /** Check that fsp flags and row formats match. @param block block to convert, it is not from the buffer pool. @retval DB_SUCCESS or error code. */ dberr_t operator()(buf_block_t* block) UNIV_NOTHROW override; + /** Get row format from the header and the root index page. */ + enum row_type get_row_format(const buf_block_t &block) + { + if (!page_is_comp(block.page.frame)) + return ROW_TYPE_REDUNDANT; + /* With full_crc32 we cannot tell between dynamic or + compact, and return not_used. We cannot simply return + dynamic or compact, as the client of this function + will not be able to tell whether it is dynamic because + of this or the other branch below. Returning default + would also work if it is immediately handled, but is + still more ambiguous than not_used, which is not a + row_format at all. */ + if (fil_space_t::full_crc32(m_space_flags)) + return ROW_TYPE_NOT_USED; + if (!(m_space_flags & FSP_FLAGS_MASK_ATOMIC_BLOBS)) + return ROW_TYPE_COMPACT; + if (FSP_FLAGS_GET_ZIP_SSIZE(m_space_flags)) + return ROW_TYPE_COMPRESSED; + return ROW_TYPE_DYNAMIC; + } + /** Update the import configuration that will be used to import the tablespace. */ dberr_t build_row_import(row_import* cfg) const UNIV_NOTHROW; - /** Table definition in server. */ + /** Table definition in server. When the table is being + created, there's no table yet so m_table is nullptr */ const dict_table_t* m_table; + /** Table row format. Only used when a (stub) table is being + created in which case m_table is null, for obtaining row + format from the .ibd for the stub table. */ + enum row_type m_row_format; + /** Index information */ Index m_index; }; @@ -2151,9 +2181,9 @@ dberr_t PageConverter::operator()(buf_block_t* block) UNIV_NOTHROW /* If we already had an old page with matching number in the buffer pool, evict it now, because we no longer evict the pages on DISCARD TABLESPACE. */ - buf_page_get_low(block->page.id(), get_zip_size(), RW_NO_LATCH, + buf_page_get_gen(block->page.id(), get_zip_size(), RW_NO_LATCH, nullptr, BUF_PEEK_IF_IN_POOL, - nullptr, nullptr, false); + nullptr, nullptr); uint16_t page_type; @@ -2207,8 +2237,9 @@ row_import_cleanup(row_prebuilt_t* prebuilt, dberr_t err, dict_table_t* fts_table = nullptr) { + dict_table_t* table = prebuilt->table; + if (err != DB_SUCCESS) { - dict_table_t* table = prebuilt->table; table->file_unreadable = true; if (table->space) { fil_close_tablespace(table->space_id); @@ -2237,6 +2268,7 @@ row_import_cleanup(row_prebuilt_t* prebuilt, if (err == DB_SUCCESS) { reload_fts_table(prebuilt, fts_table); + table= prebuilt->table; ib::warn() << "Added system generated FTS_DOC_ID " "and FTS_DOC_ID_INDEX while importing " "the tablespace " << prebuilt->table->name; @@ -2272,7 +2304,25 @@ row_import_cleanup(row_prebuilt_t* prebuilt, DBUG_EXECUTE_IF("ib_import_before_checkpoint_crash", DBUG_SUICIDE();); - return(err); + if (err != DB_SUCCESS + || !dict_table_get_first_index(table)->is_gen_clust()) { + return err; + } + + btr_cur_t cur; + mtr_t mtr; + mtr.start(); + err = cur.open_leaf(false, dict_table_get_first_index(table), + BTR_SEARCH_LEAF, &mtr); + if (err != DB_SUCCESS) { + } else if (const rec_t *rec = + page_rec_get_prev(btr_cur_get_rec(&cur))) { + if (page_rec_is_user_rec(rec)) + table->row_id= mach_read_from_6(rec); + } + mtr.commit(); + + return err; } /** Report error during tablespace import. @@ -2411,55 +2461,6 @@ row_import_adjust_root_pages_of_secondary_indexes( } /*****************************************************************//** -Ensure that dict_sys.row_id exceeds SELECT MAX(DB_ROW_ID). */ -MY_ATTRIBUTE((nonnull)) static -void -row_import_set_sys_max_row_id( -/*==========================*/ - row_prebuilt_t* prebuilt, /*!< in/out: prebuilt from - handler */ - const dict_table_t* table) /*!< in: table to import */ -{ - const rec_t* rec; - mtr_t mtr; - btr_pcur_t pcur; - row_id_t row_id = 0; - dict_index_t* index; - - index = dict_table_get_first_index(table); - ut_ad(index->is_primary()); - ut_ad(dict_index_is_auto_gen_clust(index)); - - mtr_start(&mtr); - - mtr_set_log_mode(&mtr, MTR_LOG_NO_REDO); - - if (pcur.open_leaf(false, index, BTR_SEARCH_LEAF, &mtr) - == DB_SUCCESS) { - rec = btr_pcur_move_to_prev_on_page(&pcur); - - if (!rec) { - /* The table is corrupted. */ - } else if (page_rec_is_infimum(rec)) { - /* The table is empty. */ - } else if (rec_is_metadata(rec, *index)) { - /* The clustered index contains the metadata - record only, that is, the table is empty. */ - } else { - row_id = mach_read_from_6(rec); - } - } - - mtr_commit(&mtr); - - if (row_id) { - /* Update the system row id if the imported index row id is - greater than the max system row id. */ - dict_sys.update_row_id(row_id); - } -} - -/*****************************************************************//** Read the a string from the meta data file. @return DB_SUCCESS or error code. */ static @@ -3142,17 +3143,25 @@ row_import_read_meta_data( /* decrypt and decompress page if needed */ static dberr_t decrypt_decompress(fil_space_crypt_t *space_crypt, uint32_t space_flags, span<byte> page, - uint32_t space_id, byte *page_compress_buf) + uint32_t space_id, byte *page_compress_buf, + byte *tmp_frame) { auto *data= page.data(); if (space_crypt && space_crypt->should_encrypt()) { + uint page_size= static_cast<uint>(page.size()); + if (!buf_page_verify_crypt_checksum(data, space_flags)) return DB_CORRUPTION; - if (dberr_t err= fil_space_decrypt(space_id, space_flags, space_crypt, - data, page.size(), data)) + dberr_t err= + fil_space_decrypt(space_id, space_flags, space_crypt, + tmp_frame, page_size, data); + + memcpy(data, tmp_frame, page_size); + + if (err) return err; } @@ -3404,11 +3413,16 @@ static dberr_t handle_instant_metadata(dict_table_t *table, return err; std::unique_ptr<byte[]> page_compress_buf(new byte[get_buf_size()]); + std::unique_ptr<byte[], decltype(&aligned_free)> crypt_tmp_frame( + static_cast<byte *>( + aligned_malloc(physical_size, CPU_LEVEL1_DCACHE_LINESIZE)), + &aligned_free); if (dberr_t err= decrypt_decompress(space_crypt, space_flags, {page.get(), static_cast<size_t> (physical_size)}, - space_id, page_compress_buf.get())) + space_id, page_compress_buf.get(), + crypt_tmp_frame.get())) return err; if (table->supports_instant()) @@ -3462,7 +3476,8 @@ static dberr_t handle_instant_metadata(dict_table_t *table, if (dberr_t err= decrypt_decompress(space_crypt, space_flags, {page.get(), static_cast<size_t> (physical_size)}, space_id, - page_compress_buf.get())) + page_compress_buf.get(), + crypt_tmp_frame.get())) return err; } @@ -3544,7 +3559,8 @@ static dberr_t handle_instant_metadata(dict_table_t *table, if (dberr_t err= decrypt_decompress(space_crypt, space_flags, {second_page.get(), static_cast<size_t>(physical_size)}, - space_id, page_compress_buf.get())) + space_id, page_compress_buf.get(), + crypt_tmp_frame.get())) return err; if (fil_page_get_type(second_page.get()) != FIL_PAGE_TYPE_BLOB || @@ -3627,6 +3643,35 @@ static dberr_t handle_instant_metadata(dict_table_t *table, } /** +Read the contents of a .cfg file. +@param[in] filename Path to the cfg file +@param[in] thd Connection +@param[out] cfg Contents of the .cfg file. +@return DB_SUCCESS or error code. */ +static dberr_t row_import_read_cfg_internal(const char *filename, THD *thd, + row_import &cfg) +{ + FILE *file= fopen(filename, "rb"); + + cfg.m_missing= !file; + + if (!file) + { + char msg[BUFSIZ]; + snprintf(msg, sizeof(msg), + "Error opening '%s', will attempt to import" + " without schema verification", filename); + ib_senderrf(thd, IB_LOG_LEVEL_WARN, ER_IO_READ_ERROR, + (ulong) errno, strerror(errno), msg); + return DB_FAIL; + } + + dberr_t err= row_import_read_meta_data(file, thd, cfg); + fclose(file); + return err; +} + +/** Read the contents of the <tablename>.cfg file. @return DB_SUCCESS or error code. */ static MY_ATTRIBUTE((nonnull, warn_unused_result)) @@ -3637,38 +3682,60 @@ row_import_read_cfg( THD* thd, /*!< in: session */ row_import& cfg) /*!< out: contents of the .cfg file */ { - dberr_t err; char name[OS_FILE_MAX_PATH]; cfg.m_table = table; srv_get_meta_data_filename(table, name, sizeof(name)); - FILE* file = fopen(name, "rb"); - - if (file == NULL) { - char msg[BUFSIZ]; - - snprintf(msg, sizeof(msg), - "Error opening '%s', will attempt to import" - " without schema verification", name); - - ib_senderrf( - thd, IB_LOG_LEVEL_WARN, ER_IO_READ_ERROR, - (ulong) errno, strerror(errno), msg); - - cfg.m_missing = true; + return row_import_read_cfg_internal(name, thd, cfg); +} - err = DB_FAIL; - } else { - cfg.m_missing = false; +/** Convert the InnoDB ROW_FORMAT from rec_format_enum to row_type. +@param[in] from ROW_FORMAT as a rec_format_enum +@return the row_type representation of ROW_FORMAT. */ +static enum row_type from_rec_format(const rec_format_enum from) +{ + switch (from) { + case REC_FORMAT_COMPACT: + return ROW_TYPE_COMPACT; + case REC_FORMAT_DYNAMIC: + return ROW_TYPE_DYNAMIC; + case REC_FORMAT_REDUNDANT: + return ROW_TYPE_REDUNDANT; + case REC_FORMAT_COMPRESSED: + return ROW_TYPE_COMPRESSED; + } - err = row_import_read_meta_data(file, thd, cfg); - fclose(file); - } + ut_ad("invalid format" == 0); + return ROW_TYPE_NOT_USED; +} - return(err); +/** +Read the row type from a .cfg file. +@param dir_path Path to the data directory containing the .cfg file +@param name Name of the table +@param thd Connection +@retval ROW_TYPE_COMPACT for ROW_FORMAT=COMPACT +@retval ROW_TYPE_DYNAMIC for ROW_FORMAT=DYNAMIC +@retval ROW_TYPE_REDUNDANT for ROW_FORMAT=REDUNDANT +@retval ROW_TYPE_COMPRESSED for ROW_FORMAT=COMPRESSED +@retval ROW_TYPE_NOT_USED to signal error */ +static enum row_type get_row_type_from_cfg(const char* dir_path, + const char* name, THD* thd) +{ + char* filename= fil_make_filepath(dir_path, + table_name_t(const_cast<char*>(name)), + CFG, dir_path != nullptr); + if (!filename) + return ROW_TYPE_NOT_USED; + row_import cfg; + dberr_t err= row_import_read_cfg_internal(filename, thd, cfg); + ut_free(filename); + if (err == DB_SUCCESS) + return from_rec_format(dict_tf_get_rec_format(cfg.m_flags)); + return ROW_TYPE_NOT_USED; } /** Update the root page numbers and tablespace ID of a table. @@ -3801,11 +3868,20 @@ row_import_set_discarded( ulint flags2 = mach_read_from_4( static_cast<byte*>(dfield_get_data(dfield))); +#if defined __GNUC__ && !defined __clang__ +# pragma GCC diagnostic push +# if __GNUC__ < 12 || defined WITH_UBSAN +# pragma GCC diagnostic ignored "-Wconversion" +# endif +#endif if (discard->state) { flags2 |= DICT_TF2_DISCARDED; } else { flags2 &= ~DICT_TF2_DISCARDED; } +#if defined __GNUC__ && !defined __clang__ +# pragma GCC diagnostic pop +#endif mach_write_to_4(reinterpret_cast<byte*>(&discard->flags2), flags2); @@ -3986,8 +4062,14 @@ page_corrupted: if (!buf_page_verify_crypt_checksum(readptr, m_space_flags)) goto page_corrupted; - if ((err= fil_space_decrypt(get_space_id(), m_space_flags, iter.crypt_data, - readptr, size, readptr))) + dberr_t err= fil_space_decrypt(get_space_id(), m_space_flags, + iter.crypt_data, iter.crypt_tmp_buffer, + size, readptr); + + memcpy_aligned<CPU_LEVEL1_DCACHE_LINESIZE>(readptr, iter.crypt_tmp_buffer, + size); + + if (err) goto func_exit; } @@ -4008,7 +4090,12 @@ page_corrupted: && buf_page_is_corrupted(false, readptr, m_space_flags)) goto page_corrupted; - err= this->operator()(block); + /* m_table is null iff we are trying to create a (stub) table, in + which case we want to get row format for the table creation. */ + if (m_table) + err= this->operator()(block); + else + m_row_format= get_row_format(*block); func_exit: free(page_compress_buf); return err; @@ -4331,19 +4418,21 @@ func_exit: return err; } -/********************************************************************//** -Iterate over all the pages in the tablespace. -@param table - the table definiton in the server -@param n_io_buffers - number of blocks to read and write together -@param callback - functor that will do the page updates +/** +Iterate over all or some pages in the tablespace. +@param dir_path the path to data dir storing the tablespace +@param name the table name +@param n_io_buffers number of blocks to read and write together +@param callback functor that will do the page queries or updates @return DB_SUCCESS or error code */ static dberr_t fil_tablespace_iterate( /*===================*/ - dict_table_t* table, - ulint n_io_buffers, - AbstractCallback& callback) + const char *name, + ulint n_io_buffers, + AbstractCallback &callback, + const char *dir_path) { dberr_t err; pfs_os_file_t file; @@ -4355,18 +4444,9 @@ fil_tablespace_iterate( DBUG_EXECUTE_IF("ib_import_trigger_corruption_1", return(DB_CORRUPTION);); - /* Make sure the data_dir_path is set. */ - dict_get_and_save_data_dir_path(table); - - ut_ad(!DICT_TF_HAS_DATA_DIR(table->flags) || table->data_dir_path); - - const char *data_dir_path = DICT_TF_HAS_DATA_DIR(table->flags) - ? table->data_dir_path : nullptr; - - filepath = fil_make_filepath(data_dir_path, - {table->name.m_name, - strlen(table->name.m_name)}, - IBD, data_dir_path != nullptr); + table_name_t table_name(const_cast<char*>(name)); + filepath= fil_make_filepath(dir_path, table_name, IBD, + dir_path != nullptr); if (!filepath) { return(DB_OUT_OF_MEMORY); } else { @@ -4379,9 +4459,9 @@ fil_tablespace_iterate( if (!success) { /* The following call prints an error message */ os_file_get_last_error(true); - ib::error() << "Trying to import a tablespace," - " but could not open the tablespace file " - << filepath; + sql_print_error("InnoDB: could not open the " + "tablespace file %s.\n", + filepath); ut_free(filepath); return DB_TABLESPACE_NOT_FOUND; } else { @@ -4444,17 +4524,21 @@ fil_tablespace_iterate( iter.file_size = file_size; iter.n_io_buffers = n_io_buffers; + size_t buf_size = (1 + iter.n_io_buffers) * srv_page_size; + /* Add an extra page for compressed page scratch area. */ iter.io_buffer = static_cast<byte*>( - aligned_malloc((1 + iter.n_io_buffers) - << srv_page_size_shift, srv_page_size)); + aligned_malloc(buf_size, srv_page_size)); - iter.crypt_io_buffer = iter.crypt_data - ? static_cast<byte*>( - aligned_malloc((1 + iter.n_io_buffers) - << srv_page_size_shift, - srv_page_size)) - : NULL; + if (iter.crypt_data) { + iter.crypt_io_buffer = static_cast<byte *>( + aligned_malloc(buf_size, srv_page_size)); + iter.crypt_tmp_buffer = static_cast<byte *>( + aligned_malloc(buf_size, CPU_LEVEL1_DCACHE_LINESIZE)); + } else { + iter.crypt_io_buffer = NULL; + iter.crypt_tmp_buffer = NULL; + } if (block->page.zip.ssize) { ut_ad(iter.n_io_buffers == 1); @@ -4469,6 +4553,7 @@ fil_tablespace_iterate( fil_space_destroy_crypt_data(&iter.crypt_data); } + aligned_free(iter.crypt_tmp_buffer); aligned_free(iter.crypt_io_buffer); aligned_free(iter.io_buffer); } @@ -4493,6 +4578,24 @@ fil_tablespace_iterate( return(err); } +/** +Iterate over all or some pages in the tablespace. +@param table the table definiton in the server +@param n_io_buffers number of blocks to read and write together +@param callback functor that will do the page queries or updates +@return DB_SUCCESS or error code */ +static dberr_t fil_tablespace_iterate(dict_table_t *table, ulint n_io_buffers, + AbstractCallback &callback) +{ + /* Make sure the data_dir_path is set. */ + dict_get_and_save_data_dir_path(table); + ut_ad(!DICT_TF_HAS_DATA_DIR(table->flags) || table->data_dir_path); + const char *data_dir_path= DICT_TF_HAS_DATA_DIR(table->flags) + ? table->data_dir_path : nullptr; + return fil_tablespace_iterate(table->name.m_name, n_io_buffers, callback, + data_dir_path); +} + static void row_import_autoinc(dict_table_t *table, row_prebuilt_t *prebuilt, uint64_t autoinc) { @@ -4621,8 +4724,6 @@ row_import_for_mysql( ut_ad(!table->is_readable()); ut_ad(prebuilt->table == table); - ibuf_delete_for_discarded_space(table->space_id); - #ifdef BTR_CUR_HASH_ADAPT /* On DISCARD TABLESPACE, we did not drop any adaptive hash index entries. If we replaced the discarded tablespace with a @@ -4830,12 +4931,6 @@ import_error: ut_free(filepath); - if (err == DB_SUCCESS) { - err = ibuf_check_bitmap_on_import(trx, table->space); - } - - DBUG_EXECUTE_IF("ib_import_check_bitmap_failure", err = DB_CORRUPTION;); - if (err != DB_SUCCESS) { return row_import_cleanup(prebuilt, err); } @@ -4893,13 +4988,6 @@ import_error: goto import_error; } - /* Ensure that the next available DB_ROW_ID is not smaller than - any DB_ROW_ID stored in the table. */ - - if (prebuilt->clust_index_was_generated) { - row_import_set_sys_max_row_id(prebuilt, table); - } - ib::info() << "Phase III - Flush changes to disk"; /* Ensure that all pages dirtied during the IMPORT make it to disk. @@ -4963,3 +5051,58 @@ import_error: return row_import_cleanup(prebuilt, err, table); } + +/** Prepare the create info to create a new stub table for import. +@param thd Connection +@param name Table name, format: "db/table_name". +@param create_info The create info for creating a stub. +@return ER_ error code +@retval 0 on success */ +int prepare_create_stub_for_import(THD *thd, const char *name, + HA_CREATE_INFO& create_info) +{ + DBUG_ENTER("prepare_create_stub_for_import"); + FetchIndexRootPages fetchIndexRootPages; + if (fil_tablespace_iterate(name, IO_BUFFER_SIZE(srv_page_size), + fetchIndexRootPages, fil_path_to_mysql_datadir) + != DB_SUCCESS) + { + const char *ibd_path= fil_make_filepath( + fil_path_to_mysql_datadir, table_name_t(const_cast<char*>(name)), IBD, + true); + if (!ibd_path) + return(ER_ENGINE_OUT_OF_MEMORY); + sql_print_error("InnoDB: failed to get row format from %s.\n", + ibd_path); + DBUG_RETURN(ER_INNODB_IMPORT_ERROR); + } + create_info.init(); + /* get the row format from ibd. */ + create_info.row_type= fetchIndexRootPages.m_row_format; + /* if .cfg exists, get the row format from cfg, and compare with + ibd, report error if different, except when cfg reports + compact/dynamic and ibd reports not_used (indicating either compact + or dynamic but not sure) */ + const enum row_type row_type_from_cfg= + get_row_type_from_cfg(fil_path_to_mysql_datadir, name, thd); + if (row_type_from_cfg != ROW_TYPE_NOT_USED) + { + /* if ibd reports not_used but cfg reports compact or dynamic, go + with cfg. */ + if (create_info.row_type != row_type_from_cfg && + !((row_type_from_cfg == ROW_TYPE_COMPACT || + row_type_from_cfg == ROW_TYPE_DYNAMIC) && + create_info.row_type == ROW_TYPE_NOT_USED)) + { + sql_print_error( + "InnoDB: cfg and ibd disagree on row format for table %s.\n", + name); + DBUG_RETURN(ER_INNODB_IMPORT_ERROR); + } + else + create_info.row_type= row_type_from_cfg; + } + else if (create_info.row_type == ROW_TYPE_NOT_USED) + create_info.row_type= ROW_TYPE_DYNAMIC; + DBUG_RETURN(0); +} |