diff options
Diffstat (limited to 'innobase/dict/dict0dict.c')
-rw-r--r-- | innobase/dict/dict0dict.c | 1020 |
1 files changed, 951 insertions, 69 deletions
diff --git a/innobase/dict/dict0dict.c b/innobase/dict/dict0dict.c index 10d93fc6ecf..e0a7fd327a5 100644 --- a/innobase/dict/dict0dict.c +++ b/innobase/dict/dict0dict.c @@ -17,6 +17,7 @@ Created 1/8/1996 Heikki Tuuri #include "mach0data.h" #include "dict0boot.h" #include "dict0mem.h" +#include "dict0crea.h" #include "trx0undo.h" #include "btr0btr.h" #include "btr0cur.h" @@ -24,10 +25,12 @@ Created 1/8/1996 Heikki Tuuri #include "pars0pars.h" #include "pars0sym.h" #include "que0que.h" - +#include "rem0cmp.h" dict_sys_t* dict_sys = NULL; /* the dictionary system */ +rw_lock_t dict_foreign_key_check_lock; + #define DICT_HEAP_SIZE 100 /* initial memory heap size when creating a table or index object */ #define DICT_POOL_PER_PROCEDURE_HASH 512 /* buffer pool max size per stored @@ -137,12 +140,12 @@ dict_tree_find_index_low( dict_tree_t* tree, /* in: index tree */ rec_t* rec); /* in: record for which to find correct index */ /************************************************************************** -Prints a table data. */ +Removes a foreign constraint struct from the dictionet cache. */ static void -dict_table_print_low( -/*=================*/ - dict_table_t* table); /* in: table */ +dict_foreign_remove_from_cache( +/*===========================*/ + dict_foreign_t* foreign); /* in, own: foreign constraint */ /************************************************************************** Prints a column data. */ static @@ -164,6 +167,13 @@ void dict_field_print_low( /*=================*/ dict_field_t* field); /* in: field */ +/************************************************************************* +Frees a foreign key struct. */ +static +void +dict_foreign_free( +/*==============*/ + dict_foreign_t* foreign); /* in, own: foreign key struct */ /************************************************************************ Reserves the dictionary system mutex for MySQL. */ @@ -353,7 +363,8 @@ dict_table_get_on_id( { dict_table_t* table; - if (ut_dulint_cmp(table_id, DICT_FIELDS_ID) <= 0) { + if (ut_dulint_cmp(table_id, DICT_FIELDS_ID) <= 0 + || trx->dict_operation) { /* It is a system table which will always exist in the table cache: we avoid acquiring the dictionary mutex, because if we are doing a rollback to handle an error in TABLE @@ -415,6 +426,10 @@ dict_init(void) dict_sys->size = 0; UT_LIST_INIT(dict_sys->table_LRU); + + rw_lock_create(&dict_foreign_key_check_lock); + rw_lock_set_level(&dict_foreign_key_check_lock, + SYNC_FOREIGN_KEY_CHECK); } /************************************************************************** @@ -535,6 +550,41 @@ dict_table_add_to_cache( } /************************************************************************** +Looks for an index with the given id. NOTE that we do not reserve +the dictionary mutex: this function is for emergency purposes like +printing info of a corrupt database page! */ + +dict_index_t* +dict_index_find_on_id_low( +/*======================*/ + /* out: index or NULL if not found from cache */ + dulint id) /* in: index id */ +{ + dict_table_t* table; + dict_index_t* index; + + table = UT_LIST_GET_FIRST(dict_sys->table_LRU); + + while (table) { + index = dict_table_get_first_index(table); + + while (index) { + if (0 == ut_dulint_cmp(id, index->tree->id)) { + /* Found */ + + return(index); + } + + index = dict_table_get_next_index(index); + } + + table = UT_LIST_GET_NEXT(table_LRU, table); + } + + return(NULL); +} + +/************************************************************************** Renames a table object. */ ibool @@ -544,10 +594,12 @@ dict_table_rename_in_cache( dict_table_t* table, /* in: table */ char* new_name) /* in: new name */ { - ulint fold; - ulint old_size; - char* name_buf; - ulint i; + dict_foreign_t* foreign; + dict_index_t* index; + ulint fold; + ulint old_size; + char* name_buf; + ulint i; ut_ad(table); ut_ad(mutex_own(&(dict_sys->mutex))); @@ -589,6 +641,55 @@ dict_table_rename_in_cache( dict_sys->size += (mem_heap_get_size(table->heap) - old_size); + /* Update the table_name field in indexes */ + index = dict_table_get_first_index(table); + + while (index != NULL) { + index->table_name = table->name; + + index = dict_table_get_next_index(index); + } + + /* Update the table name fields in foreign constraints */ + + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + while (foreign != NULL) { + if (ut_strlen(foreign->foreign_table_name) < + ut_strlen(table->name)) { + /* Allocate a longer name buffer; + TODO: store buf len to save memory */ + foreign->foreign_table_name = mem_heap_alloc( + foreign->heap, + ut_strlen(table->name) + 1); + } + + ut_memcpy(foreign->foreign_table_name, table->name, + ut_strlen(table->name) + 1); + foreign->foreign_table_name[ut_strlen(table->name)] = '\0'; + + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign != NULL) { + if (ut_strlen(foreign->referenced_table_name) < + ut_strlen(table->name)) { + /* Allocate a longer name buffer; + TODO: store buf len to save memory */ + foreign->referenced_table_name = mem_heap_alloc( + foreign->heap, + ut_strlen(table->name) + 1); + } + + ut_memcpy(foreign->referenced_table_name, table->name, + ut_strlen(table->name) + 1); + foreign->referenced_table_name[ut_strlen(table->name)] = '\0'; + + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } + return(TRUE); } @@ -600,6 +701,7 @@ dict_table_remove_from_cache( /*=========================*/ dict_table_t* table) /* in, own: table */ { + dict_foreign_t* foreign; dict_index_t* index; ulint size; ulint i; @@ -610,6 +712,29 @@ dict_table_remove_from_cache( /* printf("Removing table %s from dictionary cache\n", table->name); */ + /* Remove the foreign constraints from the cache */ + foreign = UT_LIST_GET_LAST(table->foreign_list); + + while (foreign != NULL) { + ut_a(0 == ut_strcmp(foreign->foreign_table_name, table->name)); + + dict_foreign_remove_from_cache(foreign); + foreign = UT_LIST_GET_LAST(table->foreign_list); + } + + /* Reset table field in referencing constraints */ + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign != NULL) { + ut_a(0 == ut_strcmp(foreign->referenced_table_name, + table->name)); + foreign->referenced_table = NULL; + foreign->referenced_index = NULL; + + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } + /* Remove the indexes from the cache */ index = UT_LIST_GET_LAST(table->indexes); @@ -856,6 +981,21 @@ dict_index_add_to_cache( new_index->tree = tree; } + if (!(new_index->type & DICT_UNIVERSAL)) { + + new_index->stat_n_diff_key_vals = + mem_heap_alloc(new_index->heap, + (1 + dict_index_get_n_unique(new_index)) + * sizeof(ib_longlong)); + /* Give some sensible values to stat_n_... in case we do + not calculate statistics quickly enough */ + + for (i = 0; i <= dict_index_get_n_unique(new_index); i++) { + + new_index->stat_n_diff_key_vals[i] = 100; + } + } + /* Add the index to the list of indexes stored in the tree */ UT_LIST_ADD_LAST(tree_indexes, tree->tree_indexes, new_index); @@ -1290,6 +1430,654 @@ dict_index_build_internal_non_clust( return(new_index); } +/*====================== FOREIGN KEY PROCESSING ========================*/ + +/************************************************************************* +Frees a foreign key struct. */ +static +void +dict_foreign_free( +/*==============*/ + dict_foreign_t* foreign) /* in, own: foreign key struct */ +{ + mem_heap_free(foreign->heap); +} + +/************************************************************************** +Removes a foreign constraint struct from the dictionary cache. */ +static +void +dict_foreign_remove_from_cache( +/*===========================*/ + dict_foreign_t* foreign) /* in, own: foreign constraint */ +{ + ut_ad(mutex_own(&(dict_sys->mutex))); + ut_a(foreign); + + if (foreign->referenced_table) { + UT_LIST_REMOVE(referenced_list, + foreign->referenced_table->referenced_list, foreign); + } + + if (foreign->foreign_table) { + UT_LIST_REMOVE(foreign_list, + foreign->foreign_table->foreign_list, foreign); + } + + dict_foreign_free(foreign); +} + +/************************************************************************** +Looks for the foreign constraint from the foreign and referenced lists +of a table. */ +static +dict_foreign_t* +dict_foreign_find( +/*==============*/ + /* out: foreign constraint */ + dict_table_t* table, /* in: table object */ + char* id) /* in: foreign constraint id */ +{ + dict_foreign_t* foreign; + + ut_ad(mutex_own(&(dict_sys->mutex))); + + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + while (foreign) { + if (ut_strcmp(id, foreign->id) == 0) { + + return(foreign); + } + + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign) { + if (ut_strcmp(id, foreign->id) == 0) { + + return(foreign); + } + + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } + + return(NULL); +} + +/************************************************************************* +Tries to find an index whose first fields are the columns in the array, +in the same order. */ +static +dict_index_t* +dict_foreign_find_index( +/*====================*/ + /* out: matching index, NULL if not found */ + dict_table_t* table, /* in: table */ + char** columns,/* in: array of column names */ + ulint n_cols, /* in: number of columns */ + dict_index_t* types_idx)/* in: NULL or an index to whose types the + column types must match */ +{ + dict_index_t* index; + char* col_name; + ulint i; + + index = dict_table_get_first_index(table); + + while (index != NULL) { + if (dict_index_get_n_fields(index) >= n_cols) { + + for (i = 0; i < n_cols; i++) { + col_name = dict_index_get_nth_field(index, i) + ->col->name; + if (ut_strlen(columns[i]) != + ut_strlen(col_name) + || 0 != ut_memcmp(columns[i], + col_name, + ut_strlen(col_name))) { + break; + } + + if (types_idx && !cmp_types_are_equal( + dict_index_get_nth_type(index, i), + dict_index_get_nth_type(types_idx, i))) { + + break; + } + } + + if (i == n_cols) { + /* We found a matching index */ + + return(index); + } + } + + index = dict_table_get_next_index(index); + } + + return(NULL); +} + +/************************************************************************** +Adds a foreign key constraint object to the dictionary cache. May free +the object if there already is an object with the same identifier in. +At least one of the foreign table and the referenced table must already +be in the dictionary cache! */ + +ulint +dict_foreign_add_to_cache( +/*======================*/ + /* out: DB_SUCCESS or error code */ + dict_foreign_t* foreign) /* in, own: foreign key constraint */ +{ + dict_table_t* for_table; + dict_table_t* ref_table; + dict_foreign_t* for_in_cache = NULL; + dict_index_t* index; + + ut_ad(mutex_own(&(dict_sys->mutex))); + + for_table = dict_table_check_if_in_cache_low( + foreign->foreign_table_name); + + ref_table = dict_table_check_if_in_cache_low( + foreign->referenced_table_name); + ut_a(for_table || ref_table); + + if (for_table) { + for_in_cache = dict_foreign_find(for_table, foreign->id); + } + + if (!for_in_cache && ref_table) { + for_in_cache = dict_foreign_find(ref_table, foreign->id); + } + + if (for_in_cache) { + /* Free the foreign object */ + mem_heap_free(foreign->heap); + } else { + for_in_cache = foreign; + } + + if (for_in_cache->referenced_table == NULL && ref_table) { + index = dict_foreign_find_index(ref_table, + for_in_cache->referenced_col_names, + for_in_cache->n_fields, + for_in_cache->foreign_index); + + if (index == NULL) { + if (for_in_cache == foreign) { + mem_heap_free(foreign->heap); + } + return(DB_CANNOT_ADD_CONSTRAINT); + } + + for_in_cache->referenced_table = ref_table; + for_in_cache->referenced_index = index; + UT_LIST_ADD_LAST(referenced_list, + ref_table->referenced_list, + for_in_cache); + } + + if (for_in_cache->foreign_table == NULL && for_table) { + index = dict_foreign_find_index(for_table, + for_in_cache->foreign_col_names, + for_in_cache->n_fields, + for_in_cache->referenced_index); + + if (index == NULL) { + if (for_in_cache == foreign) { + mem_heap_free(foreign->heap); + } + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + for_in_cache->foreign_table = for_table; + for_in_cache->foreign_index = index; + UT_LIST_ADD_LAST(foreign_list, + for_table->foreign_list, + for_in_cache); + } + + return(DB_SUCCESS); +} + +/************************************************************************* +Scans from pointer onwards. Stops if is at the start of a copy of +'string' where characters are compared without case sensitivity. Stops +also at '\0'. */ +static +char* +dict_scan_to( +/*=========*/ + + char* ptr, /* in: scan from */ + char* string) /* in: look for this */ +{ + ibool success; + ulint i; +loop: + if (*ptr == '\0') { + return(ptr); + } + + success = TRUE; + + for (i = 0; i < ut_strlen(string); i++) { + if (toupper((ulint)(ptr[i])) != toupper((ulint)(string[i]))) { + success = FALSE; + + break; + } + } + + if (success) { + + return(ptr); + } + + ptr++; + + goto loop; +} + +/************************************************************************* +Accepts a specified string. Comparisons are case-insensitive. */ +static +char* +dict_accept( +/*========*/ + /* out: if string was accepted, the pointer + is moved after that, else ptr is returned */ + char* ptr, /* in: scan from this */ + char* string, /* in: accept only this string as the next + non-whitespace string */ + ibool* success)/* out: TRUE if accepted */ +{ + char* old_ptr = ptr; + char* old_ptr2; + + *success = FALSE; + + while (isspace(*ptr)) { + ptr++; + } + + old_ptr2 = ptr; + + ptr = dict_scan_to(ptr, string); + + if (*ptr == '\0' || old_ptr2 != ptr) { + return(old_ptr); + } + + *success = TRUE; + + return(ptr + ut_strlen(string)); +} + +/************************************************************************* +Tries to scan a column name. */ +static +char* +dict_scan_col( +/*==========*/ + /* out: scanned to */ + char* ptr, /* in: scanned to */ + ibool* success,/* out: TRUE if success */ + dict_table_t* table, /* in: table in which the column is */ + dict_col_t** column, /* out: pointer to column if success */ + char** column_name)/* out: pointer to column->name if + success */ +{ + dict_col_t* col; + char* old_ptr; + ulint i; + + *success = FALSE; + + while (isspace(*ptr)) { + ptr++; + } + + if (*ptr == '\0') { + + return(ptr); + } + + old_ptr = ptr; + + while (!isspace(*ptr) && *ptr != ',' && *ptr != ')') { + ptr++; + } + + for (i = 0; i < dict_table_get_n_cols(table); i++) { + + col = dict_table_get_nth_col(table, i); + + if (ut_strlen(col->name) == (ulint)(ptr - old_ptr) + && 0 == ut_memcmp(col->name, old_ptr, + (ulint)(ptr - old_ptr))) { + + /* Found */ + + *success = TRUE; + *column = col; + *column_name = col->name; + + break; + } + } + + return(ptr); +} + +/************************************************************************* +Scans the referenced table name from an SQL string. */ +static +char* +dict_scan_table_name( +/*=================*/ + /* out: scanned to */ + char* ptr, /* in: scanned to */ + dict_table_t** table, /* out: table object or NULL if error */ + char* name) /* in: foreign key table name */ +{ + char* dot_ptr = NULL; + char* old_ptr; + ulint i; + char second_table_name[10000]; + + *table = NULL; + + while (isspace(*ptr)) { + ptr++; + } + + if (*ptr == '\0') { + + return(ptr); + } + + old_ptr = ptr; + + while (!isspace(*ptr) && *ptr != '(') { + if (*ptr == '.') { + dot_ptr = ptr; + } + + ptr++; + } + + if (ptr - old_ptr > 9000) { + return(old_ptr); + } + + if (dot_ptr == NULL) { + /* Copy the database name from 'name' to the start */ + for (i = 0;; i++) { + second_table_name[i] = name[i]; + if (name[i] == '/') { + i++; + break; + } + } + + ut_memcpy(second_table_name + i, old_ptr, ptr - old_ptr); + second_table_name[i + (ptr - old_ptr)] = '\0'; + } else { + ut_memcpy(second_table_name, old_ptr, ptr - old_ptr); + second_table_name[dot_ptr - old_ptr] = '/'; + second_table_name[ptr - old_ptr] = '\0'; + } + + *table = dict_table_get_low(second_table_name); + + return(ptr); +} + +/************************************************************************* +Returns the number of opening brackets '(' subtracted by the number +of closing brackets ')' between string and ptr. */ +static +int +dict_bracket_count( +/*===============*/ + /* out: bracket count */ + char* string, /* in: start of string */ + char* ptr) /* in: end of string */ +{ + int count = 0; + + while (string != ptr) { + if (*string == '(') { + count++; + } + if (*string == ')') { + count--; + } + + string++; + } + + return(count); +} + +/************************************************************************* +Scans a table create SQL string and adds to the data dictionary the foreign +key constraints declared in the string. This function should be called after +the indexes for a table have been created. Each foreign key constraint must +be accompanied with indexes in both participating tables. The indexes are +allowed to contain more fields than mentioned in the constraint. */ + +ulint +dict_create_foreign_constraints( +/*============================*/ + /* out: error code or DB_SUCCESS */ + trx_t* trx, /* in: transaction */ + char* sql_string, /* in: table create statement where + foreign keys are declared like: + FOREIGN KEY (a, b) REFERENCES table2(c, d), + table2 can be written also with the database + name before it: test.table2; the default + database id the database of parameter name */ + char* name) /* in: table full name in the normalized form + database_name/table_name */ +{ + dict_table_t* table; + dict_table_t* referenced_table; + dict_index_t* index; + dict_foreign_t* foreign; + char* ptr = sql_string; + ibool success; + ulint error; + ulint i; + dict_col_t* columns[1000]; + char* column_names[1000]; + + ut_ad(mutex_own(&(dict_sys->mutex))); + + table = dict_table_get_low(name); + + if (table == NULL) { + return(DB_ERROR); + } +loop: + ptr = dict_scan_to(ptr, "FOREIGN"); + + if (*ptr == '\0' || dict_bracket_count(sql_string, ptr) != 1) { + + /* The following call adds the foreign key constraints + to the data dictionary system tables on disk */ + + error = dict_create_add_foreigns_to_dictionary(table, trx); + + return(error); + } + + ptr = dict_accept(ptr, "FOREIGN", &success); + + if (!isspace(*ptr)) { + return(DB_CANNOT_ADD_CONSTRAINT); + } + + ptr = dict_accept(ptr, "KEY", &success); + + if (!success) { + goto loop; + } + + ptr = dict_accept(ptr, "(", &success); + + if (!success) { + goto loop; + } + + i = 0; + + /* Scan the columns in the first list */ +col_loop1: + ptr = dict_scan_col(ptr, &success, table, columns + i, + column_names + i); + if (!success) { + return(DB_CANNOT_ADD_CONSTRAINT); + } + + i++; + + ptr = dict_accept(ptr, ",", &success); + + if (success) { + goto col_loop1; + } + + ptr = dict_accept(ptr, ")", &success); + + if (!success) { + return(DB_CANNOT_ADD_CONSTRAINT); + } + + /* Try to find an index which contains the columns + as the first fields and in the right order */ + + index = dict_foreign_find_index(table, column_names, i, NULL); + + if (!index) { + return(DB_CANNOT_ADD_CONSTRAINT); + } + + ptr = dict_accept(ptr, "REFERENCES", &success); + + if (!success || !isspace(*ptr)) { + return(DB_CANNOT_ADD_CONSTRAINT); + } + + /* Let us create a constraint struct */ + + foreign = dict_mem_foreign_create(); + + foreign->foreign_table = table; + foreign->foreign_table_name = table->name; + foreign->foreign_index = index; + foreign->n_fields = i; + foreign->foreign_col_names = mem_heap_alloc(foreign->heap, + i * sizeof(void*)); + for (i = 0; i < foreign->n_fields; i++) { + foreign->foreign_col_names[i] = mem_heap_alloc(foreign->heap, + 1 + ut_strlen(columns[i]->name)); + ut_memcpy(foreign->foreign_col_names[i], columns[i]->name, + 1 + ut_strlen(columns[i]->name)); + } + + ptr = dict_scan_table_name(ptr, &referenced_table, name); + + if (!referenced_table) { + dict_foreign_free(foreign); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + ptr = dict_accept(ptr, "(", &success); + + if (!success) { + dict_foreign_free(foreign); + return(DB_CANNOT_ADD_CONSTRAINT); + } + + /* Scan the columns in the second list */ + i = 0; + +col_loop2: + ptr = dict_scan_col(ptr, &success, referenced_table, columns + i, + column_names + i); + i++; + + if (!success) { + dict_foreign_free(foreign); + return(DB_CANNOT_ADD_CONSTRAINT); + } + + ptr = dict_accept(ptr, ",", &success); + + if (success) { + goto col_loop2; + } + + ptr = dict_accept(ptr, ")", &success); + + if (!success || foreign->n_fields != i) { + dict_foreign_free(foreign); + + return(DB_CANNOT_ADD_CONSTRAINT); + } + + /* Try to find an index which contains the columns as the first fields + and in the right order, and the types are the same as in + foreign->foreign_index */ + + index = dict_foreign_find_index(referenced_table, column_names, i, + foreign->foreign_index); + + if (!index) { + dict_foreign_free(foreign); + return(DB_CANNOT_ADD_CONSTRAINT); + } + + foreign->referenced_index = index; + foreign->referenced_table = referenced_table; + + foreign->referenced_table_name = mem_heap_alloc(foreign->heap, + 1 + ut_strlen(referenced_table->name)); + + ut_memcpy(foreign->referenced_table_name, referenced_table->name, + 1 + ut_strlen(referenced_table->name)); + + foreign->referenced_col_names = mem_heap_alloc(foreign->heap, + i * sizeof(void*)); + for (i = 0; i < foreign->n_fields; i++) { + foreign->referenced_col_names[i] + = mem_heap_alloc(foreign->heap, + 1 + ut_strlen(columns[i]->name)); + ut_memcpy( + foreign->referenced_col_names[i], columns[i]->name, + 1 + ut_strlen(columns[i]->name)); + } + + /* We found an ok constraint definition: add to the lists */ + + UT_LIST_ADD_LAST(foreign_list, table->foreign_list, foreign); + UT_LIST_ADD_LAST(referenced_list, referenced_table->referenced_list, + foreign); + goto loop; +} + +/*==================== END OF FOREIGN KEY PROCESSING ====================*/ + /************************************************************************** Adds a stored procedure object to the dictionary cache. */ @@ -1733,77 +2521,127 @@ dict_tree_build_data_tuple( } /************************************************************************* -Calculates new estimates for table and index statistics. The statistics -are used in query optimization. */ +Calculates the minimum record length in an index. */ -void -dict_update_statistics( -/*===================*/ - dict_table_t* table) /* in: table */ +ulint +dict_index_calc_min_rec_len( +/*========================*/ + dict_index_t* index) /* in: index */ { - mem_heap_t* heap; - dict_index_t* index; - dtuple_t* start; - dtuple_t* end; - ulint n_rows; - ulint n_vals; - ulint size; - ulint sum_of_index_sizes = 0; - - /* Estimate the number of records in the clustered index */ - index = dict_table_get_first_index(table); - - heap = mem_heap_create(500); - - start = dtuple_create(heap, 0); - end = dtuple_create(heap, 0); + ulint sum = 0; + ulint i; - n_rows = btr_estimate_n_rows_in_range(index, start, PAGE_CUR_G, - end, PAGE_CUR_L); - mem_heap_free(heap); + for (i = 0; i < dict_index_get_n_fields(index); i++) { + sum += dtype_get_fixed_size(dict_index_get_nth_type(index, i)); + } - if (n_rows > 0) { - /* For small tables our estimate function tends to give - values 1 too big */ - n_rows--; + if (sum > 127) { + sum += 2 * dict_index_get_n_fields(index); + } else { + sum += dict_index_get_n_fields(index); } - mutex_enter(&(dict_sys->mutex)); + sum += REC_N_EXTRA_BYTES; - table->stat_last_estimate_counter = table->stat_modif_counter; - table->stat_n_rows = n_rows; + return(sum); +} - mutex_exit(&(dict_sys->mutex)); +/************************************************************************* +Calculates new estimates for table and index statistics. The statistics +are used in query optimization. */ + +void +dict_update_statistics_low( +/*=======================*/ + dict_table_t* table, /* in: table */ + ibool has_dict_mutex) /* in: TRUE if the caller has the + dictionary mutex */ +{ + dict_index_t* index; + ulint size; + ulint sum_of_index_sizes = 0; /* Find out the sizes of the indexes and how many different values for the key they approximately have */ - + + index = dict_table_get_first_index(table); + while (index) { - n_vals = btr_estimate_number_of_different_key_vals(index); size = btr_get_size(index, BTR_TOTAL_SIZE); + index->stat_index_size = size; + sum_of_index_sizes += size; - mutex_enter(&(dict_sys->mutex)); + size = btr_get_size(index, BTR_N_LEAF_PAGES); - index->stat_n_diff_key_vals = n_vals; - index->stat_index_size = size; + if (size == 0) { + /* The root node of the tree is a leaf */ + size = 1; + } - mutex_exit(&(dict_sys->mutex)); + index->stat_n_leaf_pages = size; + + btr_estimate_number_of_different_key_vals(index); index = dict_table_get_next_index(index); } index = dict_table_get_first_index(table); + table->stat_n_rows = index->stat_n_diff_key_vals[ + dict_index_get_n_unique(index)]; + table->stat_clustered_index_size = index->stat_index_size; table->stat_sum_of_other_index_sizes = sum_of_index_sizes - - index->stat_index_size; + - index->stat_index_size; table->stat_last_estimate_counter = table->stat_modif_counter; } +/************************************************************************* +Calculates new estimates for table and index statistics. The statistics +are used in query optimization. */ + +void +dict_update_statistics( +/*===================*/ + dict_table_t* table) /* in: table */ +{ + dict_update_statistics_low(table, FALSE); +} + +/************************************************************************** +Prints info of a foreign key constraint. */ +static +void +dict_foreign_print_low( +/*===================*/ + dict_foreign_t* foreign) /* in: foreign key constraint */ +{ + ulint i; + + ut_ad(mutex_own(&(dict_sys->mutex))); + + printf(" FOREIGN KEY CONSTRAINT %s: %s (", foreign->id, + foreign->foreign_table_name); + + for (i = 0; i < foreign->n_fields; i++) { + printf(" %s", foreign->foreign_col_names[i]); + } + + printf(" )\n"); + + printf(" REFERENCES %s (", foreign->referenced_table_name); + + for (i = 0; i < foreign->n_fields; i++) { + printf(" %s", foreign->referenced_col_names[i]); + } + + printf(" )\n"); +} + /************************************************************************** Prints a table data. */ @@ -1839,31 +2677,57 @@ dict_table_print_by_name( /************************************************************************** Prints a table data. */ -static + void dict_table_print_low( /*=================*/ dict_table_t* table) /* in: table */ { - ulint i; dict_index_t* index; + dict_foreign_t* foreign; + ulint i; ut_ad(mutex_own(&(dict_sys->mutex))); + dict_update_statistics_low(table, TRUE); + printf("--------------------------------------\n"); - printf("TABLE INFO: name %s, columns %lu, indexes %lu\n", table->name, - table->n_cols, UT_LIST_GET_LEN(table->indexes)); - for (i = 0; i < table->n_cols; i++) { - printf(" "); + printf( + "TABLE: name %s, id %lu %lu, columns %lu, indexes %lu, appr.rows %lu\n", + table->name, + ut_dulint_get_high(table->id), + ut_dulint_get_low(table->id), + table->n_cols, UT_LIST_GET_LEN(table->indexes), + (ulint)table->stat_n_rows); + printf(" COLUMNS: "); + + for (i = 0; i < table->n_cols - 1; i++) { dict_col_print_low(dict_table_get_nth_col(table, i)); + printf("; "); } + printf("\n"); + index = UT_LIST_GET_FIRST(table->indexes); while (index != NULL) { dict_index_print_low(index); index = UT_LIST_GET_NEXT(indexes, index); } + + foreign = UT_LIST_GET_FIRST(table->foreign_list); + + while (foreign != NULL) { + dict_foreign_print_low(foreign); + foreign = UT_LIST_GET_NEXT(foreign_list, foreign); + } + + foreign = UT_LIST_GET_FIRST(table->referenced_list); + + while (foreign != NULL) { + dict_foreign_print_low(foreign); + foreign = UT_LIST_GET_NEXT(referenced_list, foreign); + } } /************************************************************************** @@ -1879,7 +2743,7 @@ dict_col_print_low( ut_ad(mutex_own(&(dict_sys->mutex))); type = dict_col_get_type(col); - printf("COLUMN: name %s; ", col->name); + printf("%s: ", col->name); dtype_print(type); } @@ -1892,28 +2756,47 @@ dict_index_print_low( /*=================*/ dict_index_t* index) /* in: index */ { - ulint i; dict_tree_t* tree; + ib_longlong n_vals; + ulint i; ut_ad(mutex_own(&(dict_sys->mutex))); tree = index->tree; - + + if (index->n_user_defined_cols > 0) { + n_vals = index->stat_n_diff_key_vals[ + index->n_user_defined_cols]; + } else { + n_vals = index->stat_n_diff_key_vals[1]; + } + + printf( - "INDEX INFO: name %s, table name %s, fields %lu, type %lu\n", - index->name, index->table_name, index->n_fields, - index->type); - printf(" root node: space %lu, page number %lu\n", - tree->space, tree->page); + " INDEX: name %s, table name %s, id %lu %lu, fields %lu/%lu, type %lu\n", + index->name, index->table_name, + ut_dulint_get_high(tree->id), + ut_dulint_get_low(tree->id), + index->n_user_defined_cols, + index->n_fields, index->type); + printf( + " root page %lu, appr.key vals %lu, leaf pages %lu, size pages %lu\n", + tree->page, + (ulint)n_vals, + index->stat_n_leaf_pages, + index->stat_index_size); + printf(" FIELDS: "); + for (i = 0; i < index->n_fields; i++) { - printf(" "); dict_field_print_low(dict_index_get_nth_field(index, i)); } - btr_print_size(tree); + printf("\n"); + +/* btr_print_size(tree); */ - btr_print_tree(tree, 7); +/* btr_print_tree(tree, 7); */ } /************************************************************************** @@ -1926,6 +2809,5 @@ dict_field_print_low( { ut_ad(mutex_own(&(dict_sys->mutex))); - printf("FIELD: column name %s, order criterion %lu\n", field->name, - field->order); + printf(" %s", field->name); } |