diff options
Diffstat (limited to 'storage/innobase/dict/dict0load.c')
-rw-r--r-- | storage/innobase/dict/dict0load.c | 1355 |
1 files changed, 1355 insertions, 0 deletions
diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c new file mode 100644 index 00000000000..289b5dab4f2 --- /dev/null +++ b/storage/innobase/dict/dict0load.c @@ -0,0 +1,1355 @@ +/****************************************************** +Loads to the memory cache database object definitions +from dictionary tables + +(c) 1996 Innobase Oy + +Created 4/24/1996 Heikki Tuuri +*******************************************************/ + +#include "dict0load.h" +#ifndef UNIV_HOTBACKUP +#include "mysql_version.h" +#endif /* !UNIV_HOTBACKUP */ + +#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 "rem0cmp.h" +#include "srv0start.h" +#include "srv0srv.h" + +/************************************************************************ +Finds the first table name in the given database. */ + +char* +dict_get_first_table_name_in_db( +/*============================*/ + /* out, own: table name, NULL if + does not exist; the caller must + free the memory in the string! */ + 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; + rec_t* rec; + byte* field; + ulint len; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + 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_a(!sys_tables->comp); + + 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, &mtr)) { + /* Not found */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + field = rec_get_nth_field_old(rec, 0, &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, sys_tables->comp)) { + + /* 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. */ + +void +dict_print(void) +/*============*/ +{ + dict_table_t* sys_tables; + dict_index_t* sys_index; + dict_table_t* table; + btr_pcur_t pcur; + rec_t* rec; + byte* field; + ulint len; + mtr_t mtr; + + /* Enlarge the fatal semaphore wait timeout during the InnoDB table + monitor printout */ + + mutex_enter(&kernel_mutex); + srv_fatal_semaphore_wait_threshold += 7200; /* 2 hours */ + mutex_exit(&kernel_mutex); + + 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); + + btr_pcur_open_at_index_side(TRUE, sys_index, BTR_SEARCH_LEAF, &pcur, + TRUE, &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, &mtr)) { + /* end of index */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + mutex_exit(&(dict_sys->mutex)); + + /* Restore the fatal semaphore wait timeout */ + + mutex_enter(&kernel_mutex); + srv_fatal_semaphore_wait_threshold -= 7200; /* 2 hours */ + mutex_exit(&kernel_mutex); + + return; + } + + field = rec_get_nth_field_old(rec, 0, &len); + + if (!rec_get_deleted_flag(rec, sys_tables->comp)) { + + /* We found one */ + + char* table_name = mem_strdupl((char*) field, len); + + btr_pcur_store_position(&pcur, &mtr); + + mtr_commit(&mtr); + + table = dict_table_get_low(table_name); + mem_free(table_name); + + if (table == NULL) { + fputs("InnoDB: Failed to load table ", stderr); + ut_print_namel(stderr, NULL, (char*) field, len); + putc('\n', stderr); + } else { + /* The table definition was corrupt if there + is no index */ + + if (dict_table_get_first_index(table)) { + dict_update_statistics_low(table, TRUE); + } + + dict_table_print_low(table); + } + + mtr_start(&mtr); + + btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); + } + + goto loop; +} + +/************************************************************************ +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. */ + +void +dict_check_tablespaces_and_store_max_id( +/*====================================*/ + ibool in_crash_recovery) /* in: are we doing a crash recovery */ +{ + dict_table_t* sys_tables; + dict_index_t* sys_index; + btr_pcur_t pcur; + rec_t* rec; + byte* field; + ulint len; + ulint space_id; + ulint max_space_id = 0; + mtr_t mtr; + + 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_a(!sys_tables->comp); + + btr_pcur_open_at_index_side(TRUE, sys_index, BTR_SEARCH_LEAF, &pcur, + TRUE, &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, &mtr)) { + /* 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)); + + return; + } + + field = rec_get_nth_field_old(rec, 0, &len); + + if (!rec_get_deleted_flag(rec, sys_tables->comp)) { + + /* We found one */ + + char* name = mem_strdupl((char*) field, len); + + field = rec_get_nth_field_old(rec, 9, &len); + ut_a(len == 4); + + space_id = mach_read_from_4(field); + + btr_pcur_store_position(&pcur, &mtr); + + mtr_commit(&mtr); + + if (space_id != 0 && in_crash_recovery) { + /* Check that the tablespace (the .ibd file) really + exists; print a warning to the .err log if not */ + + fil_space_for_table_exists_in_mem(space_id, name, + FALSE, TRUE, TRUE); + } + + if (space_id != 0 && !in_crash_recovery) { + /* It is a normal database startup: create the space + object and check that the .ibd file exists. */ + + fil_open_single_table_tablespace(FALSE, space_id, + name); + } + + mem_free(name); + + if (space_id > max_space_id) { + max_space_id = space_id; + } + + mtr_start(&mtr); + + btr_pcur_restore_position(BTR_SEARCH_LEAF, &pcur, &mtr); + } + + goto loop; +} + +/************************************************************************ +Loads definitions for table columns. */ +static +void +dict_load_columns( +/*==============*/ + dict_table_t* table, /* in: table */ + mem_heap_t* heap) /* in: 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; + rec_t* rec; + byte* field; + ulint len; + byte* buf; + char* name; + ulint mtype; + ulint prtype; + ulint col_len; + ulint prec; + ulint i; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + mtr_start(&mtr); + + sys_columns = dict_table_get_low("SYS_COLUMNS"); + sys_index = UT_LIST_GET_FIRST(sys_columns->indexes); + ut_a(!sys_columns->comp); + + tuple = dtuple_create(heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + buf = 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 < table->n_cols - DATA_N_SYS_COLS; i++) { + + rec = btr_pcur_get_rec(&pcur); + + ut_a(btr_pcur_is_on_user_rec(&pcur, &mtr)); + + ut_a(!rec_get_deleted_flag(rec, sys_columns->comp)); + + field = rec_get_nth_field_old(rec, 0, &len); + ut_ad(len == 8); + ut_a(ut_dulint_cmp(table->id, mach_read_from_8(field)) == 0); + + field = rec_get_nth_field_old(rec, 1, &len); + ut_ad(len == 4); + ut_a(i == mach_read_from_4(field)); + + ut_a(0 == ut_strcmp("NAME", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 4))->name)); + + field = rec_get_nth_field_old(rec, 4, &len); + name = mem_heap_strdupl(heap, (char*) field, len); + + field = rec_get_nth_field_old(rec, 5, &len); + mtype = mach_read_from_4(field); + + field = rec_get_nth_field_old(rec, 6, &len); + prtype = mach_read_from_4(field); + + if (dtype_is_non_binary_string_type(mtype, prtype) + && dtype_get_charset_coll(prtype) == 0) { + /* This is a non-binary string type, and the table + was created with < 4.1.2. Use the default charset. */ + + prtype = dtype_form_prtype(prtype, + data_mysql_default_charset_coll); + } + + field = rec_get_nth_field_old(rec, 7, &len); + col_len = mach_read_from_4(field); + + ut_a(0 == ut_strcmp("PREC", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 8))->name)); + + field = rec_get_nth_field_old(rec, 8, &len); + prec = mach_read_from_4(field); + + dict_mem_table_add_col(table, name, mtype, prtype, col_len, + prec); + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); +} + +/************************************************************************ +Report that an index field or index for a table has been delete marked. */ +static +void +dict_load_report_deleted_index( +/*===========================*/ + const char* name, /* in: table name */ + ulint field) /* in: index field, or ULINT_UNDEFINED */ +{ + fprintf(stderr, "InnoDB: Error: data dictionary entry" + " for table %s is corrupt!\n", name); + if (field != ULINT_UNDEFINED) { + fprintf(stderr, + "InnoDB: Index field %lu is delete marked.\n", field); + } else { + fputs("InnoDB: An index is delete marked.\n", stderr); + } +} + +/************************************************************************ +Loads definitions for index fields. */ +static +void +dict_load_fields( +/*=============*/ + dict_table_t* table, /* in: table */ + dict_index_t* index, /* in: 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; + ulint pos_and_prefix_len; + ulint prefix_len; + rec_t* rec; + byte* field; + ulint len; + byte* buf; + ulint i; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + UT_NOT_USED(table); + + mtr_start(&mtr); + + sys_fields = dict_table_get_low("SYS_FIELDS"); + sys_index = UT_LIST_GET_FIRST(sys_fields->indexes); + ut_a(!sys_fields->comp); + + tuple = dtuple_create(heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + buf = 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++) { + + rec = btr_pcur_get_rec(&pcur); + + ut_a(btr_pcur_is_on_user_rec(&pcur, &mtr)); + if (rec_get_deleted_flag(rec, sys_fields->comp)) { + dict_load_report_deleted_index(table->name, i); + } + + field = rec_get_nth_field_old(rec, 0, &len); + ut_ad(len == 8); + ut_a(ut_memcmp(buf, field, len) == 0); + + field = rec_get_nth_field_old(rec, 1, &len); + ut_a(len == 4); + + /* 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 (== i) and the + low 2 bytes the prefix length for the field. Otherwise the + field number (== i) is contained in the 2 LOW bytes. */ + + pos_and_prefix_len = mach_read_from_4(field); + + ut_a((pos_and_prefix_len & 0xFFFFUL) == i + || (pos_and_prefix_len & 0xFFFF0000UL) == (i << 16)); + + if ((i == 0 && pos_and_prefix_len > 0) + || (pos_and_prefix_len & 0xFFFF0000UL) > 0) { + + prefix_len = pos_and_prefix_len & 0xFFFFUL; + } else { + prefix_len = 0; + } + + ut_a(0 == ut_strcmp("COL_NAME", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 4))->name)); + + field = rec_get_nth_field_old(rec, 4, &len); + + dict_mem_index_add_field(index, + mem_heap_strdupl(heap, (char*) field, len), 0, prefix_len); + + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); +} + +/************************************************************************ +Loads definitions for table indexes. Adds them to the data dictionary +cache. */ +static +ibool +dict_load_indexes( +/*==============*/ + /* out: TRUE if ok, FALSE if corruption + of dictionary table */ + dict_table_t* table, /* in: table */ + mem_heap_t* heap) /* in: memory heap for temporary storage */ +{ + dict_table_t* sys_indexes; + dict_index_t* sys_index; + dict_index_t* index; + btr_pcur_t pcur; + dtuple_t* tuple; + dfield_t* dfield; + rec_t* rec; + byte* field; + ulint len; + ulint name_len; + char* name_buf; + ulint type; + ulint space; + ulint page_no; + ulint n_fields; + byte* buf; + ibool is_sys_table; + dulint id; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + if ((ut_dulint_get_high(table->id) == 0) + && (ut_dulint_get_low(table->id) < DICT_HDR_FIRST_ID)) { + is_sys_table = TRUE; + } else { + is_sys_table = FALSE; + } + + mtr_start(&mtr); + + sys_indexes = dict_table_get_low("SYS_INDEXES"); + sys_index = UT_LIST_GET_FIRST(sys_indexes->indexes); + ut_a(!sys_indexes->comp); + + tuple = dtuple_create(heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + buf = 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 (;;) { + if (!btr_pcur_is_on_user_rec(&pcur, &mtr)) { + + break; + } + + rec = btr_pcur_get_rec(&pcur); + + field = rec_get_nth_field_old(rec, 0, &len); + ut_ad(len == 8); + + if (ut_memcmp(buf, field, len) != 0) { + break; + } + + if (rec_get_deleted_flag(rec, table->comp)) { + dict_load_report_deleted_index(table->name, + ULINT_UNDEFINED); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + return(FALSE); + } + + field = rec_get_nth_field_old(rec, 1, &len); + ut_ad(len == 8); + id = mach_read_from_8(field); + + ut_a(0 == ut_strcmp("NAME", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 4))->name)); + + field = rec_get_nth_field_old(rec, 4, &name_len); + name_buf = mem_heap_strdupl(heap, (char*) field, name_len); + + field = rec_get_nth_field_old(rec, 5, &len); + n_fields = mach_read_from_4(field); + + field = rec_get_nth_field_old(rec, 6, &len); + type = mach_read_from_4(field); + + field = rec_get_nth_field_old(rec, 7, &len); + space = mach_read_from_4(field); + + ut_a(0 == ut_strcmp("PAGE_NO", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 8))->name)); + + field = rec_get_nth_field_old(rec, 8, &len); + page_no = mach_read_from_4(field); + + if (page_no == FIL_NULL) { + + fprintf(stderr, + "InnoDB: Error: trying to load index %s for table %s\n" + "InnoDB: but the index tree has been freed!\n", + name_buf, table->name); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + return(FALSE); + } + + if ((type & DICT_CLUSTERED) == 0 + && NULL == dict_table_get_first_index(table)) { + + fprintf(stderr, + "InnoDB: Error: trying to load index %s for table %s\n" + "InnoDB: but the first index is not clustered!\n", + name_buf, table->name); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + return(FALSE); + } + + if (is_sys_table + && ((type & DICT_CLUSTERED) + || ((table == dict_sys->sys_tables) + && (name_len == (sizeof "ID_IND") - 1) + && (0 == ut_memcmp(name_buf, "ID_IND", + name_len))))) { + + /* The index was created in memory already at booting + of the database server */ + } else { + index = dict_mem_index_create(table->name, name_buf, + space, type, n_fields); + index->id = id; + + dict_load_fields(table, index, heap); + dict_index_add_to_cache(table, index, page_no); + } + + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + return(TRUE); +} + +/************************************************************************ +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. */ + +dict_table_t* +dict_load_table( +/*============*/ + /* out: 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 */ + const char* name) /* in: table name in the + databasename/tablename format */ +{ + ibool ibd_file_missing = FALSE; + 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; + rec_t* rec; + byte* field; + ulint len; + ulint space; + ulint n_cols; + ulint err; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + 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_a(!sys_tables->comp); + + 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, &mtr) + || rec_get_deleted_flag(rec, sys_tables->comp)) { + /* Not found */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + field = rec_get_nth_field_old(rec, 0, &len); + + /* Check if the table name in record is the searched one */ + if (len != ut_strlen(name) || ut_memcmp(name, field, len) != 0) { + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + ut_a(0 == ut_strcmp("SPACE", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 9))->name)); + + field = rec_get_nth_field_old(rec, 9, &len); + space = mach_read_from_4(field); + + /* Check if the tablespace exists and has the right name */ + if (space != 0) { + if (fil_space_for_table_exists_in_mem(space, name, FALSE, + FALSE, FALSE)) { + /* Ok; (if we did a crash recovery then the tablespace + can already be in the memory cache) */ + } else { + /* In >= 4.1.9, InnoDB scans the data dictionary also + at a normal mysqld startup. It is an error if the + space object does not exist in memory. */ + + ut_print_timestamp(stderr); + fprintf(stderr, +" InnoDB: error: space object of table %s,\n" +"InnoDB: space id %lu did not exist in memory. Retrying an open.\n", + name, (ulong)space); + /* Try to open the tablespace */ + if (!fil_open_single_table_tablespace(TRUE, + space, name)) { + /* We failed to find a sensible tablespace + file */ + + ibd_file_missing = TRUE; + } + } + } + + ut_a(0 == ut_strcmp("N_COLS", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 4))->name)); + + field = rec_get_nth_field_old(rec, 4, &len); + n_cols = mach_read_from_4(field); + + /* The high-order bit of N_COLS is the "compact format" flag. */ + table = dict_mem_table_create(name, space, + n_cols & ~0x80000000UL, + !!(n_cols & 0x80000000UL)); + + table->ibd_file_missing = ibd_file_missing; + + ut_a(0 == ut_strcmp("ID", + dict_field_get_col( + dict_index_get_nth_field(sys_index, 3))->name)); + + field = rec_get_nth_field_old(rec, 3, &len); + table->id = mach_read_from_8(field); + + field = rec_get_nth_field_old(rec, 5, &len); + table->type = mach_read_from_4(field); + + if (table->type == DICT_TABLE_CLUSTER_MEMBER) { + ut_error; +#if 0 /* clustered tables have not been implemented yet */ + field = rec_get_nth_field_old(rec, 6, &len); + table->mix_id = mach_read_from_8(field); + + field = rec_get_nth_field_old(rec, 8, &len); + table->cluster_name = mem_heap_strdupl(heap, (char*) field, len); +#endif + } + + if ((table->type == DICT_TABLE_CLUSTER) + || (table->type == DICT_TABLE_CLUSTER_MEMBER)) { + + field = rec_get_nth_field_old(rec, 7, &len); + ut_a(len == 4); + table->mix_len = mach_read_from_4(field); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + if (table->type == DICT_TABLE_CLUSTER_MEMBER) { + /* Load the cluster table definition if not yet in + memory cache */ + dict_table_get_low(table->cluster_name); + } + + dict_load_columns(table, heap); + + dict_table_add_to_cache(table); + + dict_load_indexes(table, heap); + + err = dict_load_foreigns(table->name); +/* + if (err != DB_SUCCESS) { + + mutex_enter(&dict_foreign_err_mutex); + + ut_print_timestamp(stderr); + + fprintf(stderr, +" InnoDB: Error: could not make a foreign key definition to match\n" +"InnoDB: the foreign key table or the referenced table!\n" +"InnoDB: The data dictionary of InnoDB is corrupt. You may need to drop\n" +"InnoDB: and recreate the foreign key table or the referenced table.\n" +"InnoDB: Submit a detailed bug report to http://bugs.mysql.com\n" +"InnoDB: Latest foreign key error printout:\n%s\n", dict_foreign_err_buf); + + mutex_exit(&dict_foreign_err_mutex); + } +*/ + mem_heap_free(heap); + + return(table); +} + +/*************************************************************************** +Loads a table object based on the table id. */ + +dict_table_t* +dict_load_table_on_id( +/*==================*/ + /* out: table; NULL if table does not exist */ + dulint table_id) /* in: table id */ +{ + 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; + rec_t* rec; + byte* field; + ulint len; + dict_table_t* table; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + /* 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_a(!sys_tables->comp); + 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 (!btr_pcur_is_on_user_rec(&pcur, &mtr) + || rec_get_deleted_flag(rec, sys_tables->comp)) { + /* Not found */ + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + /*---------------------------------------------------*/ + /* Now we have the record in the secondary index containing the + table ID and NAME */ + + rec = btr_pcur_get_rec(&pcur); + field = rec_get_nth_field_old(rec, 0, &len); + ut_ad(len == 8); + + /* Check if the table id in record is the one searched for */ + if (ut_dulint_cmp(table_id, mach_read_from_8(field)) != 0) { + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + return(NULL); + } + + /* Now we get the table name from the record */ + field = rec_get_nth_field_old(rec, 1, &len); + /* Load the table definition to memory */ + table = dict_load_table(mem_heap_strdupl(heap, (char*) field, len)); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + 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. */ + +void +dict_load_sys_table( +/*================*/ + dict_table_t* table) /* in: system table */ +{ + mem_heap_t* heap; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + heap = mem_heap_create(1000); + + dict_load_indexes(table, heap); + + mem_heap_free(heap); +} + +/************************************************************************ +Loads foreign key constraint col names (also for the referenced table). */ +static +void +dict_load_foreign_cols( +/*===================*/ + const char* id, /* in: foreign constraint id as a null- + terminated string */ + dict_foreign_t* foreign)/* in: foreign constraint object */ +{ + dict_table_t* sys_foreign_cols; + dict_index_t* sys_index; + btr_pcur_t pcur; + dtuple_t* tuple; + dfield_t* dfield; + rec_t* rec; + byte* field; + ulint len; + ulint i; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + foreign->foreign_col_names = mem_heap_alloc(foreign->heap, + foreign->n_fields * sizeof(void*)); + + foreign->referenced_col_names = 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_a(!sys_foreign_cols->comp); + + tuple = dtuple_create(foreign->heap, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + dfield_set_data(dfield, id, ut_strlen(id)); + 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, &mtr)); + ut_a(!rec_get_deleted_flag(rec, sys_foreign_cols->comp)); + + field = rec_get_nth_field_old(rec, 0, &len); + ut_a(len == ut_strlen(id)); + ut_a(ut_memcmp(id, field, len) == 0); + + field = rec_get_nth_field_old(rec, 1, &len); + ut_a(len == 4); + ut_a(i == mach_read_from_4(field)); + + field = rec_get_nth_field_old(rec, 4, &len); + foreign->foreign_col_names[i] = + mem_heap_strdupl(foreign->heap, (char*) field, len); + + field = rec_get_nth_field_old(rec, 5, &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. */ +static +ulint +dict_load_foreign( +/*==============*/ + /* out: DB_SUCCESS or error code */ + const char* id) /* in: foreign constraint id as a + null-terminated string */ +{ + 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; + rec_t* rec; + byte* field; + ulint len; + ulint err; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + 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_a(!sys_foreign->comp); + + tuple = dtuple_create(heap2, 1); + dfield = dtuple_get_nth_field(tuple, 0); + + dfield_set_data(dfield, id, ut_strlen(id)); + 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, &mtr) + || rec_get_deleted_flag(rec, sys_foreign->comp)) { + /* Not found */ + + fprintf(stderr, + "InnoDB: Error A: cannot load foreign constraint %s\n", + id); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap2); + + return(DB_ERROR); + } + + field = rec_get_nth_field_old(rec, 0, &len); + + /* Check if the id in record is the searched one */ + if (len != ut_strlen(id) || ut_memcmp(id, field, len) != 0) { + + fprintf(stderr, + "InnoDB: Error B: cannot load foreign constraint %s\n", + id); + + 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(); + + foreign->n_fields = + mach_read_from_4(rec_get_nth_field_old(rec, 5, &len)); + + ut_a(len == 4); + + /* We store the type to the bits 24-31 of n_fields */ + + foreign->type = foreign->n_fields >> 24; + foreign->n_fields = foreign->n_fields & 0xFFFFFFUL; + + foreign->id = mem_heap_strdup(foreign->heap, id); + + field = rec_get_nth_field_old(rec, 3, &len); + foreign->foreign_table_name = + mem_heap_strdupl(foreign->heap, (char*) field, len); + + field = rec_get_nth_field_old(rec, 4, &len); + foreign->referenced_table_name = + mem_heap_strdupl(foreign->heap, (char*) field, len); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + dict_load_foreign_cols(id, foreign); + + /* 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. */ + + dict_table_get_low(foreign->foreign_table_name); + + /* 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. */ + + err = dict_foreign_add_to_cache(foreign); + + return(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. */ + +ulint +dict_load_foreigns( +/*===============*/ + /* out: DB_SUCCESS or error code */ + const char* table_name) /* in: table name */ +{ + btr_pcur_t pcur; + mem_heap_t* heap; + dtuple_t* tuple; + dfield_t* dfield; + dict_index_t* sec_index; + dict_table_t* sys_foreign; + rec_t* rec; + byte* field; + ulint len; + char* id ; + ulint err; + mtr_t mtr; + +#ifdef UNIV_SYNC_DEBUG + ut_ad(mutex_own(&(dict_sys->mutex))); +#endif /* UNIV_SYNC_DEBUG */ + + 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_a(!sys_foreign->comp); + 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)); +start_load: + heap = mem_heap_create(256); + + tuple = dtuple_create(heap, 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, &mtr)) { + /* 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 */ + + rec = btr_pcur_get_rec(&pcur); + field = rec_get_nth_field_old(rec, 0, &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), + 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 (0 != ut_memcmp(field, table_name, len)) { + + goto next_rec; + } + + if (rec_get_deleted_flag(rec, sys_foreign->comp)) { + + goto next_rec; + } + + /* Now we get a foreign key constraint id */ + field = rec_get_nth_field_old(rec, 1, &len); + id = mem_heap_strdupl(heap, (char*) field, len); + + btr_pcur_store_position(&pcur, &mtr); + + mtr_commit(&mtr); + + /* Load the foreign constraint definition to the dictionary cache */ + + err = dict_load_foreign(id); + + if (err != DB_SUCCESS) { + btr_pcur_close(&pcur); + mem_heap_free(heap); + + 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); + mem_heap_free(heap); + + sec_index = dict_table_get_next_index(sec_index); + + if (sec_index != NULL) { + + mtr_start(&mtr); + + goto start_load; + } + + return(DB_SUCCESS); +} |