summaryrefslogtreecommitdiffstats
path: root/storage/innobase/row/row0import.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/row/row0import.cc')
-rw-r--r--storage/innobase/row/row0import.cc409
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);
+}