diff options
Diffstat (limited to 'storage/xtradb/dict/dict0dict.c')
-rw-r--r-- | storage/xtradb/dict/dict0dict.c | 559 |
1 files changed, 387 insertions, 172 deletions
diff --git a/storage/xtradb/dict/dict0dict.c b/storage/xtradb/dict/dict0dict.c index 1d0517f5cc7..1ad540f47ab 100644 --- a/storage/xtradb/dict/dict0dict.c +++ b/storage/xtradb/dict/dict0dict.c @@ -53,7 +53,7 @@ UNIV_INTERN dict_index_t* dict_ind_compact; #include "rem0cmp.h" #include "row0merge.h" #include "m_ctype.h" /* my_isspace() */ -#include "ha_prototypes.h" /* innobase_strcasecmp() */ +#include "ha_prototypes.h" /* innobase_strcasecmp(), innobase_casedn_str()*/ #include "srv0start.h" /* SRV_LOG_SPACE_FIRST_ID */ #include <ctype.h> @@ -71,6 +71,18 @@ we need this; NOTE: a transaction which reserves this must keep book on the mode in trx_struct::dict_operation_lock_mode */ UNIV_INTERN rw_lock_t dict_operation_lock; +/* Keys to register rwlocks and mutexes with performance schema */ +#ifdef UNIV_PFS_RWLOCK +UNIV_INTERN mysql_pfs_key_t dict_operation_lock_key; +UNIV_INTERN mysql_pfs_key_t index_tree_rw_lock_key; +UNIV_INTERN mysql_pfs_key_t dict_table_stats_latch_key; +#endif /* UNIV_PFS_RWLOCK */ + +#ifdef UNIV_PFS_MUTEX +UNIV_INTERN mysql_pfs_key_t dict_sys_mutex_key; +UNIV_INTERN mysql_pfs_key_t dict_foreign_err_mutex_key; +#endif /* UNIV_PFS_MUTEX */ + #define DICT_HEAP_SIZE 100 /*!< initial memory heap size when creating a table or index object */ #define DICT_POOL_PER_TABLE_HASH 512 /*!< buffer pool max size per table @@ -81,9 +93,18 @@ UNIV_INTERN rw_lock_t dict_operation_lock; /** Identifies generated InnoDB foreign key names */ static char dict_ibfk[] = "_ibfk_"; -/** array of mutexes protecting dict_index_t::stat_n_diff_key_vals[] */ -#define DICT_INDEX_STAT_MUTEX_SIZE 32 -static mutex_t dict_index_stat_mutex[DICT_INDEX_STAT_MUTEX_SIZE]; +/** array of rw locks protecting +dict_table_t::stat_initialized +dict_table_t::stat_n_rows (*) +dict_table_t::stat_clustered_index_size +dict_table_t::stat_sum_of_other_index_sizes +dict_table_t::stat_modified_counter (*) +dict_table_t::indexes*::stat_n_diff_key_vals[] +dict_table_t::indexes*::stat_index_size +dict_table_t::indexes*::stat_n_leaf_pages +(*) those are not always protected for performance reasons */ +#define DICT_TABLE_STATS_LATCHES_SIZE 64 +static rw_lock_t dict_table_stats_latches[DICT_TABLE_STATS_LATCHES_SIZE]; /*******************************************************************//** Tries to find column names for the index and sets the col field of the @@ -244,43 +265,65 @@ dict_mutex_exit_for_mysql(void) mutex_exit(&(dict_sys->mutex)); } -/** Get the mutex that protects index->stat_n_diff_key_vals[] */ -#define GET_INDEX_STAT_MUTEX(index) \ - (&dict_index_stat_mutex[ut_fold_dulint(index->id) \ - % DICT_INDEX_STAT_MUTEX_SIZE]) +/** Get the latch that protects the stats of a given table */ +#define GET_TABLE_STATS_LATCH(table) \ + (&dict_table_stats_latches[ut_fold_ull(table->id) \ + % DICT_TABLE_STATS_LATCHES_SIZE]) /**********************************************************************//** -Lock the appropriate mutex to protect index->stat_n_diff_key_vals[]. -index->id is used to pick the right mutex and it should not change -before dict_index_stat_mutex_exit() is called on this index. */ +Lock the appropriate latch to protect a given table's statistics. +table->id is used to pick the corresponding latch from a global array of +latches. */ UNIV_INTERN void -dict_index_stat_mutex_enter( -/*========================*/ - const dict_index_t* index) /*!< in: index */ +dict_table_stats_lock( +/*==================*/ + const dict_table_t* table, /*!< in: table */ + ulint latch_mode) /*!< in: RW_S_LATCH or + RW_X_LATCH */ { - ut_ad(index != NULL); - ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); - ut_ad(index->cached); - ut_ad(!index->to_be_dropped); + ut_ad(table != NULL); + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); - mutex_enter(GET_INDEX_STAT_MUTEX(index)); + switch (latch_mode) { + case RW_S_LATCH: + rw_lock_s_lock(GET_TABLE_STATS_LATCH(table)); + break; + case RW_X_LATCH: + rw_lock_x_lock(GET_TABLE_STATS_LATCH(table)); + break; + case RW_NO_LATCH: + /* fall through */ + default: + ut_error; + } } /**********************************************************************//** -Unlock the appropriate mutex that protects index->stat_n_diff_key_vals[]. */ +Unlock the latch that has been locked by dict_table_stats_lock() */ UNIV_INTERN void -dict_index_stat_mutex_exit( -/*=======================*/ - const dict_index_t* index) /*!< in: index */ +dict_table_stats_unlock( +/*====================*/ + const dict_table_t* table, /*!< in: table */ + ulint latch_mode) /*!< in: RW_S_LATCH or + RW_X_LATCH */ { - ut_ad(index != NULL); - ut_ad(index->magic_n == DICT_INDEX_MAGIC_N); - ut_ad(index->cached); - ut_ad(!index->to_be_dropped); + ut_ad(table != NULL); + ut_ad(table->magic_n == DICT_TABLE_MAGIC_N); - mutex_exit(GET_INDEX_STAT_MUTEX(index)); + switch (latch_mode) { + case RW_S_LATCH: + rw_lock_s_unlock(GET_TABLE_STATS_LATCH(table)); + break; + case RW_X_LATCH: + rw_lock_x_unlock(GET_TABLE_STATS_LATCH(table)); + break; + case RW_NO_LATCH: + /* fall through */ + default: + ut_error; + } } /********************************************************************//** @@ -415,14 +458,14 @@ dict_index_t* dict_index_get_on_id_low( /*=====================*/ dict_table_t* table, /*!< in: table */ - dulint id) /*!< in: index id */ + index_id_t id) /*!< in: index id */ { dict_index_t* index; index = dict_table_get_first_index(table); while (index) { - if (0 == ut_dulint_cmp(id, index->id)) { + if (id == index->id) { /* Found */ return(index); @@ -564,12 +607,13 @@ UNIV_INTERN dict_table_t* dict_table_get_on_id( /*=================*/ - dulint table_id, /*!< in: table id */ - trx_t* trx) /*!< in: transaction handle */ + table_id_t table_id, /*!< in: table id */ + trx_t* trx) /*!< in: transaction handle */ { dict_table_t* table; - if (trx->dict_operation_lock_mode == RW_X_LATCH) { + if (table_id <= DICT_FIELDS_ID + || trx->dict_operation_lock_mode == RW_X_LATCH) { /* Note: An X latch implies that the transaction already owns the dictionary mutex. */ @@ -652,7 +696,7 @@ dict_init(void) dict_sys = mem_alloc(sizeof(dict_sys_t)); - mutex_create(&dict_sys->mutex, SYNC_DICT); + mutex_create(dict_sys_mutex_key, &dict_sys->mutex, SYNC_DICT); dict_sys->table_hash = hash_create(buf_pool_get_curr_size() / (DICT_POOL_PER_TABLE_HASH @@ -664,15 +708,18 @@ dict_init(void) UT_LIST_INIT(dict_sys->table_LRU); - rw_lock_create(&dict_operation_lock, SYNC_DICT_OPERATION); + rw_lock_create(dict_operation_lock_key, + &dict_operation_lock, SYNC_DICT_OPERATION); dict_foreign_err_file = os_file_create_tmpfile(); ut_a(dict_foreign_err_file); - mutex_create(&dict_foreign_err_mutex, SYNC_ANY_LATCH); + mutex_create(dict_foreign_err_mutex_key, + &dict_foreign_err_mutex, SYNC_ANY_LATCH); - for (i = 0; i < DICT_INDEX_STAT_MUTEX_SIZE; i++) { - mutex_create(&dict_index_stat_mutex[i], SYNC_INDEX_TREE); + for (i = 0; i < DICT_TABLE_STATS_LATCHES_SIZE; i++) { + rw_lock_create(dict_table_stats_latch_key, + &dict_table_stats_latches[i], SYNC_INDEX_TREE); } } @@ -704,13 +751,12 @@ dict_table_get( mutex_exit(&(dict_sys->mutex)); - if (table != NULL) { - if (!table->stat_initialized && !table->is_corrupt) { - /* If table->ibd_file_missing == TRUE, this will - print an error message and return without doing - anything. */ - dict_update_statistics(table, FALSE); - } + if (table != NULL && !table->is_corrupt) { + /* If table->ibd_file_missing == TRUE, this will + print an error message and return without doing + anything. */ + dict_update_statistics(table, TRUE /* only update stats + if they have not been initialized */, FALSE); } return(table); @@ -788,7 +834,7 @@ dict_table_add_to_cache( table->cached = TRUE; fold = ut_fold_string(table->name); - id_fold = ut_fold_dulint(table->id); + id_fold = ut_fold_ull(table->id); row_len = 0; for (i = 0; i < table->n_def; i++) { @@ -830,7 +876,7 @@ dict_table_add_to_cache( dict_table_t* table2; HASH_SEARCH(id_hash, dict_sys->table_id_hash, id_fold, dict_table_t*, table2, ut_ad(table2->cached), - ut_dulint_cmp(table2->id, table->id) == 0); + table2->id == table->id); ut_a(table2 == NULL); #ifdef UNIV_DEBUG @@ -865,7 +911,7 @@ UNIV_INTERN dict_index_t* dict_index_find_on_id_low( /*======================*/ - dulint id) /*!< in: index id */ + index_id_t id) /*!< in: index id */ { dict_table_t* table; dict_index_t* index; @@ -876,7 +922,7 @@ dict_index_find_on_id_low( index = dict_table_get_first_index(table); while (index) { - if (0 == ut_dulint_cmp(id, index->id)) { + if (id == index->id) { /* Found */ return(index); @@ -907,7 +953,7 @@ dict_table_rename_in_cache( dict_foreign_t* foreign; dict_index_t* index; ulint fold; - char old_name[MAX_TABLE_NAME_LEN + 1]; + char old_name[MAX_FULL_NAME_LEN + 1]; ut_ad(table); ut_ad(mutex_own(&(dict_sys->mutex))); @@ -919,7 +965,7 @@ dict_table_rename_in_cache( ut_print_timestamp(stderr); fprintf(stderr, "InnoDB: too long table name: '%s', " "max length is %d\n", table->name, - MAX_TABLE_NAME_LEN); + MAX_FULL_NAME_LEN); ut_error; } @@ -969,11 +1015,11 @@ dict_table_rename_in_cache( ut_fold_string(old_name), table); if (strlen(new_name) > strlen(table->name)) { - /* We allocate MAX_TABLE_NAME_LEN+1 bytes here to avoid + /* We allocate MAX_FULL_NAME_LEN + 1 bytes here to avoid memory fragmentation, we assume a repeated calls of ut_realloc() with the same size do not cause fragmentation */ - ut_a(strlen(new_name) <= MAX_TABLE_NAME_LEN); - table->name = ut_realloc(table->name, MAX_TABLE_NAME_LEN + 1); + ut_a(strlen(new_name) <= MAX_FULL_NAME_LEN); + table->name = ut_realloc(table->name, MAX_FULL_NAME_LEN + 1); } memcpy(table->name, new_name, strlen(new_name) + 1); @@ -1040,13 +1086,13 @@ dict_table_rename_in_cache( /* 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); + foreign->foreign_table_name = mem_heap_strdup( + foreign->heap, table->name); + dict_mem_foreign_table_name_lookup_set(foreign, TRUE); + } else { + strcpy(foreign->foreign_table_name, table->name); + dict_mem_foreign_table_name_lookup_set(foreign, FALSE); } - - strcpy(foreign->foreign_table_name, table->name); - if (strchr(foreign->id, '/')) { ulint db_len; char* old_id; @@ -1112,12 +1158,14 @@ dict_table_rename_in_cache( /* Allocate a longer name buffer; TODO: store buf len to save memory */ - foreign->referenced_table_name = mem_heap_alloc( - foreign->heap, strlen(table->name) + 1); + foreign->referenced_table_name = mem_heap_strdup( + foreign->heap, table->name); + dict_mem_referenced_table_name_lookup_set(foreign, TRUE); + } else { + /* Use the same buffer */ + strcpy(foreign->referenced_table_name, table->name); + dict_mem_referenced_table_name_lookup_set(foreign, FALSE); } - - strcpy(foreign->referenced_table_name, table->name); - foreign = UT_LIST_GET_NEXT(referenced_list, foreign); } @@ -1132,7 +1180,7 @@ void dict_table_change_id_in_cache( /*==========================*/ dict_table_t* table, /*!< in/out: table object already in cache */ - dulint new_id) /*!< in: new id to set */ + table_id_t new_id) /*!< in: new id to set */ { ut_ad(table); ut_ad(mutex_own(&(dict_sys->mutex))); @@ -1141,12 +1189,12 @@ dict_table_change_id_in_cache( /* Remove the table from the hash table of id's */ HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, - ut_fold_dulint(table->id), table); + ut_fold_ull(table->id), table); table->id = new_id; /* Add the table back to the hash table */ HASH_INSERT(dict_table_t, id_hash, dict_sys->table_id_hash, - ut_fold_dulint(table->id), table); + ut_fold_ull(table->id), table); } /**********************************************************************//** @@ -1202,7 +1250,7 @@ dict_table_remove_from_cache( HASH_DELETE(dict_table_t, name_hash, dict_sys->table_hash, ut_fold_string(table->name), table); HASH_DELETE(dict_table_t, id_hash, dict_sys->table_id_hash, - ut_fold_dulint(table->id), table); + ut_fold_ull(table->id), table); /* Remove table from LRU list of tables */ UT_LIST_REMOVE(table_LRU, dict_sys->table_LRU, table); @@ -1367,36 +1415,63 @@ dict_index_too_big_for_undo( ulint fixed_size = dict_col_get_fixed_size(col, dict_table_is_comp(table)); + ulint max_prefix + = col->max_prefix; if (fixed_size) { /* Fixed-size columns are stored locally. */ max_size = fixed_size; } else if (max_size <= BTR_EXTERN_FIELD_REF_SIZE * 2) { /* Short columns are stored locally. */ - } else if (!col->ord_part) { + } else if (!col->ord_part + || (col->max_prefix + < (ulint) DICT_MAX_FIELD_LEN_BY_FORMAT(table))) { /* See if col->ord_part would be set - because of new_index. */ + because of new_index. Also check if the new + index could have longer prefix on columns + that already had ord_part set */ ulint j; for (j = 0; j < new_index->n_uniq; j++) { if (dict_index_get_nth_col( new_index, j) == col) { + const dict_field_t* field + = dict_index_get_nth_field( + new_index, j); + + if (field->prefix_len + > col->max_prefix) { + max_prefix = + field->prefix_len; + } goto is_ord_part; } } + if (col->ord_part) { + goto is_ord_part; + } + /* This is not an ordering column in any index. Thus, it can be stored completely externally. */ max_size = BTR_EXTERN_FIELD_REF_SIZE; } else { + ulint max_field_len; is_ord_part: + max_field_len = DICT_MAX_FIELD_LEN_BY_FORMAT(table); + /* This is an ordering column in some index. A long enough prefix must be written to the undo log. See trx_undo_page_fetch_ext(). */ + max_size = ut_min(max_size, max_field_len); + + /* We only store the needed prefix length in undo log */ + if (max_prefix) { + ut_ad(dict_table_get_format(table) + >= DICT_TF_FORMAT_ZIP); - if (max_size > REC_MAX_INDEX_COL_LEN) { - max_size = REC_MAX_INDEX_COL_LEN; + max_size = ut_min(max_prefix, max_size); } max_size += BTR_EXTERN_FIELD_REF_SIZE; @@ -1650,15 +1725,16 @@ too_big: /* In dtuple_convert_big_rec(), variable-length columns that are longer than BTR_EXTERN_FIELD_REF_SIZE * 2 may be chosen for external storage. If the column appears - in an ordering column of an index, a longer prefix of - REC_MAX_INDEX_COL_LEN will be copied to the undo log - by trx_undo_page_report_modify() and + in an ordering column of an index, a longer prefix determined + by dict_max_field_len_store_undo() will be copied to the undo + log by trx_undo_page_report_modify() and trx_undo_page_fetch_ext(). It suffices to check the capacity of the undo log whenever new_index includes a column prefix on a column that may be stored externally. */ if (field->prefix_len /* prefix index */ - && !col->ord_part /* not yet ordering column */ + && (!col->ord_part /* not yet ordering column */ + || field->prefix_len > col->max_prefix) && !dict_col_get_fixed_size(col, TRUE) /* variable-length */ && dict_col_get_max_size(col) > BTR_EXTERN_FIELD_REF_SIZE * 2 /* long enough */) { @@ -1675,11 +1751,17 @@ too_big: } undo_size_ok: - /* Flag the ordering columns */ + /* Flag the ordering columns and also set column max_prefix */ for (i = 0; i < n_ord; i++) { + const dict_field_t* field + = dict_index_get_nth_field(new_index, i); + + field->col->ord_part = 1; - dict_index_get_nth_field(new_index, i)->col->ord_part = 1; + if (field->prefix_len > field->col->max_prefix) { + field->col->max_prefix = field->prefix_len; + } } /* Add the new index as the last index for the table */ @@ -1694,7 +1776,8 @@ undo_size_ok: new_index->stat_n_leaf_pages = 1; new_index->page = page_no; - rw_lock_create(&new_index->lock, SYNC_INDEX_TREE); + rw_lock_create(index_tree_rw_lock_key, &new_index->lock, + SYNC_INDEX_TREE); if (!UNIV_UNLIKELY(new_index->type & DICT_UNIVERSAL)) { @@ -1702,6 +1785,12 @@ undo_size_ok: new_index->heap, (1 + dict_index_get_n_unique(new_index)) * sizeof(ib_int64_t)); + + new_index->stat_n_non_null_key_vals = mem_heap_zalloc( + new_index->heap, + (1 + dict_index_get_n_unique(new_index)) + * sizeof(*new_index->stat_n_non_null_key_vals)); + /* Give some sensible values to stat_n_... in case we do not calculate statistics quickly enough */ @@ -1756,7 +1845,7 @@ dict_index_remove_from_cache( zero. */ for (;;) { - ulint ref_count = btr_search_info_get_ref_count(info); + ulint ref_count = btr_search_info_get_ref_count(info, index->id); if (ref_count == 0) { break; } @@ -1880,14 +1969,14 @@ dict_index_add_col( variable-length fields, so that the extern flag can be embedded in the length word. */ - if (field->fixed_len > DICT_MAX_INDEX_COL_LEN) { + if (field->fixed_len > DICT_MAX_FIXED_COL_LEN) { field->fixed_len = 0; } -#if DICT_MAX_INDEX_COL_LEN != 768 +#if DICT_MAX_FIXED_COL_LEN != 768 /* The comparison limit above must be constant. If it were changed, the disk format of some fixed-length columns would change, which would be a disaster. */ -# error "DICT_MAX_INDEX_COL_LEN != 768" +# error "DICT_MAX_FIXED_COL_LEN != 768" #endif if (!(col->prtype & DATA_NOT_NULL)) { @@ -2526,8 +2615,7 @@ dict_table_get_index_by_max_id( /* We found a matching index, select the index with the higher id*/ - if (!found - || ut_dulint_cmp(index->id, found->id) > 0) { + if (!found || index->id > found->id) { found = index; } @@ -2606,10 +2694,10 @@ dict_foreign_add_to_cache( ut_ad(mutex_own(&(dict_sys->mutex))); for_table = dict_table_check_if_in_cache_low( - foreign->foreign_table_name); + foreign->foreign_table_name_lookup); ref_table = dict_table_check_if_in_cache_low( - foreign->referenced_table_name); + foreign->referenced_table_name_lookup); ut_a(for_table || ref_table); if (for_table) { @@ -2726,7 +2814,7 @@ dict_scan_to( quote = '\0'; } else if (quote) { /* Within quotes: do nothing. */ - } else if (*ptr == '`' || *ptr == '"') { + } else if (*ptr == '`' || *ptr == '"' || *ptr == '\'') { /* Starting quote: remember the quote character. */ quote = *ptr; } else { @@ -3038,19 +3126,25 @@ dict_scan_table_name( memcpy(ref, database_name, database_name_len); ref[database_name_len] = '/'; memcpy(ref + database_name_len + 1, table_name, table_name_len + 1); -#ifndef __WIN__ - if (srv_lower_case_table_names) { -#endif /* !__WIN__ */ - /* The table name is always put to lower case on Windows. */ + + /* Values; 0 = Store and compare as given; case sensitive + 1 = Store and compare in lower; case insensitive + 2 = Store as given, compare in lower; case semi-sensitive */ + if (innobase_get_lower_case_table_names() == 2) { innobase_casedn_str(ref); -#ifndef __WIN__ + *table = dict_table_get_low(ref); + memcpy(ref, database_name, database_name_len); + ref[database_name_len] = '/'; + memcpy(ref + database_name_len + 1, table_name, table_name_len + 1); + } else { + if (innobase_get_lower_case_table_names() == 1) { + innobase_casedn_str(ref); + } + *table = dict_table_get_low(ref); } -#endif /* !__WIN__ */ *success = TRUE; *ref_name = ref; - *table = dict_table_get_low(ref); - return(ptr); } @@ -3506,7 +3600,7 @@ col_loop1: start_of_latest_foreign); mutex_exit(&dict_foreign_err_mutex); - return(DB_CANNOT_ADD_CONSTRAINT); + return(DB_CHILD_NO_INDEX); } ptr = dict_accept(cs, ptr, "REFERENCES", &success); @@ -3539,8 +3633,10 @@ col_loop1: } foreign->foreign_table = table; - foreign->foreign_table_name = mem_heap_strdup(foreign->heap, - table->name); + foreign->foreign_table_name = mem_heap_strdup( + foreign->heap, table->name); + dict_mem_foreign_table_name_lookup_set(foreign, TRUE); + foreign->foreign_index = index; foreign->n_fields = (unsigned int) i; foreign->foreign_col_names = mem_heap_alloc(foreign->heap, @@ -3787,7 +3883,7 @@ try_find_index: start_of_latest_foreign); mutex_exit(&dict_foreign_err_mutex); - return(DB_CANNOT_ADD_CONSTRAINT); + return(DB_PARENT_NO_INDEX); } } else { ut_a(trx->check_foreigns == FALSE); @@ -3797,8 +3893,9 @@ try_find_index: foreign->referenced_index = index; foreign->referenced_table = referenced_table; - foreign->referenced_table_name - = mem_heap_strdup(foreign->heap, referenced_table_name); + foreign->referenced_table_name = mem_heap_strdup( + foreign->heap, referenced_table_name); + dict_mem_referenced_table_name_lookup_set(foreign, TRUE); foreign->referenced_col_names = mem_heap_alloc(foreign->heap, i * sizeof(void*)); @@ -4015,7 +4112,7 @@ UNIV_INTERN dict_index_t* dict_index_get_if_in_cache_low( /*===========================*/ - dulint index_id) /*!< in: index id */ + index_id_t index_id) /*!< in: index id */ { ut_ad(mutex_own(&(dict_sys->mutex))); @@ -4030,7 +4127,7 @@ UNIV_INTERN dict_index_t* dict_index_get_if_in_cache( /*=======================*/ - dulint index_id) /*!< in: index id */ + index_id_t index_id) /*!< in: index id */ { dict_index_t* index; @@ -4301,15 +4398,18 @@ dict_reload_statistics( ulint key_cols; ulint n_cols; const rec_t* rec; + ulint n_fields; const byte* field; ulint len; ib_int64_t* stat_n_diff_key_vals_tmp; + ib_int64_t* stat_n_non_null_key_vals_tmp; byte* buf; ulint i; mtr_t mtr; n_cols = dict_index_get_n_unique(index); stat_n_diff_key_vals_tmp = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t)); + stat_n_non_null_key_vals_tmp = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t)); sys_stats = dict_sys->sys_stats; sys_index = UT_LIST_GET_FIRST(sys_stats->indexes); @@ -4332,11 +4432,11 @@ dict_reload_statistics( rec = btr_pcur_get_rec(&pcur); if (!btr_pcur_is_on_user_rec(&pcur) - || ut_dulint_cmp(mach_read_from_8(rec_get_nth_field_old(rec, 0, &len)), - index->id)) { + || mach_read_from_8(rec_get_nth_field_old(rec, 0, &len)) + != index->id) { /* not found: even 1 if not found should not be alowed */ fprintf(stderr, "InnoDB: Warning: stats for %s/%s (%lu/%lu)" - " not fonund in SYS_STATS\n", + " not found in SYS_STATS\n", index->table_name, index->name, i, n_cols); btr_pcur_close(&pcur); mtr_commit(&mtr); @@ -4345,9 +4445,13 @@ dict_reload_statistics( } if (rec_get_deleted_flag(rec, 0)) { + /* don't count */ + i--; goto next_rec; } + n_fields = rec_get_n_fields_old(rec); + field = rec_get_nth_field_old(rec, 1, &len); ut_a(len == 4); @@ -4358,7 +4462,22 @@ dict_reload_statistics( field = rec_get_nth_field_old(rec, DICT_SYS_STATS_DIFF_VALS_FIELD, &len); ut_a(len == 8); - stat_n_diff_key_vals_tmp[i] = ut_conv_dulint_to_longlong(mach_read_from_8(field)); + stat_n_diff_key_vals_tmp[i] = mach_read_from_8(field); + + if (n_fields > DICT_SYS_STATS_NON_NULL_VALS_FIELD) { + field = rec_get_nth_field_old(rec, DICT_SYS_STATS_NON_NULL_VALS_FIELD, &len); + ut_a(len == 8); + + stat_n_non_null_key_vals_tmp[i] = mach_read_from_8(field); + } else { + /* not enough fields: should be older */ + fprintf(stderr, "InnoDB: Notice: stats for %s/%s (%lu/%lu)" + " in SYS_STATS seems older format. " + "Please execute ANALYZE TABLE for it.\n", + index->table_name, index->name, i, n_cols); + + stat_n_non_null_key_vals_tmp[i] = ((ib_int64_t)(-1)); + } next_rec: btr_pcur_move_to_next_user_rec(&pcur, &mtr); } @@ -4366,11 +4485,15 @@ next_rec: btr_pcur_close(&pcur); mtr_commit(&mtr); - dict_index_stat_mutex_enter(index); for (i = 0; i <= n_cols; i++) { index->stat_n_diff_key_vals[i] = stat_n_diff_key_vals_tmp[i]; + if (stat_n_non_null_key_vals_tmp[i] == ((ib_int64_t)(-1))) { + /* approximate value */ + index->stat_n_non_null_key_vals[i] = stat_n_diff_key_vals_tmp[n_cols]; + } else { + index->stat_n_non_null_key_vals[i] = stat_n_non_null_key_vals_tmp[i]; + } } - dict_index_stat_mutex_exit(index); } /*===========================================*/ @@ -4414,21 +4537,23 @@ dict_store_statistics( ulint n_cols; ulint rests; const rec_t* rec; + ulint n_fields; const byte* field; ulint len; ib_int64_t* stat_n_diff_key_vals_tmp; + ib_int64_t* stat_n_non_null_key_vals_tmp; byte* buf; ulint i; mtr_t mtr; n_cols = dict_index_get_n_unique(index); stat_n_diff_key_vals_tmp = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t)); + stat_n_non_null_key_vals_tmp = mem_heap_zalloc(heap, (n_cols + 1) * sizeof(ib_int64_t)); - dict_index_stat_mutex_enter(index); for (i = 0; i <= n_cols; i++) { stat_n_diff_key_vals_tmp[i] = index->stat_n_diff_key_vals[i]; + stat_n_non_null_key_vals_tmp[i] = index->stat_n_non_null_key_vals[i]; } - dict_index_stat_mutex_exit(index); sys_stats = dict_sys->sys_stats; sys_index = UT_LIST_GET_FIRST(sys_stats->indexes); @@ -4452,15 +4577,29 @@ dict_store_statistics( rec = btr_pcur_get_rec(&pcur); if (!btr_pcur_is_on_user_rec(&pcur) - || ut_dulint_cmp(mach_read_from_8(rec_get_nth_field_old(rec, 0, &len)), - index->id)) { + || mach_read_from_8(rec_get_nth_field_old(rec, 0, &len)) + != index->id) { /* not found */ - btr_pcur_close(&pcur); - mtr_commit(&mtr); + + break; } + btr_pcur_store_position(&pcur, &mtr); + if (rec_get_deleted_flag(rec, 0)) { + /* don't count */ + i--; + goto next_rec; + } + + n_fields = rec_get_n_fields_old(rec); + + if (n_fields <= DICT_SYS_STATS_NON_NULL_VALS_FIELD) { + /* not update for the older smaller format */ + fprintf(stderr, "InnoDB: Notice: stats for %s/%s (%lu/%lu)" + " in SYS_STATS seems older format. Please ANALYZE TABLE it.\n", + index->table_name, index->name, i, n_cols); goto next_rec; } @@ -4472,14 +4611,20 @@ dict_store_statistics( field = rec_get_nth_field_old(rec, DICT_SYS_STATS_DIFF_VALS_FIELD, &len); ut_a(len == 8); - mlog_write_dulint((byte*)field, - ut_dulint_create((ulint) (stat_n_diff_key_vals_tmp[key_cols] >> 32), - (ulint) stat_n_diff_key_vals_tmp[key_cols] & 0xFFFFFFFF), - &mtr); + mlog_write_ull((byte*)field, stat_n_diff_key_vals_tmp[key_cols], &mtr); + + field = rec_get_nth_field_old(rec, DICT_SYS_STATS_NON_NULL_VALS_FIELD, &len); + ut_a(len == 8); + + mlog_write_ull((byte*)field, stat_n_non_null_key_vals_tmp[key_cols], &mtr); rests--; next_rec: + mtr_commit(&mtr); + mtr_start(&mtr); + btr_pcur_restore_position(BTR_MODIFY_LEAF, &pcur, &mtr); + btr_pcur_move_to_next_user_rec(&pcur, &mtr); } btr_pcur_close(&pcur); @@ -4504,12 +4649,13 @@ Calculates new estimates for table and index statistics. The statistics are used in query optimization. */ UNIV_INTERN void -dict_update_statistics_low( -/*=======================*/ +dict_update_statistics( +/*===================*/ dict_table_t* table, /*!< in/out: table */ - ibool has_dict_mutex __attribute__((unused)), - /*!< in: TRUE if the caller has the - dictionary mutex */ + ibool only_calc_if_missing_stats,/*!< in: only + update/recalc the stats if they have + not been initialized yet, otherwise + do nothing */ ibool sync) /*!< in: TRUE if must update SYS_STATS */ { dict_index_t* index; @@ -4528,15 +4674,19 @@ dict_update_statistics_low( } if (srv_use_sys_stats_table && !((table->flags >> DICT_TF2_SHIFT) & DICT_TF2_TEMPORARY) && !sync) { + dict_table_stats_lock(table, RW_X_LATCH); + /* reload statistics from SYS_STATS table */ if (dict_reload_statistics(table, &sum_of_index_sizes)) { /* success */ #ifdef UNIV_DEBUG - fprintf(stderr, "InnoDB: DEBUG: reload_statistics is scceeded for %s.\n", + fprintf(stderr, "InnoDB: DEBUG: reload_statistics succeeded for %s.\n", table->name); #endif goto end; } + + dict_table_stats_unlock(table, RW_X_LATCH); } #ifdef UNIV_DEBUG fprintf(stderr, "InnoDB: DEBUG: update_statistics for %s.\n", @@ -4555,17 +4705,26 @@ dict_update_statistics_low( return; } - do { - if (table->is_corrupt) { - ut_a(srv_pass_corrupt_table); - return; - } + dict_table_stats_lock(table, RW_X_LATCH); + if (only_calc_if_missing_stats && table->stat_initialized) { + dict_table_stats_unlock(table, RW_X_LATCH); + return; + } + + do { if (UNIV_LIKELY (srv_force_recovery < SRV_FORCE_NO_IBUF_MERGE || (srv_force_recovery < SRV_FORCE_NO_LOG_REDO && dict_index_is_clust(index)))) { ulint size; + + if (table->is_corrupt) { + ut_a(srv_pass_corrupt_table); + dict_table_stats_unlock(table, RW_X_LATCH); + return; + } + size = btr_get_size(index, BTR_TOTAL_SIZE); index->stat_index_size = size; @@ -4597,6 +4756,10 @@ dict_update_statistics_low( for (i = dict_index_get_n_unique(index); i; ) { index->stat_n_diff_key_vals[i--] = 1; } + + memset(index->stat_n_non_null_key_vals, 0, + (1 + dict_index_get_n_unique(index)) + * sizeof(*index->stat_n_non_null_key_vals)); } index = dict_table_get_next_index(index); @@ -4609,13 +4772,9 @@ dict_update_statistics_low( end: index = dict_table_get_first_index(table); - dict_index_stat_mutex_enter(index); - table->stat_n_rows = index->stat_n_diff_key_vals[ dict_index_get_n_unique(index)]; - dict_index_stat_mutex_exit(index); - table->stat_clustered_index_size = index->stat_index_size; table->stat_sum_of_other_index_sizes = sum_of_index_sizes @@ -4624,19 +4783,80 @@ end: table->stat_initialized = TRUE; table->stat_modified_counter = 0; + + dict_table_stats_unlock(table, RW_X_LATCH); } /*********************************************************************//** -Calculates new estimates for table and index statistics. The statistics -are used in query optimization. */ +*/ UNIV_INTERN -void -dict_update_statistics( -/*===================*/ - dict_table_t* table, /*!< in/out: table */ - ibool sync) +ibool +dict_is_older_statistics( +/*=====================*/ + dict_index_t* index) { - dict_update_statistics_low(table, FALSE, sync); + mem_heap_t* heap; + dict_table_t* sys_stats; + dict_index_t* sys_index; + btr_pcur_t pcur; + dtuple_t* tuple; + dfield_t* dfield; + const rec_t* rec; + ulint n_fields; + ulint len; + byte* buf; + mtr_t mtr; + + heap = mem_heap_create(100); + + sys_stats = dict_sys->sys_stats; + sys_index = UT_LIST_GET_FIRST(sys_stats->indexes); + ut_a(!dict_table_is_comp(sys_stats)); + + 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); + + mtr_start(&mtr); + + btr_pcur_open_on_user_rec(sys_index, tuple, PAGE_CUR_GE, + BTR_SEARCH_LEAF, &pcur, &mtr); + +next_rec: + rec = btr_pcur_get_rec(&pcur); + + if (!btr_pcur_is_on_user_rec(&pcur) + || mach_read_from_8(rec_get_nth_field_old(rec, 0, &len)) + != index->id) { + /* not found */ + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + /* no statistics == not older statistics */ + return(FALSE); + } + + if (rec_get_deleted_flag(rec, 0)) { + btr_pcur_move_to_next_user_rec(&pcur, &mtr); + goto next_rec; + } + + n_fields = rec_get_n_fields_old(rec); + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + mem_heap_free(heap); + + if (n_fields > DICT_SYS_STATS_NON_NULL_VALS_FIELD) { + return(FALSE); + } else { + return(TRUE); + } } /**********************************************************************//** @@ -4717,16 +4937,17 @@ dict_table_print_low( ut_ad(mutex_own(&(dict_sys->mutex))); if (srv_stats_auto_update) - dict_update_statistics_low(table, TRUE, FALSE); + dict_update_statistics(table, FALSE /* update even if initialized */, FALSE); + + dict_table_stats_lock(table, RW_S_LATCH); fprintf(stderr, "--------------------------------------\n" - "TABLE: name %s, id %lu %lu, flags %lx, columns %lu," + "TABLE: name %s, id %llu, flags %lx, columns %lu," " indexes %lu, appr.rows %lu\n" " COLUMNS: ", table->name, - (ulong) ut_dulint_get_high(table->id), - (ulong) ut_dulint_get_low(table->id), + (ullint) table->id, (ulong) table->flags, (ulong) table->n_cols, (ulong) UT_LIST_GET_LEN(table->indexes), @@ -4746,6 +4967,8 @@ dict_table_print_low( index = UT_LIST_GET_NEXT(indexes, index); } + dict_table_stats_unlock(table, RW_S_LATCH); + foreign = UT_LIST_GET_FIRST(table->foreign_list); while (foreign != NULL) { @@ -4791,12 +5014,9 @@ dict_index_print_low( { ib_int64_t n_vals; ulint i; - const char* type_string; ut_ad(mutex_own(&(dict_sys->mutex))); - dict_index_stat_mutex_enter(index); - if (index->n_user_defined_cols > 0) { n_vals = index->stat_n_diff_key_vals[ index->n_user_defined_cols]; @@ -4804,25 +5024,14 @@ dict_index_print_low( n_vals = index->stat_n_diff_key_vals[1]; } - dict_index_stat_mutex_exit(index); - - if (dict_index_is_clust(index)) { - type_string = "clustered index"; - } else if (dict_index_is_unique(index)) { - type_string = "unique index"; - } else { - type_string = "secondary index"; - } - fprintf(stderr, - " INDEX: name %s, id %lu %lu, fields %lu/%lu," + " INDEX: name %s, id %llu, fields %lu/%lu," " uniq %lu, type %lu\n" " root page %lu, appr.key vals %lu," " leaf pages %lu, size pages %lu\n" " FIELDS: ", index->name, - (ulong) ut_dulint_get_high(index->id), - (ulong) ut_dulint_get_low(index->id), + (ullint) index->id, (ulong) index->n_user_defined_cols, (ulong) index->n_fields, (ulong) index->n_uniq, @@ -4909,8 +5118,8 @@ dict_print_info_on_foreign_key_in_create_format( fputs(") REFERENCES ", file); - if (dict_tables_have_same_db(foreign->foreign_table_name, - foreign->referenced_table_name)) { + if (dict_tables_have_same_db(foreign->foreign_table_name_lookup, + foreign->referenced_table_name_lookup)) { /* Do not print the database name of the referenced table */ ut_print_name(file, trx, TRUE, dict_remove_db_name( @@ -5157,7 +5366,8 @@ void dict_table_replace_index_in_foreign_list( /*=====================================*/ dict_table_t* table, /*!< in/out: table */ - dict_index_t* index) /*!< in: index to be replaced */ + dict_index_t* index, /*!< in: index to be replaced */ + const trx_t* trx) /*!< in: transaction handle */ { dict_foreign_t* foreign; @@ -5168,7 +5378,13 @@ dict_table_replace_index_in_foreign_list( if (foreign->foreign_index == index) { dict_index_t* new_index = dict_foreign_find_equiv_index(foreign); - ut_a(new_index); + + /* There must exist an alternative index if + check_foreigns (FOREIGN_KEY_CHECKS) is on, + since ha_innobase::prepare_drop_index had done + the check before we reach here. */ + + ut_a(new_index || !trx->check_foreigns); foreign->foreign_index = new_index; } @@ -5194,8 +5410,7 @@ dict_table_get_index_on_name_and_min_id( while (index != NULL) { if (ut_strcmp(index->name, name) == 0) { - if (!min_index - || ut_dulint_cmp(index->id, min_index->id) < 0) { + if (!min_index || index->id < min_index->id) { min_index = index; } @@ -5302,8 +5517,8 @@ dict_close(void) mem_free(dict_sys); dict_sys = NULL; - for (i = 0; i < DICT_INDEX_STAT_MUTEX_SIZE; i++) { - mutex_free(&dict_index_stat_mutex[i]); + for (i = 0; i < DICT_TABLE_STATS_LATCHES_SIZE; i++) { + rw_lock_free(&dict_table_stats_latches[i]); } } |