summaryrefslogtreecommitdiff
path: root/storage/innobase/dict/dict0load.cc
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/dict/dict0load.cc')
-rw-r--r--storage/innobase/dict/dict0load.cc2560
1 files changed, 1637 insertions, 923 deletions
diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc
index 04e31aff088..f38ad85e903 100644
--- a/storage/innobase/dict/dict0load.cc
+++ b/storage/innobase/dict/dict0load.cc
@@ -25,27 +25,32 @@ from dictionary tables
Created 4/24/1996 Heikki Tuuri
*******************************************************/
-#include "dict0load.h"
-#include "mysql_version.h"
+#include "ha_prototypes.h"
+#include "dict0load.h"
#ifdef UNIV_NONINL
#include "dict0load.ic"
#endif
+#include "mysql_version.h"
#include "btr0pcur.h"
#include "btr0btr.h"
-#include "page0page.h"
-#include "mach0data.h"
-#include "dict0dict.h"
#include "dict0boot.h"
+#include "dict0crea.h"
+#include "dict0dict.h"
+#include "dict0mem.h"
+#include "dict0priv.h"
#include "dict0stats.h"
+#include "fsp0file.h"
+#include "fsp0sysspace.h"
+#include "fts0priv.h"
+#include "mach0data.h"
+#include "page0page.h"
#include "rem0cmp.h"
#include "srv0start.h"
#include "srv0srv.h"
-#include "dict0crea.h"
-#include "dict0priv.h"
-#include "ha_prototypes.h" /* innobase_casedn_str() */
-#include "fts0priv.h"
+#include <stack>
+#include <set>
/** Following are the InnoDB system tables. The positions in
this array are referenced by enum dict_system_table_id. */
@@ -57,17 +62,57 @@ static const char* SYSTEM_TABLE_NAME[] = {
"SYS_FOREIGN",
"SYS_FOREIGN_COLS",
"SYS_TABLESPACES",
- "SYS_DATAFILES"
+ "SYS_DATAFILES",
+ "SYS_VIRTUAL"
};
+/** Loads a table definition and also all its index definitions.
+
+Loads those foreign key constraints whose referenced table is already in
+dictionary cache. If a foreign key constraint is not loaded, then the
+referenced table is pushed into the output stack (fk_tables), if it is not
+NULL. These tables must be subsequently loaded so that all the foreign
+key constraints are loaded into memory.
+
+@param[in] name Table name in the db/tablename format
+@param[in] cached true=add to cache, false=do not
+@param[in] ignore_err Error to be ignored when loading table
+ and its index definition
+@param[out] fk_tables Related table names that must also be
+ loaded to ensure that all foreign key
+ constraints are loaded.
+@return table, NULL if does not exist; if the table is stored in an
+.ibd file, but the file does not exist, then we set the
+ibd_file_missing flag TRUE in the table object we return */
+static
+dict_table_t*
+dict_load_table_one(
+ table_name_t& name,
+ bool cached,
+ dict_err_ignore_t ignore_err,
+ dict_names_t& fk_tables);
+
+/** Loads a table definition from a SYS_TABLES record to dict_table_t.
+Does not load any columns or indexes.
+@param[in] name Table name
+@param[in] rec SYS_TABLES record
+@param[out,own] table Table, or NULL
+@return error message, or NULL on success */
+static
+const char*
+dict_load_table_low(
+ table_name_t& name,
+ const rec_t* rec,
+ dict_table_t** table);
+
/* If this flag is TRUE, then we will load the cluster index's (and tables')
metadata even if it is marked as "corrupted". */
-UNIV_INTERN my_bool srv_load_corrupted = FALSE;
+my_bool srv_load_corrupted = FALSE;
#ifdef UNIV_DEBUG
/****************************************************************//**
Compare the name of an index column.
-@return TRUE if the i'th column of index is 'name'. */
+@return TRUE if the i'th column of index is 'name'. */
static
ibool
name_of_col_is(
@@ -89,7 +134,6 @@ name_of_col_is(
Finds the first table name in the given database.
@return own: table name, NULL if does not exist; the caller must free
the memory in the string! */
-UNIV_INTERN
char*
dict_get_first_table_name_in_db(
/*============================*/
@@ -106,7 +150,7 @@ dict_get_first_table_name_in_db(
ulint len;
mtr_t mtr;
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ ut_ad(mutex_own(&dict_sys->mutex));
heap = mem_heap_create(1000);
@@ -170,68 +214,8 @@ loop:
}
/********************************************************************//**
-Prints to the standard output information on all tables found in the data
-dictionary system table. */
-UNIV_INTERN
-void
-dict_print(void)
-/*============*/
-{
- dict_table_t* table;
- btr_pcur_t pcur;
- const rec_t* rec;
- mem_heap_t* heap;
- mtr_t mtr;
-
- /* Enlarge the fatal semaphore wait timeout during the InnoDB table
- monitor printout */
-
- os_increment_counter_by_amount(
- server_mutex,
- srv_fatal_semaphore_wait_threshold,
- SRV_SEMAPHORE_WAIT_EXTENSION);
-
- heap = mem_heap_create(1000);
- mutex_enter(&(dict_sys->mutex));
- mtr_start(&mtr);
-
- rec = dict_startscan_system(&pcur, &mtr, SYS_TABLES);
-
- while (rec) {
- const char* err_msg;
-
- err_msg = static_cast<const char*>(
- dict_process_sys_tables_rec_and_mtr_commit(
- heap, rec, &table, DICT_TABLE_LOAD_FROM_CACHE,
- &mtr));
-
- if (!err_msg) {
- dict_table_print(table);
- } else {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB: %s\n", err_msg);
- }
-
- mem_heap_empty(heap);
-
- mtr_start(&mtr);
- rec = dict_getnext_system(&pcur, &mtr);
- }
-
- mtr_commit(&mtr);
- mutex_exit(&(dict_sys->mutex));
- mem_heap_free(heap);
-
- /* Restore the fatal semaphore wait timeout */
- os_decrement_counter_by_amount(
- server_mutex,
- srv_fatal_semaphore_wait_threshold,
- SRV_SEMAPHORE_WAIT_EXTENSION);
-}
-
-/********************************************************************//**
This function gets the next system table record as it scans the table.
-@return the next record if found, NULL if end of scan */
+@return the next record if found, NULL if end of scan */
static
const rec_t*
dict_getnext_system_low(
@@ -263,8 +247,7 @@ dict_getnext_system_low(
/********************************************************************//**
This function opens a system table, and returns the first record.
-@return first record of the system table */
-UNIV_INTERN
+@return first record of the system table */
const rec_t*
dict_startscan_system(
/*==================*/
@@ -293,8 +276,7 @@ dict_startscan_system(
/********************************************************************//**
This function gets the next system table record as it scans the table.
-@return the next record if found, NULL if end of scan */
-UNIV_INTERN
+@return the next record if found, NULL if end of scan */
const rec_t*
dict_getnext_system(
/*================*/
@@ -318,7 +300,6 @@ This function processes one SYS_TABLES record and populate the dict_table_t
struct for the table. Extracted out of dict_print() to be used by
both monitor table output and information schema innodb_sys_tables output.
@return error message, or NULL on success */
-UNIV_INTERN
const char*
dict_process_sys_tables_rec_and_mtr_commit(
/*=======================================*/
@@ -335,7 +316,7 @@ dict_process_sys_tables_rec_and_mtr_commit(
ulint len;
const char* field;
const char* err_msg = NULL;
- char* table_name;
+ table_name_t table_name;
field = (const char*) rec_get_nth_field_old(
rec, DICT_FLD__SYS_TABLES__NAME, &len);
@@ -345,7 +326,7 @@ dict_process_sys_tables_rec_and_mtr_commit(
ut_ad(mtr_memo_contains_page(mtr, rec, MTR_MEMO_PAGE_S_FIX));
/* Get the table name */
- table_name = mem_heap_strdupl(heap, field, len);
+ table_name.m_name = mem_heap_strdupl(heap, field, len);
/* If DICT_TABLE_LOAD_FROM_CACHE is set, first check
whether there is cached dict_table_t struct */
@@ -354,7 +335,7 @@ dict_process_sys_tables_rec_and_mtr_commit(
/* Commit before load the table again */
mtr_commit(mtr);
- *table = dict_table_get_low(table_name);
+ *table = dict_table_get_low(table_name.m_name);
if (!(*table)) {
err_msg = "Table not found in cache";
@@ -376,7 +357,6 @@ This function parses a SYS_INDEXES record and populate a dict_index_t
structure with the information from the record. For detail information
about SYS_INDEXES fields, please refer to dict_boot() function.
@return error message, or NULL on success */
-UNIV_INTERN
const char*
dict_process_sys_indexes_rec(
/*=========================*/
@@ -403,7 +383,6 @@ dict_process_sys_indexes_rec(
This function parses a SYS_COLUMNS record and populate a dict_column_t
structure with the information from the record.
@return error message, or NULL on success */
-UNIV_INTERN
const char*
dict_process_sys_columns_rec(
/*=========================*/
@@ -411,22 +390,47 @@ dict_process_sys_columns_rec(
const rec_t* rec, /*!< in: current SYS_COLUMNS rec */
dict_col_t* column, /*!< out: dict_col_t to be filled */
table_id_t* table_id, /*!< out: table id */
- const char** col_name) /*!< out: column name */
+ const char** col_name, /*!< out: column name */
+ ulint* nth_v_col) /*!< out: if virtual col, this is
+ record's sequence number */
{
const char* err_msg;
/* Parse the record, and get "dict_col_t" struct filled */
err_msg = dict_load_column_low(NULL, heap, column,
- table_id, col_name, rec);
+ table_id, col_name, rec, nth_v_col);
return(err_msg);
}
+/** This function parses a SYS_VIRTUAL record and extracts virtual column
+information
+@param[in,out] heap heap memory
+@param[in] rec current SYS_COLUMNS rec
+@param[in,out] table_id table id
+@param[in,out] pos virtual column position
+@param[in,out] base_pos base column position
+@return error message, or NULL on success */
+const char*
+dict_process_sys_virtual_rec(
+ mem_heap_t* heap,
+ const rec_t* rec,
+ table_id_t* table_id,
+ ulint* pos,
+ ulint* base_pos)
+{
+ const char* err_msg;
+
+ /* Parse the record, and get "dict_col_t" struct filled */
+ err_msg = dict_load_virtual_low(NULL, heap, NULL, table_id,
+ pos, base_pos, rec);
+
+ return(err_msg);
+}
/********************************************************************//**
This function parses a SYS_FIELDS record and populates a dict_field_t
structure with the information from the record.
@return error message, or NULL on success */
-UNIV_INTERN
const char*
dict_process_sys_fields_rec(
/*========================*/
@@ -461,7 +465,6 @@ This function parses a SYS_FOREIGN record and populate a dict_foreign_t
structure with the information from the record. For detail information
about SYS_FOREIGN fields, please refer to dict_load_foreign() function.
@return error message, or NULL on success */
-UNIV_INTERN
const char*
dict_process_sys_foreign_rec(
/*=========================*/
@@ -542,7 +545,6 @@ err_len:
This function parses a SYS_FOREIGN_COLS record and extract necessary
information from the record and return to caller.
@return error message, or NULL on success */
-UNIV_INTERN
const char*
dict_process_sys_foreign_col_rec(
/*=============================*/
@@ -612,7 +614,6 @@ err_len:
This function parses a SYS_TABLESPACES record, extracts necessary
information from the record and returns to caller.
@return error message, or NULL on success */
-UNIV_INTERN
const char*
dict_process_sys_tablespaces(
/*=========================*/
@@ -679,7 +680,6 @@ err_len:
This function parses a SYS_DATAFILES record, extracts necessary
information from the record and returns it to the caller.
@return error message, or NULL on success */
-UNIV_INTERN
const char*
dict_process_sys_datafiles(
/*=======================*/
@@ -729,65 +729,13 @@ err_len:
return(NULL);
}
-/********************************************************************//**
-Determine the flags of a table as stored in SYS_TABLES.TYPE and N_COLS.
-@return ULINT_UNDEFINED if error, else a valid dict_table_t::flags. */
-static
-ulint
-dict_sys_tables_get_flags(
-/*======================*/
- const rec_t* rec) /*!< in: a record of SYS_TABLES */
-{
- const byte* field;
- ulint len;
- ulint type;
- ulint n_cols;
-
- /* read the 4 byte flags from the TYPE field */
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__TYPE, &len);
- ut_a(len == 4);
- type = mach_read_from_4(field);
-
- /* The low order bit of SYS_TABLES.TYPE is always set to 1. But in
- dict_table_t::flags the low order bit is used to determine if the
- row format is Redundant or Compact when the format is Antelope.
- Read the 4 byte N_COLS field and look at the high order bit. It
- should be set for COMPACT and later. It should not be set for
- REDUNDANT. */
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__N_COLS, &len);
- ut_a(len == 4);
- n_cols = mach_read_from_4(field);
-
- /* This validation function also combines the DICT_N_COLS_COMPACT
- flag in n_cols into the type field to effectively make it a
- dict_table_t::flags. */
-
- if (ULINT_UNDEFINED == dict_sys_tables_type_validate(type, n_cols)) {
- return(ULINT_UNDEFINED);
- }
-
- return(dict_sys_tables_type_to_tf(type, n_cols));
-}
-
-/********************************************************************//**
-Gets the filepath for a spaceid from SYS_DATAFILES and checks it against
-the contents of a link file. This function is called when there is no
-fil_node_t entry for this space ID so both durable locations on disk
-must be checked and compared.
-We use a temporary heap here for the table lookup, but not for the path
-returned which the caller must free.
-This function can return NULL if the space ID is not found in SYS_DATAFILES,
-then the caller will assume that the ibd file is in the normal datadir.
-@return own: A copy of the first datafile found in SYS_DATAFILES.PATH for
-the given space ID. NULL if space ID is zero or not found. */
-UNIV_INTERN
+/** Get the first filepath from SYS_DATAFILES for a given space_id.
+@param[in] space_id Tablespace ID
+@return First filepath (caller must invoke ut_free() on it)
+@retval NULL if no SYS_DATAFILES entry was found. */
char*
dict_get_first_path(
-/*================*/
- ulint space, /*!< in: space id */
- const char* name) /*!< in: tablespace name */
+ ulint space_id)
{
mtr_t mtr;
dict_table_t* sys_datafiles;
@@ -799,15 +747,16 @@ dict_get_first_path(
const rec_t* rec;
const byte* field;
ulint len;
- char* dict_filepath = NULL;
+ char* filepath = NULL;
mem_heap_t* heap = mem_heap_create(1024);
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ ut_ad(mutex_own(&dict_sys->mutex));
mtr_start(&mtr);
sys_datafiles = dict_table_get_low("SYS_DATAFILES");
sys_index = UT_LIST_GET_FIRST(sys_datafiles->indexes);
+
ut_ad(!dict_table_is_comp(sys_datafiles));
ut_ad(name_of_col_is(sys_datafiles, sys_index,
DICT_FLD__SYS_DATAFILES__SPACE, "SPACE"));
@@ -818,7 +767,7 @@ dict_get_first_path(
dfield = dtuple_get_nth_field(tuple, DICT_FLD__SYS_DATAFILES__SPACE);
buf = static_cast<byte*>(mem_heap_alloc(heap, 4));
- mach_write_to_4(buf, space);
+ mach_write_to_4(buf, space_id);
dfield_set_data(dfield, buf, 4);
dict_index_copy_types(tuple, sys_index, 1);
@@ -828,44 +777,155 @@ dict_get_first_path(
rec = btr_pcur_get_rec(&pcur);
- /* If the file-per-table tablespace was created with
- an earlier version of InnoDB, then this record is not
- in SYS_DATAFILES. But a link file still might exist. */
+ /* Get the filepath from this SYS_DATAFILES record. */
+ if (btr_pcur_is_on_user_rec(&pcur)) {
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_DATAFILES__SPACE, &len);
+ ut_a(len == 4);
+ if (space_id == mach_read_from_4(field)) {
+ /* A record for this space ID was found. */
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_DATAFILES__PATH, &len);
+
+ ut_ad(len > 0);
+ ut_ad(len < OS_FILE_MAX_PATH);
+
+ if (len > 0 && len != UNIV_SQL_NULL) {
+ filepath = mem_strdupl(
+ reinterpret_cast<const char*>(field),
+ len);
+ ut_ad(filepath != NULL);
+
+ /* The dictionary may have been written on
+ another OS. */
+ os_normalize_path(filepath);
+ }
+ }
+ }
+
+ btr_pcur_close(&pcur);
+ mtr_commit(&mtr);
+ mem_heap_free(heap);
+
+ return(filepath);
+}
+
+/** Gets the space name from SYS_TABLESPACES for a given space ID.
+@param[in] space_id Tablespace ID
+@param[in] callers_heap A heap to allocate from, may be NULL
+@return Tablespace name (caller is responsible to free it)
+@retval NULL if no dictionary entry was found. */
+static
+char*
+dict_space_get_name(
+ ulint space_id,
+ mem_heap_t* callers_heap)
+{
+ mtr_t mtr;
+ dict_table_t* sys_tablespaces;
+ dict_index_t* sys_index;
+ dtuple_t* tuple;
+ dfield_t* dfield;
+ byte* buf;
+ btr_pcur_t pcur;
+ const rec_t* rec;
+ const byte* field;
+ ulint len;
+ char* space_name = NULL;
+ mem_heap_t* heap = mem_heap_create(1024);
+
+ ut_ad(mutex_own(&dict_sys->mutex));
+
+ sys_tablespaces = dict_table_get_low("SYS_TABLESPACES");
+ if (sys_tablespaces == NULL) {
+ ut_a(!srv_sys_tablespaces_open);
+ return(NULL);
+ }
+
+ sys_index = UT_LIST_GET_FIRST(sys_tablespaces->indexes);
+
+ ut_ad(!dict_table_is_comp(sys_tablespaces));
+ ut_ad(name_of_col_is(sys_tablespaces, sys_index,
+ DICT_FLD__SYS_TABLESPACES__SPACE, "SPACE"));
+ ut_ad(name_of_col_is(sys_tablespaces, sys_index,
+ DICT_FLD__SYS_TABLESPACES__NAME, "NAME"));
+
+ tuple = dtuple_create(heap, 1);
+ dfield = dtuple_get_nth_field(tuple, DICT_FLD__SYS_TABLESPACES__SPACE);
+
+ buf = static_cast<byte*>(mem_heap_alloc(heap, 4));
+ mach_write_to_4(buf, space_id);
+
+ dfield_set_data(dfield, buf, 4);
+ dict_index_copy_types(tuple, sys_index, 1);
+
+ mtr_start(&mtr);
+
+ btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE,
+ BTR_SEARCH_LEAF, &pcur, &mtr);
+
+ rec = btr_pcur_get_rec(&pcur);
+
+ /* Get the tablespace name from this SYS_TABLESPACES record. */
if (btr_pcur_is_on_user_rec(&pcur)) {
- /* A record for this space ID was found. */
field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_DATAFILES__PATH, &len);
- ut_a(len > 0 || len == UNIV_SQL_NULL);
- ut_a(len < OS_FILE_MAX_PATH);
- dict_filepath = mem_strdupl((char*) field, len);
- ut_a(dict_filepath);
+ rec, DICT_FLD__SYS_TABLESPACES__SPACE, &len);
+ ut_a(len == 4);
+
+ if (space_id == mach_read_from_4(field)) {
+ /* A record for this space ID was found. */
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLESPACES__NAME, &len);
+
+ ut_ad(len > 0);
+ ut_ad(len < OS_FILE_MAX_PATH);
+
+ if (len > 0 && len != UNIV_SQL_NULL) {
+ /* Found a tablespace name. */
+ if (callers_heap == NULL) {
+ space_name = mem_strdupl(
+ reinterpret_cast<
+ const char*>(field),
+ len);
+ } else {
+ space_name = mem_heap_strdupl(
+ callers_heap,
+ reinterpret_cast<
+ const char*>(field),
+ len);
+ }
+ ut_ad(space_name);
+ }
+ }
}
btr_pcur_close(&pcur);
mtr_commit(&mtr);
mem_heap_free(heap);
- return(dict_filepath);
+ return(space_name);
}
-/********************************************************************//**
-Update the record for space_id in SYS_TABLESPACES to this filepath.
-@return DB_SUCCESS if OK, dberr_t if the insert failed */
-UNIV_INTERN
+/** Update the record for space_id in SYS_TABLESPACES to this filepath.
+@param[in] space_id Tablespace ID
+@param[in] filepath Tablespace filepath
+@return DB_SUCCESS if OK, dberr_t if the insert failed */
dberr_t
dict_update_filepath(
-/*=================*/
- ulint space_id, /*!< in: space id */
- const char* filepath) /*!< in: filepath */
+ ulint space_id,
+ const char* filepath)
{
+ if (!srv_sys_tablespaces_open) {
+ /* Startup procedure is not yet ready for updates. */
+ return(DB_SUCCESS);
+ }
+
dberr_t err = DB_SUCCESS;
trx_t* trx;
-#ifdef UNIV_SYNC_DEBUG
- ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
-#endif /* UNIV_SYNC_DEBUG */
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
+ ut_ad(mutex_own(&dict_sys->mutex));
trx = trx_allocate_for_background();
trx->op_info = "update filepath";
@@ -892,39 +952,48 @@ dict_update_filepath(
if (err == DB_SUCCESS) {
/* We just updated SYS_DATAFILES due to the contents in
a link file. Make a note that we did this. */
- ib_logf(IB_LOG_LEVEL_INFO,
- "The InnoDB data dictionary table SYS_DATAFILES "
- "for tablespace ID %lu was updated to use file %s.",
- (ulong) space_id, filepath);
+ ib::info() << "The InnoDB data dictionary table SYS_DATAFILES"
+ " for tablespace ID " << space_id
+ << " was updated to use file " << filepath << ".";
} else {
- ib_logf(IB_LOG_LEVEL_WARN,
- "Problem updating InnoDB data dictionary table "
- "SYS_DATAFILES for tablespace ID %lu to file %s.",
- (ulong) space_id, filepath);
+ ib::warn() << "Error occurred while updating InnoDB data"
+ " dictionary table SYS_DATAFILES for tablespace ID "
+ << space_id << " to file " << filepath << ": "
+ << ut_strerr(err) << ".";
}
return(err);
}
-/********************************************************************//**
-Insert records into SYS_TABLESPACES and SYS_DATAFILES.
-@return DB_SUCCESS if OK, dberr_t if the insert failed */
-UNIV_INTERN
+/** Replace records in SYS_TABLESPACES and SYS_DATAFILES associated with
+the given space_id using an independent transaction.
+@param[in] space_id Tablespace ID
+@param[in] name Tablespace name
+@param[in] filepath First filepath
+@param[in] fsp_flags Tablespace flags
+@return DB_SUCCESS if OK, dberr_t if the insert failed */
dberr_t
-dict_insert_tablespace_and_filepath(
-/*================================*/
- ulint space, /*!< in: space id */
- const char* name, /*!< in: talespace name */
- const char* filepath, /*!< in: filepath */
- ulint fsp_flags) /*!< in: tablespace flags */
+dict_replace_tablespace_and_filepath(
+ ulint space_id,
+ const char* name,
+ const char* filepath,
+ ulint fsp_flags)
{
+ if (!srv_sys_tablespaces_open) {
+ /* Startup procedure is not yet ready for updates.
+ Return success since this will likely get updated
+ later. */
+ return(DB_SUCCESS);
+ }
+
dberr_t err = DB_SUCCESS;
trx_t* trx;
-#ifdef UNIV_SYNC_DEBUG
- ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
-#endif /* UNIV_SYNC_DEBUG */
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ DBUG_EXECUTE_IF("innodb_fail_to_update_tablespace_dict",
+ return(DB_INTERRUPTED););
+
+ ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
+ ut_ad(mutex_own(&dict_sys->mutex));
ut_ad(filepath);
trx = trx_allocate_for_background();
@@ -934,9 +1003,9 @@ dict_insert_tablespace_and_filepath(
/* A record for this space ID was not found in
SYS_DATAFILES. Assume the record is also missing in
- SYS_TABLESPACES. Insert records onto them both. */
- err = dict_create_add_tablespace_to_dictionary(
- space, name, fsp_flags, filepath, trx, false);
+ SYS_TABLESPACES. Insert records into them both. */
+ err = dict_replace_tablespace_in_dictionary(
+ space_id, name, fsp_flags, filepath, trx, false);
trx_commit_for_mysql(trx);
trx->dict_operation_lock_mode = 0;
@@ -945,264 +1014,538 @@ dict_insert_tablespace_and_filepath(
return(err);
}
-/********************************************************************//**
-This function looks at each table defined in SYS_TABLES. It checks the
-tablespace for any table with a space_id > 0. It looks up the tablespace
-in SYS_DATAFILES to ensure the correct path.
+/** Check the validity of a SYS_TABLES record
+Make sure the fields are the right length and that they
+do not contain invalid contents.
+@param[in] rec SYS_TABLES record
+@return error message, or NULL on success */
+static
+const char*
+dict_sys_tables_rec_check(
+ const rec_t* rec)
+{
+ const byte* field;
+ ulint len;
-In a crash recovery we already have all the tablespace objects created.
-This function compares the space id information in the InnoDB data dictionary
-to what we already read with fil_load_single_table_tablespaces().
+ ut_ad(mutex_own(&dict_sys->mutex));
-In a normal startup, we create the tablespace objects for every table in
-InnoDB's data dictionary, if the corresponding .ibd file exists.
-We also scan the biggest space id, and store it to fil_system. */
-UNIV_INTERN
-void
-dict_check_tablespaces_and_store_max_id(
-/*====================================*/
- dict_check_t dict_check) /*!< in: how to check */
+ if (rec_get_deleted_flag(rec, 0)) {
+ return("delete-marked record in SYS_TABLES");
+ }
+
+ if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_TABLES) {
+ return("wrong number of columns in SYS_TABLES record");
+ }
+
+ rec_get_nth_field_offs_old(
+ rec, DICT_FLD__SYS_TABLES__NAME, &len);
+ if (len == 0 || len == UNIV_SQL_NULL) {
+err_len:
+ return("incorrect column length in SYS_TABLES");
+ }
+ rec_get_nth_field_offs_old(
+ rec, DICT_FLD__SYS_TABLES__DB_TRX_ID, &len);
+ if (len != DATA_TRX_ID_LEN && len != UNIV_SQL_NULL) {
+ goto err_len;
+ }
+ rec_get_nth_field_offs_old(
+ rec, DICT_FLD__SYS_TABLES__DB_ROLL_PTR, &len);
+ if (len != DATA_ROLL_PTR_LEN && len != UNIV_SQL_NULL) {
+ goto err_len;
+ }
+
+ rec_get_nth_field_offs_old(rec, DICT_FLD__SYS_TABLES__ID, &len);
+ if (len != 8) {
+ goto err_len;
+ }
+
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLES__N_COLS, &len);
+ if (field == NULL || len != 4) {
+ goto err_len;
+ }
+
+ rec_get_nth_field_offs_old(rec, DICT_FLD__SYS_TABLES__TYPE, &len);
+ if (len != 4) {
+ goto err_len;
+ }
+
+ rec_get_nth_field_offs_old(
+ rec, DICT_FLD__SYS_TABLES__MIX_ID, &len);
+ if (len != 8) {
+ goto err_len;
+ }
+
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLES__MIX_LEN, &len);
+ if (field == NULL || len != 4) {
+ goto err_len;
+ }
+
+ rec_get_nth_field_offs_old(
+ rec, DICT_FLD__SYS_TABLES__CLUSTER_ID, &len);
+ if (len != UNIV_SQL_NULL) {
+ goto err_len;
+ }
+
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLES__SPACE, &len);
+ if (field == NULL || len != 4) {
+ goto err_len;
+ }
+
+ return(NULL);
+}
+
+/** Read and return the contents of a SYS_TABLESPACES record.
+@param[in] rec A record of SYS_TABLESPACES
+@param[out] id Pointer to the space_id for this table
+@param[in,out] name Buffer for Tablespace Name of length NAME_LEN
+@param[out] flags Pointer to tablespace flags
+@return true if the record was read correctly, false if not. */
+bool
+dict_sys_tablespaces_rec_read(
+ const rec_t* rec,
+ ulint* id,
+ char* name,
+ ulint* flags)
{
- dict_table_t* sys_tables;
- dict_index_t* sys_index;
+ const byte* field;
+ ulint len;
+
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLESPACES__SPACE, &len);
+ if (len != DICT_FLD_LEN_SPACE) {
+ ib::error() << "Wrong field length in SYS_TABLESPACES.SPACE: "
+ << len;
+ return(false);
+ }
+ *id = mach_read_from_4(field);
+
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLESPACES__NAME, &len);
+ if (len == 0 || len == UNIV_SQL_NULL) {
+ ib::error() << "Wrong field length in SYS_TABLESPACES.NAME: "
+ << len;
+ return(false);
+ }
+ strncpy(name, reinterpret_cast<const char*>(field), NAME_LEN);
+
+ /* read the 4 byte flags from the TYPE field */
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLESPACES__FLAGS, &len);
+ if (len != 4) {
+ ib::error() << "Wrong field length in SYS_TABLESPACES.FLAGS: "
+ << len;
+ return(false);
+ }
+ *flags = mach_read_from_4(field);
+
+ return(true);
+}
+
+/** Load and check each general tablespace mentioned in the SYS_TABLESPACES.
+Ignore system and file-per-table tablespaces.
+If it is valid, add it to the file_system list.
+@param[in] validate true when the previous shutdown was not clean
+@return the highest space ID found. */
+UNIV_INLINE
+ulint
+dict_check_sys_tablespaces(
+ bool validate)
+{
+ ulint max_space_id = 0;
btr_pcur_t pcur;
const rec_t* rec;
- ulint max_space_id;
mtr_t mtr;
- rw_lock_x_lock(&dict_operation_lock);
- mutex_enter(&(dict_sys->mutex));
+ DBUG_ENTER("dict_check_sys_tablespaces");
+
+ ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
+ ut_ad(mutex_own(&dict_sys->mutex));
+
+ /* Before traversing it, let's make sure we have
+ SYS_TABLESPACES and SYS_DATAFILES loaded. */
+ dict_table_get_low("SYS_TABLESPACES");
+ dict_table_get_low("SYS_DATAFILES");
mtr_start(&mtr);
- sys_tables = dict_table_get_low("SYS_TABLES");
- sys_index = UT_LIST_GET_FIRST(sys_tables->indexes);
- ut_ad(!dict_table_is_comp(sys_tables));
+ for (rec = dict_startscan_system(&pcur, &mtr, SYS_TABLESPACES);
+ rec != NULL;
+ rec = dict_getnext_system(&pcur, &mtr))
+ {
+ char space_name[NAME_LEN];
+ ulint space_id = 0;
+ ulint fsp_flags;
+
+ if (!dict_sys_tablespaces_rec_read(rec, &space_id,
+ space_name, &fsp_flags)) {
+ continue;
+ }
- max_space_id = mtr_read_ulint(dict_hdr_get(&mtr)
- + DICT_HDR_MAX_SPACE_ID,
- MLOG_4BYTES, &mtr);
- fil_set_max_space_id_if_bigger(max_space_id);
+ /* Ignore system and file-per-table tablespaces. */
+ if (is_system_tablespace(space_id)
+ || !fsp_is_shared_tablespace(fsp_flags)) {
+ continue;
+ }
- btr_pcur_open_at_index_side(true, sys_index, BTR_SEARCH_LEAF, &pcur,
- true, 0, &mtr);
-loop:
- btr_pcur_move_to_next_user_rec(&pcur, &mtr);
+ /* Ignore tablespaces that already are in the tablespace
+ cache. */
+ if (fil_space_for_table_exists_in_mem(
+ space_id, space_name, false, true, NULL, 0, NULL)) {
+ /* Recovery can open a datafile that does not
+ match SYS_DATAFILES. If they don't match, update
+ SYS_DATAFILES. */
+ char *dict_path = dict_get_first_path(space_id);
+ char *fil_path = fil_space_get_first_path(space_id);
+ if (dict_path && fil_path
+ && strcmp(dict_path, fil_path)) {
+ dict_update_filepath(space_id, fil_path);
+ }
+ ut_free(dict_path);
+ ut_free(fil_path);
+ continue;
+ }
- rec = btr_pcur_get_rec(&pcur);
+ /* Set the expected filepath from the data dictionary.
+ If the file is found elsewhere (from an ISL or the default
+ location) or this path is the same file but looks different,
+ fil_ibd_open() will update the dictionary with what is
+ opened. */
+ char* filepath = dict_get_first_path(space_id);
+
+ validate = true; /* Encryption */
+
+ /* Check that the .ibd file exists. */
+ dberr_t err = fil_ibd_open(
+ validate,
+ !srv_read_only_mode && srv_log_file_size != 0,
+ FIL_TYPE_TABLESPACE,
+ space_id,
+ fsp_flags,
+ space_name,
+ filepath,
+ NULL);
- if (!btr_pcur_is_on_user_rec(&pcur)) {
- /* end of index */
+ if (err != DB_SUCCESS) {
+ ib::warn() << "Ignoring tablespace "
+ << id_name_t(space_name)
+ << " because it could not be opened.";
+ }
- btr_pcur_close(&pcur);
- mtr_commit(&mtr);
+ max_space_id = ut_max(max_space_id, space_id);
- /* We must make the tablespace cache aware of the biggest
- known space id */
+ ut_free(filepath);
+ }
- /* printf("Biggest space id in data dictionary %lu\n",
- max_space_id); */
- fil_set_max_space_id_if_bigger(max_space_id);
+ mtr_commit(&mtr);
- mutex_exit(&(dict_sys->mutex));
- rw_lock_x_unlock(&dict_operation_lock);
+ DBUG_RETURN(max_space_id);
+}
- return;
- }
+/** Read and return 5 integer fields from a SYS_TABLES record.
+@param[in] rec A record of SYS_TABLES
+@param[in] name Table Name, the same as SYS_TABLES.NAME
+@param[out] table_id Pointer to the table_id for this table
+@param[out] space_id Pointer to the space_id for this table
+@param[out] n_cols Pointer to number of columns for this table.
+@param[out] flags Pointer to table flags
+@param[out] flags2 Pointer to table flags2
+@return true if the record was read correctly, false if not. */
+static
+bool
+dict_sys_tables_rec_read(
+ const rec_t* rec,
+ const table_name_t& table_name,
+ table_id_t* table_id,
+ ulint* space_id,
+ ulint* n_cols,
+ ulint* flags,
+ ulint* flags2)
+{
+ const byte* field;
+ ulint len;
+ ulint type;
- if (!rec_get_deleted_flag(rec, 0)) {
+ *flags2 = 0;
- /* We found one */
- const byte* field;
- ulint len;
- ulint space_id;
- ulint flags;
- char* name;
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLES__ID, &len);
+ ut_ad(len == 8);
+ *table_id = static_cast<table_id_t>(mach_read_from_8(field));
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__NAME, &len);
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLES__SPACE, &len);
+ ut_ad(len == 4);
+ *space_id = mach_read_from_4(field);
- name = mem_strdupl((char*) field, len);
+ /* Read the 4 byte flags from the TYPE field */
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLES__TYPE, &len);
+ ut_a(len == 4);
+ type = mach_read_from_4(field);
+
+ /* The low order bit of SYS_TABLES.TYPE is always set to 1. But in
+ dict_table_t::flags the low order bit is used to determine if the
+ row format is Redundant (0) or Compact (1) when the format is Antelope.
+ Read the 4 byte N_COLS field and look at the high order bit. It
+ should be set for COMPACT and later. It should not be set for
+ REDUNDANT. */
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLES__N_COLS, &len);
+ ut_a(len == 4);
+ *n_cols = mach_read_from_4(field);
- char table_name[MAX_FULL_NAME_LEN + 1];
+ /* This validation function also combines the DICT_N_COLS_COMPACT
+ flag in n_cols into the type field to effectively make it a
+ dict_table_t::flags. */
- innobase_format_name(
- table_name, sizeof(table_name), name, FALSE);
+ if (ULINT_UNDEFINED == dict_sys_tables_type_validate(type, *n_cols)) {
+ ib::error() << "Table " << table_name << " in InnoDB"
+ " data dictionary contains invalid flags."
+ " SYS_TABLES.TYPE=" << type <<
+ " SYS_TABLES.N_COLS=" << *n_cols;
+ *flags = ULINT_UNDEFINED;
+ return(false);
+ }
- flags = dict_sys_tables_get_flags(rec);
- if (UNIV_UNLIKELY(flags == ULINT_UNDEFINED)) {
- /* Read again the 4 bytes from rec. */
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__TYPE, &len);
- ut_ad(len == 4); /* this was checked earlier */
- flags = mach_read_from_4(field);
-
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Table '%s' in InnoDB data dictionary"
- " has unknown type %lx", table_name, flags);
- mem_free(name);
- goto loop;
- }
+ *flags = dict_sys_tables_type_to_tf(type, *n_cols);
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__SPACE, &len);
- ut_a(len == 4);
+ /* Get flags2 from SYS_TABLES.MIX_LEN */
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLES__MIX_LEN, &len);
+ *flags2 = mach_read_from_4(field);
- space_id = mach_read_from_4(field);
+ /* DICT_TF2_FTS will be set when indexes are being loaded */
+ *flags2 &= ~DICT_TF2_FTS;
- btr_pcur_store_position(&pcur, &mtr);
+ /* Now that we have used this bit, unset it. */
+ *n_cols &= ~DICT_N_COLS_COMPACT;
+ return(true);
+}
- mtr_commit(&mtr);
+/** Load and check each non-predefined tablespace mentioned in SYS_TABLES.
+Search SYS_TABLES and check each tablespace mentioned that has not
+already been added to the fil_system. If it is valid, add it to the
+file_system list. Perform extra validation on the table if recovery from
+the REDO log occurred.
+@param[in] validate Whether to do validation on the table.
+@return the highest space ID found. */
+UNIV_INLINE
+ulint
+dict_check_sys_tables(
+ bool validate)
+{
+ ulint max_space_id = 0;
+ btr_pcur_t pcur;
+ const rec_t* rec;
+ mtr_t mtr;
- /* For tables created with old versions of InnoDB,
- SYS_TABLES.MIX_LEN may contain garbage. Such tables
- would always be in ROW_FORMAT=REDUNDANT. Pretend that
- all such tables are non-temporary. That is, do not
- suppress error printouts about temporary or discarded
- tablespaces not being found. */
+ DBUG_ENTER("dict_check_sys_tables");
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__MIX_LEN, &len);
+ ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X));
+ ut_ad(mutex_own(&dict_sys->mutex));
- bool is_temp = false;
- bool discarded = false;
- ib_uint32_t flags2 = static_cast<ib_uint32_t>(
- mach_read_from_4(field));
+ mtr_start(&mtr);
- /* Check that the tablespace (the .ibd file) really
- exists; print a warning to the .err log if not.
- Do not print warnings for temporary tables or for
- tablespaces that have been discarded. */
+ /* Before traversing SYS_TABLES, let's make sure we have
+ SYS_TABLESPACES and SYS_DATAFILES loaded. */
+ dict_table_t* sys_tablespaces;
+ dict_table_t* sys_datafiles;
+ sys_tablespaces = dict_table_get_low("SYS_TABLESPACES");
+ ut_a(sys_tablespaces != NULL);
+ sys_datafiles = dict_table_get_low("SYS_DATAFILES");
+ ut_a(sys_datafiles != NULL);
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__N_COLS, &len);
+ for (rec = dict_startscan_system(&pcur, &mtr, SYS_TABLES);
+ rec != NULL;
+ rec = dict_getnext_system(&pcur, &mtr)) {
+ const byte* field;
+ ulint len;
+ char* space_name;
+ table_name_t table_name;
+ table_id_t table_id;
+ ulint space_id;
+ ulint n_cols;
+ ulint flags;
+ ulint flags2;
- /* MIX_LEN valid only for ROW_FORMAT > REDUNDANT. */
- if (mach_read_from_4(field) & DICT_N_COLS_COMPACT) {
+ /* If a table record is not useable, ignore it and continue
+ on to the next record. Error messages were logged. */
+ if (dict_sys_tables_rec_check(rec) != NULL) {
+ continue;
+ }
- is_temp = !!(flags2 & DICT_TF2_TEMPORARY);
- discarded = !!(flags2 & DICT_TF2_DISCARDED);
+ /* Copy the table name from rec */
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_TABLES__NAME, &len);
+ table_name.m_name = mem_strdupl((char*) field, len);
+ DBUG_PRINT("dict_check_sys_tables",
+ ("name: %p, '%s'", table_name.m_name,
+ table_name.m_name));
+
+ dict_sys_tables_rec_read(rec, table_name,
+ &table_id, &space_id,
+ &n_cols, &flags, &flags2);
+ if (flags == ULINT_UNDEFINED
+ || is_system_tablespace(space_id)) {
+ ut_free(table_name.m_name);
+ continue;
}
- if (space_id == 0) {
- /* The system tablespace always exists. */
- ut_ad(!discarded);
- goto next_tablespace;
+ if (flags2 & DICT_TF2_DISCARDED) {
+ ib::info() << "Ignoring tablespace " << table_name
+ << " because the DISCARD flag is set .";
+ ut_free(table_name.m_name);
+ continue;
}
- switch (dict_check) {
- case DICT_CHECK_ALL_LOADED:
- /* All tablespaces should have been found in
- fil_load_single_table_tablespaces(). */
- if (fil_space_for_table_exists_in_mem(
- space_id, name, TRUE, !(is_temp || discarded),
- false, NULL, 0)
- && !(is_temp || discarded)) {
- /* If user changes the path of .ibd files in
- *.isl files before doing crash recovery ,
- then this leads to inconsistency in
- SYS_DATAFILES system table because the
- tables are loaded from the updated path
- but the SYS_DATAFILES still points to the
- old path.Therefore after crash recovery
- update SYS_DATAFILES with the updated path.*/
- ut_ad(space_id);
- ut_ad(recv_needed_recovery);
- char *dict_path = dict_get_first_path(space_id,
- name);
- char *remote_path = fil_read_link_file(name);
- if(dict_path && remote_path) {
- if(strcmp(dict_path,remote_path)) {
- dict_update_filepath(space_id,
- remote_path);
- }
- }
- if(dict_path)
- mem_free(dict_path);
- if(remote_path)
- mem_free(remote_path);
+ /* If the table is not a predefined tablespace then it must
+ be in a file-per-table or shared tablespace.
+ Note that flags2 is not available for REDUNDANT tables,
+ so don't check those. */
+ ut_ad(DICT_TF_HAS_SHARED_SPACE(flags)
+ || !DICT_TF_GET_COMPACT(flags)
+ || flags2 & DICT_TF2_USE_FILE_PER_TABLE);
+
+ /* Look up the tablespace name in the data dictionary if this
+ is a shared tablespace. For file-per-table, the table_name
+ and the tablespace_name are the same.
+ Some hidden tables like FTS AUX tables may not be found in
+ the dictionary since they can always be found in the default
+ location. If so, then dict_space_get_name() will return NULL,
+ the space name must be the table_name, and the filepath can be
+ discovered in the default location.*/
+ char* shared_space_name = dict_space_get_name(space_id, NULL);
+ space_name = shared_space_name == NULL
+ ? table_name.m_name
+ : shared_space_name;
+
+ /* Now that we have the proper name for this tablespace,
+ whether it is a shared tablespace or a single table
+ tablespace, look to see if it is already in the tablespace
+ cache. */
+ if (fil_space_for_table_exists_in_mem(
+ space_id, space_name, false, true, NULL, 0, NULL)) {
+ /* Recovery can open a datafile that does not
+ match SYS_DATAFILES. If they don't match, update
+ SYS_DATAFILES. */
+ char *dict_path = dict_get_first_path(space_id);
+ char *fil_path = fil_space_get_first_path(space_id);
+ if (dict_path && fil_path
+ && strcmp(dict_path, fil_path)) {
+ dict_update_filepath(space_id, fil_path);
}
- break;
+ ut_free(dict_path);
+ ut_free(fil_path);
+ ut_free(table_name.m_name);
+ ut_free(shared_space_name);
+ continue;
+ }
- case DICT_CHECK_SOME_LOADED:
- /* Some tablespaces may have been opened in
- trx_resurrect_table_locks(). */
- if (fil_space_for_table_exists_in_mem(
- space_id, name, FALSE, FALSE,
- false, NULL, 0)) {
- break;
- }
- /* fall through */
- case DICT_CHECK_NONE_LOADED:
- if (discarded) {
- ib_logf(IB_LOG_LEVEL_INFO,
- "DISCARD flag set for table '%s',"
- " ignored.",
- table_name);
- break;
- }
+ /* Set the expected filepath from the data dictionary.
+ If the file is found elsewhere (from an ISL or the default
+ location) or this path is the same file but looks different,
+ fil_ibd_open() will update the dictionary with what is
+ opened. */
+ char* filepath = dict_get_first_path(space_id);
+
+ /* Check that the .ibd file exists. */
+ bool is_temp = flags2 & DICT_TF2_TEMPORARY;
+ bool is_encrypted = flags2 & DICT_TF2_ENCRYPTION;
+ ulint fsp_flags = dict_tf_to_fsp_flags(flags,
+ is_temp,
+ is_encrypted);
+ validate = true; /* Encryption */
+
+ dberr_t err = fil_ibd_open(
+ validate,
+ !srv_read_only_mode && srv_log_file_size != 0,
+ FIL_TYPE_TABLESPACE,
+ space_id,
+ fsp_flags,
+ space_name,
+ filepath,
+ NULL);
- /* It is a normal database startup: create the
- space object and check that the .ibd file exists.
- If the table uses a remote tablespace, look for the
- space_id in SYS_DATAFILES to find the filepath */
+ if (err != DB_SUCCESS) {
+ ib::warn() << "Ignoring tablespace "
+ << id_name_t(space_name)
+ << " because it could not be opened.";
+ }
- /* Use the remote filepath if known. */
- char* filepath = NULL;
- if (DICT_TF_HAS_DATA_DIR(flags)) {
- filepath = dict_get_first_path(
- space_id, name);
- }
+ max_space_id = ut_max(max_space_id, space_id);
- /* We need to read page 0 to get (optional) IV
- regardless if encryptions is turned on or not,
- since if it's off we should decrypt a potentially
- already encrypted table */
- bool read_page_0 = true;
-
- /* We set the 2nd param (fix_dict = true)
- here because we already have an x-lock on
- dict_operation_lock and dict_sys->mutex. Besides,
- this is at startup and we are now single threaded.
- If the filepath is not known, it will need to
- be discovered. */
- dberr_t err = fil_open_single_table_tablespace(
- read_page_0, srv_read_only_mode ? false : true,
- space_id, dict_tf_to_fsp_flags(flags),
- name, filepath, NULL);
-
- if (err != DB_SUCCESS) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Tablespace open failed for '%s', "
- "ignored.", table_name);
- }
+ ut_free(table_name.m_name);
+ ut_free(shared_space_name);
+ ut_free(filepath);
+ }
- if (filepath) {
- mem_free(filepath);
- }
+ mtr_commit(&mtr);
- break;
- }
+ DBUG_RETURN(max_space_id);
+}
- if (space_id > max_space_id) {
- max_space_id = space_id;
- }
+/** Check each tablespace found in the data dictionary.
+Look at each general tablespace found in SYS_TABLESPACES.
+Then look at each table defined in SYS_TABLES that has a space_id > 0
+to find all the file-per-table tablespaces.
-next_tablespace:
- mem_free(name);
- mtr_start(&mtr);
+In a crash recovery we already have some tablespace objects created from
+processing the REDO log. Any other tablespace in SYS_TABLESPACES not
+previously used in recovery will be opened here. We will compare the
+space_id information in the data dictionary to what we find in the
+tablespace file. In addition, more validation will be done if recovery
+was needed and force_recovery is not set.
- btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr);
- }
+We also scan the biggest space id, and store it to fil_system.
+@param[in] validate true if recovery was needed */
+void
+dict_check_tablespaces_and_store_max_id(
+ bool validate)
+{
+ mtr_t mtr;
- goto loop;
+ DBUG_ENTER("dict_check_tablespaces_and_store_max_id");
+
+ rw_lock_x_lock(dict_operation_lock);
+ mutex_enter(&dict_sys->mutex);
+
+ /* Initialize the max space_id from sys header */
+ mtr_start(&mtr);
+ ulint max_space_id = mtr_read_ulint(
+ dict_hdr_get(&mtr) + DICT_HDR_MAX_SPACE_ID,
+ MLOG_4BYTES, &mtr);
+ mtr_commit(&mtr);
+
+ fil_set_max_space_id_if_bigger(max_space_id);
+
+ /* Open all general tablespaces found in SYS_TABLESPACES. */
+ ulint max1 = dict_check_sys_tablespaces(validate);
+
+ /* Open all tablespaces referenced in SYS_TABLES.
+ This will update SYS_TABLESPACES and SYS_DATAFILES if it
+ finds any file-per-table tablespaces not already there. */
+ ulint max2 = dict_check_sys_tables(validate);
+
+ /* Store the max space_id found */
+ max_space_id = ut_max(max1, max2);
+ fil_set_max_space_id_if_bigger(max_space_id);
+
+ mutex_exit(&dict_sys->mutex);
+ rw_lock_x_unlock(dict_operation_lock);
+
+ DBUG_VOID_RETURN;
}
+/** Error message for a delete-marked record in dict_load_column_low() */
+static const char* dict_load_column_del = "delete-marked record in SYS_COLUMN";
+
/********************************************************************//**
Loads a table column definition from a SYS_COLUMNS record to
dict_table_t.
@return error message, or NULL on success */
-UNIV_INTERN
const char*
dict_load_column_low(
/*=================*/
@@ -1216,7 +1559,10 @@ dict_load_column_low(
or NULL if table != NULL */
table_id_t* table_id, /*!< out: table id */
const char** col_name, /*!< out: column name */
- const rec_t* rec) /*!< in: SYS_COLUMNS record */
+ const rec_t* rec, /*!< in: SYS_COLUMNS record */
+ ulint* nth_v_col) /*!< out: if not NULL, this
+ records the "n" of "nth" virtual
+ column */
{
char* name;
const byte* field;
@@ -1225,11 +1571,12 @@ dict_load_column_low(
ulint prtype;
ulint col_len;
ulint pos;
+ ulint num_base;
ut_ad(table || column);
if (rec_get_deleted_flag(rec, 0)) {
- return("delete-marked record in SYS_COLUMNS");
+ return(dict_load_column_del);
}
if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_COLUMNS) {
@@ -1252,16 +1599,11 @@ err_len:
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_COLUMNS__POS, &len);
if (len != 4) {
-
goto err_len;
}
pos = mach_read_from_4(field);
- if (table && table->n_def != pos) {
- return("SYS_COLUMNS.POS mismatch");
- }
-
rec_get_nth_field_offs_old(
rec, DICT_FLD__SYS_COLUMNS__DB_TRX_ID, &len);
if (len != DATA_TRX_ID_LEN && len != UNIV_SQL_NULL) {
@@ -1321,6 +1663,10 @@ err_len:
}
}
+ if (table && table->n_def != pos && !(prtype & DATA_VIRTUAL)) {
+ return("SYS_COLUMNS.POS mismatch");
+ }
+
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_COLUMNS__LEN, &len);
if (len != 4) {
@@ -1332,18 +1678,124 @@ err_len:
if (len != 4) {
goto err_len;
}
+ num_base = mach_read_from_4(field);
- if (!column) {
- dict_mem_table_add_col(table, heap, name, mtype,
- prtype, col_len);
+ if (column == NULL) {
+ if (prtype & DATA_VIRTUAL) {
+#ifdef UNIV_DEBUG
+ dict_v_col_t* vcol =
+#endif
+ dict_mem_table_add_v_col(
+ table, heap, name, mtype,
+ prtype, col_len,
+ dict_get_v_col_mysql_pos(pos), num_base);
+ ut_ad(vcol->v_pos == dict_get_v_col_pos(pos));
+ } else {
+ ut_ad(num_base == 0);
+ dict_mem_table_add_col(table, heap, name, mtype,
+ prtype, col_len);
+ }
} else {
dict_mem_fill_column_struct(column, pos, mtype,
prtype, col_len);
}
+ /* Report the virtual column number */
+ if ((prtype & DATA_VIRTUAL) && nth_v_col != NULL) {
+ *nth_v_col = dict_get_v_col_pos(pos);
+ }
+
return(NULL);
}
+/** Error message for a delete-marked record in dict_load_virtual_low() */
+static const char* dict_load_virtual_del = "delete-marked record in SYS_VIRTUAL";
+
+/** Loads a virtual column "mapping" (to base columns) information
+from a SYS_VIRTUAL record
+@param[in,out] table table
+@param[in,out] heap memory heap
+@param[in,out] column mapped base column's dict_column_t
+@param[in,out] table_id table id
+@param[in,out] pos virtual column position
+@param[in,out] base_pos base column position
+@param[in] rec SYS_VIRTUAL record
+@return error message, or NULL on success */
+const char*
+dict_load_virtual_low(
+ dict_table_t* table,
+ mem_heap_t* heap,
+ dict_col_t** column,
+ table_id_t* table_id,
+ ulint* pos,
+ ulint* base_pos,
+ const rec_t* rec)
+{
+ const byte* field;
+ ulint len;
+ ulint base;
+
+ if (rec_get_deleted_flag(rec, 0)) {
+ return(dict_load_virtual_del);
+ }
+
+ if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_VIRTUAL) {
+ return("wrong number of columns in SYS_VIRTUAL record");
+ }
+
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_VIRTUAL__TABLE_ID, &len);
+ if (len != 8) {
+err_len:
+ return("incorrect column length in SYS_VIRTUAL");
+ }
+
+ if (table_id != NULL) {
+ *table_id = mach_read_from_8(field);
+ } else if (table->id != mach_read_from_8(field)) {
+ return("SYS_VIRTUAL.TABLE_ID mismatch");
+ }
+
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_VIRTUAL__POS, &len);
+ if (len != 4) {
+ goto err_len;
+ }
+
+ if (pos != NULL) {
+ *pos = mach_read_from_4(field);
+ }
+
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_VIRTUAL__BASE_POS, &len);
+ if (len != 4) {
+ goto err_len;
+ }
+
+ base = mach_read_from_4(field);
+
+ if (base_pos != NULL) {
+ *base_pos = base;
+ }
+
+ rec_get_nth_field_offs_old(
+ rec, DICT_FLD__SYS_VIRTUAL__DB_TRX_ID, &len);
+ if (len != DATA_TRX_ID_LEN && len != UNIV_SQL_NULL) {
+ goto err_len;
+ }
+
+ rec_get_nth_field_offs_old(
+ rec, DICT_FLD__SYS_VIRTUAL__DB_ROLL_PTR, &len);
+ if (len != DATA_ROLL_PTR_LEN && len != UNIV_SQL_NULL) {
+ goto err_len;
+ }
+
+ if (column != NULL) {
+ *column = dict_table_get_nth_col(table, base);
+ }
+
+ return(NULL);
+}
/********************************************************************//**
Loads definitions for table columns. */
static
@@ -1363,8 +1815,9 @@ dict_load_columns(
byte* buf;
ulint i;
mtr_t mtr;
+ ulint n_skipped = 0;
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ ut_ad(mutex_own(&dict_sys->mutex));
mtr_start(&mtr);
@@ -1388,26 +1841,37 @@ dict_load_columns(
btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE,
BTR_SEARCH_LEAF, &pcur, &mtr);
- for (i = 0; i + DATA_N_SYS_COLS < (ulint) table->n_cols; i++) {
+
+ ut_ad(table->n_t_cols == static_cast<ulint>(
+ table->n_cols) + static_cast<ulint>(table->n_v_cols));
+
+ for (i = 0;
+ i + DATA_N_SYS_COLS < table->n_t_cols + n_skipped;
+ i++) {
const char* err_msg;
const char* name = NULL;
+ ulint nth_v_col = ULINT_UNDEFINED;
rec = btr_pcur_get_rec(&pcur);
ut_a(btr_pcur_is_on_user_rec(&pcur));
err_msg = dict_load_column_low(table, heap, NULL, NULL,
- &name, rec);
+ &name, rec, &nth_v_col);
- if (err_msg) {
- fprintf(stderr, "InnoDB: %s\n", err_msg);
- ut_error;
+ if (err_msg == dict_load_column_del) {
+ n_skipped++;
+ goto next_rec;
+ } else if (err_msg) {
+ ib::fatal() << err_msg;
}
/* Note: Currently we have one DOC_ID column that is
- shared by all FTS indexes on a table. */
+ shared by all FTS indexes on a table. And only non-virtual
+ column can be used for FULLTEXT index */
if (innobase_strcasecmp(name,
- FTS_DOC_ID_COL_NAME) == 0) {
+ FTS_DOC_ID_COL_NAME) == 0
+ && nth_v_col == ULINT_UNDEFINED) {
dict_col_t* col;
/* As part of normal loading of tables the
table->flag is not set for tables with FTS
@@ -1424,7 +1888,7 @@ dict_load_columns(
ut_a(table->fts->doc_col == ULINT_UNDEFINED);
- col = dict_table_get_nth_col(table, i);
+ col = dict_table_get_nth_col(table, i - n_skipped);
ut_ad(col->len == sizeof(doc_id_t));
@@ -1435,7 +1899,103 @@ dict_load_columns(
table, DICT_TF2_FTS_ADD_DOC_ID);
}
- table->fts->doc_col = i;
+ table->fts->doc_col = i - n_skipped;
+ }
+next_rec:
+ btr_pcur_move_to_next_user_rec(&pcur, &mtr);
+ }
+
+ btr_pcur_close(&pcur);
+ mtr_commit(&mtr);
+}
+
+/** Loads SYS_VIRTUAL info for one virtual column
+@param[in,out] table table
+@param[in] nth_v_col virtual column sequence num
+@param[in,out] v_col virtual column
+@param[in,out] heap memory heap
+*/
+static
+void
+dict_load_virtual_one_col(
+ dict_table_t* table,
+ ulint nth_v_col,
+ dict_v_col_t* v_col,
+ mem_heap_t* heap)
+{
+ dict_table_t* sys_virtual;
+ dict_index_t* sys_virtual_index;
+ btr_pcur_t pcur;
+ dtuple_t* tuple;
+ dfield_t* dfield;
+ const rec_t* rec;
+ byte* buf;
+ ulint i = 0;
+ mtr_t mtr;
+ ulint skipped = 0;
+
+ ut_ad(mutex_own(&dict_sys->mutex));
+
+ if (v_col->num_base == 0) {
+ return;
+ }
+
+ mtr_start(&mtr);
+
+ sys_virtual = dict_table_get_low("SYS_VIRTUAL");
+ sys_virtual_index = UT_LIST_GET_FIRST(sys_virtual->indexes);
+ ut_ad(!dict_table_is_comp(sys_virtual));
+
+ ut_ad(name_of_col_is(sys_virtual, sys_virtual_index,
+ DICT_FLD__SYS_VIRTUAL__POS, "POS"));
+
+ tuple = dtuple_create(heap, 2);
+
+ /* table ID field */
+ dfield = dtuple_get_nth_field(tuple, 0);
+
+ buf = static_cast<byte*>(mem_heap_alloc(heap, 8));
+ mach_write_to_8(buf, table->id);
+
+ dfield_set_data(dfield, buf, 8);
+
+ /* virtual column pos field */
+ dfield = dtuple_get_nth_field(tuple, 1);
+
+ buf = static_cast<byte*>(mem_heap_alloc(heap, 4));
+ ulint vcol_pos = dict_create_v_col_pos(nth_v_col, v_col->m_col.ind);
+ mach_write_to_4(buf, vcol_pos);
+
+ dfield_set_data(dfield, buf, 4);
+
+ dict_index_copy_types(tuple, sys_virtual_index, 2);
+
+ btr_pcur_open_on_user_rec(sys_virtual_index, tuple, PAGE_CUR_GE,
+ BTR_SEARCH_LEAF, &pcur, &mtr);
+
+ for (i = 0; i < v_col->num_base + skipped; i++) {
+ const char* err_msg;
+ ulint pos;
+
+ ut_ad(btr_pcur_is_on_user_rec(&pcur));
+
+ rec = btr_pcur_get_rec(&pcur);
+
+ ut_a(btr_pcur_is_on_user_rec(&pcur));
+
+ err_msg = dict_load_virtual_low(table, heap,
+ &v_col->base_col[i - skipped],
+ NULL,
+ &pos, NULL, rec);
+
+ if (err_msg) {
+ if (err_msg != dict_load_virtual_del) {
+ ib::fatal() << err_msg;
+ } else {
+ skipped++;
+ }
+ } else {
+ ut_ad(pos == vcol_pos);
}
btr_pcur_move_to_next_user_rec(&pcur, &mtr);
@@ -1445,6 +2005,23 @@ dict_load_columns(
mtr_commit(&mtr);
}
+/** Loads info from SYS_VIRTUAL for virtual columns.
+@param[in,out] table table
+@param[in] heap memory heap
+*/
+static
+void
+dict_load_virtual(
+ dict_table_t* table,
+ mem_heap_t* heap)
+{
+ for (ulint i = 0; i < table->n_v_cols; i++) {
+ dict_v_col_t* v_col = dict_table_get_nth_v_col(table, i);
+
+ dict_load_virtual_one_col(table, i, v_col, heap);
+ }
+}
+
/** Error message for a delete-marked record in dict_load_field_low() */
static const char* dict_load_field_del = "delete-marked record in SYS_FIELDS";
@@ -1452,7 +2029,6 @@ static const char* dict_load_field_del = "delete-marked record in SYS_FIELDS";
Loads an index field definition from a SYS_FIELDS record to
dict_index_t.
@return error message, or NULL on success */
-UNIV_INTERN
const char*
dict_load_field_low(
/*================*/
@@ -1592,7 +2168,7 @@ dict_load_fields(
mtr_t mtr;
dberr_t error;
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ ut_ad(mutex_own(&dict_sys->mutex));
mtr_start(&mtr);
@@ -1630,7 +2206,7 @@ dict_load_fields(
goto next_rec;
} else if (err_msg) {
- fprintf(stderr, "InnoDB: %s\n", err_msg);
+ ib::error() << err_msg;
error = DB_CORRUPTION;
goto func_exit;
}
@@ -1656,7 +2232,6 @@ If allocate=TRUE, we will create a dict_index_t structure and fill it
accordingly. If allocated=FALSE, the dict_index_t will be supplied by
the caller and filled with information read from the record. @return
error message, or NULL on success */
-UNIV_INTERN
const char*
dict_load_index_low(
/*================*/
@@ -1679,6 +2254,7 @@ dict_load_index_low(
ulint n_fields;
ulint type;
ulint space;
+ ulint merge_threshold;
if (allocate) {
/* If allocate=TRUE, no dict_index_t will
@@ -1690,7 +2266,27 @@ dict_load_index_low(
return(dict_load_index_del);
}
- if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_INDEXES) {
+ if (rec_get_n_fields_old(rec) == DICT_NUM_FIELDS__SYS_INDEXES) {
+ /* MERGE_THRESHOLD exists */
+ field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_INDEXES__MERGE_THRESHOLD, &len);
+ switch (len) {
+ case 4:
+ merge_threshold = mach_read_from_4(field);
+ break;
+ case UNIV_SQL_NULL:
+ merge_threshold = DICT_INDEX_MERGE_THRESHOLD_DEFAULT;
+ break;
+ default:
+ return("incorrect MERGE_THRESHOLD length"
+ " in SYS_INDEXES");
+ }
+ } else if (rec_get_n_fields_old(rec)
+ == DICT_NUM_FIELDS__SYS_INDEXES - 1) {
+ /* MERGE_THRESHOLD doesn't exist */
+
+ merge_threshold = DICT_INDEX_MERGE_THRESHOLD_DEFAULT;
+ } else {
return("wrong number of columns in SYS_INDEXES record");
}
@@ -1781,6 +2377,7 @@ err_len:
(*index)->id = id;
(*index)->page = mach_read_from_4(field);
ut_ad((*index)->page);
+ (*index)->merge_threshold = merge_threshold;
return(NULL);
}
@@ -1810,7 +2407,7 @@ dict_load_indexes(
mtr_t mtr;
dberr_t error = DB_SUCCESS;
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ ut_ad(mutex_own(&dict_sys->mutex));
mtr_start(&mtr);
@@ -1845,11 +2442,10 @@ dict_load_indexes(
for drop table */
if (dict_table_get_first_index(table) == NULL
&& !(ignore_err & DICT_ERR_IGNORE_CORRUPT)) {
- ib_logf(IB_LOG_LEVEL_WARN,
- "Cannot load table %s "
- "because it has no indexes in "
- "InnoDB internal data dictionary.",
- table->name);
+ ib::warn() << "Cannot load table "
+ << table->name
+ << " because it has no indexes in"
+ " InnoDB internal data dictionary.";
error = DB_CORRUPTION;
goto func_exit;
}
@@ -1860,15 +2456,20 @@ dict_load_indexes(
rec = btr_pcur_get_rec(&pcur);
if ((ignore_err & DICT_ERR_IGNORE_RECOVER_LOCK)
- && rec_get_n_fields_old(rec)
- == DICT_NUM_FIELDS__SYS_INDEXES) {
+ && (rec_get_n_fields_old(rec)
+ == DICT_NUM_FIELDS__SYS_INDEXES
+ /* a record for older SYS_INDEXES table
+ (missing merge_threshold column) is acceptable. */
+ || rec_get_n_fields_old(rec)
+ == DICT_NUM_FIELDS__SYS_INDEXES - 1)) {
const byte* field;
ulint len;
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_INDEXES__NAME, &len);
if (len != UNIV_SQL_NULL
- && char(*field) == char(TEMP_INDEX_PREFIX)) {
+ && static_cast<char>(*field)
+ == static_cast<char>(*TEMP_INDEX_PREFIX_STR)) {
/* Skip indexes whose name starts with
TEMP_INDEX_PREFIX, because they will
be dropped during crash recovery. */
@@ -1876,8 +2477,8 @@ dict_load_indexes(
}
}
- err_msg = dict_load_index_low(buf, table->name, heap, rec,
- TRUE, &index);
+ err_msg = dict_load_index_low(
+ buf, table->name.m_name, heap, rec, TRUE, &index);
ut_ad((index == NULL && err_msg != NULL)
|| (index != NULL && err_msg == NULL));
@@ -1887,13 +2488,15 @@ dict_load_indexes(
if (dict_table_get_first_index(table) == NULL
&& !(ignore_err & DICT_ERR_IGNORE_CORRUPT)) {
- ib_logf(IB_LOG_LEVEL_WARN,
- "Failed to load the "
- "clustered index for table %s "
- "because of the following error: %s. "
- "Refusing to load the rest of the "
- "indexes (if any) and the whole table "
- "altogether.", table->name, err_msg);
+
+ ib::warn() << "Failed to load the"
+ " clustered index for table "
+ << table->name
+ << " because of the following error: "
+ << err_msg << "."
+ " Refusing to load the rest of the"
+ " indexes (if any) and the whole table"
+ " altogether.";
error = DB_CORRUPTION;
goto func_exit;
}
@@ -1903,7 +2506,7 @@ dict_load_indexes(
/* Skip delete-marked records. */
goto next_rec;
} else if (err_msg) {
- fprintf(stderr, "InnoDB: %s\n", err_msg);
+ ib::error() << err_msg;
if (ignore_err & DICT_ERR_IGNORE_CORRUPT) {
goto next_rec;
}
@@ -1915,10 +2518,10 @@ dict_load_indexes(
/* Check whether the index is corrupted */
if (dict_index_is_corrupted(index)) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: ", stderr);
- dict_index_name_print(stderr, NULL, index);
- fputs(" is corrupted\n", stderr);
+
+ ib::error() << "Index " << index->name
+ << " of table " << table->name
+ << " is corrupted";
if (!srv_load_corrupted
&& !(ignore_err & DICT_ERR_IGNORE_CORRUPT)
@@ -1934,15 +2537,14 @@ dict_load_indexes(
DICT_ERR_IGNORE_CORRUPT
3) if the index corrupted is a secondary
index */
- ut_print_timestamp(stderr);
- fputs(" InnoDB: load corrupted index ", stderr);
- dict_index_name_print(stderr, NULL, index);
- putc('\n', stderr);
+ ib::info() << "Load corrupted index "
+ << index->name
+ << " of table " << table->name;
}
}
if (index->type & DICT_FTS
- && !DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS)) {
+ && !dict_table_has_fts_index(table)) {
/* This should have been created by now. */
ut_a(table->fts != NULL);
DICT_TF2_FLAG_SET(table, DICT_TF2_FTS);
@@ -1951,10 +2553,12 @@ dict_load_indexes(
/* We check for unsupported types first, so that the
subsequent checks are relevant for the supported types. */
if (index->type & ~(DICT_CLUSTERED | DICT_UNIQUE
- | DICT_CORRUPT | DICT_FTS)) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Unknown type %lu of index %s of table %s",
- (ulong) index->type, index->name, table->name);
+ | DICT_CORRUPT | DICT_FTS
+ | DICT_SPATIAL | DICT_VIRTUAL)) {
+
+ ib::error() << "Unknown type " << index->type
+ << " of index " << index->name
+ << " of table " << table->name;
error = DB_UNSUPPORTED;
dict_mem_index_free(index);
@@ -1963,11 +2567,9 @@ dict_load_indexes(
&& !table->ibd_file_missing
&& (!(index->type & DICT_FTS))) {
- fprintf(stderr,
- "InnoDB: Error: trying to load index %s"
- " for table %s\n"
- "InnoDB: but the index tree has been freed!\n",
- index->name, table->name);
+ ib::error() << "Trying to load index " << index->name
+ << " for table " << table->name
+ << ", but the index tree has been freed!";
if (ignore_err & DICT_ERR_IGNORE_INDEX_ROOT) {
/* If caller can tolerate this error,
@@ -1978,12 +2580,11 @@ dict_load_indexes(
dictionary cache for such metadata corruption,
since we would always be able to set it
when loading the dictionary cache */
- dict_set_corrupted_index_cache_only(
- index, table);
+ index->table = table;
+ dict_set_corrupted_index_cache_only(index);
- fprintf(stderr,
- "InnoDB: Index is corrupt but forcing"
- " load into data dictionary\n");
+ ib::info() << "Index is corrupt but forcing"
+ " load into data dictionary";
} else {
corrupted:
dict_mem_index_free(index);
@@ -1993,13 +2594,9 @@ corrupted:
} else if (!dict_index_is_clust(index)
&& NULL == dict_table_get_first_index(table)) {
- fputs("InnoDB: Error: trying to load index ",
- stderr);
- ut_print_name(stderr, NULL, FALSE, index->name);
- fputs(" for table ", stderr);
- ut_print_name(stderr, NULL, TRUE, table->name);
- fputs("\nInnoDB: but the first index"
- " is not clustered!\n", stderr);
+ ib::error() << "Trying to load index " << index->name
+ << " for table " << table->name
+ << ", but the first index is not clustered!";
goto corrupted;
} else if (dict_is_sys_table(table->id)
@@ -2030,8 +2627,16 @@ next_rec:
btr_pcur_move_to_next_user_rec(&pcur, &mtr);
}
+ ut_ad(table->fts_doc_id_index == NULL);
+
+ if (table->fts != NULL) {
+ table->fts_doc_id_index = dict_table_get_index_on_name(
+ table, FTS_DOC_ID_INDEX_NAME);
+ }
+
/* If the table contains FTS indexes, populate table->fts->indexes */
- if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS)) {
+ if (dict_table_has_fts_index(table)) {
+ ut_ad(table->fts_doc_id_index != NULL);
/* table->fts->indexes should have been created. */
ut_a(table->fts->indexes != NULL);
dict_table_get_all_fts_indexes(table, table->fts->indexes);
@@ -2044,151 +2649,44 @@ func_exit:
return(error);
}
-/********************************************************************//**
-Loads a table definition from a SYS_TABLES record to dict_table_t.
+/** Loads a table definition from a SYS_TABLES record to dict_table_t.
Does not load any columns or indexes.
+@param[in] name Table name
+@param[in] rec SYS_TABLES record
+@param[out,own] table table, or NULL
@return error message, or NULL on success */
-UNIV_INTERN
+static
const char*
dict_load_table_low(
-/*================*/
- const char* name, /*!< in: table name */
- const rec_t* rec, /*!< in: SYS_TABLES record */
- dict_table_t** table) /*!< out,own: table, or NULL */
+ table_name_t& name,
+ const rec_t* rec,
+ dict_table_t** table)
{
- const byte* field;
- ulint len;
- ulint space;
+ table_id_t table_id;
+ ulint space_id;
ulint n_cols;
- ulint flags = 0;
+ ulint t_num;
+ ulint flags;
ulint flags2;
+ ulint n_v_col;
- if (rec_get_deleted_flag(rec, 0)) {
- return("delete-marked record in SYS_TABLES");
- }
-
- if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_TABLES) {
- return("wrong number of columns in SYS_TABLES record");
- }
-
- rec_get_nth_field_offs_old(
- rec, DICT_FLD__SYS_TABLES__NAME, &len);
- if (len == 0 || len == UNIV_SQL_NULL) {
-err_len:
- return("incorrect column length in SYS_TABLES");
- }
- rec_get_nth_field_offs_old(
- rec, DICT_FLD__SYS_TABLES__DB_TRX_ID, &len);
- if (len != DATA_TRX_ID_LEN && len != UNIV_SQL_NULL) {
- goto err_len;
- }
- rec_get_nth_field_offs_old(
- rec, DICT_FLD__SYS_TABLES__DB_ROLL_PTR, &len);
- if (len != DATA_ROLL_PTR_LEN && len != UNIV_SQL_NULL) {
- goto err_len;
- }
-
- rec_get_nth_field_offs_old(rec, DICT_FLD__SYS_TABLES__ID, &len);
- if (len != 8) {
- goto err_len;
- }
-
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__N_COLS, &len);
- if (len != 4) {
- goto err_len;
- }
-
- n_cols = mach_read_from_4(field);
-
- rec_get_nth_field_offs_old(rec, DICT_FLD__SYS_TABLES__TYPE, &len);
- if (len != 4) {
- goto err_len;
- }
-
- rec_get_nth_field_offs_old(
- rec, DICT_FLD__SYS_TABLES__MIX_ID, &len);
- if (len != 8) {
- goto err_len;
- }
-
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__MIX_LEN, &len);
- if (len != 4) {
- goto err_len;
- }
-
- /* MIX_LEN may hold additional flags in post-antelope file formats. */
- flags2 = mach_read_from_4(field);
-
- /* DICT_TF2_FTS will be set when indexes is being loaded */
- flags2 &= ~DICT_TF2_FTS;
-
- rec_get_nth_field_offs_old(
- rec, DICT_FLD__SYS_TABLES__CLUSTER_ID, &len);
- if (len != UNIV_SQL_NULL) {
- goto err_len;
- }
-
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__SPACE, &len);
- if (len != 4) {
- goto err_len;
+ const char* error_text = dict_sys_tables_rec_check(rec);
+ if (error_text != NULL) {
+ return(error_text);
}
- space = mach_read_from_4(field);
-
- /* Check if the tablespace exists and has the right name */
- flags = dict_sys_tables_get_flags(rec);
+ dict_sys_tables_rec_read(rec, name, &table_id, &space_id,
+ &t_num, &flags, &flags2);
- if (UNIV_UNLIKELY(flags == ULINT_UNDEFINED)) {
- field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_TABLES__TYPE, &len);
- ut_ad(len == 4); /* this was checked earlier */
- flags = mach_read_from_4(field);
-
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Error: table ", stderr);
- ut_print_filename(stderr, name);
- fprintf(stderr, "\n"
- "InnoDB: in InnoDB data dictionary"
- " has unknown type %lx.\n",
- (ulong) flags);
+ if (flags == ULINT_UNDEFINED) {
return("incorrect flags in SYS_TABLES");
}
- /* The high-order bit of N_COLS is the "compact format" flag.
- For tables in that format, MIX_LEN may hold additional flags. */
- if (n_cols & DICT_N_COLS_COMPACT) {
- ut_ad(flags & DICT_TF_COMPACT);
-
- if (flags2 & ~DICT_TF2_BIT_MASK) {
- ut_print_timestamp(stderr);
- fputs(" InnoDB: Warning: table ", stderr);
- ut_print_filename(stderr, name);
- fprintf(stderr, "\n"
- "InnoDB: in InnoDB data dictionary"
- " has unknown flags %lx.\n",
- (ulong) flags2);
-
- /* Clean it up and keep going */
- flags2 &= DICT_TF2_BIT_MASK;
- }
- } else {
- /* Do not trust the MIX_LEN field when the
- row format is Redundant. */
- flags2 = 0;
- }
+ dict_table_decode_n_col(t_num, &n_cols, &n_v_col);
- /* See if the tablespace is available. */
*table = dict_mem_table_create(
- name, space, n_cols & ~DICT_N_COLS_COMPACT, flags, flags2);
-
- field = rec_get_nth_field_old(rec, DICT_FLD__SYS_TABLES__ID, &len);
- ut_ad(len == 8); /* this was checked earlier */
-
- (*table)->id = mach_read_from_8(field);
-
+ name.m_name, space_id, n_cols + n_v_col, n_v_col, flags, flags2);
+ (*table)->id = table_id;
(*table)->ibd_file_missing = FALSE;
return(NULL);
@@ -2200,47 +2698,44 @@ table->data_dir_path and replace the 'databasename/tablename.ibd'
portion with 'tablename'.
This allows SHOW CREATE TABLE to return the correct DATA DIRECTORY path.
Make this data directory path only if it has not yet been saved. */
-UNIV_INTERN
void
dict_save_data_dir_path(
/*====================*/
dict_table_t* table, /*!< in/out: table */
char* filepath) /*!< in: filepath of tablespace */
{
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ ut_ad(mutex_own(&dict_sys->mutex));
ut_a(DICT_TF_HAS_DATA_DIR(table->flags));
ut_a(!table->data_dir_path);
ut_a(filepath);
/* Be sure this filepath is not the default filepath. */
- char* default_filepath = fil_make_ibd_name(table->name, false);
- if (strcmp(filepath, default_filepath)) {
- ulint pathlen = strlen(filepath);
- ut_a(pathlen < OS_FILE_MAX_PATH);
- ut_a(0 == strcmp(filepath + pathlen - 4, ".ibd"));
-
- table->data_dir_path = mem_heap_strdup(table->heap, filepath);
- os_file_make_data_dir_path(table->data_dir_path);
- } else {
- /* This does not change SYS_DATAFILES or SYS_TABLES
- or FSP_FLAGS on the header page of the tablespace,
- but it makes dict_table_t consistent */
- table->flags &= ~DICT_TF_MASK_DATA_DIR;
+ char* default_filepath = fil_make_filepath(
+ NULL, table->name.m_name, IBD, false);
+ if (default_filepath) {
+ if (0 != strcmp(filepath, default_filepath)) {
+ ulint pathlen = strlen(filepath);
+ ut_a(pathlen < OS_FILE_MAX_PATH);
+ ut_a(0 == strcmp(filepath + pathlen - 4, DOT_IBD));
+
+ table->data_dir_path = mem_heap_strdup(
+ table->heap, filepath);
+ os_file_make_data_dir_path(table->data_dir_path);
+ }
+
+ ut_free(default_filepath);
}
- mem_free(default_filepath);
}
-/*****************************************************************//**
-Make sure the data_file_name is saved in dict_table_t if needed. Try to
-read it from the file dictionary first, then from SYS_DATAFILES. */
-UNIV_INTERN
+/** Make sure the data_dir_path is saved in dict_table_t if DATA DIRECTORY
+was used. Try to read it from the fil_system first, then from SYS_DATAFILES.
+@param[in] table Table object
+@param[in] dict_mutex_own true if dict_sys->mutex is owned already */
void
dict_get_and_save_data_dir_path(
-/*============================*/
- dict_table_t* table, /*!< in/out: table */
- bool dict_mutex_own) /*!< in: true if dict_sys->mutex
- is owned already */
+ dict_table_t* table,
+ bool dict_mutex_own)
{
bool is_temp = DICT_TF2_FLAG_IS_SET(table, DICT_TF2_TEMPORARY);
@@ -2250,15 +2745,23 @@ dict_get_and_save_data_dir_path(
if (!dict_mutex_own) {
dict_mutex_enter_for_mysql();
}
- if (!path) {
- path = dict_get_first_path(
- table->space, table->name);
+
+ if (path == NULL) {
+ path = dict_get_first_path(table->space);
}
- if (path) {
+ if (path != NULL) {
table->flags |= (1 << DICT_TF_POS_DATA_DIR);
dict_save_data_dir_path(table, path);
- mem_free(path);
+ ut_free(path);
+ }
+
+ if (table->data_dir_path == NULL) {
+ /* Since we did not set the table data_dir_path,
+ unset the flag. This does not change SYS_DATAFILES
+ or SYS_TABLES or FSP_FLAGS on the header page of the
+ tablespace, but it makes dict_table_t consistent. */
+ table->flags &= ~DICT_TF_MASK_DATA_DIR;
}
if (!dict_mutex_own) {
@@ -2267,25 +2770,268 @@ dict_get_and_save_data_dir_path(
}
}
-/********************************************************************//**
-Loads a table definition and also all its index definitions, and also
+/** Make sure the tablespace name is saved in dict_table_t if the table
+uses a general tablespace.
+Try to read it from the fil_system_t first, then from SYS_TABLESPACES.
+@param[in] table Table object
+@param[in] dict_mutex_own) true if dict_sys->mutex is owned already */
+void
+dict_get_and_save_space_name(
+ dict_table_t* table,
+ bool dict_mutex_own)
+{
+ /* Do this only for general tablespaces. */
+ if (!DICT_TF_HAS_SHARED_SPACE(table->flags)) {
+ return;
+ }
+
+ bool use_cache = true;
+ if (table->tablespace != NULL) {
+
+ if (srv_sys_tablespaces_open
+ && dict_table_has_temp_general_tablespace_name(
+ table->tablespace)) {
+ /* We previous saved the temporary name,
+ get the real one now. */
+ use_cache = false;
+ } else {
+ /* Keep and use this name */
+ return;
+ }
+ }
+
+ if (use_cache) {
+ fil_space_t* space = fil_space_acquire_silent(table->space);
+
+ if (space != NULL) {
+ /* Use this name unless it is a temporary general
+ tablespace name and we can now replace it. */
+ if (!srv_sys_tablespaces_open
+ || !dict_table_has_temp_general_tablespace_name(
+ space->name)) {
+
+ /* Use this tablespace name */
+ table->tablespace = mem_heap_strdup(
+ table->heap, space->name);
+
+ fil_space_release(space);
+ return;
+ }
+ fil_space_release(space);
+ }
+ }
+
+ /* Read it from the dictionary. */
+ if (srv_sys_tablespaces_open) {
+ if (!dict_mutex_own) {
+ dict_mutex_enter_for_mysql();
+ }
+
+ table->tablespace = dict_space_get_name(
+ table->space, table->heap);
+
+ if (!dict_mutex_own) {
+ dict_mutex_exit_for_mysql();
+ }
+ }
+}
+
+/** Loads a table definition and also all its index definitions, and also
the cluster definition if the table is a member in a cluster. Also loads
all foreign key constraints where the foreign key is in the table or where
-a foreign key references columns in this table. Adds all these to the data
-dictionary cache.
+a foreign key references columns in this table.
+@param[in] name Table name in the dbname/tablename format
+@param[in] cached true=add to cache, false=do not
+@param[in] ignore_err Error to be ignored when loading
+ table and its index definition
@return table, NULL if does not exist; if the table is stored in an
-.ibd file, but the file does not exist, then we set the
-ibd_file_missing flag TRUE in the table object we return */
-UNIV_INTERN
+.ibd file, but the file does not exist, then we set the ibd_file_missing
+flag in the table object we return. */
dict_table_t*
dict_load_table(
-/*============*/
- const char* name, /*!< in: table name in the
- databasename/tablename format */
- ibool cached, /*!< in: TRUE=add to cache, FALSE=do not */
+ const char* name,
+ bool cached,
dict_err_ignore_t ignore_err)
- /*!< in: error to be ignored when loading
- table and its indexes' definition */
+{
+ dict_names_t fk_list;
+ dict_table_t* result;
+ dict_names_t::iterator i;
+ table_name_t table_name;
+
+ DBUG_ENTER("dict_load_table");
+ DBUG_PRINT("dict_load_table", ("loading table: '%s'", name));
+
+ ut_ad(mutex_own(&dict_sys->mutex));
+
+ table_name.m_name = const_cast<char*>(name);
+
+ result = dict_table_check_if_in_cache_low(name);
+
+ if (!result) {
+ result = dict_load_table_one(table_name, cached, ignore_err,
+ fk_list);
+ while (!fk_list.empty()) {
+ table_name_t fk_table_name;
+ dict_table_t* fk_table;
+
+ fk_table_name.m_name =
+ const_cast<char*>(fk_list.front());
+ fk_table = dict_table_check_if_in_cache_low(
+ fk_table_name.m_name);
+ if (!fk_table) {
+ dict_load_table_one(fk_table_name, cached,
+ ignore_err, fk_list);
+ }
+ fk_list.pop_front();
+ }
+ }
+
+ DBUG_RETURN(result);
+}
+
+/** Opens a tablespace for dict_load_table_one()
+@param[in,out] table A table that refers to the tablespace to open
+@param[in] heap A memory heap
+@param[in] ignore_err Whether to ignore an error. */
+UNIV_INLINE
+void
+dict_load_tablespace(
+ dict_table_t* table,
+ mem_heap_t* heap,
+ dict_err_ignore_t ignore_err)
+{
+ /* The system tablespace is always available. */
+ if (is_system_tablespace(table->space)) {
+ return;
+ }
+
+ if (table->flags2 & DICT_TF2_DISCARDED) {
+ ib::warn() << "Tablespace for table " << table->name
+ << " is set as discarded.";
+ table->ibd_file_missing = TRUE;
+ return;
+ }
+
+ if (dict_table_is_temporary(table)) {
+ /* Do not bother to retry opening temporary tables. */
+ table->ibd_file_missing = TRUE;
+ return;
+ }
+
+ /* A file-per-table table name is also the tablespace name.
+ A general tablespace name is not the same as the table name.
+ Use the general tablespace name if it can be read from the
+ dictionary, if not use 'innodb_general_##. */
+ char* shared_space_name = NULL;
+ char* space_name;
+ if (DICT_TF_HAS_SHARED_SPACE(table->flags)) {
+ if (srv_sys_tablespaces_open) {
+ shared_space_name =
+ dict_space_get_name(table->space, NULL);
+
+ } else {
+ /* Make the temporary tablespace name. */
+ shared_space_name = static_cast<char*>(
+ ut_malloc_nokey(
+ strlen(general_space_name) + 20));
+
+ sprintf(shared_space_name, "%s_" ULINTPF,
+ general_space_name,
+ static_cast<ulint>(table->space));
+ }
+ space_name = shared_space_name;
+ } else {
+ space_name = table->name.m_name;
+ }
+
+ /* The tablespace may already be open. */
+ if (fil_space_for_table_exists_in_mem(
+ table->space, space_name, false,
+ true, heap, table->id, table)) {
+ ut_free(shared_space_name);
+ return;
+ }
+
+ if (!(ignore_err & DICT_ERR_IGNORE_RECOVER_LOCK)) {
+ ib::error() << "Failed to find tablespace for table "
+ << table->name << " in the cache. Attempting"
+ " to load the tablespace with space id "
+ << table->space;
+ }
+
+ /* Use the remote filepath if needed. This parameter is optional
+ in the call to fil_ibd_open(). If not supplied, it will be built
+ from the space_name. */
+ char* filepath = NULL;
+ if (DICT_TF_HAS_DATA_DIR(table->flags)) {
+ /* This will set table->data_dir_path from either
+ fil_system or SYS_DATAFILES */
+ dict_get_and_save_data_dir_path(table, true);
+
+ if (table->data_dir_path) {
+ filepath = fil_make_filepath(
+ table->data_dir_path,
+ table->name.m_name, IBD, true);
+ }
+
+ } else if (DICT_TF_HAS_SHARED_SPACE(table->flags)) {
+ /* Set table->tablespace from either
+ fil_system or SYS_TABLESPACES */
+ dict_get_and_save_space_name(table, true);
+
+ /* Set the filepath from either
+ fil_system or SYS_DATAFILES. */
+ filepath = dict_get_first_path(table->space);
+ if (filepath == NULL) {
+ ib::warn() << "Could not find the filepath"
+ " for table " << table->name <<
+ ", space ID " << table->space;
+ }
+ }
+
+ /* Try to open the tablespace. We set the 2nd param (fix_dict) to
+ false because we do not have an x-lock on dict_operation_lock */
+ ulint fsp_flags = dict_tf_to_fsp_flags(table->flags,
+ false,
+ dict_table_is_encrypted(table));
+ dberr_t err = fil_ibd_open(
+ true, false, FIL_TYPE_TABLESPACE, table->space,
+ fsp_flags, space_name, filepath, table);
+
+ if (err != DB_SUCCESS) {
+ /* We failed to find a sensible tablespace file */
+ table->ibd_file_missing = TRUE;
+ }
+
+ ut_free(shared_space_name);
+ ut_free(filepath);
+}
+
+/** Loads a table definition and also all its index definitions.
+
+Loads those foreign key constraints whose referenced table is already in
+dictionary cache. If a foreign key constraint is not loaded, then the
+referenced table is pushed into the output stack (fk_tables), if it is not
+NULL. These tables must be subsequently loaded so that all the foreign
+key constraints are loaded into memory.
+
+@param[in] name Table name in the db/tablename format
+@param[in] cached true=add to cache, false=do not
+@param[in] ignore_err Error to be ignored when loading table
+ and its index definition
+@param[out] fk_tables Related table names that must also be
+ loaded to ensure that all foreign key
+ constraints are loaded.
+@return table, NULL if does not exist; if the table is stored in an
+.ibd file, but the file does not exist, then we set the
+ibd_file_missing flag TRUE in the table object we return */
+static
+dict_table_t*
+dict_load_table_one(
+ table_name_t& name,
+ bool cached,
+ dict_err_ignore_t ignore_err,
+ dict_names_t& fk_tables)
{
dberr_t err;
dict_table_t* table;
@@ -2298,11 +3044,13 @@ dict_load_table(
const rec_t* rec;
const byte* field;
ulint len;
- char* filepath = NULL;
const char* err_msg;
mtr_t mtr;
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ DBUG_ENTER("dict_load_table_one");
+ DBUG_PRINT("dict_load_table_one", ("table: %s", name.m_name));
+
+ ut_ad(mutex_own(&dict_sys->mutex));
heap = mem_heap_create(32000);
@@ -2325,7 +3073,7 @@ dict_load_table(
tuple = dtuple_create(heap, 1);
dfield = dtuple_get_nth_field(tuple, 0);
- dfield_set_data(dfield, name, ut_strlen(name));
+ dfield_set_data(dfield, name.m_name, ut_strlen(name.m_name));
dict_index_copy_types(tuple, sys_index, 1);
btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE,
@@ -2340,14 +3088,15 @@ err_exit:
mtr_commit(&mtr);
mem_heap_free(heap);
- return(NULL);
+ DBUG_RETURN(NULL);
}
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_TABLES__NAME, &len);
/* Check if the table name in record is the searched one */
- if (len != ut_strlen(name) || ut_memcmp(name, field, len) != 0) {
+ if (len != ut_strlen(name.m_name)
+ || 0 != ut_memcmp(name.m_name, field, len)) {
goto err_exit;
}
@@ -2356,79 +3105,19 @@ err_exit:
if (err_msg) {
- ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB: %s\n", err_msg);
+ ib::error() << err_msg;
goto err_exit;
}
- char table_name[MAX_FULL_NAME_LEN + 1];
-
- innobase_format_name(table_name, sizeof(table_name), name, FALSE);
-
btr_pcur_close(&pcur);
mtr_commit(&mtr);
- if (table->space == 0) {
- /* The system tablespace is always available. */
- } else if (table->flags2 & DICT_TF2_DISCARDED) {
-
- ib_logf(IB_LOG_LEVEL_WARN,
- "Table '%s' tablespace is set as discarded.",
- table_name);
-
- table->ibd_file_missing = TRUE;
-
- } else if (!fil_space_for_table_exists_in_mem(
- table->space, name, FALSE, FALSE, true, heap,
- table->id)) {
-
- if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_TEMPORARY)) {
- /* Do not bother to retry opening temporary tables. */
- table->ibd_file_missing = TRUE;
-
- } else {
- if (!(ignore_err & DICT_ERR_IGNORE_RECOVER_LOCK)) {
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Failed to find tablespace for "
- "table '%s' in the cache. "
- "Attempting to load the tablespace "
- "with space id %lu.",
- table_name, (ulong) table->space);
- }
-
- /* Use the remote filepath if needed. */
- /* This needs to be added to the tablex1
- from SYS_DATAFILES */
- dict_get_and_save_data_dir_path(table, true);
-
- if (table->data_dir_path) {
- filepath = os_file_make_remote_pathname(
- table->data_dir_path,
- table->name, "ibd");
- }
-
- /* Try to open the tablespace. We set the
- 2nd param (fix_dict = false) here because we
- do not have an x-lock on dict_operation_lock */
- err = fil_open_single_table_tablespace(
- true, false, table->space,
- dict_tf_to_fsp_flags(table->flags),
- name, filepath, table);
-
- if (err != DB_SUCCESS) {
- /* We failed to find a sensible
- tablespace file */
-
- table->ibd_file_missing = TRUE;
- }
- if (filepath) {
- mem_free(filepath);
- }
- }
- }
+ dict_load_tablespace(table, heap, ignore_err);
dict_load_columns(table, heap);
+ dict_load_virtual(table, heap);
+
if (cached) {
dict_table_add_to_cache(table, TRUE, heap);
} else {
@@ -2453,13 +3142,11 @@ err_exit:
/* Refuse to load the table if the table has a corrupted
cluster index */
if (!srv_load_corrupted) {
- fprintf(stderr, "InnoDB: Error: Load table ");
- ut_print_name(stderr, NULL, TRUE, table->name);
- fprintf(stderr, " failed, the table has corrupted"
- " clustered indexes. Turn on"
- " 'innodb_force_load_corrupted'"
- " to drop it\n");
+ ib::error() << "Load table " << table->name
+ << " failed, the table has"
+ " corrupted clustered indexes. Turn on"
+ " 'innodb_force_load_corrupted' to drop it";
dict_table_remove_from_cache(table);
table = NULL;
goto func_exit;
@@ -2473,6 +3160,32 @@ err_exit:
}
}
+ /* We don't trust the table->flags2(retrieved from SYS_TABLES.MIX_LEN
+ field) if the datafiles are from 3.23.52 version. To identify this
+ version, we do the below check and reset the flags. */
+ if (!DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)
+ && table->space == srv_sys_space.space_id()
+ && table->flags == 0) {
+ table->flags2 = 0;
+ }
+
+ DBUG_EXECUTE_IF("ib_table_invalid_flags",
+ if(strcmp(table->name.m_name, "test/t1") == 0) {
+ table->flags2 = 255;
+ table->flags = 255;
+ });
+
+ if (!dict_tf2_is_valid(table->flags, table->flags2)) {
+ ib::error() << "Table " << table->name << " in InnoDB"
+ " data dictionary contains invalid flags."
+ " SYS_TABLES.MIX_LEN=" << table->flags2;
+ table->flags2 &= ~(DICT_TF2_TEMPORARY|DICT_TF2_INTRINSIC);
+ dict_table_remove_from_cache(table);
+ table = NULL;
+ err = DB_FAIL;
+ goto func_exit;
+ }
+
/* Initialize table foreign_child value. Its value could be
changed when dict_load_foreigns() is called below */
table->fk_max_recusive_level = 0;
@@ -2484,19 +3197,20 @@ err_exit:
if (!cached || table->ibd_file_missing) {
/* Don't attempt to load the indexes from disk. */
} else if (err == DB_SUCCESS) {
- err = dict_load_foreigns(table->name, NULL, true, true,
- ignore_err);
+ err = dict_load_foreigns(table->name.m_name, NULL,
+ true, true,
+ ignore_err, fk_tables);
if (err != DB_SUCCESS) {
- ib_logf(IB_LOG_LEVEL_WARN,
- "Load table '%s' failed, the table has missing "
- "foreign key indexes. Turn off "
- "'foreign_key_checks' and try again.",
- table->name);
+ ib::warn() << "Load table " << table->name
+ << " failed, the table has missing"
+ " foreign key indexes. Turn off"
+ " 'foreign_key_checks' and try again.";
dict_table_remove_from_cache(table);
table = NULL;
} else {
+ dict_mem_table_fill_foreign_vcol_set(table);
table->fk_max_recusive_level = 0;
}
} else {
@@ -2547,13 +3261,12 @@ func_exit:
ut_ad(err != DB_SUCCESS || dict_foreign_set_validate(*table));
- return(table);
+ DBUG_RETURN(table);
}
/***********************************************************************//**
Loads a table object based on the table id.
-@return table; NULL if table does not exist */
-UNIV_INTERN
+@return table; NULL if table does not exist */
dict_table_t*
dict_load_table_on_id(
/*==================*/
@@ -2574,7 +3287,7 @@ dict_load_table_on_id(
dict_table_t* table;
mtr_t mtr;
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ ut_ad(mutex_own(&dict_sys->mutex));
table = NULL;
@@ -2634,10 +3347,9 @@ check_rec:
field = rec_get_nth_field_old(rec,
DICT_FLD__SYS_TABLE_IDS__NAME, &len);
/* Load the table definition to memory */
- table = dict_load_table(
- mem_heap_strdupl(
- heap, (char*) field, len),
- TRUE, ignore_err);
+ char* table_name = mem_heap_strdupl(
+ heap, (char*) field, len);
+ table = dict_load_table(table_name, true, ignore_err);
}
}
}
@@ -2649,104 +3361,10 @@ check_rec:
return(table);
}
-/***********************************************************************//**
-Loads a table id based on the index id.
-@return true if found */
-static
-bool
-dict_load_table_id_on_index_id(
-/*==================*/
- index_id_t index_id, /*!< in: index id */
- table_id_t* table_id) /*!< out: table id */
-{
- /* check hard coded indexes */
- switch(index_id) {
- case DICT_TABLES_ID:
- case DICT_COLUMNS_ID:
- case DICT_INDEXES_ID:
- case DICT_FIELDS_ID:
- *table_id = index_id;
- return true;
- case DICT_TABLE_IDS_ID:
- /* The following is a secondary index on SYS_TABLES */
- *table_id = DICT_TABLES_ID;
- return true;
- }
-
- bool found = false;
- mtr_t mtr;
-
- ut_ad(mutex_own(&(dict_sys->mutex)));
-
- /* NOTE that the operation of this function is protected by
- the dictionary mutex, and therefore no deadlocks can occur
- with other dictionary operations. */
-
- mtr_start(&mtr);
-
- btr_pcur_t pcur;
- const rec_t* rec = dict_startscan_system(&pcur, &mtr, SYS_INDEXES);
-
- while (rec) {
- ulint len;
- const byte* field = rec_get_nth_field_old(
- rec, DICT_FLD__SYS_INDEXES__ID, &len);
- ut_ad(len == 8);
-
- /* Check if the index id is the one searched for */
- if (index_id == mach_read_from_8(field)) {
- found = true;
- /* Now we get the table id */
- const byte* field = rec_get_nth_field_old(
- rec,
- DICT_FLD__SYS_INDEXES__TABLE_ID,
- &len);
- *table_id = mach_read_from_8(field);
- break;
- }
- mtr_commit(&mtr);
- mtr_start(&mtr);
- rec = dict_getnext_system(&pcur, &mtr);
- }
-
- btr_pcur_close(&pcur);
- mtr_commit(&mtr);
-
- return(found);
-}
-
-UNIV_INTERN
-dict_table_t*
-dict_table_open_on_index_id(
-/*==================*/
- index_id_t index_id, /*!< in: index id */
- bool dict_locked) /*!< in: dict locked */
-{
- if (!dict_locked) {
- mutex_enter(&dict_sys->mutex);
- }
-
- ut_ad(mutex_own(&dict_sys->mutex));
- table_id_t table_id;
- dict_table_t * table = NULL;
- if (dict_load_table_id_on_index_id(index_id, &table_id)) {
- bool local_dict_locked = true;
- table = dict_table_open_on_id(table_id,
- local_dict_locked,
- DICT_TABLE_OP_LOAD_TABLESPACE);
- }
-
- if (!dict_locked) {
- mutex_exit(&dict_sys->mutex);
- }
- return table;
-}
-
/********************************************************************//**
This function is called when the database is booted. Loads system table
index definitions except for the clustered index which is added to the
dictionary cache at booting before calling this function. */
-UNIV_INTERN
void
dict_load_sys_table(
/*================*/
@@ -2754,7 +3372,7 @@ dict_load_sys_table(
{
mem_heap_t* heap;
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ ut_ad(mutex_own(&dict_sys->mutex));
heap = mem_heap_create(1000);
@@ -2791,7 +3409,7 @@ dict_load_foreign_cols(
mtr_t mtr;
size_t id_len;
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ ut_ad(mutex_own(&dict_sys->mutex));
id_len = strlen(foreign->id);
@@ -2848,20 +3466,21 @@ dict_load_foreign_cols(
rec, DICT_FLD__SYS_FOREIGN_COLS__REF_COL_NAME,
&ref_col_name_len);
- ib_logf(IB_LOG_LEVEL_ERROR,
- "Unable to load columns names for foreign "
- "key '%s' because it was not found in "
- "InnoDB internal table SYS_FOREIGN_COLS. The "
- "closest entry we found is: "
- "(ID='%.*s', POS=%lu, FOR_COL_NAME='%.*s', "
- "REF_COL_NAME='%.*s')",
- foreign->id,
- (int) len, field,
- mach_read_from_4(pos),
- (int) for_col_name_len, for_col_name,
- (int) ref_col_name_len, ref_col_name);
-
- ut_error;
+ ib::fatal sout;
+
+ sout << "Unable to load column names for foreign"
+ " key '" << foreign->id
+ << "' because it was not found in"
+ " InnoDB internal table SYS_FOREIGN_COLS. The"
+ " closest entry we found is:"
+ " (ID='";
+ sout.write(field, len);
+ sout << "', POS=" << mach_read_from_4(pos)
+ << ", FOR_COL_NAME='";
+ sout.write(for_col_name, for_col_name_len);
+ sout << "', REF_COL_NAME='";
+ sout.write(ref_col_name, ref_col_name_len);
+ sout << "')";
}
field = rec_get_nth_field_old(
@@ -2887,8 +3506,9 @@ dict_load_foreign_cols(
}
/***********************************************************************//**
-Loads a foreign key constraint to the dictionary cache.
-@return DB_SUCCESS or error code */
+Loads a foreign key constraint to the dictionary cache. If the referenced
+table is not yet loaded, it is added in the output parameter (fk_tables).
+@return DB_SUCCESS or error code */
static MY_ATTRIBUTE((nonnull(1), warn_unused_result))
dberr_t
dict_load_foreign(
@@ -2906,8 +3526,15 @@ dict_load_foreign(
bool check_charsets,
/*!< in: whether to check charset
compatibility */
- dict_err_ignore_t ignore_err)
+ dict_err_ignore_t ignore_err,
/*!< in: error to be ignored */
+ dict_names_t& fk_tables)
+ /*!< out: the foreign key constraint is added
+ to the dictionary cache only if the referenced
+ table is already in cache. Otherwise, the
+ foreign key constraint is not added to cache,
+ and the referenced table is added to this
+ stack. */
{
dict_foreign_t* foreign;
dict_table_t* sys_foreign;
@@ -2925,7 +3552,11 @@ dict_load_foreign(
dict_table_t* ref_table;
size_t id_len;
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ DBUG_ENTER("dict_load_foreign");
+ DBUG_PRINT("dict_load_foreign",
+ ("id: '%s', check_recursive: %d", id, check_recursive));
+
+ ut_ad(mutex_own(&dict_sys->mutex));
id_len = strlen(id);
@@ -2952,16 +3583,15 @@ dict_load_foreign(
|| rec_get_deleted_flag(rec, 0)) {
/* Not found */
- fprintf(stderr,
- "InnoDB: Error: cannot load foreign constraint "
- "%s: could not find the relevant record in "
- "SYS_FOREIGN\n", id);
+ ib::error() << "Cannot load foreign constraint " << id
+ << ": could not find the relevant record in "
+ << "SYS_FOREIGN";
btr_pcur_close(&pcur);
mtr_commit(&mtr);
mem_heap_free(heap2);
- return(DB_ERROR);
+ DBUG_RETURN(DB_ERROR);
}
field = rec_get_nth_field_old(rec, DICT_FLD__SYS_FOREIGN__ID, &len);
@@ -2969,16 +3599,19 @@ dict_load_foreign(
/* Check if the id in record is the searched one */
if (len != id_len || ut_memcmp(id, field, len) != 0) {
- fprintf(stderr,
- "InnoDB: Error: cannot load foreign constraint "
- "%s: found %.*s instead in SYS_FOREIGN\n",
- id, (int) len, field);
+ {
+ ib::error err;
+ err << "Cannot load foreign constraint " << id
+ << ": found ";
+ err.write(field, len);
+ err << " instead in SYS_FOREIGN";
+ }
btr_pcur_close(&pcur);
mtr_commit(&mtr);
mem_heap_free(heap2);
- return(DB_ERROR);
+ DBUG_RETURN(DB_ERROR);
}
/* Read the table names and the number of columns associated
@@ -3008,6 +3641,8 @@ dict_load_foreign(
foreign->heap, (char*) field, len);
dict_mem_foreign_table_name_lookup_set(foreign, TRUE);
+ const ulint foreign_table_name_len = len;
+
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN__REF_NAME, &len);
foreign->referenced_table_name = mem_heap_strdupl(
@@ -3020,54 +3655,33 @@ dict_load_foreign(
dict_load_foreign_cols(foreign);
ref_table = dict_table_check_if_in_cache_low(
- foreign->referenced_table_name_lookup);
-
- /* We could possibly wind up in a deep recursive calls if
- we call dict_table_get_low() again here if there
- is a chain of tables concatenated together with
- foreign constraints. In such case, each table is
- both a parent and child of the other tables, and
- act as a "link" in such table chains.
- To avoid such scenario, we would need to check the
- number of ancesters the current table has. If that
- exceeds DICT_FK_MAX_CHAIN_LEN, we will stop loading
- the child table.
- Foreign constraints are loaded in a Breath First fashion,
- that is, the index on FOR_NAME is scanned first, and then
- index on REF_NAME. So foreign constrains in which
- current table is a child (foreign table) are loaded first,
- and then those constraints where current table is a
- parent (referenced) table.
- Thus we could check the parent (ref_table) table's
- reference count (fk_max_recusive_level) to know how deep the
- recursive call is. If the parent table (ref_table) is already
- loaded, and its fk_max_recusive_level is larger than
- DICT_FK_MAX_CHAIN_LEN, we will stop the recursive loading
- by skipping loading the child table. It will not affect foreign
- constraint check for DMLs since child table will be loaded
- at that time for the constraint check. */
- if (!ref_table
- || ref_table->fk_max_recusive_level < DICT_FK_MAX_RECURSIVE_LOAD) {
-
- /* If the foreign table is not yet in the dictionary cache, we
- have to load it so that we are able to make type comparisons
- in the next function call. */
-
- for_table = dict_table_get_low(foreign->foreign_table_name_lookup);
-
- if (for_table && ref_table && check_recursive) {
- /* This is to record the longest chain of ancesters
- this table has, if the parent has more ancesters
- than this table has, record it after add 1 (for this
- parent */
- if (ref_table->fk_max_recusive_level
- >= for_table->fk_max_recusive_level) {
- for_table->fk_max_recusive_level =
- ref_table->fk_max_recusive_level + 1;
- }
- }
+ foreign->referenced_table_name_lookup);
+ for_table = dict_table_check_if_in_cache_low(
+ foreign->foreign_table_name_lookup);
+
+ if (!for_table) {
+ /* To avoid recursively loading the tables related through
+ the foreign key constraints, the child table name is saved
+ here. The child table will be loaded later, along with its
+ foreign key constraint. */
+
+ lint old_size = mem_heap_get_size(ref_table->heap);
+
+ ut_a(ref_table != NULL);
+ fk_tables.push_back(
+ mem_heap_strdupl(ref_table->heap,
+ foreign->foreign_table_name_lookup,
+ foreign_table_name_len));
+
+ lint new_size = mem_heap_get_size(ref_table->heap);
+ dict_sys->size += new_size - old_size;
+
+ dict_foreign_remove_from_cache(foreign);
+ DBUG_RETURN(DB_SUCCESS);
}
+ ut_a(for_table || ref_table);
+
/* Note that there may already be a foreign constraint object in
the dictionary cache for this constraint: then the following
call only sets the pointers in it to point to the appropriate table
@@ -3076,18 +3690,21 @@ dict_load_foreign(
a new foreign key constraint but loading one from the data
dictionary. */
- return(dict_foreign_add_to_cache(foreign, col_names, check_charsets,
- ignore_err));
+ DBUG_RETURN(dict_foreign_add_to_cache(foreign, col_names,
+ check_charsets,
+ ignore_err));
}
/***********************************************************************//**
Loads foreign key constraints where the table is either the foreign key
holder or where the table is referenced by a foreign key. Adds these
-constraints to the data dictionary. Note that we know that the dictionary
-cache already contains all constraints where the other relevant table is
-already in the dictionary cache.
-@return DB_SUCCESS or error code */
-UNIV_INTERN
+constraints to the data dictionary.
+
+The foreign key constraint is loaded only if the referenced table is also
+in the dictionary cache. If the referenced table is not in dictionary
+cache, then it is added to the output parameter (fk_tables).
+
+@return DB_SUCCESS or error code */
dberr_t
dict_load_foreigns(
const char* table_name, /*!< in: table name */
@@ -3098,8 +3715,12 @@ dict_load_foreigns(
chained by FK */
bool check_charsets, /*!< in: whether to check
charset compatibility */
- dict_err_ignore_t ignore_err) /*!< in: error to be ignored */
-/*===============*/
+ dict_err_ignore_t ignore_err, /*!< in: error to be ignored */
+ dict_names_t& fk_tables)
+ /*!< out: stack of table
+ names which must be loaded
+ subsequently to load all the
+ foreign key constraints. */
{
ulint tuple_buf[(DTUPLE_EST_ALLOC(1) + sizeof(ulint) - 1)
/ sizeof(ulint)];
@@ -3114,18 +3735,17 @@ dict_load_foreigns(
dberr_t err;
mtr_t mtr;
- ut_ad(mutex_own(&(dict_sys->mutex)));
+ DBUG_ENTER("dict_load_foreigns");
+
+ ut_ad(mutex_own(&dict_sys->mutex));
sys_foreign = dict_table_get_low("SYS_FOREIGN");
if (sys_foreign == NULL) {
/* No foreign keys defined yet in this database */
- fprintf(stderr,
- "InnoDB: Error: no foreign key system tables"
- " in the database\n");
-
- return(DB_ERROR);
+ ib::info() << "No foreign key system tables in the database";
+ DBUG_RETURN(DB_ERROR);
}
ut_ad(!dict_table_is_comp(sys_foreign));
@@ -3139,7 +3759,7 @@ dict_load_foreigns(
ut_ad(!dict_index_is_clust(sec_index));
start_load:
- tuple = dtuple_create_from_mem(tuple_buf, sizeof(tuple_buf), 1);
+ tuple = dtuple_create_from_mem(tuple_buf, sizeof(tuple_buf), 1, 0);
dfield = dtuple_get_nth_field(tuple, 0);
dfield_set_data(dfield, table_name, ut_strlen(table_name));
@@ -3211,12 +3831,13 @@ loop:
/* Load the foreign constraint definition to the dictionary cache */
err = dict_load_foreign(fk_id, col_names,
- check_recursive, check_charsets, ignore_err);
+ check_recursive, check_charsets, ignore_err,
+ fk_tables);
if (err != DB_SUCCESS) {
btr_pcur_close(&pcur);
- return(err);
+ DBUG_RETURN(err);
}
mtr_start(&mtr);
@@ -3245,5 +3866,98 @@ load_next_index:
goto start_load;
}
- return(DB_SUCCESS);
+ DBUG_RETURN(DB_SUCCESS);
+}
+
+/***********************************************************************//**
+Loads a table id based on the index id.
+@return true if found */
+static
+bool
+dict_load_table_id_on_index_id(
+/*===========================*/
+ index_id_t index_id, /*!< in: index id */
+ table_id_t* table_id) /*!< out: table id */
+{
+ /* check hard coded indexes */
+ switch(index_id) {
+ case DICT_TABLES_ID:
+ case DICT_COLUMNS_ID:
+ case DICT_INDEXES_ID:
+ case DICT_FIELDS_ID:
+ *table_id = index_id;
+ return true;
+ case DICT_TABLE_IDS_ID:
+ /* The following is a secondary index on SYS_TABLES */
+ *table_id = DICT_TABLES_ID;
+ return true;
+ }
+
+ bool found = false;
+ mtr_t mtr;
+
+ ut_ad(mutex_own(&(dict_sys->mutex)));
+
+ /* NOTE that the operation of this function is protected by
+ the dictionary mutex, and therefore no deadlocks can occur
+ with other dictionary operations. */
+
+ mtr_start(&mtr);
+
+ btr_pcur_t pcur;
+ const rec_t* rec = dict_startscan_system(&pcur, &mtr, SYS_INDEXES);
+
+ while (rec) {
+ ulint len;
+ const byte* field = rec_get_nth_field_old(
+ rec, DICT_FLD__SYS_INDEXES__ID, &len);
+ ut_ad(len == 8);
+
+ /* Check if the index id is the one searched for */
+ if (index_id == mach_read_from_8(field)) {
+ found = true;
+ /* Now we get the table id */
+ const byte* field = rec_get_nth_field_old(
+ rec,
+ DICT_FLD__SYS_INDEXES__TABLE_ID,
+ &len);
+ *table_id = mach_read_from_8(field);
+ break;
+ }
+ mtr_commit(&mtr);
+ mtr_start(&mtr);
+ rec = dict_getnext_system(&pcur, &mtr);
+ }
+
+ btr_pcur_close(&pcur);
+ mtr_commit(&mtr);
+
+ return(found);
+}
+
+UNIV_INTERN
+dict_table_t*
+dict_table_open_on_index_id(
+/*========================*/
+ index_id_t index_id, /*!< in: index id */
+ bool dict_locked) /*!< in: dict locked */
+{
+ if (!dict_locked) {
+ mutex_enter(&dict_sys->mutex);
+ }
+
+ ut_ad(mutex_own(&dict_sys->mutex));
+ table_id_t table_id;
+ dict_table_t * table = NULL;
+ if (dict_load_table_id_on_index_id(index_id, &table_id)) {
+ bool local_dict_locked = true;
+ table = dict_table_open_on_id(table_id,
+ local_dict_locked,
+ DICT_TABLE_OP_LOAD_TABLESPACE);
+ }
+
+ if (!dict_locked) {
+ mutex_exit(&dict_sys->mutex);
+ }
+ return table;
}