/**************************************************//**
@file dict/dict0load.cc
Loads to the memory cache database object definitions
from dictionary tables
Created 4/24/1996 Heikki Tuuri
*******************************************************/
/** Load an index field definition from a SYS_FIELDS record to dict_index_t. @returnerrormessage
@retval NULL on success */ static constchar*
dict_load_field_low(
byte* index_id, /*!< in/out: index id (8 bytes) an"in"valueifindex!=NULL
and "out" if index == NULL */ bool uncommitted, /*!< in: false=READ COMMITTED,
true=READ UNCOMMITTED */
dict_index_t* index, /*!< in/out: index, could be NULL ifwejustpopulateadict_field_t structwithinformationfrom
a SYS_FIELDS record */
dict_field_t* sys_field, /*!< out: dict_field_t to be
filled */
ulint* pos, /*!< out: Field position */
byte* last_index_id, /*!< in: last index id */
mem_heap_t* heap, /*!< in/out: memory heap
for temporary storage */
mtr_t* mtr, /*!< in/out: mini-transaction */ const rec_t* rec); /*!< in: SYS_FIELDS record */
#ifdef UNIV_DEBUG /****************************************************************//**
Compare the name of an index column.
@returnTRUEif the i'th column of index is 'name'. */ static
ibool
name_of_col_is( /*===========*/ const dict_table_t* table, /*!< in: table */ const dict_index_t* index, /*!< in: index */
ulint i, /*!< in: index field offset */ constchar* name) /*!< in: name to compare to */
{
ulint tmp = dict_col_get_no(dict_field_get_col(
dict_index_get_nth_field(
index, i)));
const rec_t*
dict_getnext_system_low(btr_pcur_t *pcur, mtr_t *mtr)
{
rec_t *rec = nullptr; while (!rec)
{
btr_pcur_move_to_next_user_rec(pcur, mtr);
rec = btr_pcur_get_rec(pcur); if (!btr_pcur_is_on_user_rec(pcur))
{ /* end of index */
btr_pcur_close(pcur); return nullptr;
}
} /* Get a record, let's save the position */
btr_pcur_store_position(pcur, mtr); return rec;
}
/********************************************************************//** This function opens a system table, and returns the first record.
@return first record of the system table */ const rec_t*
dict_startscan_system( /*==================*/
btr_pcur_t* pcur, /*!< out: persistent cursor to
the record */
mtr_t* mtr, /*!< in: the mini-transaction */
dict_table_t* table) /*!< in: system table */
{
btr_pcur_init(pcur); if (pcur->open_leaf(true, table->indexes.start, BTR_SEARCH_LEAF, mtr) !=
DB_SUCCESS) return nullptr; const rec_t *rec; do
rec= dict_getnext_system_low(pcur, mtr); while (rec && rec_get_deleted_flag(rec, 0)); return rec;
}
/********************************************************************//** This function gets the next system table record as it scans the table.
@return the next record if found, NULL if end of scan */ const rec_t*
dict_getnext_system( /*================*/
btr_pcur_t* pcur, /*!< in/out: persistent cursor
to the record */
mtr_t* mtr) /*!< in: the mini-transaction */
{ const rec_t *rec=nullptr; if (pcur->restore_position(BTR_SEARCH_LEAF, mtr) != btr_pcur_t::CORRUPTED) do
rec= dict_getnext_system_low(pcur, mtr); while (rec && rec_get_deleted_flag(rec, 0)); return rec;
}
/********************************************************************//** 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 */ constchar*
dict_process_sys_indexes_rec( /*=========================*/
mem_heap_t* heap, /*!< in/out: heap memory */ const rec_t* rec, /*!< in: current SYS_INDEXES rec */
dict_index_t* index, /*!< out: index to be filled */
table_id_t* table_id) /*!< out: index table id */
{
byte buf[8];
/* Parse the record, and get "dict_index_t" struct filled */ constchar *err_msg= dict_load_index_low(buf, false, heap, rec,
nullptr, nullptr, &index);
*table_id= mach_read_from_8(buf); return err_msg;
}
/********************************************************************//** 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 */ constchar*
dict_process_sys_columns_rec( /*=========================*/
mem_heap_t* heap, /*!< in/out: heap memory */ 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 */ constchar** col_name, /*!< out: column name */
ulint* nth_v_col) /*!< out: if virtual col, this is
record's sequence number */
{ constchar* err_msg;
/* Parse the record, and get "dict_col_t" struct filled */
err_msg = dict_load_column_low(NULL, 0, heap, column,
table_id, col_name, rec, nullptr,
nth_v_col);
return(err_msg);
}
/** This function parses a SYS_VIRTUAL record and extracts virtual column information @param[in]reccurrentSYS_COLUMNSrec @param[in,out]table_idtableid @param[in,out]posvirtualcolumnposition @param[in,out]base_posbasecolumnposition
@return error message, or NULL on success */ constchar*
dict_process_sys_virtual_rec( const rec_t* rec,
table_id_t* table_id,
ulint* pos,
ulint* base_pos)
{ return dict_load_virtual_low(nullptr, false, nullptr, table_id,
pos, base_pos, rec);
}
/********************************************************************//** 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 */ constchar*
dict_process_sys_fields_rec( /*========================*/
mem_heap_t* heap, /*!< in/out: heap memory */ const rec_t* rec, /*!< in: current SYS_FIELDS rec */
dict_field_t* sys_field, /*!< out: dict_field_t to be
filled */
ulint* pos, /*!< out: Field position */
index_id_t* index_id, /*!< out: current index id */
index_id_t last_id) /*!< in: previous index id */
{
byte buf[8];
byte last_index_id[8]; constchar* err_msg;
/********************************************************************//** 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 */ constchar*
dict_process_sys_foreign_rec( /*=========================*/
mem_heap_t* heap, /*!< in/out: heap memory */ const rec_t* rec, /*!< in: current SYS_FOREIGN rec */
dict_foreign_t* foreign) /*!< out: dict_foreign_t struct
to be filled */
{
ulint len; const byte* field;
if (rec_get_deleted_flag(rec, 0)) { return("delete-marked record in SYS_FOREIGN");
}
if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_FOREIGN) { return("wrong number of columns in SYS_FOREIGN record");
}
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN__ID, &len); if (len == 0 || len == UNIV_SQL_NULL) {
err_len: return("incorrect column length in SYS_FOREIGN");
}
/* This receives a dict_foreign_t* that points to a stack variable. Sodict_foreign_free(foreign)isnotusedaselsewhere. Sincetheheapusedhereisfreedelsewhere,foreign->heap
is not assigned. */
foreign->id = mem_heap_strdupl(heap, (constchar*) field, len);
rec_get_nth_field_offs_old(
rec, DICT_FLD__SYS_FOREIGN__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_FOREIGN__DB_ROLL_PTR, &len); if (len != DATA_ROLL_PTR_LEN && len != UNIV_SQL_NULL) { goto err_len;
}
/* The _lookup versions of the referenced and foreign table names
are not assigned since they are not used in this dict_foreign_t */
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN__FOR_NAME, &len); if (len == 0 || len == UNIV_SQL_NULL) { goto err_len;
}
foreign->foreign_table_name = mem_heap_strdupl(
heap, (constchar*) field, len);
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN__REF_NAME, &len); if (len == 0 || len == UNIV_SQL_NULL) { goto err_len;
}
foreign->referenced_table_name = mem_heap_strdupl(
heap, (constchar*) field, len);
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN__N_COLS, &len); if (len != 4) { goto err_len;
}
uint32_t n_fields_and_type = mach_read_from_4(field);
/********************************************************************//** This function parses a SYS_FOREIGN_COLS record and extract necessary
information from the record andreturn to caller.
@return error message, or NULL on success */ constchar*
dict_process_sys_foreign_col_rec( /*=============================*/
mem_heap_t* heap, /*!< in/out: heap memory */ const rec_t* rec, /*!< in: current SYS_FOREIGN_COLS rec */ constchar** name, /*!< out: foreign key constraint name */ constchar** for_col_name, /*!< out: referencing column name */ constchar** ref_col_name, /*!< out: referenced column name
in referenced table */
ulint* pos) /*!< out: column position */
{
ulint len; const byte* field;
if (rec_get_deleted_flag(rec, 0)) { return("delete-marked record in SYS_FOREIGN_COLS");
}
if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_FOREIGN_COLS) { return("wrong number of columns in SYS_FOREIGN_COLS record");
}
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN_COLS__ID, &len); if (len == 0 || len == UNIV_SQL_NULL) {
err_len: return("incorrect column length in SYS_FOREIGN_COLS");
}
*name = mem_heap_strdupl(heap, (char*) field, len);
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN_COLS__POS, &len); if (len != 4) { goto err_len;
}
*pos = mach_read_from_4(field);
rec_get_nth_field_offs_old(
rec, DICT_FLD__SYS_FOREIGN_COLS__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_FOREIGN_COLS__DB_ROLL_PTR, &len); if (len != DATA_ROLL_PTR_LEN && len != UNIV_SQL_NULL) { goto err_len;
}
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN_COLS__FOR_COL_NAME, &len); if (len == 0 || len == UNIV_SQL_NULL) { goto err_len;
}
*for_col_name = mem_heap_strdupl(heap, (char*) field, len);
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN_COLS__REF_COL_NAME, &len); if (len == 0 || len == UNIV_SQL_NULL) { goto err_len;
}
*ref_col_name = mem_heap_strdupl(heap, (char*) field, len);
return(NULL);
}
/** Check the validity of a SYS_TABLES record Makesurethefieldsaretherightlengthandthatthey donotcontaininvalidcontents. @param[in]recSYS_TABLESrecord
@return error message, or NULL on success */ static constchar*
dict_sys_tables_rec_check( const rec_t* rec)
{ const byte* field;
ulint len;
ut_ad(dict_sys.locked());
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;
}
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_TABLES__SPACE, &len); if (field == NULL || len != 4) { goto err_len;
}
return(NULL);
}
/** Check if SYS_TABLES.TYPE is valid @param[in]typeSYS_TABLES.TYPE @param[in]not_redundantwhetherROW_FORMAT=REDUNDANTisnotused
@return whether the SYS_TABLES.TYPE value is valid */ static bool
dict_sys_tables_type_valid(ulint type, bool not_redundant)
{ /* The DATA_DIRECTORY flag can be assigned fully independently
of all other persistent table flags. */
type &= ~DICT_TF_MASK_DATA_DIR;
if (type == 1) { return(true); /* ROW_FORMAT=REDUNDANT or ROW_FORMAT=COMPACT */
}
if (!(type & 1)) { /* For ROW_FORMAT=REDUNDANT and ROW_FORMAT=COMPACT, SYS_TABLES.TYPE=1.Else,itisthesameas dict_table_t::flags,andtheleastsignificantbit
would be set. So, the bit never can be 0. */ return(false);
}
if (!not_redundant) { /* SYS_TABLES.TYPE must be 1 or 1|DICT_TF_MASK_NO_ROLLBACK
for ROW_FORMAT=REDUNDANT. */ return !(type & ~(1U | DICT_TF_MASK_NO_ROLLBACK));
}
if (type >= 1U << DICT_TF_POS_UNUSED) { /* Some unknown bits are set. */ return(false);
}
/* 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);
uint32_t type = mach_read_from_4(field);
if ((type & 0x19f) != 0x101) { /* The table cannot have been created with MariaDB 10.2.2to10.2.6,becausetheywouldwritethe low-orderbitsofSYS_TABLES.TYPEas0b10xx00001for
PAGE_COMPRESSED=YES. No adjustment is applicable. */
} elseif (type >= 3 << 13) { /* 10.2.2 and 10.2.3 write ATOMIC_WRITES less than 3, andnootherflagsabovethatcanbesetforthe SYS_TABLES.TYPEtobeinthe10.2.2..10.2.6format. Thiswouldinanycasebeinvalidformatfor10.2and
earlier releases. */
ut_ad(!dict_sys_tables_type_valid(type, true));
} else { /* SYS_TABLES.TYPE is of the form AALLLL10DB00001. We muststillvalidatethattheLLLLbitsarebetween0
and 9 before we can discard the extraneous 0 bit. */
ut_ad(!DICT_TF_GET_PAGE_COMPRESSION(type));
/* The low order bit of SYS_TABLES.TYPE is always set to 1. But in dict_table_t::flagstheloworderbitisusedtodetermineifthe ROW_FORMAT=REDUNDANT(0)oranythingelse(1). Readthe4byteN_COLSfieldandlookatthehighorderbit.It shouldbesetforCOMPACTandlater.Itshouldnotbesetfor
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);
/* For tables created before MySQL 4.1, there may be garbageinSYS_TABLES.MIX_LENwhereflags2arefound.Suchtables wouldalwaysbeinROW_FORMAT=REDUNDANTwhichdonothavethe highbitsetinn_cols,andflagswouldbezero. MySQL4.1wasthefirstversiontosupportinnodb_file_per_table,
that is, *space_id != 0. */ if (not_redundant || *space_id != 0 || *n_cols & DICT_N_COLS_COMPACT
|| fil_system.sys_space->full_crc32()) {
/* 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);
if (!dict_tf2_is_valid(*flags, *flags2)) {
sql_print_error("InnoDB: Table %.*s in InnoDB" " data dictionary" " contains invalid flags." " SYS_TABLES.TYPE=" UINT32PF " SYS_TABLES.MIX_LEN=" UINT32PF, int(rec_get_field_start_offs(rec, 1)),
rec,
type, *flags2); goto err_exit;
}
/* DICT_TF2_FTS will be set when indexes are being loaded */
*flags2 &= ~DICT_TF2_FTS;
/* Now that we have used this bit, unset it. */
*n_cols &= ~DICT_N_COLS_COMPACT;
} else {
*flags2 = 0;
}
if (UNIV_LIKELY_NULL(heap)) {
mem_heap_free(heap);
}
/* 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)) { continue;
}
/* For tables or partitions using .ibd files, the flag DICT_TF2_USE_FILE_PER_TABLEwasnotsetinMIX_LEN beforeMySQL5.6.5.Theflagshouldnothavebeen introducedinpersistentstorage.MariaDBwillkeep settingtheflagwhenwritingSYS_TABLESentriesfor newlycreatedorrebuilttablesorpartitions,but
will otherwise ignore the flag. */
if (fil_space_for_table_exists_in_mem(space_id, flags)) { continue;
}
if (spaces && spaces->find(uint32_t(space_id))
== spaces->end()) { continue;
}
if (flags2 & DICT_TF2_DISCARDED) {
sql_print_information("InnoDB: Ignoring tablespace" " for %.*s because " "the DISCARD flag is set", static_cast<int>(len), field); continue;
}
/* Check that the .ibd file exists. */ if (fil_ibd_open(space_id, dict_tf_to_fsp_flags(flags),
not_dropped
? fil_space_t::VALIDATE_NOTHING
: fil_space_t::MAYBE_MISSING,
name, filepath)) {
} elseif (!not_dropped) {
} elseif (srv_operation == SRV_OPERATION_NORMAL
&& srv_start_after_restore
&& srv_force_recovery < SRV_FORCE_NO_BACKGROUND
&& dict_table_t::is_temporary_name(filepath)) { /* Mariabackup will not copy files whose namesstartwith#sql-.Thistableoughtto bedroppedbydrop_garbage_tables_after_restore()
a little later. */
} else {
sql_print_warning("InnoDB: Ignoring tablespace for" " %.*s because it" " could not be opened.", static_cast<int>(len), field);
}
max_space_id = ut_max(max_space_id, space_id);
ut_free(filepath);
}
done:
mtr.commit();
fil_set_max_space_id_if_bigger(max_space_id);
dict_sys.unlock();
}
/** Error message for a delete-marked record in dict_load_column_low() */ staticconstchar *dict_load_column_del= "delete-marked record in SYS_COLUMNS"; /** Error message for a missing record in dict_load_column_low() */ staticconstchar *dict_load_column_none= "SYS_COLUMNS record not found"; /** Message for incomplete instant ADD/DROP in dict_load_column_low() */ staticconstchar *dict_load_column_instant= "incomplete instant ADD/DROP";
/* 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() */ staticconstchar *dict_load_virtual_del= "delete-marked record in SYS_VIRTUAL"; staticconstchar *dict_load_virtual_none= "SYS_VIRTUAL record not found";
/* Note: Currently we have one DOC_ID column that is sharedbyallFTSindexesonatable.Andonlynon-virtual
column can be used for FULLTEXT index */ if (Lex_ident_column(Lex_cstring_strlen(name)).
streq(FTS_DOC_ID)
&& nth_v_col == ULINT_UNDEFINED) {
dict_col_t* col; /* As part of normal loading of tables the table->flagisnotsetfortableswithFTS tillaftertheFTSindexesareloaded.Sowe createthefts_tinstancehereifthereisn't onealreadycreated.
Thiscasedoesnotarisefortablecreateas
the flag is set before the table is created. */ if (table->fts == NULL) {
table->fts = fts_create(table);
table->fts->cache = fts_cache_create(table);
DICT_TF2_FLAG_SET(table, DICT_TF2_FTS_AUX_HEX_NAME);
}
ut_a(table->fts->doc_col == ULINT_UNDEFINED);
col = dict_table_get_nth_col(table, i - n_skipped);
/** Loads info from SYS_VIRTUAL for virtual columns. @parammtrmini-transaction @paramtabletabledefinition
@param uncommitted false=READ COMMITTED, true=READ UNCOMMITTED */
MY_ATTRIBUTE((nonnull, warn_unused_result)) static dberr_t dict_load_virtual(mtr_t &mtr, dict_table_t *table, bool uncommitted)
{ for (ulint i= 0; i < table->n_v_cols; i++) if (dberr_t err= dict_load_virtual_col(mtr, table, uncommitted, i)) return err; return DB_SUCCESS;
}
/** Error message for a delete-marked record in dict_load_field_low() */ staticconstchar *dict_load_field_del= "delete-marked record in SYS_FIELDS";
staticconstchar *dict_load_field_none= "SYS_FIELDS record not found";
/** Load an index field definition from a SYS_FIELDS record to dict_index_t. @returnerrormessage
@retval NULL on success */ static constchar*
dict_load_field_low(
byte* index_id, /*!< in/out: index id (8 bytes) an"in"valueifindex!=NULL
and "out" if index == NULL */ bool uncommitted, /*!< in: false=READ COMMITTED,
true=READ UNCOMMITTED */
dict_index_t* index, /*!< in/out: index, could be NULL ifwejustpopulateadict_field_t structwithinformationfrom
a SYS_FIELDS record */
dict_field_t* sys_field, /*!< out: dict_field_t to be
filled */
ulint* pos, /*!< out: Field position */
byte* last_index_id, /*!< in: last index id */
mem_heap_t* heap, /*!< in/out: memory heap
for temporary storage */
mtr_t* mtr, /*!< in/out: mini-transaction */ const rec_t* rec) /*!< in: SYS_FIELDS record */
{ const byte* field;
ulint len; unsigned pos_and_prefix_len; unsigned prefix_len; bool descending; bool first_field;
ulint position;
/* Either index or sys_field is supplied, not both */
ut_ad((!index) != (!sys_field));
ut_ad((!index) == !mtr);
if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_FIELDS) { return("wrong number of columns in SYS_FIELDS record");
}
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FIELDS__INDEX_ID, &len); if (len != 8) {
err_len: return("incorrect column length in SYS_FIELDS");
}
/* The next field stores the field position in the index and a possiblecolumnprefixlengthiftheindexfielddoesnot containthewholecolumn.Thestorageformatislikethis:if thereisatleastoneprefixfieldintheindex,thentheHIGH 2bytescontainthefieldnumber(index->n_def)andthelow2 bytestheprefixlengthforthefield.Otherwisethefield
number (index->n_def) is contained in the 2 LOW bytes. */
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FIELDS__POS, &len); if (len != 4) { goto err_len;
}
for (ulint i = 0; i < index->n_fields; i++) { constchar *err_msg = btr_pcur_is_on_user_rec(&pcur)
? dict_load_field_low(index_id, uncommitted, index,
nullptr, nullptr, nullptr,
heap, &mtr,
btr_pcur_get_rec(&pcur))
: dict_load_field_none;
if (!err_msg) {
} elseif (err_msg == dict_load_field_del) { /* There could be delete marked records in SYS_FIELDSbecauseSYS_FIELDS.INDEX_IDcanbe
updated by ALTER TABLE ADD INDEX. */
} else { if (err_msg != dict_load_field_none
|| strstr(index->table->name.m_name, "/" TEMP_FILE_PREFIX_INNODB)) {
ib::error() << err_msg << " for index "
<< index->name
<< " of table "
<< index->table->name;
}
error = DB_CORRUPTION; break;
}
/** Error message for a delete-marked record in dict_load_index_low() */ staticconstchar *dict_load_index_del= "delete-marked record in SYS_INDEXES"; /** Error message for table->id mismatch in dict_load_index_low() */ staticconstchar *dict_load_index_none= "SYS_INDEXES record not found"; /** Error message for SYS_TABLES flags mismatch in dict_load_table_low() */ staticconstchar *dict_load_table_flags= "incorrect flags in SYS_TABLES";
/** Load an index definition from a SYS_INDEXES record to dict_index_t. @returnerrormessage
@retval NULL on success */ static constchar*
dict_load_index_low(
byte* table_id, /*!< in/out: table id (8 bytes), an"in"valueifmtr
and "out" when !mtr */ bool uncommitted, /*!< in: false=READ COMMITTED,
true=READ UNCOMMITTED */
mem_heap_t* heap, /*!< in/out: temporary memory heap */ const rec_t* rec, /*!< in: SYS_INDEXES record */
mtr_t* mtr, /*!< in/out: mini-transaction, ornullptrifapre-allocated
*index is to be filled in */
dict_table_t* table, /*!< in/out: table, or NULL */
dict_index_t** index) /*!< out,own: index, or NULL */
{ const byte* field;
ulint len;
index_id_t id;
ulint n_fields;
ulint type; unsigned merge_threshold;
merge_threshold = DICT_INDEX_MERGE_THRESHOLD_DEFAULT;
} else { return("wrong number of columns in SYS_INDEXES record");
}
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_INDEXES__TABLE_ID, &len); if (len != 8) {
err_len: return("incorrect column length in SYS_INDEXES");
}
if (!mtr) { /* We are reading a SYS_INDEXES record. Copy the table_id */
memcpy(table_id, (constchar*) field, 8);
} elseif (memcmp(field, table_id, 8)) { /* Caller supplied table_id, verify it is the same
id as on the index record */ return dict_load_index_none;
}
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_INDEXES__ID, &len); if (len != 8) { goto err_len;
}
id = mach_read_from_8(field);
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_INDEXES__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_INDEXES__DB_ROLL_PTR, &len); if (len != DATA_ROLL_PTR_LEN && len != UNIV_SQL_NULL) { goto err_len;
}
/* Check whether the index is corrupted */ if (ignore_err != DICT_ERR_IGNORE_DROP
&& index->is_corrupted() && index->is_clust()) {
dict_mem_index_free(index);
error = DB_TABLE_CORRUPT; goto func_exit;
}
if (index->type & DICT_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);
}
/* 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
| DICT_SPATIAL | DICT_VIRTUAL)) {
ib::error() << "Unknown type " << index->type
<< " of index " << index->name
<< " of table " << table->name;
/* The index was created in memory already at booting
of the database server */
dict_mem_index_free(index);
} else {
error = dict_load_fields(*mtr, index, uncommitted,
heap); if (error != DB_SUCCESS) { goto func_exit;
}
/* The data dictionary tables should never contain invalidindexdefinitions.Ifweignoredthiserror andsimplydidnotloadthisindexdefinition,the .frmfilewoulddisagreewiththeindexdefinitions
inside InnoDB. */ if ((error = dict_index_add_to_cache(index,
index->page))
!= DB_SUCCESS) { goto func_exit;
}
#ifdef UNIV_DEBUG // The following assertion doesn't hold for FTS indexes // as it may have prefix_len=1 with any charset if (index->type != DICT_FTS) { for (uint i = 0; i < index->n_fields; i++) {
dict_field_t &f = index->fields[i];
ut_ad(f.col->mbmaxlen == 0
|| f.prefix_len
% f.col->mbmaxlen == 0);
}
} #endif/* UNIV_DEBUG */
}
next_rec:
btr_pcur_move_to_next_user_rec(&pcur, mtr);
}
if (!dict_table_get_first_index(table)
&& !(ignore_err & DICT_ERR_IGNORE_INDEX)) {
ib::warn() << "No indexes found for table " << table->name;
error = DB_CORRUPTION; goto func_exit;
}
/* If the table contains FTS indexes, populate table->fts->indexes */ 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);
}
func_exit:
mtr->commit(); return error;
}
/** Load a table definition from a SYS_TABLES record to dict_table_t. Donotloadanycolumnsorindexes. @param[in,out]mtrmini-transaction @param[in]uncommittedwhethertouseREADUNCOMMITTEDisolationlevel @param[in]recSYS_TABLESrecord @param[out,own]tabletable,ornullptr @returnerrormessage
@retval nullptr on success */ constchar *dict_load_table_low(mtr_t *mtr, bool uncommitted, const rec_t *rec, dict_table_t **table)
{
table_id_t table_id;
uint32_t space_id, t_num, flags, flags2;
ulint n_cols, n_v_col;
trx_id_t trx_id;
/** Opens a tablespace for dict_load_table_one() @param[in,out]tableAtablethatreferstothetablespacetoopen
@param[in] ignore_err Whether to ignore an error. */
UNIV_INLINE void
dict_load_tablespace(
dict_table_t* table,
dict_err_ignore_t ignore_err)
{
ut_ad(!table->is_temporary());
ut_ad(!table->space);
ut_ad(table->space_id < SRV_SPACE_ID_UPPER_BOUND);
ut_ad(fil_system.sys_space);
if (table->space_id == TRX_SYS_SPACE) {
table->space = fil_system.sys_space; return;
}
if (table->flags2 & DICT_TF2_DISCARDED) {
ib::warn() << "Tablespace for table " << table->name
<< " is set as discarded.";
table->file_unreadable = true; return;
}
/* The tablespace may already be open. */
table->space = fil_space_for_table_exists_in_mem(table->space_id,
table->flags); if (table->space || table->file_unreadable) { return;
}
/* Use the remote filepath if needed. This parameter is optional inthecalltofil_ibd_open().Ifnotsupplied,itwillbebuilt
from the table->name. */ char* filepath = NULL; if (DICT_TF_HAS_DATA_DIR(table->flags)) { /* This will set table->data_dir_path from fil_system */
dict_get_and_save_data_dir_path(table);
if (err != DB_SUCCESS || !btr_pcur_is_on_user_rec(&pcur)) { /* Not found */
err_exit:
mtr.commit();
DBUG_RETURN(nullptr);
}
const rec_t* rec = btr_pcur_get_rec(&pcur);
/* Check if the table name in record is the searched one */ if (rec_get_field_start_offs(rec, 1) != name.size()
|| memcmp(name.data(), rec, name.size())) { goto err_exit;
}
dict_table_t* table; if (constchar* err_msg =
dict_load_table_low(&mtr, uncommitted, rec, &table)) { if (err_msg != dict_load_table_flags) {
ib::error() << err_msg;
} goto err_exit;
} if (!table) { goto err_exit;
}
/* If there is no tablespace for the table then we only need to loadtheindexdefinitions.SothatwecanIMPORTthetablespace later.Whenrecoveringtablelocksforresurrectedincomplete transactions,thetablespaceshouldexist,becauseDDLoperations
were not allowed while the table is being locked by a transaction. */
dict_err_ignore_t index_load_err =
!(ignore_err & DICT_ERR_IGNORE_RECOVER_LOCK)
&& !table->is_readable()
? DICT_ERR_IGNORE_ALL
: ignore_err;
if (err == DB_TABLE_CORRUPT) { /* Refuse to load the table if the table has a corrupted
cluster index */
ut_ad(index_load_err != DICT_ERR_IGNORE_DROP);
ib::error() << "Refusing to load corrupted table "
<< table->name;
evict:
dict_sys.remove(table);
mem_heap_free(heap);
DBUG_RETURN(nullptr);
}
if (err != DB_SUCCESS || !table->is_readable()) {
} elseif (dict_index_t* pk = dict_table_get_first_index(table)) {
ut_ad(pk->is_primary()); if (pk->is_corrupted()
|| pk->page >= table->space->get_size()) {
corrupted:
table->corrupted = true;
table->file_unreadable = true;
err = DB_TABLE_CORRUPT;
} elseif (table->space->id
&& ignore_err == DICT_ERR_IGNORE_DROP) { /* Do not bother to load data from .ibd files
only to delete the .ibd files. */ goto corrupted;
} else {
mtr.start(); bool ok = false; if (buf_block_t* b = buf_page_get(
page_id_t(table->space->id, pk->page),
table->space->zip_size(),
RW_S_LATCH, &mtr)) { switch (mach_read_from_2(FIL_PAGE_TYPE
+ b->page.frame)) { case FIL_PAGE_INDEX: case FIL_PAGE_TYPE_INSTANT:
ok = true;
}
}
mtr.commit(); if (!ok) { goto corrupted;
}
/* We will load the foreign key information only if
all indexes were loaded. */ if (!table->is_readable()) { /* Don't attempt to load the indexes from disk. */
} elseif (err == DB_SUCCESS) { auto i = fk_tables.size();
err = dict_load_foreigns(mtr, table->name.m_name, nullptr, 0, true, ignore_err, fk_tables);
if (err != DB_SUCCESS) {
fk_tables.erase(fk_tables.begin() + i, fk_tables.end());
ib::warn() << "Load table " << table->name
<< " failed, the table has missing" " foreign key indexes. Turn off" " 'foreign_key_checks' and try again."; goto evict;
} else {
dict_mem_table_fill_foreign_vcol_set(table);
}
}
if (table && table->fts) { if (!(dict_table_has_fts_index(table)
|| DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID)
|| DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_ADD_DOC_ID))) { /* the table->fts could be created in dict_load_column whenauserdefinedFTS_DOC_IDispresent,butno
FTS */
table->fts->~fts_t();
table->fts = nullptr;
} elseif (fts_optimize_wq) {
fts_optimize_add_table(table);
} elseif (table->can_be_evicted) { /* fts_optimize_thread is not started yet.
So make the table as non-evictable from cache. */
dict_sys.prevent_eviction(table);
}
}
/***********************************************************************//**
Loads a table object based on the table id.
@return table; NULL if table does not exist */
dict_table_t*
dict_load_table_on_id( /*==================*/
table_id_t table_id, /*!< in: table id */
dict_err_ignore_t ignore_err) /*!< in: errors to ignore
when loading the table */
{
byte id_buf[8];
btr_pcur_t pcur; const byte* field;
ulint len;
mtr_t mtr{nullptr};
ut_ad(dict_sys.locked());
/* NOTE that the operation of this function is protected by dict_sys.latch,andthereforenodeadlockscanoccur
with other dictionary operations. */
mtr.start(); /*---------------------------------------------------*/ /* Get the secondary index based on ID for table SYS_TABLES */
dict_index_t *sys_table_ids =
dict_sys.sys_tables->indexes.start->indexes.next;
/* Write the table id in byte format to id_buf */
mach_write_to_8(id_buf, table_id);
dfield_set_data(&dfield, id_buf, 8);
dict_index_copy_types(&tuple, sys_table_ids, 1);
pcur.btr_cur.page_cur.index = sys_table_ids;
dict_table_t* table = nullptr;
if (btr_pcur_open_on_user_rec(&tuple, BTR_SEARCH_LEAF, &pcur, &mtr)
== DB_SUCCESS
&& btr_pcur_is_on_user_rec(&pcur)) { /*---------------------------------------------------*/ /* Now we have the record in the secondary index
containing the table ID and NAME */ const rec_t* rec = btr_pcur_get_rec(&pcur);
check_rec:
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_TABLE_IDS__ID, &len);
ut_ad(len == 8);
/* Check if the table id in record is the one searched for */ if (table_id == mach_read_from_8(field)) {
field = rec_get_nth_field_old(rec,
DICT_FLD__SYS_TABLE_IDS__NAME, &len);
table = dict_sys.load_table(
{reinterpret_cast<constchar*>(field),
len}, ignore_err); if (table && table->id != table_id) {
ut_ad(rec_get_deleted_flag(rec, 0));
table = nullptr;
} if (!table) { while (btr_pcur_move_to_next(&pcur, &mtr)) {
rec = btr_pcur_get_rec(&pcur);
if (page_rec_is_user_rec(rec)) { goto check_rec;
}
}
}
}
}
mtr.commit(); return table;
}
MY_ATTRIBUTE((nonnull, warn_unused_result)) /********************************************************************//**
Loads foreign key constraint col names (also for the referenced table).
Members that must be set (and valid) in foreign:
foreign->heap
foreign->n_fields
foreign->id ('\0'-terminated)
Members that will be created and set by this function:
foreign->foreign_col_names[i]
foreign->referenced_col_names[i]
(for i=0..foreign->n_fields-1) */ static dberr_t dict_load_foreign_cols(mtr_t &mtr, dict_foreign_t *foreign,
trx_id_t trx_id)
{
btr_pcur_t pcur;
size_t id_len;
if (!for_table) { /* To avoid recursively loading the tables related through theforeignkeyconstraints,thechildtablenameissaved here.Thechildtablewillbeloadedlater,alongwithits
foreign key constraint. */
/* Note that there may already be a foreign constraint object in thedictionarycacheforthisconstraint:thenthefollowing callonlysetsthepointersinittopointtotheappropriatetable andindexobjectsandfreesthenewlycreatedobjectforeign. Addingtothecacheshouldalwayssucceedsincewearenotcreating anewforeignkeyconstraintbutloadingonefromthedata
dictionary. */
/***********************************************************************//**
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.
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(
mtr_t& mtr, /*!< in/out: mini-transaction*/ constchar* table_name, /*!< in: table name */ constchar** col_names, /*!< in: column names, or NULL
to use table->col_names */
trx_id_t trx_id, /*!< in: DDL transaction id, or0tocheck recursiveloadoftables
chained by FK */ bool check_charsets, /*!< in: whether to check
charset compatibility */
dict_err_ignore_t ignore_err, /*!< in: error to be ignored */
dict_names_t& fk_tables) /*!< out: stack of table nameswhichmustbeloaded subsequentlytoloadallthe
foreign key constraints. */
{
btr_pcur_t pcur;
DBUG_ENTER("dict_load_foreigns");
ut_ad(dict_sys.locked());
if (!dict_sys.sys_foreign || !dict_sys.sys_foreign_cols) { if (ignore_err & DICT_ERR_IGNORE_FK_NOKEY) {
DBUG_RETURN(DB_SUCCESS);
}
sql_print_information("InnoDB: No foreign key system tables" " in the database");
DBUG_RETURN(DB_ERROR);
}
if (!btr_pcur_is_on_user_rec(&pcur)) { /* End of index */
goto load_next_index;
}
/* Now we have the record in the secondary index containing a table
name and a foreign constraint ID */
ulint len;
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN_FOR_NAME__NAME, &len);
/* Check if the table name in the record is the one searched for; the followingcalldoesthecomparisoninthelatin1_swedish_ci
charset-collation, in a case-insensitive way. */
/* Since table names in SYS_FOREIGN are stored in a case-insensitive order,wehavetocheckthatthetablenamematchesalsoinabinary stringcomparison.OnUnix,MySQLallowstablenamesthatonlydiffer incharactercase.Iflower_case_table_names=2thenwhatisstored maynotbethesamecase,butthepreviouscomparisonshowedthatthey
match with no-case. */
/* Now we get a foreign key constraint id */
field = rec_get_nth_field_old(
rec, DICT_FLD__SYS_FOREIGN_FOR_NAME__ID, &len);
/* Copy the string because the page may be modified or evicted
after mtr.commit() below. */ char fk_id[MAX_TABLE_NAME_LEN + NAME_LEN];
err = DB_SUCCESS; if (UNIV_LIKELY(len < sizeof fk_id)) {
memcpy(fk_id, field, len);
}
btr_pcur_store_position(&pcur, &mtr);
mtr.commit();
/* Load the foreign constraint definition to the dictionary cache */
switch (err) { case DB_SUCCESS: break; case DB_NOT_FOUND: if (maybe_deleted) { break;
}
sql_print_error("InnoDB: Cannot load foreign constraint %.*s:" " could not find the relevant record in " "SYS_FOREIGN", int(len), fk_id); /* fall through */ default:
corrupted:
ut_free(pcur.old_rec_buf);
DBUG_RETURN(err);
}
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.122Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-10-08)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.