diff options
Diffstat (limited to 'storage/xtradb/dict/dict0load.cc')
-rw-r--r-- | storage/xtradb/dict/dict0load.cc | 3275 |
1 files changed, 0 insertions, 3275 deletions
diff --git a/storage/xtradb/dict/dict0load.cc b/storage/xtradb/dict/dict0load.cc deleted file mode 100644 index 4c3dd47761f..00000000000 --- a/storage/xtradb/dict/dict0load.cc +++ /dev/null @@ -1,3275 +0,0 @@ -/***************************************************************************** - -Copyright (c) 1996, 2016, Oracle and/or its affiliates. All Rights Reserved. -Copyright (c) 2016, 2017, MariaDB Corporation. - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; version 2 of the License. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA - -*****************************************************************************/ - -/**************************************************//** -@file dict/dict0load.cc -Loads to the memory cache database object definitions -from dictionary tables - -Created 4/24/1996 Heikki Tuuri -*******************************************************/ - -#include "dict0load.h" -#include "mysql_version.h" - -#ifdef UNIV_NONINL -#include "dict0load.ic" -#endif - -#include "btr0pcur.h" -#include "btr0btr.h" -#include "page0page.h" -#include "mach0data.h" -#include "dict0dict.h" -#include "dict0boot.h" -#include "dict0stats.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" - -/** Following are the InnoDB system tables. The positions in -this array are referenced by enum dict_system_table_id. */ -static const char* SYSTEM_TABLE_NAME[] = { - "SYS_TABLES", - "SYS_INDEXES", - "SYS_COLUMNS", - "SYS_FIELDS", - "SYS_FOREIGN", - "SYS_FOREIGN_COLS", - "SYS_TABLESPACES", - "SYS_DATAFILES" -}; - -/* 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; - -#ifdef UNIV_DEBUG -/****************************************************************//** -Compare the name of an index column. -@return TRUE if 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 */ - const char* name) /*!< in: name to compare to */ -{ - ulint tmp = dict_col_get_no(dict_field_get_col( - dict_index_get_nth_field( - index, i))); - - return(strcmp(name, dict_table_get_col_name(table, tmp)) == 0); -} -#endif /* UNIV_DEBUG */ - -/********************************************************************//** -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( -/*============================*/ - const char* name) /*!< in: database name which ends in '/' */ -{ - dict_table_t* sys_tables; - btr_pcur_t pcur; - dict_index_t* sys_index; - dtuple_t* tuple; - mem_heap_t* heap; - dfield_t* dfield; - const rec_t* rec; - const byte* field; - ulint len; - mtr_t mtr; - - ut_ad(mutex_own(&(dict_sys->mutex))); - - heap = mem_heap_create(1000); - - 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)); - - tuple = dtuple_create(heap, 1); - dfield = dtuple_get_nth_field(tuple, 0); - - dfield_set_data(dfield, name, ut_strlen(name)); - dict_index_copy_types(tuple, sys_index, 1); - - btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr); -loop: - rec = btr_pcur_get_rec(&pcur); - - if (!btr_pcur_is_on_user_rec(&pcur)) { - /* Not found */ - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap); - - return(NULL); - } - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_TABLES__NAME, &len); - - if (len < strlen(name) - || ut_memcmp(name, field, strlen(name)) != 0) { - /* Not found */ - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap); - - return(NULL); - } - - if (!rec_get_deleted_flag(rec, 0)) { - - /* We found one */ - - char* table_name = mem_strdupl((char*) field, len); - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap); - - return(table_name); - } - - btr_pcur_move_to_next_user_rec(&pcur, &mtr); - - goto 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 */ -static -const rec_t* -dict_getnext_system_low( -/*====================*/ - btr_pcur_t* pcur, /*!< in/out: persistent cursor to the - record*/ - mtr_t* mtr) /*!< in: the mini-transaction */ -{ - rec_t* rec = NULL; - - while (!rec || rec_get_deleted_flag(rec, 0)) { - 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(NULL); - } - } - - /* 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 */ -UNIV_INTERN -const rec_t* -dict_startscan_system( -/*==================*/ - btr_pcur_t* pcur, /*!< out: persistent cursor to - the record */ - mtr_t* mtr, /*!< in: the mini-transaction */ - dict_system_id_t system_id) /*!< in: which system table to open */ -{ - dict_table_t* system_table; - dict_index_t* clust_index; - const rec_t* rec; - - ut_a(system_id < SYS_NUM_SYSTEM_TABLES); - - system_table = dict_table_get_low(SYSTEM_TABLE_NAME[system_id]); - - clust_index = UT_LIST_GET_FIRST(system_table->indexes); - - btr_pcur_open_at_index_side(true, clust_index, BTR_SEARCH_LEAF, pcur, - true, 0, mtr); - - rec = dict_getnext_system_low(pcur, mtr); - - 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 */ -UNIV_INTERN -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; - - /* Restore the position */ - btr_pcur_restore_position(BTR_SEARCH_LEAF, pcur, mtr); - - /* Get the next record */ - rec = dict_getnext_system_low(pcur, mtr); - - return(rec); -} - -/********************************************************************//** -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( -/*=======================================*/ - mem_heap_t* heap, /*!< in/out: temporary memory heap */ - const rec_t* rec, /*!< in: SYS_TABLES record */ - dict_table_t** table, /*!< out: dict_table_t to fill */ - dict_table_info_t status, /*!< in: status bit controls - options such as whether we shall - look for dict_table_t from cache - first */ - mtr_t* mtr) /*!< in/out: mini-transaction, - will be committed */ -{ - ulint len; - const char* field; - const char* err_msg = NULL; - char* table_name; - - field = (const char*) rec_get_nth_field_old( - rec, DICT_FLD__SYS_TABLES__NAME, &len); - - ut_a(!rec_get_deleted_flag(rec, 0)); - - 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); - - /* If DICT_TABLE_LOAD_FROM_CACHE is set, first check - whether there is cached dict_table_t struct */ - if (status & DICT_TABLE_LOAD_FROM_CACHE) { - - /* Commit before load the table again */ - mtr_commit(mtr); - - *table = dict_table_get_low(table_name); - - if (!(*table)) { - err_msg = "Table not found in cache"; - } - } else { - err_msg = dict_load_table_low(table_name, rec, table); - mtr_commit(mtr); - } - - if (err_msg) { - return(err_msg); - } - - return(NULL); -} - -/********************************************************************//** -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( -/*=========================*/ - 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 */ -{ - const char* err_msg; - byte* buf; - - buf = static_cast<byte*>(mem_heap_alloc(heap, 8)); - - /* Parse the record, and get "dict_index_t" struct filled */ - err_msg = dict_load_index_low(buf, NULL, - heap, rec, FALSE, &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 */ -UNIV_INTERN -const char* -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 */ - const char** col_name) /*!< out: column name */ -{ - 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); - - 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( -/*========================*/ - 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; - byte* last_index_id; - const char* err_msg; - - buf = static_cast<byte*>(mem_heap_alloc(heap, 8)); - - last_index_id = static_cast<byte*>(mem_heap_alloc(heap, 8)); - mach_write_to_8(last_index_id, last_id); - - err_msg = dict_load_field_low(buf, NULL, sys_field, - pos, last_index_id, heap, rec); - - *index_id = mach_read_from_8(buf); - - return(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 */ -UNIV_INTERN -const char* -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; - ulint n_fields_and_type; - - 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. - So dict_foreign_free(foreign) is not used as elsewhere. - Since the heap used here is freed elsewhere, foreign->heap - is not assigned. */ - foreign->id = mem_heap_strdupl(heap, (const char*) 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, (const char*) 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, (const char*) field, len); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_FOREIGN__N_COLS, &len); - if (len != 4) { - goto err_len; - } - n_fields_and_type = mach_read_from_4(field); - - foreign->type = (unsigned int) (n_fields_and_type >> 24); - foreign->n_fields = (unsigned int) (n_fields_and_type & 0x3FFUL); - - return(NULL); -} - -/********************************************************************//** -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( -/*=============================*/ - mem_heap_t* heap, /*!< in/out: heap memory */ - const rec_t* rec, /*!< in: current SYS_FOREIGN_COLS rec */ - const char** name, /*!< out: foreign key constraint name */ - const char** for_col_name, /*!< out: referencing column name */ - const char** 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); -} - -/********************************************************************//** -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( -/*=========================*/ - mem_heap_t* heap, /*!< in/out: heap memory */ - const rec_t* rec, /*!< in: current SYS_TABLESPACES rec */ - ulint* space, /*!< out: space id */ - const char** name, /*!< out: tablespace name */ - ulint* flags) /*!< out: tablespace flags */ -{ - ulint len; - const byte* field; - - /* Initialize the output values */ - *space = ULINT_UNDEFINED; - *name = NULL; - *flags = ULINT_UNDEFINED; - - if (rec_get_deleted_flag(rec, 0)) { - return("delete-marked record in SYS_TABLESPACES"); - } - - if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_TABLESPACES) { - return("wrong number of columns in SYS_TABLESPACES record"); - } - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_TABLESPACES__SPACE, &len); - if (len != DICT_FLD_LEN_SPACE) { -err_len: - return("incorrect column length in SYS_TABLESPACES"); - } - *space = mach_read_from_4(field); - - rec_get_nth_field_offs_old( - rec, DICT_FLD__SYS_TABLESPACES__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_TABLESPACES__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_TABLESPACES__NAME, &len); - if (len == 0 || len == UNIV_SQL_NULL) { - goto err_len; - } - *name = mem_heap_strdupl(heap, (char*) field, len); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_TABLESPACES__FLAGS, &len); - if (len != DICT_FLD_LEN_FLAGS) { - goto err_len; - } - *flags = mach_read_from_4(field); - - return(NULL); -} - -/********************************************************************//** -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( -/*=======================*/ - mem_heap_t* heap, /*!< in/out: heap memory */ - const rec_t* rec, /*!< in: current SYS_DATAFILES rec */ - ulint* space, /*!< out: space id */ - const char** path) /*!< out: datafile paths */ -{ - ulint len; - const byte* field; - - if (rec_get_deleted_flag(rec, 0)) { - return("delete-marked record in SYS_DATAFILES"); - } - - if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_DATAFILES) { - return("wrong number of columns in SYS_DATAFILES record"); - } - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_DATAFILES__SPACE, &len); - if (len != DICT_FLD_LEN_SPACE) { -err_len: - return("incorrect column length in SYS_DATAFILES"); - } - *space = mach_read_from_4(field); - - rec_get_nth_field_offs_old( - rec, DICT_FLD__SYS_DATAFILES__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_DATAFILES__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_DATAFILES__PATH, &len); - if (len == 0 || len == UNIV_SQL_NULL) { - goto err_len; - } - *path = mem_heap_strdupl(heap, (char*) field, 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 -char* -dict_get_first_path( -/*================*/ - ulint space, /*!< in: space id */ - const char* name) /*!< in: tablespace name */ -{ - mtr_t mtr; - dict_table_t* sys_datafiles; - 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* dict_filepath = NULL; - mem_heap_t* heap = mem_heap_create(1024); - - 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")); - ut_ad(name_of_col_is(sys_datafiles, sys_index, - DICT_FLD__SYS_DATAFILES__PATH, "PATH")); - - tuple = dtuple_create(heap, 1); - 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); - - dfield_set_data(dfield, buf, 4); - dict_index_copy_types(tuple, sys_index, 1); - - btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr); - - 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. */ - - 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); - } - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap); - - return(dict_filepath); -} - -/********************************************************************//** -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 -dberr_t -dict_update_filepath( -/*=================*/ - ulint space_id, /*!< in: space id */ - const char* filepath) /*!< in: filepath */ -{ - 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))); - - trx = trx_allocate_for_background(); - trx->op_info = "update filepath"; - trx->dict_operation_lock_mode = RW_X_LATCH; - trx_start_for_ddl(trx, TRX_DICT_OP_INDEX); - - pars_info_t* info = pars_info_create(); - - pars_info_add_int4_literal(info, "space", space_id); - pars_info_add_str_literal(info, "path", filepath); - - err = que_eval_sql(info, - "PROCEDURE UPDATE_FILEPATH () IS\n" - "BEGIN\n" - "UPDATE SYS_DATAFILES" - " SET PATH = :path\n" - " WHERE SPACE = :space;\n" - "END;\n", FALSE, trx); - - trx_commit_for_mysql(trx); - trx->dict_operation_lock_mode = 0; - trx_free_for_background(trx); - - 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); - } 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); - } - - return(err); -} - -/********************************************************************//** -Insert records into SYS_TABLESPACES and SYS_DATAFILES. -@return DB_SUCCESS if OK, dberr_t if the insert failed */ -UNIV_INTERN -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 */ -{ - 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(filepath); - - trx = trx_allocate_for_background(); - trx->op_info = "insert tablespace and filepath"; - trx->dict_operation_lock_mode = RW_X_LATCH; - trx_start_for_ddl(trx, TRX_DICT_OP_INDEX); - - /* 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); - - trx_commit_for_mysql(trx); - trx->dict_operation_lock_mode = 0; - trx_free_for_background(trx); - - return(err); -} - -/* Set by Xtrabackup */ -my_bool (*dict_check_if_skip_table)(const char* name) = 0; - - -/********************************************************************//** -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. - -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(). - -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 */ -{ - dict_table_t* sys_tables; - dict_index_t* sys_index; - 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)); - - 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)); - - 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); - - 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); - - rec = btr_pcur_get_rec(&pcur); - - if (!btr_pcur_is_on_user_rec(&pcur)) { - /* end of index */ - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - - /* We must make the tablespace cache aware of the biggest - known space id */ - - /* printf("Biggest space id in data dictionary %lu\n", - max_space_id); */ - fil_set_max_space_id_if_bigger(max_space_id); - - mutex_exit(&(dict_sys->mutex)); - rw_lock_x_unlock(&dict_operation_lock); - - return; - } - - if (!rec_get_deleted_flag(rec, 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__NAME, &len); - - name = mem_strdupl((char*) field, len); - - char table_name[MAX_FULL_NAME_LEN + 1]; - - innobase_format_name( - table_name, sizeof(table_name), name, 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; - } - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_TABLES__SPACE, &len); - ut_a(len == 4); - - space_id = mach_read_from_4(field); - - btr_pcur_store_position(&pcur, &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. */ - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_TABLES__MIX_LEN, &len); - - bool is_temp = false; - bool discarded = false; - bool print_error_if_does_not_exist; - bool remove_from_data_dict_if_does_not_exist; - - ib_uint32_t flags2 = static_cast<ib_uint32_t>( - mach_read_from_4(field)); - - /* 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. */ - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_TABLES__N_COLS, &len); - - /* MIX_LEN valid only for ROW_FORMAT > REDUNDANT. */ - if (mach_read_from_4(field) & DICT_N_COLS_COMPACT) { - - is_temp = !!(flags2 & DICT_TF2_TEMPORARY); - discarded = !!(flags2 & DICT_TF2_DISCARDED); - } - - if (space_id == 0) { - /* The system tablespace always exists. */ - ut_ad(!discarded); - mem_free(name); - goto loop; - } - - - ut_a(!IS_XTRABACKUP() || dict_check_if_skip_table); - - if (is_temp || discarded || - (IS_XTRABACKUP() && dict_check_if_skip_table(name))) { - print_error_if_does_not_exist = false; - } - else { - print_error_if_does_not_exist = true; - } - - remove_from_data_dict_if_does_not_exist = IS_XTRABACKUP() && !(is_temp || discarded); - - mtr_commit(&mtr); - - 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, print_error_if_does_not_exist, - remove_from_data_dict_if_does_not_exist , false, NULL, 0, flags) - && !(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); - } - break; - - 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, flags)) { - 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; - } - - /* 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 */ - - /* Use the remote filepath if known. */ - char* filepath = NULL; - if (DICT_TF_HAS_DATA_DIR(flags)) { - filepath = dict_get_first_path( - space_id, name); - } - - /* We could read page 0 to get (optional) IV - if encryption is turned on, if it's off - we will read the page 0 later and find out - if we should decrypt a potentially - already encrypted table - bool read_page_0 = srv_encrypt_tables; */ - - bool read_page_0 = false; - - /* 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); - - if (err != DB_SUCCESS) { - ib_logf(IB_LOG_LEVEL_ERROR, - "Tablespace open failed for '%s', " - "ignored.", table_name); - } - - if (filepath) { - mem_free(filepath); - } - - break; - } - - if (space_id > max_space_id) { - max_space_id = space_id; - } - - mem_free(name); - mtr_start(&mtr); - - btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); - } - - goto loop; -} - -/********************************************************************//** -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( -/*=================*/ - dict_table_t* table, /*!< in/out: table, could be NULL - if we just populate a dict_column_t - struct with information from - a SYS_COLUMNS record */ - mem_heap_t* heap, /*!< in/out: memory heap - for temporary storage */ - dict_col_t* column, /*!< out: dict_column_t to fill, - 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 */ -{ - char* name; - const byte* field; - ulint len; - ulint mtype; - ulint prtype; - ulint col_len; - ulint pos; - - ut_ad(table || column); - - if (rec_get_deleted_flag(rec, 0)) { - return("delete-marked record in SYS_COLUMNS"); - } - - if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_COLUMNS) { - return("wrong number of columns in SYS_COLUMNS record"); - } - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_COLUMNS__TABLE_ID, &len); - if (len != 8) { -err_len: - return("incorrect column length in SYS_COLUMNS"); - } - - if (table_id) { - *table_id = mach_read_from_8(field); - } else if (table->id != mach_read_from_8(field)) { - return("SYS_COLUMNS.TABLE_ID mismatch"); - } - - 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) { - goto err_len; - } - rec_get_nth_field_offs_old( - rec, DICT_FLD__SYS_COLUMNS__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_COLUMNS__NAME, &len); - if (len == 0 || len == UNIV_SQL_NULL) { - goto err_len; - } - - name = mem_heap_strdupl(heap, (const char*) field, len); - - if (col_name) { - *col_name = name; - } - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_COLUMNS__MTYPE, &len); - if (len != 4) { - goto err_len; - } - - mtype = mach_read_from_4(field); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_COLUMNS__PRTYPE, &len); - if (len != 4) { - goto err_len; - } - prtype = mach_read_from_4(field); - - if (dtype_get_charset_coll(prtype) == 0 - && dtype_is_string_type(mtype)) { - /* The table was created with < 4.1.2. */ - - if (dtype_is_binary_string_type(mtype, prtype)) { - /* Use the binary collation for - string columns of binary type. */ - - prtype = dtype_form_prtype( - prtype, - DATA_MYSQL_BINARY_CHARSET_COLL); - } else { - /* Use the default charset for - other than binary columns. */ - - prtype = dtype_form_prtype( - prtype, - data_mysql_default_charset_coll); - } - } - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_COLUMNS__LEN, &len); - if (len != 4) { - goto err_len; - } - col_len = mach_read_from_4(field); - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_COLUMNS__PREC, &len); - if (len != 4) { - goto err_len; - } - - if (!column) { - dict_mem_table_add_col(table, heap, name, mtype, - prtype, col_len); - } else { - dict_mem_fill_column_struct(column, pos, mtype, - prtype, col_len); - } - - return(NULL); -} - -/********************************************************************//** -Loads definitions for table columns. */ -static -void -dict_load_columns( -/*==============*/ - dict_table_t* table, /*!< in/out: table */ - mem_heap_t* heap) /*!< in/out: memory heap - for temporary storage */ -{ - dict_table_t* sys_columns; - dict_index_t* sys_index; - btr_pcur_t pcur; - dtuple_t* tuple; - dfield_t* dfield; - const rec_t* rec; - byte* buf; - ulint i; - mtr_t mtr; - - ut_ad(mutex_own(&(dict_sys->mutex))); - - mtr_start(&mtr); - - sys_columns = dict_table_get_low("SYS_COLUMNS"); - sys_index = UT_LIST_GET_FIRST(sys_columns->indexes); - ut_ad(!dict_table_is_comp(sys_columns)); - - ut_ad(name_of_col_is(sys_columns, sys_index, - DICT_FLD__SYS_COLUMNS__NAME, "NAME")); - ut_ad(name_of_col_is(sys_columns, sys_index, - DICT_FLD__SYS_COLUMNS__PREC, "PREC")); - - tuple = dtuple_create(heap, 1); - 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); - dict_index_copy_types(tuple, sys_index, 1); - - 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++) { - const char* err_msg; - const char* name = NULL; - - 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); - - if (err_msg) { - fprintf(stderr, "InnoDB: %s\n", err_msg); - ut_error; - } - - /* Note: Currently we have one DOC_ID column that is - shared by all FTS indexes on a table. */ - if (innobase_strcasecmp(name, - FTS_DOC_ID_COL_NAME) == 0) { - dict_col_t* col; - /* As part of normal loading of tables the - table->flag is not set for tables with FTS - till after the FTS indexes are loaded. So we - create the fts_t instance here if there isn't - one already created. - - This case does not arise for table create as - the flag is set before the table is created. */ - if (table->fts == NULL) { - table->fts = fts_create(table); - fts_optimize_add_table(table); - } - - ut_a(table->fts->doc_col == ULINT_UNDEFINED); - - col = dict_table_get_nth_col(table, i); - - ut_ad(col->len == sizeof(doc_id_t)); - - if (col->prtype & DATA_FTS_DOC_ID) { - DICT_TF2_FLAG_SET( - table, DICT_TF2_FTS_HAS_DOC_ID); - DICT_TF2_FLAG_UNSET( - table, DICT_TF2_FTS_ADD_DOC_ID); - } - - table->fts->doc_col = i; - } - - btr_pcur_move_to_next_user_rec(&pcur, &mtr); - } - - btr_pcur_close(&pcur); - mtr_commit(&mtr); -} - -/** 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"; - -/********************************************************************//** -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( -/*================*/ - byte* index_id, /*!< in/out: index id (8 bytes) - an "in" value if index != NULL - and "out" if index == NULL */ - dict_index_t* index, /*!< in/out: index, could be NULL - if we just populate a dict_field_t - struct with information from - 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 */ - const rec_t* rec) /*!< in: SYS_FIELDS record */ -{ - const byte* field; - ulint len; - ulint pos_and_prefix_len; - ulint prefix_len; - ibool first_field; - ulint position; - - /* Either index or sys_field is supplied, not both */ - ut_a((!index) || (!sys_field)); - - if (rec_get_deleted_flag(rec, 0)) { - return(dict_load_field_del); - } - - 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"); - } - - if (!index) { - ut_a(last_index_id); - memcpy(index_id, (const char*) field, 8); - first_field = memcmp(index_id, last_index_id, 8); - } else { - first_field = (index->n_def == 0); - if (memcmp(field, index_id, 8)) { - return("SYS_FIELDS.INDEX_ID mismatch"); - } - } - - /* The next field stores the field position in the index and a - possible column prefix length if the index field does not - contain the whole column. The storage format is like this: if - there is at least one prefix field in the index, then the HIGH - 2 bytes contain the field number (index->n_def) and the low 2 - bytes the prefix length for the field. Otherwise the field - 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; - } - - pos_and_prefix_len = mach_read_from_4(field); - - if (index && UNIV_UNLIKELY - ((pos_and_prefix_len & 0xFFFFUL) != index->n_def - && (pos_and_prefix_len >> 16 & 0xFFFF) != index->n_def)) { - return("SYS_FIELDS.POS mismatch"); - } - - if (first_field || pos_and_prefix_len > 0xFFFFUL) { - prefix_len = pos_and_prefix_len & 0xFFFFUL; - position = (pos_and_prefix_len & 0xFFFF0000UL) >> 16; - } else { - prefix_len = 0; - position = pos_and_prefix_len & 0xFFFFUL; - } - - rec_get_nth_field_offs_old( - rec, DICT_FLD__SYS_FIELDS__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_FIELDS__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_FIELDS__COL_NAME, &len); - if (len == 0 || len == UNIV_SQL_NULL) { - goto err_len; - } - - if (index) { - dict_mem_index_add_field( - index, mem_heap_strdupl(heap, (const char*) field, len), - prefix_len); - } else { - ut_a(sys_field); - ut_a(pos); - - sys_field->name = mem_heap_strdupl( - heap, (const char*) field, len); - sys_field->prefix_len = prefix_len; - *pos = position; - } - - return(NULL); -} - -/********************************************************************//** -Loads definitions for index fields. -@return DB_SUCCESS if ok, DB_CORRUPTION if corruption */ -static -ulint -dict_load_fields( -/*=============*/ - dict_index_t* index, /*!< in/out: index whose fields to load */ - mem_heap_t* heap) /*!< in: memory heap for temporary storage */ -{ - dict_table_t* sys_fields; - dict_index_t* sys_index; - btr_pcur_t pcur; - dtuple_t* tuple; - dfield_t* dfield; - const rec_t* rec; - byte* buf; - ulint i; - mtr_t mtr; - dberr_t error; - - ut_ad(mutex_own(&(dict_sys->mutex))); - - mtr_start(&mtr); - - sys_fields = dict_table_get_low("SYS_FIELDS"); - sys_index = UT_LIST_GET_FIRST(sys_fields->indexes); - ut_ad(!dict_table_is_comp(sys_fields)); - ut_ad(name_of_col_is(sys_fields, sys_index, - DICT_FLD__SYS_FIELDS__COL_NAME, "COL_NAME")); - - tuple = dtuple_create(heap, 1); - dfield = dtuple_get_nth_field(tuple, 0); - - buf = static_cast<byte*>(mem_heap_alloc(heap, 8)); - mach_write_to_8(buf, index->id); - - dfield_set_data(dfield, buf, 8); - dict_index_copy_types(tuple, sys_index, 1); - - btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr); - for (i = 0; i < index->n_fields; i++) { - const char* err_msg; - - rec = btr_pcur_get_rec(&pcur); - - ut_a(btr_pcur_is_on_user_rec(&pcur)); - - err_msg = dict_load_field_low(buf, index, NULL, NULL, NULL, - heap, rec); - - if (err_msg == dict_load_field_del) { - /* There could be delete marked records in - SYS_FIELDS because SYS_FIELDS.INDEX_ID can be - updated by ALTER TABLE ADD INDEX. */ - - goto next_rec; - } else if (err_msg) { - fprintf(stderr, "InnoDB: %s\n", err_msg); - error = DB_CORRUPTION; - goto func_exit; - } -next_rec: - btr_pcur_move_to_next_user_rec(&pcur, &mtr); - } - - error = DB_SUCCESS; -func_exit: - btr_pcur_close(&pcur); - mtr_commit(&mtr); - return(error); -} - -/** Error message for a delete-marked record in dict_load_index_low() */ -static const char* dict_load_index_del = "delete-marked record in SYS_INDEXES"; -/** Error message for table->id mismatch in dict_load_index_low() */ -static const char* dict_load_index_id_err = "SYS_INDEXES.TABLE_ID mismatch"; - -/********************************************************************//** -Loads an index definition from a SYS_INDEXES record to dict_index_t. -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( -/*================*/ - byte* table_id, /*!< in/out: table id (8 bytes), - an "in" value if allocate=TRUE - and "out" when allocate=FALSE */ - const char* table_name, /*!< in: table name */ - mem_heap_t* heap, /*!< in/out: temporary memory heap */ - const rec_t* rec, /*!< in: SYS_INDEXES record */ - ibool allocate, /*!< in: TRUE=allocate *index, - FALSE=fill in a pre-allocated - *index */ - dict_index_t** index) /*!< out,own: index, or NULL */ -{ - const byte* field; - ulint len; - ulint name_len; - char* name_buf; - index_id_t id; - ulint n_fields; - ulint type; - ulint space; - - if (allocate) { - /* If allocate=TRUE, no dict_index_t will - be supplied. Initialize "*index" to NULL */ - *index = NULL; - } - - if (rec_get_deleted_flag(rec, 0)) { - return(dict_load_index_del); - } - - if (rec_get_n_fields_old(rec) != DICT_NUM_FIELDS__SYS_INDEXES) { - 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 (!allocate) { - /* We are reading a SYS_INDEXES record. Copy the table_id */ - memcpy(table_id, (const char*) field, 8); - } else if (memcmp(field, table_id, 8)) { - /* Caller supplied table_id, verify it is the same - id as on the index record */ - return(dict_load_index_id_err); - } - - 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); - - rec_get_nth_field_offs_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; - } - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_INDEXES__NAME, &name_len); - if (name_len == UNIV_SQL_NULL) { - goto err_len; - } - - name_buf = mem_heap_strdupl(heap, (const char*) field, - name_len); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_INDEXES__N_FIELDS, &len); - if (len != 4) { - goto err_len; - } - n_fields = mach_read_from_4(field); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_INDEXES__TYPE, &len); - if (len != 4) { - goto err_len; - } - type = mach_read_from_4(field); - if (type & (~0U << DICT_IT_BITS)) { - return("unknown SYS_INDEXES.TYPE bits"); - } - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_INDEXES__SPACE, &len); - if (len != 4) { - goto err_len; - } - space = mach_read_from_4(field); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_INDEXES__PAGE_NO, &len); - if (len != 4) { - goto err_len; - } - - if (allocate) { - *index = dict_mem_index_create(table_name, name_buf, - space, type, n_fields); - } else { - ut_a(*index); - - dict_mem_fill_index_struct(*index, NULL, NULL, name_buf, - space, type, n_fields); - } - - (*index)->id = id; - (*index)->page = mach_read_from_4(field); - btr_search_index_init(*index); - ut_ad((*index)->page); - - return(NULL); -} - -/********************************************************************//** -Loads definitions for table indexes. Adds them to the data dictionary -cache. -@return DB_SUCCESS if ok, DB_CORRUPTION if corruption of dictionary -table or DB_UNSUPPORTED if table has unknown index type */ -static MY_ATTRIBUTE((nonnull)) -dberr_t -dict_load_indexes( -/*==============*/ - dict_table_t* table, /*!< in/out: table */ - mem_heap_t* heap, /*!< in: memory heap for temporary storage */ - dict_err_ignore_t ignore_err) - /*!< in: error to be ignored when - loading the index definition */ -{ - dict_table_t* sys_indexes; - dict_index_t* sys_index; - btr_pcur_t pcur; - dtuple_t* tuple; - dfield_t* dfield; - const rec_t* rec; - byte* buf; - mtr_t mtr; - dberr_t error = DB_SUCCESS; - - ut_ad(mutex_own(&(dict_sys->mutex))); - - mtr_start(&mtr); - - sys_indexes = dict_table_get_low("SYS_INDEXES"); - sys_index = UT_LIST_GET_FIRST(sys_indexes->indexes); - ut_ad(!dict_table_is_comp(sys_indexes)); - ut_ad(name_of_col_is(sys_indexes, sys_index, - DICT_FLD__SYS_INDEXES__NAME, "NAME")); - ut_ad(name_of_col_is(sys_indexes, sys_index, - DICT_FLD__SYS_INDEXES__PAGE_NO, "PAGE_NO")); - - tuple = dtuple_create(heap, 1); - 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); - dict_index_copy_types(tuple, sys_index, 1); - - btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr); - for (;;) { - dict_index_t* index = NULL; - const char* err_msg; - - if (!btr_pcur_is_on_user_rec(&pcur)) { - - /* We should allow the table to open even - without index when DICT_ERR_IGNORE_CORRUPT is set. - DICT_ERR_IGNORE_CORRUPT is currently only set - 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); - error = DB_CORRUPTION; - goto func_exit; - } - - break; - } - - 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) { - 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)) { - /* Skip indexes whose name starts with - TEMP_INDEX_PREFIX, because they will - be dropped during crash recovery. */ - goto next_rec; - } - } - - err_msg = dict_load_index_low(buf, table->name, heap, rec, - TRUE, &index); - ut_ad((index == NULL && err_msg != NULL) - || (index != NULL && err_msg == NULL)); - - if (err_msg == dict_load_index_id_err) { - /* TABLE_ID mismatch means that we have - run out of index definitions for the table. */ - - 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); - error = DB_CORRUPTION; - goto func_exit; - } - - break; - } else if (err_msg == dict_load_index_del) { - /* Skip delete-marked records. */ - goto next_rec; - } else if (err_msg) { - fprintf(stderr, "InnoDB: %s\n", err_msg); - if (ignore_err & DICT_ERR_IGNORE_CORRUPT) { - goto next_rec; - } - error = DB_CORRUPTION; - goto func_exit; - } - - ut_ad(index); - - /* 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); - - if (!srv_load_corrupted - && !(ignore_err & DICT_ERR_IGNORE_CORRUPT) - && dict_index_is_clust(index)) { - dict_mem_index_free(index); - - error = DB_INDEX_CORRUPT; - goto func_exit; - } else { - /* We will load the index if - 1) srv_load_corrupted is TRUE - 2) ignore_err is set with - 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); - } - } - - if (index->type & DICT_FTS - && !DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS)) { - /* 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)) { - ib_logf(IB_LOG_LEVEL_ERROR, - "Unknown type %lu of index %s of table %s", - (ulong) index->type, index->name, table->name); - - error = DB_UNSUPPORTED; - dict_mem_index_free(index); - goto func_exit; - } else if (index->page == FIL_NULL - && !table->file_unreadable - && (!(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); - - if (ignore_err & DICT_ERR_IGNORE_INDEX_ROOT) { - /* If caller can tolerate this error, - we will continue to load the index and - let caller deal with this error. However - mark the index and table corrupted. We - only need to mark such in the index - 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); - - fprintf(stderr, - "InnoDB: Index is corrupt but forcing" - " load into data dictionary\n"); - } else { -corrupted: - dict_mem_index_free(index); - error = DB_CORRUPTION; - goto func_exit; - } - } 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); - - goto corrupted; - } else if (dict_is_sys_table(table->id) - && (dict_index_is_clust(index) - || ((table == dict_sys->sys_tables) - && !strcmp("ID_IND", index->name)))) { - - /* The index was created in memory already at booting - of the database server */ - dict_mem_index_free(index); - } else { - dict_load_fields(index, heap); - - error = dict_index_add_to_cache( - table, index, index->page, FALSE); - - /* The data dictionary tables should never contain - invalid index definitions. If we ignored this error - and simply did not load this index definition, the - .frm file would disagree with the index definitions - inside InnoDB. */ - if (UNIV_UNLIKELY(error != DB_SUCCESS)) { - - goto func_exit; - } - } -next_rec: - btr_pcur_move_to_next_user_rec(&pcur, &mtr); - } - - /* If the table contains FTS indexes, populate table->fts->indexes */ - if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS)) { - /* 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: - btr_pcur_close(&pcur); - mtr_commit(&mtr); - - return(error); -} - -/********************************************************************//** -Loads a table definition from a SYS_TABLES record to dict_table_t. -Does not load any columns or indexes. -@return error message, or NULL on success */ -UNIV_INTERN -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 */ -{ - const byte* field; - ulint len; - ulint space; - ulint n_cols; - ulint flags = 0; - ulint flags2; - - 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; - } - - space = mach_read_from_4(field); - - /* Check if the tablespace exists and has the right name */ - flags = dict_sys_tables_get_flags(rec); - - 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); - 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; - } - - /* 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); - - (*table)->file_unreadable = false; - - return(NULL); -} - -/********************************************************************//** -Using the table->heap, copy the null-terminated filepath into -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_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; - } - 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 -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 */ -{ - bool is_temp = DICT_TF2_FLAG_IS_SET(table, DICT_TF2_TEMPORARY); - - if (!is_temp && !table->data_dir_path && table->space) { - char* path = fil_space_get_first_path(table->space); - - if (!dict_mutex_own) { - dict_mutex_enter_for_mysql(); - } - if (!path) { - path = dict_get_first_path( - table->space, table->name); - } - - if (path) { - table->flags |= (1 << DICT_TF_POS_DATA_DIR); - dict_save_data_dir_path(table, path); - mem_free(path); - } - - 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. -@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 -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 */ - dict_err_ignore_t ignore_err) - /*!< in: error to be ignored when loading - table and its indexes' definition */ -{ - dberr_t err; - dict_table_t* table; - dict_table_t* sys_tables; - btr_pcur_t pcur; - dict_index_t* sys_index; - dtuple_t* tuple; - mem_heap_t* heap; - dfield_t* dfield; - 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))); - - heap = mem_heap_create(32000); - - 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)); - ut_ad(name_of_col_is(sys_tables, sys_index, - DICT_FLD__SYS_TABLES__ID, "ID")); - ut_ad(name_of_col_is(sys_tables, sys_index, - DICT_FLD__SYS_TABLES__N_COLS, "N_COLS")); - ut_ad(name_of_col_is(sys_tables, sys_index, - DICT_FLD__SYS_TABLES__TYPE, "TYPE")); - ut_ad(name_of_col_is(sys_tables, sys_index, - DICT_FLD__SYS_TABLES__MIX_LEN, "MIX_LEN")); - ut_ad(name_of_col_is(sys_tables, sys_index, - DICT_FLD__SYS_TABLES__SPACE, "SPACE")); - - tuple = dtuple_create(heap, 1); - dfield = dtuple_get_nth_field(tuple, 0); - - dfield_set_data(dfield, name, ut_strlen(name)); - dict_index_copy_types(tuple, sys_index, 1); - - btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr); - rec = btr_pcur_get_rec(&pcur); - - if (!btr_pcur_is_on_user_rec(&pcur) - || rec_get_deleted_flag(rec, 0)) { - /* Not found */ -err_exit: - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap); - - 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) { - - goto err_exit; - } - - err_msg = dict_load_table_low(name, rec, &table); - - if (err_msg) { - - ut_print_timestamp(stderr); - fprintf(stderr, " InnoDB: %s\n", 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->file_unreadable = true; - - } else if (!fil_space_for_table_exists_in_mem( - table->space, name, false, IS_XTRABACKUP(), true, heap, - table->id, table->flags)) { - - if (DICT_TF2_FLAG_IS_SET(table, DICT_TF2_TEMPORARY)) { - /* Do not bother to retry opening temporary tables. */ - table->file_unreadable = 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 table - 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); - - if (err != DB_SUCCESS) { - /* We failed to find a sensible - tablespace file */ - - table->file_unreadable = true; - } - - if (filepath) { - mem_free(filepath); - } - } - } - - dict_load_columns(table, heap); - - if (cached) { - dict_table_add_to_cache(table, TRUE, heap); - } else { - dict_table_add_system_columns(table, heap); - } - - mem_heap_empty(heap); - - /* If there is no tablespace for the table then we only need to - load the index definitions. So that we can IMPORT the tablespace - later. When recovering table locks for resurrected incomplete - transactions, the tablespace should exist, because DDL operations - 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->file_unreadable - ? DICT_ERR_IGNORE_ALL - : ignore_err; - - err = dict_load_indexes(table, heap, index_load_err); - - if (err == DB_INDEX_CORRUPT) { - /* 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"); - - dict_table_remove_from_cache(table); - table = NULL; - goto func_exit; - } else { - dict_index_t* clust_index; - clust_index = dict_table_get_first_index(table); - - if (dict_index_is_corrupted(clust_index)) { - table->corrupted = TRUE; - } - } - } - - /* Initialize table foreign_child value. Its value could be - changed when dict_load_foreigns() is called below */ - table->fk_max_recusive_level = 0; - - /* If the force recovery flag is set, we open the table irrespective - of the error condition, since the user may want to dump data from the - clustered index. However we load the foreign key information only if - all indexes were loaded. */ - if (!cached || table->file_unreadable) { - /* 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); - - 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); - - dict_table_remove_from_cache(table); - table = NULL; - } else { - table->fk_max_recusive_level = 0; - } - } else { - dict_index_t* index; - - /* Make sure that at least the clustered index was loaded. - Otherwise refuse to load the table */ - index = dict_table_get_first_index(table); - - if (!srv_force_recovery - || !index - || !dict_index_is_clust(index)) { - - dict_table_remove_from_cache(table); - table = NULL; - - } else if (dict_index_is_corrupted(index) - && !table->file_unreadable) { - - /* It is possible we force to load a corrupted - clustered index if srv_load_corrupted is set. - Mark the table as corrupted in this case */ - table->corrupted = true; - } - } - -func_exit: - mem_heap_free(heap); - - ut_ad(!table - || ignore_err != DICT_ERR_IGNORE_NONE - || table->file_unreadable - || !table->corrupted); - - 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 - when a user defined FTS_DOC_ID is present, but no - FTS */ - fts_optimize_remove_table(table); - fts_free(table); - } else { - fts_optimize_add_table(table); - } - } - - ut_ad(err != DB_SUCCESS || dict_foreign_set_validate(*table)); - - return(table); -} - -/***********************************************************************//** -Loads a table object based on the table id. -@return table; NULL if table does not exist */ -UNIV_INTERN -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; - mem_heap_t* heap; - dtuple_t* tuple; - dfield_t* dfield; - dict_index_t* sys_table_ids; - dict_table_t* sys_tables; - const rec_t* rec; - const byte* field; - ulint len; - dict_table_t* table; - mtr_t mtr; - - ut_ad(mutex_own(&(dict_sys->mutex))); - - table = NULL; - - /* 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); - /*---------------------------------------------------*/ - /* Get the secondary index based on ID for table SYS_TABLES */ - sys_tables = dict_sys->sys_tables; - sys_table_ids = dict_table_get_next_index( - dict_table_get_first_index(sys_tables)); - ut_ad(!dict_table_is_comp(sys_tables)); - ut_ad(!dict_index_is_clust(sys_table_ids)); - heap = mem_heap_create(256); - - tuple = dtuple_create(heap, 1); - dfield = dtuple_get_nth_field(tuple, 0); - - /* 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); - - btr_pcur_open_on_user_rec(sys_table_ids, tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr); - - rec = btr_pcur_get_rec(&pcur); - - if (page_rec_is_user_rec(rec)) { - /*---------------------------------------------------*/ - /* Now we have the record in the secondary index - containing the table ID and NAME */ -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)) { - if (rec_get_deleted_flag(rec, 0)) { - /* Until purge has completed, there - may be delete-marked duplicate records - for the same SYS_TABLES.ID, but different - SYS_TABLES.NAME. */ - while (btr_pcur_move_to_next(&pcur, &mtr)) { - rec = btr_pcur_get_rec(&pcur); - - if (page_rec_is_user_rec(rec)) { - goto check_rec; - } - } - } else { - /* Now we get the table name from the record */ - 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); - } - } - } - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap); - - 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( -/*================*/ - dict_table_t* table) /*!< in: system table */ -{ - mem_heap_t* heap; - - ut_ad(mutex_own(&(dict_sys->mutex))); - - heap = mem_heap_create(1000); - - dict_load_indexes(table, heap, DICT_ERR_IGNORE_NONE); - - mem_heap_free(heap); -} - -/********************************************************************//** -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 -void -dict_load_foreign_cols( -/*===================*/ - dict_foreign_t* foreign)/*!< in/out: foreign constraint object */ -{ - dict_table_t* sys_foreign_cols; - dict_index_t* sys_index; - btr_pcur_t pcur; - dtuple_t* tuple; - dfield_t* dfield; - const rec_t* rec; - const byte* field; - ulint len; - ulint i; - mtr_t mtr; - size_t id_len; - - ut_ad(mutex_own(&(dict_sys->mutex))); - - id_len = strlen(foreign->id); - - foreign->foreign_col_names = static_cast<const char**>( - mem_heap_alloc(foreign->heap, - foreign->n_fields * sizeof(void*))); - - foreign->referenced_col_names = static_cast<const char**>( - mem_heap_alloc(foreign->heap, - foreign->n_fields * sizeof(void*))); - - mtr_start(&mtr); - - sys_foreign_cols = dict_table_get_low("SYS_FOREIGN_COLS"); - - sys_index = UT_LIST_GET_FIRST(sys_foreign_cols->indexes); - ut_ad(!dict_table_is_comp(sys_foreign_cols)); - - tuple = dtuple_create(foreign->heap, 1); - dfield = dtuple_get_nth_field(tuple, 0); - - dfield_set_data(dfield, foreign->id, id_len); - dict_index_copy_types(tuple, sys_index, 1); - - btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr); - for (i = 0; i < foreign->n_fields; i++) { - - rec = btr_pcur_get_rec(&pcur); - - ut_a(btr_pcur_is_on_user_rec(&pcur)); - ut_a(!rec_get_deleted_flag(rec, 0)); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_FOREIGN_COLS__ID, &len); - - if (len != id_len || ut_memcmp(foreign->id, field, len) != 0) { - const rec_t* pos; - ulint pos_len; - const rec_t* for_col_name; - ulint for_col_name_len; - const rec_t* ref_col_name; - ulint ref_col_name_len; - - pos = rec_get_nth_field_old( - rec, DICT_FLD__SYS_FOREIGN_COLS__POS, - &pos_len); - - for_col_name = rec_get_nth_field_old( - rec, DICT_FLD__SYS_FOREIGN_COLS__FOR_COL_NAME, - &for_col_name_len); - - ref_col_name = rec_get_nth_field_old( - 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; - } - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_FOREIGN_COLS__POS, &len); - ut_a(len == 4); - ut_a(i == mach_read_from_4(field)); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_FOREIGN_COLS__FOR_COL_NAME, &len); - foreign->foreign_col_names[i] = mem_heap_strdupl( - foreign->heap, (char*) field, len); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_FOREIGN_COLS__REF_COL_NAME, &len); - foreign->referenced_col_names[i] = mem_heap_strdupl( - foreign->heap, (char*) field, len); - - btr_pcur_move_to_next_user_rec(&pcur, &mtr); - } - - btr_pcur_close(&pcur); - mtr_commit(&mtr); -} - -/***********************************************************************//** -Loads a foreign key constraint to the dictionary cache. -@return DB_SUCCESS or error code */ -static MY_ATTRIBUTE((nonnull(1), warn_unused_result)) -dberr_t -dict_load_foreign( -/*==============*/ - const char* id, - /*!< in: foreign constraint id, must be - '\0'-terminated */ - const char** col_names, - /*!< in: column names, or NULL - to use foreign->foreign_table->col_names */ - bool check_recursive, - /*!< in: whether to record the foreign table - parent count to avoid unlimited recursive - load of chained foreign tables */ - bool check_charsets, - /*!< in: whether to check charset - compatibility */ - dict_err_ignore_t ignore_err) - /*!< in: error to be ignored */ -{ - dict_foreign_t* foreign; - dict_table_t* sys_foreign; - btr_pcur_t pcur; - dict_index_t* sys_index; - dtuple_t* tuple; - mem_heap_t* heap2; - dfield_t* dfield; - const rec_t* rec; - const byte* field; - ulint len; - ulint n_fields_and_type; - mtr_t mtr; - dict_table_t* for_table; - dict_table_t* ref_table; - size_t id_len; - - ut_ad(mutex_own(&(dict_sys->mutex))); - - id_len = strlen(id); - - heap2 = mem_heap_create(1000); - - mtr_start(&mtr); - - sys_foreign = dict_table_get_low("SYS_FOREIGN"); - - sys_index = UT_LIST_GET_FIRST(sys_foreign->indexes); - ut_ad(!dict_table_is_comp(sys_foreign)); - - tuple = dtuple_create(heap2, 1); - dfield = dtuple_get_nth_field(tuple, 0); - - dfield_set_data(dfield, id, id_len); - dict_index_copy_types(tuple, sys_index, 1); - - btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr); - rec = btr_pcur_get_rec(&pcur); - - if (!btr_pcur_is_on_user_rec(&pcur) - || 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); - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap2); - - return(DB_ERROR); - } - - field = rec_get_nth_field_old(rec, DICT_FLD__SYS_FOREIGN__ID, &len); - - /* 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); - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - mem_heap_free(heap2); - - return(DB_ERROR); - } - - /* Read the table names and the number of columns associated - with the constraint */ - - mem_heap_free(heap2); - - foreign = dict_mem_foreign_create(); - - n_fields_and_type = mach_read_from_4( - rec_get_nth_field_old( - rec, DICT_FLD__SYS_FOREIGN__N_COLS, &len)); - - ut_a(len == 4); - - /* We store the type in the bits 24..29 of n_fields_and_type. */ - - foreign->type = (unsigned int) (n_fields_and_type >> 24); - foreign->n_fields = (unsigned int) (n_fields_and_type & 0x3FFUL); - - foreign->id = mem_heap_strdupl(foreign->heap, id, id_len); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_FOREIGN__FOR_NAME, &len); - - foreign->foreign_table_name = mem_heap_strdupl( - foreign->heap, (char*) field, len); - dict_mem_foreign_table_name_lookup_set(foreign, TRUE); - - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_FOREIGN__REF_NAME, &len); - foreign->referenced_table_name = mem_heap_strdupl( - foreign->heap, (char*) field, len); - dict_mem_referenced_table_name_lookup_set(foreign, TRUE); - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - - 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; - } - } - } - - /* 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 - and index objects and frees the newly created object foreign. - Adding to the cache should always succeed since we are not creating - 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)); -} - -/***********************************************************************//** -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 -dberr_t -dict_load_foreigns( -/*===============*/ - const char* table_name, /*!< in: table name */ - const char** col_names, /*!< in: column names, or NULL - to use table->col_names */ - bool check_recursive,/*!< in: Whether to check - recursive load of tables - chained by FK */ - bool check_charsets, /*!< in: whether to check - charset compatibility */ - dict_err_ignore_t ignore_err) /*!< in: error to be ignored */ -{ - ulint tuple_buf[(DTUPLE_EST_ALLOC(1) + sizeof(ulint) - 1) - / sizeof(ulint)]; - btr_pcur_t pcur; - dtuple_t* tuple; - dfield_t* dfield; - dict_index_t* sec_index; - dict_table_t* sys_foreign; - const rec_t* rec; - const byte* field; - ulint len; - dberr_t err; - mtr_t mtr; - - 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); - } - - ut_ad(!dict_table_is_comp(sys_foreign)); - mtr_start(&mtr); - - /* Get the secondary index based on FOR_NAME from table - SYS_FOREIGN */ - - sec_index = dict_table_get_next_index( - dict_table_get_first_index(sys_foreign)); - ut_ad(!dict_index_is_clust(sec_index)); -start_load: - - tuple = dtuple_create_from_mem(tuple_buf, sizeof(tuple_buf), 1); - dfield = dtuple_get_nth_field(tuple, 0); - - dfield_set_data(dfield, table_name, ut_strlen(table_name)); - dict_index_copy_types(tuple, sec_index, 1); - - btr_pcur_open_on_user_rec(sec_index, tuple, PAGE_CUR_GE, - BTR_SEARCH_LEAF, &pcur, &mtr); -loop: - rec = btr_pcur_get_rec(&pcur); - - 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 */ - - 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 - following call does the comparison in the latin1_swedish_ci - charset-collation, in a case-insensitive way. */ - - if (0 != cmp_data_data(dfield_get_type(dfield)->mtype, - dfield_get_type(dfield)->prtype, - static_cast<const byte*>( - dfield_get_data(dfield)), - dfield_get_len(dfield), - field, len)) { - - goto load_next_index; - } - - /* Since table names in SYS_FOREIGN are stored in a case-insensitive - order, we have to check that the table name matches also in a binary - string comparison. On Unix, MySQL allows table names that only differ - in character case. If lower_case_table_names=2 then what is stored - may not be the same case, but the previous comparison showed that they - match with no-case. */ - - if (rec_get_deleted_flag(rec, 0)) { - goto next_rec; - } - - if ((innobase_get_lower_case_table_names() != 2) - && (0 != ut_memcmp(field, table_name, len))) { - goto next_rec; - } - - /* 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 + 1]; - - ut_a(len <= MAX_TABLE_NAME_LEN); - memcpy(fk_id, field, len); - fk_id[len] = '\0'; - - btr_pcur_store_position(&pcur, &mtr); - - mtr_commit(&mtr); - - /* Load the foreign constraint definition to the dictionary cache */ - - err = dict_load_foreign(fk_id, col_names, - check_recursive, check_charsets, ignore_err); - - if (err != DB_SUCCESS) { - btr_pcur_close(&pcur); - - return(err); - } - - mtr_start(&mtr); - - btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); -next_rec: - btr_pcur_move_to_next_user_rec(&pcur, &mtr); - - goto loop; - -load_next_index: - btr_pcur_close(&pcur); - mtr_commit(&mtr); - - sec_index = dict_table_get_next_index(sec_index); - - if (sec_index != NULL) { - - mtr_start(&mtr); - - /* Switch to scan index on REF_NAME, fk_max_recusive_level - already been updated when scanning FOR_NAME index, no need to - update again */ - check_recursive = FALSE; - - goto start_load; - } - - return(DB_SUCCESS); -} |