diff options
Diffstat (limited to 'storage/innobase/dict')
-rw-r--r-- | storage/innobase/dict/dict0boot.cc | 2 | ||||
-rw-r--r-- | storage/innobase/dict/dict0crea.cc | 203 | ||||
-rw-r--r-- | storage/innobase/dict/dict0defrag_bg.cc | 403 | ||||
-rw-r--r-- | storage/innobase/dict/dict0dict.cc | 318 | ||||
-rw-r--r-- | storage/innobase/dict/dict0load.cc | 280 | ||||
-rw-r--r-- | storage/innobase/dict/dict0mem.cc | 285 | ||||
-rw-r--r-- | storage/innobase/dict/dict0stats.cc | 149 | ||||
-rw-r--r-- | storage/innobase/dict/dict0stats_bg.cc | 274 |
8 files changed, 1334 insertions, 580 deletions
diff --git a/storage/innobase/dict/dict0boot.cc b/storage/innobase/dict/dict0boot.cc index 4ffcf640a26..5c4e2049723 100644 --- a/storage/innobase/dict/dict0boot.cc +++ b/storage/innobase/dict/dict0boot.cc @@ -516,9 +516,9 @@ dict_boot(void) dict_load_sys_table(dict_sys->sys_indexes); dict_load_sys_table(dict_sys->sys_fields); } + } mutex_exit(&dict_sys->mutex); - } return(err); } diff --git a/storage/innobase/dict/dict0crea.cc b/storage/innobase/dict/dict0crea.cc index 5c7d41a9edb..31952424119 100644 --- a/storage/innobase/dict/dict0crea.cc +++ b/storage/innobase/dict/dict0crea.cc @@ -494,8 +494,11 @@ dict_build_tablespace_for_table( /* Determine the tablespace flags. */ bool is_temp = dict_table_is_temporary(table); + bool is_encrypted = dict_table_is_encrypted(table); bool has_data_dir = DICT_TF_HAS_DATA_DIR(table->flags); - ulint fsp_flags = dict_tf_to_fsp_flags(table->flags, is_temp); + ulint fsp_flags = dict_tf_to_fsp_flags(table->flags, + is_temp, + is_encrypted); /* Determine the full filepath */ if (is_temp) { @@ -544,9 +547,14 @@ dict_build_tablespace_for_table( mtr.set_named_space(table->space); dict_disable_redo_if_temporary(table, &mtr); - fsp_header_init(table->space, FIL_IBD_FILE_INITIAL_SIZE, &mtr); + bool ret = fsp_header_init(table->space, + FIL_IBD_FILE_INITIAL_SIZE, + &mtr); mtr_commit(&mtr); + if (!ret) { + return(DB_ERROR); + } } else { /* We do not need to build a tablespace for this table. It is already built. Just find the correct tablespace ID. */ @@ -2290,6 +2298,197 @@ dict_create_add_foreign_to_dictionary( DBUG_RETURN(error); } +/** Check whether a column is in an index by the column name +@param[in] col_name column name for the column to be checked +@param[in] index the index to be searched +@return true if this column is in the index, otherwise, false */ +static +bool +dict_index_has_col_by_name( +/*=======================*/ + const char* col_name, + const dict_index_t* index) +{ + for (ulint i = 0; i < index->n_fields; i++) { + dict_field_t* field = dict_index_get_nth_field(index, i); + + if (strcmp(field->name, col_name) == 0) { + return(true); + } + } + return(false); +} + +/** Check whether the foreign constraint could be on a column that is +part of a virtual index (index contains virtual column) in the table +@param[in] fk_col_name FK column name to be checked +@param[in] table the table +@return true if this column is indexed with other virtual columns */ +bool +dict_foreign_has_col_in_v_index( + const char* fk_col_name, + const dict_table_t* table) +{ + /* virtual column can't be Primary Key, so start with secondary index */ + for (dict_index_t* index = dict_table_get_next_index( + dict_table_get_first_index(table)); + index; + index = dict_table_get_next_index(index)) { + + if (dict_index_has_virtual(index)) { + if (dict_index_has_col_by_name(fk_col_name, index)) { + return(true); + } + } + } + + return(false); +} + + +/** Check whether the foreign constraint could be on a column that is +a base column of some indexed virtual columns. +@param[in] col_name column name for the column to be checked +@param[in] table the table +@return true if this column is a base column, otherwise, false */ +bool +dict_foreign_has_col_as_base_col( + const char* col_name, + const dict_table_t* table) +{ + /* Loop through each virtual column and check if its base column has + the same name as the column name being checked */ + for (ulint i = 0; i < table->n_v_cols; i++) { + dict_v_col_t* v_col = dict_table_get_nth_v_col(table, i); + + /* Only check if the virtual column is indexed */ + if (!v_col->m_col.ord_part) { + continue; + } + + for (ulint j = 0; j < v_col->num_base; j++) { + if (strcmp(col_name, dict_table_get_col_name( + table, + v_col->base_col[j]->ind)) == 0) { + return(true); + } + } + } + + return(false); +} + +/** Check if a foreign constraint is on the given column name. +@param[in] col_name column name to be searched for fk constraint +@param[in] table table to which foreign key constraint belongs +@return true if fk constraint is present on the table, false otherwise. */ +static +bool +dict_foreign_base_for_stored( + const char* col_name, + const dict_table_t* table) +{ + /* Loop through each stored column and check if its base column has + the same name as the column name being checked */ + dict_s_col_list::const_iterator it; + for (it = table->s_cols->begin(); + it != table->s_cols->end(); ++it) { + dict_s_col_t s_col = *it; + + for (ulint j = 0; j < s_col.num_base; j++) { + if (strcmp(col_name, dict_table_get_col_name( + table, + s_col.base_col[j]->ind)) == 0) { + return(true); + } + } + } + + return(false); +} + +/** Check if a foreign constraint is on columns served as base columns +of any stored column. This is to prevent creating SET NULL or CASCADE +constraint on such columns +@param[in] local_fk_set set of foreign key objects, to be added to +the dictionary tables +@param[in] table table to which the foreign key objects in +local_fk_set belong to +@return true if yes, otherwise, false */ +bool +dict_foreigns_has_s_base_col( + const dict_foreign_set& local_fk_set, + const dict_table_t* table) +{ + dict_foreign_t* foreign; + + if (table->s_cols == NULL) { + return (false); + } + + for (dict_foreign_set::const_iterator it = local_fk_set.begin(); + it != local_fk_set.end(); ++it) { + + foreign = *it; + ulint type = foreign->type; + + type &= ~(DICT_FOREIGN_ON_DELETE_NO_ACTION + | DICT_FOREIGN_ON_UPDATE_NO_ACTION); + + if (type == 0) { + continue; + } + + for (ulint i = 0; i < foreign->n_fields; i++) { + /* Check if the constraint is on a column that + is a base column of any stored column */ + if (dict_foreign_base_for_stored( + foreign->foreign_col_names[i], table)) { + return(true); + } + } + } + + return(false); +} + +/** Check if a column is in foreign constraint with CASCADE properties or +SET NULL +@param[in] table table +@param[in] fk_col_name name for the column to be checked +@return true if the column is in foreign constraint, otherwise, false */ +bool +dict_foreigns_has_this_col( + const dict_table_t* table, + const char* col_name) +{ + dict_foreign_t* foreign; + const dict_foreign_set* local_fk_set = &table->foreign_set; + + for (dict_foreign_set::const_iterator it = local_fk_set->begin(); + it != local_fk_set->end(); + ++it) { + foreign = *it; + ut_ad(foreign->id != NULL); + ulint type = foreign->type; + + type &= ~(DICT_FOREIGN_ON_DELETE_NO_ACTION + | DICT_FOREIGN_ON_UPDATE_NO_ACTION); + + if (type == 0) { + continue; + } + + for (ulint i = 0; i < foreign->n_fields; i++) { + if (strcmp(foreign->foreign_col_names[i], + col_name) == 0) { + return(true); + } + } + } + return(false); +} + /** Adds the given set of foreign key objects to the dictionary tables in the database. This function does not modify the dictionary cache. The caller must ensure that all foreign key objects contain a valid constraint diff --git a/storage/innobase/dict/dict0defrag_bg.cc b/storage/innobase/dict/dict0defrag_bg.cc new file mode 100644 index 00000000000..82aa3abcde6 --- /dev/null +++ b/storage/innobase/dict/dict0defrag_bg.cc @@ -0,0 +1,403 @@ +/***************************************************************************** + +Copyright (c) 2016, MariaDB Corporation. All Rights Reserved. + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; version 2 of the License. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + +You should have received a copy of the GNU General Public License along with +this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA + +*****************************************************************************/ + +/**************************************************//** +@file dict/dict0defrag_bg.cc +Defragmentation routines. + +Created 25/08/2016 Jan Lindström +*******************************************************/ + +#include "dict0dict.h" +#include "dict0stats.h" +#include "dict0stats_bg.h" +#include "dict0defrag_bg.h" +#include "row0mysql.h" +#include "srv0start.h" +#include "ut0new.h" + +#ifdef UNIV_NONINL +# include "dict0stats_bg.ic" +#endif + +#include <vector> + +static ib_mutex_t defrag_pool_mutex; + +#ifdef MYSQL_PFS +static mysql_pfs_key_t defrag_pool_mutex_key; +#endif + +/** The number of tables that can be added to "defrag_pool" before +it is enlarged */ +static const ulint DEFRAG_POOL_INITIAL_SLOTS = 128; + +/** Indices whose defrag stats need to be saved to persistent storage.*/ +struct defrag_pool_item_t { + table_id_t table_id; + index_id_t index_id; +}; + +/** Allocator type, used by std::vector */ +typedef ut_allocator<defrag_pool_item_t> + defrag_pool_allocator_t; + +/** The multitude of tables to be defragmented- an STL vector */ +typedef std::vector<defrag_pool_item_t, defrag_pool_allocator_t> + defrag_pool_t; + +/** Iterator type for iterating over the elements of objects of type +defrag_pool_t. */ +typedef defrag_pool_t::iterator defrag_pool_iterator_t; + +/** Pool where we store information on which tables are to be processed +by background defragmentation. */ +static defrag_pool_t* defrag_pool; + +extern bool dict_stats_start_shutdown; + +/*****************************************************************//** +Initialize the defrag pool, called once during thread initialization. */ +void +dict_defrag_pool_init(void) +/*=======================*/ +{ + ut_ad(!srv_read_only_mode); + /* JAN: TODO: MySQL 5.7 PSI + const PSI_memory_key key2 = mem_key_dict_defrag_pool_t; + + defrag_pool = UT_NEW(defrag_pool_t(defrag_pool_allocator_t(key2)), key2); + + recalc_pool->reserve(RECALC_POOL_INITIAL_SLOTS); + */ + defrag_pool = new std::vector<defrag_pool_item_t, defrag_pool_allocator_t>(); + + /* We choose SYNC_STATS_DEFRAG to be below SYNC_FSP_PAGE. */ + mutex_create(LATCH_ID_DEFRAGMENT_MUTEX, &defrag_pool_mutex); +} + +/*****************************************************************//** +Free the resources occupied by the defrag pool, called once during +thread de-initialization. */ +void +dict_defrag_pool_deinit(void) +/*=========================*/ +{ + ut_ad(!srv_read_only_mode); + + defrag_pool->clear(); + mutex_free(&defrag_pool_mutex); + + UT_DELETE(defrag_pool); +} + +/*****************************************************************//** +Get an index from the auto defrag pool. The returned index id is removed +from the pool. +@return true if the pool was non-empty and "id" was set, false otherwise */ +static +bool +dict_stats_defrag_pool_get( +/*=======================*/ + table_id_t* table_id, /*!< out: table id, or unmodified if + list is empty */ + index_id_t* index_id) /*!< out: index id, or unmodified if + list is empty */ +{ + ut_ad(!srv_read_only_mode); + + mutex_enter(&defrag_pool_mutex); + + if (defrag_pool->empty()) { + mutex_exit(&defrag_pool_mutex); + return(false); + } + + defrag_pool_item_t& item = defrag_pool->back(); + *table_id = item.table_id; + *index_id = item.index_id; + + defrag_pool->pop_back(); + + mutex_exit(&defrag_pool_mutex); + + return(true); +} + +/*****************************************************************//** +Add an index in a table to the defrag pool, which is processed by the +background stats gathering thread. Only the table id and index id are +added to the list, so the table can be closed after being enqueued and +it will be opened when needed. If the table or index does not exist later +(has been DROPped), then it will be removed from the pool and skipped. */ +void +dict_stats_defrag_pool_add( +/*=======================*/ + const dict_index_t* index) /*!< in: table to add */ +{ + defrag_pool_item_t item; + + ut_ad(!srv_read_only_mode); + + mutex_enter(&defrag_pool_mutex); + + /* quit if already in the list */ + for (defrag_pool_iterator_t iter = defrag_pool->begin(); + iter != defrag_pool->end(); + ++iter) { + if ((*iter).table_id == index->table->id + && (*iter).index_id == index->id) { + mutex_exit(&defrag_pool_mutex); + return; + } + } + + item.table_id = index->table->id; + item.index_id = index->id; + defrag_pool->push_back(item); + + mutex_exit(&defrag_pool_mutex); + + os_event_set(dict_stats_event); +} + +/*****************************************************************//** +Delete a given index from the auto defrag pool. */ +void +dict_stats_defrag_pool_del( +/*=======================*/ + const dict_table_t* table, /*!<in: if given, remove + all entries for the table */ + const dict_index_t* index) /*!< in: if given, remove this index */ +{ + ut_a((table && !index) || (!table && index)); + ut_ad(!srv_read_only_mode); + ut_ad(mutex_own(&dict_sys->mutex)); + + mutex_enter(&defrag_pool_mutex); + + defrag_pool_iterator_t iter = defrag_pool->begin(); + while (iter != defrag_pool->end()) { + if ((table && (*iter).table_id == table->id) + || (index + && (*iter).table_id == index->table->id + && (*iter).index_id == index->id)) { + /* erase() invalidates the iterator */ + iter = defrag_pool->erase(iter); + if (index) + break; + } else { + iter++; + } + } + + mutex_exit(&defrag_pool_mutex); +} + +/*****************************************************************//** +Get the first index that has been added for updating persistent defrag +stats and eventually save its stats. */ +static +void +dict_stats_process_entry_from_defrag_pool() +/*=======================================*/ +{ + table_id_t table_id; + index_id_t index_id; + dberr_t err = DB_SUCCESS; + + ut_ad(!srv_read_only_mode); + + /* pop the first index from the auto defrag pool */ + if (!dict_stats_defrag_pool_get(&table_id, &index_id)) { + /* no index in defrag pool */ + return; + } + + dict_table_t* table; + + mutex_enter(&dict_sys->mutex); + + /* If the table is no longer cached, we've already lost the in + memory stats so there's nothing really to write to disk. */ + table = dict_table_open_on_id(table_id, TRUE, + DICT_TABLE_OP_OPEN_ONLY_IF_CACHED); + + if (table == NULL) { + mutex_exit(&dict_sys->mutex); + return; + } + + /* Check whether table is corrupted */ + if (table->corrupted) { + dict_table_close(table, TRUE, FALSE); + mutex_exit(&dict_sys->mutex); + return; + } + mutex_exit(&dict_sys->mutex); + + dict_index_t* index = dict_table_find_index_on_id(table, index_id); + + if (index == NULL) { + return; + } + + /* Check whether index is corrupted */ + if (dict_index_is_corrupted(index)) { + dict_table_close(table, FALSE, FALSE); + return; + } + + err = dict_stats_save_defrag_stats(index); + + if (err != DB_SUCCESS) { + ib::error() << "Saving defragmentation status for table " + << index->table->name.m_name + << " index " << index->name() + << " failed " << err; + } + + dict_table_close(table, FALSE, FALSE); +} + +/*****************************************************************//** +Get the first index that has been added for updating persistent defrag +stats and eventually save its stats. */ +void +dict_defrag_process_entries_from_defrag_pool() +/*==========================================*/ +{ + while (defrag_pool->size() && !dict_stats_start_shutdown) { + dict_stats_process_entry_from_defrag_pool(); + } +} + +/*********************************************************************//** +Save defragmentation result. +@return DB_SUCCESS or error code */ +dberr_t +dict_stats_save_defrag_summary( +/*============================*/ + dict_index_t* index) /*!< in: index */ +{ + dberr_t ret=DB_SUCCESS; + lint now = (lint) ut_time(); + + if (dict_index_is_univ(index)) { + return DB_SUCCESS; + } + + rw_lock_x_lock(dict_operation_lock); + mutex_enter(&dict_sys->mutex); + + ret = dict_stats_save_index_stat(index, now, "n_pages_freed", + index->stat_defrag_n_pages_freed, + NULL, + "Number of pages freed during" + " last defragmentation run.", + NULL); + + mutex_exit(&dict_sys->mutex); + rw_lock_x_unlock(dict_operation_lock); + + return (ret); +} + +/*********************************************************************//** +Save defragmentation stats for a given index. +@return DB_SUCCESS or error code */ +dberr_t +dict_stats_save_defrag_stats( +/*============================*/ + dict_index_t* index) /*!< in: index */ +{ + dberr_t ret; + + if (index->table->ibd_file_missing) { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Cannot save defragment stats because " + ".ibd file is missing.\n"); + return (DB_TABLESPACE_DELETED); + } + if (dict_index_is_corrupted(index)) { + ut_print_timestamp(stderr); + fprintf(stderr, + " InnoDB: Cannot save defragment stats because " + "index is corrupted.\n"); + return(DB_CORRUPTION); + } + + if (dict_index_is_univ(index)) { + return DB_SUCCESS; + } + + lint now = (lint) ut_time(); + mtr_t mtr; + ulint n_leaf_pages; + ulint n_leaf_reserved; + mtr_start(&mtr); + mtr_s_lock(dict_index_get_lock(index), &mtr); + n_leaf_reserved = btr_get_size_and_reserved(index, BTR_N_LEAF_PAGES, + &n_leaf_pages, &mtr); + mtr_commit(&mtr); + + if (n_leaf_reserved == ULINT_UNDEFINED) { + // The index name is different during fast index creation, + // so the stats won't be associated with the right index + // for later use. We just return without saving. + return DB_SUCCESS; + } + + rw_lock_x_lock(dict_operation_lock); + + mutex_enter(&dict_sys->mutex); + ret = dict_stats_save_index_stat(index, now, "n_page_split", + index->stat_defrag_n_page_split, + NULL, + "Number of new page splits on leaves" + " since last defragmentation.", + NULL); + if (ret != DB_SUCCESS) { + goto end; + } + + ret = dict_stats_save_index_stat( + index, now, "n_leaf_pages_defrag", + n_leaf_pages, + NULL, + "Number of leaf pages when this stat is saved to disk", + NULL); + if (ret != DB_SUCCESS) { + goto end; + } + + ret = dict_stats_save_index_stat( + index, now, "n_leaf_pages_reserved", + n_leaf_reserved, + NULL, + "Number of pages reserved for this index leaves when this stat " + "is saved to disk", + NULL); + +end: + mutex_exit(&dict_sys->mutex); + rw_lock_x_unlock(dict_operation_lock); + + return (ret); +} diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 687353cb1b9..6a33de63b69 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -25,6 +25,9 @@ Data dictionary system Created 1/8/1996 Heikki Tuuri ***********************************************************************/ +#include <my_config.h> +#include <string> + #include "ha_prototypes.h" #include <mysqld.h> #include <strfunc.h> @@ -33,7 +36,6 @@ Created 1/8/1996 Heikki Tuuri #include "fts0fts.h" #include "fil0fil.h" #include <algorithm> -#include <string> #ifdef UNIV_NONINL #include "dict0dict.ic" @@ -569,6 +571,8 @@ dict_table_close_and_drop( trx_t* trx, /*!< in: data dictionary transaction */ dict_table_t* table) /*!< in/out: table */ { + dberr_t err = DB_SUCCESS; + ut_ad(mutex_own(&dict_sys->mutex)); ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X)); ut_ad(trx->dict_operation != TRX_DICT_OP_NONE); @@ -583,7 +587,13 @@ dict_table_close_and_drop( ut_a(!table->stat_initialized); #endif /* UNIV_DEBUG || UNIV_DDL_DEBUG */ - row_merge_drop_table(trx, table); + err = row_merge_drop_table(trx, table); + + if (err != DB_SUCCESS) { + ib::error() << "At " << __FILE__ << ":" << __LINE__ + << " row_merge_drop_table returned error: " << err + << " table: " << table->name.m_name; + } } /** Check if the table has a given (non_virtual) column. @@ -684,6 +694,7 @@ dict_table_get_col_name_for_mysql( return(s); } + /** Returns a virtual column's name. @param[in] table target table @param[in] col_nr virtual column number (nth virtual column) @@ -1237,7 +1248,7 @@ dict_init(void) dict_operation_lock, SYNC_DICT_OPERATION); if (!srv_read_only_mode) { - dict_foreign_err_file = os_file_create_tmpfile(); + dict_foreign_err_file = os_file_create_tmpfile(NULL); ut_a(dict_foreign_err_file); } @@ -1309,9 +1320,7 @@ dict_table_open_on_name( if (ignore_err == DICT_ERR_IGNORE_NONE && table->is_encrypted) { /* Make life easy for drop table. */ - if (table->can_be_evicted) { - dict_table_move_from_lru_to_non_lru(table); - } + dict_table_prevent_eviction(table); if (table->can_be_evicted) { dict_move_to_mru(table); @@ -1328,10 +1337,8 @@ dict_table_open_on_name( /* If table is corrupted, return NULL */ else if (ignore_err == DICT_ERR_IGNORE_NONE && table->corrupted) { - /* Make life easy for drop table. */ dict_table_prevent_eviction(table); - if (!dict_locked) { mutex_exit(&dict_sys->mutex); } @@ -1668,7 +1675,6 @@ dict_table_move_from_lru_to_non_lru( @param[in] table table instance @param[in] id index id @return index or NULL */ -UNIV_INTERN dict_index_t* dict_table_find_index_on_id( const dict_table_t* table, @@ -1764,6 +1770,7 @@ dict_table_rename_in_cache( dict_index_t* index; ulint fold; char old_name[MAX_FULL_NAME_LEN + 1]; + os_file_type_t ftype; ut_ad(mutex_own(&dict_sys->mutex)); @@ -1798,7 +1805,6 @@ dict_table_rename_in_cache( .ibd file and rebuild the .isl file if needed. */ if (dict_table_is_discarded(table)) { - os_file_type_t type; bool exists; char* filepath; @@ -1826,7 +1832,7 @@ dict_table_rename_in_cache( fil_delete_tablespace(table->space, BUF_REMOVE_ALL_NO_WRITE); /* Delete any temp file hanging around. */ - if (os_file_status(filepath, &exists, &type) + if (os_file_status(filepath, &exists, &ftype) && exists && !os_file_delete_if_exists(innodb_temp_file_key, filepath, NULL)) { @@ -1860,19 +1866,31 @@ dict_table_rename_in_cache( ut_free(old_path); return(DB_TABLESPACE_EXISTS); } + } else { + new_path = fil_make_filepath( + NULL, new_name, IBD, false); + } + + /* New filepath must not exist. */ + err = fil_rename_tablespace_check( + table->space, old_path, new_path, false); + if (err != DB_SUCCESS) { + ut_free(old_path); + ut_free(new_path); + return(err); } bool success = fil_rename_tablespace( table->space, old_path, new_name, new_path); ut_free(old_path); + ut_free(new_path); /* If the tablespace is remote, a new .isl file was created - If success, delete the old one. If not, delete the new one. */ - if (new_path) { - - ut_free(new_path); - RemoteDatafile::delete_link_file(success ? old_name : new_name); + If success, delete the old one. If not, delete the new one. */ + if (DICT_TF_HAS_DATA_DIR(table->flags)) { + RemoteDatafile::delete_link_file( + success ? old_name : new_name); } if (!success) { @@ -2271,6 +2289,12 @@ dict_table_remove_from_cache_low( trx_free_for_background(trx); } + /* Free virtual column template if any */ + if (table->vc_templ != NULL) { + dict_free_vc_templ(table->vc_templ); + UT_DELETE(table->vc_templ); + } + size = mem_heap_get_size(table->heap) + strlen(table->name.m_name) + 1; ut_ad(dict_sys->size >= size); @@ -2516,7 +2540,7 @@ dict_index_too_big_for_tree( REC_STATUS_ORDINARY records. */ field_max_size = dict_col_get_fixed_size(col, comp); - if (field_max_size) { + if (field_max_size && field->fixed_len != 0) { /* dict_index_add_col() should guarantee this */ ut_ad(!field->prefix_len || field->fixed_len == field->prefix_len); @@ -2681,18 +2705,31 @@ dict_index_add_to_cache_w_vcol( } n_ord = new_index->n_uniq; - /* 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; - - if (field->prefix_len > field->col->max_prefix) { + /* Check the column being added in the index for + the first time and flag the ordering column. */ + if (field->col->ord_part == 0 ) { + field->col->max_prefix = field->prefix_len; + field->col->ord_part = 1; + } else if (field->prefix_len == 0) { + /* Set the max_prefix for a column to 0 if + its prefix length is 0 (for this index) + even if it was a part of any other index + with some prefix length. */ + field->col->max_prefix = 0; + } else if (field->col->max_prefix != 0 + && field->prefix_len + > field->col->max_prefix) { + /* Set the max_prefix value based on the + prefix_len. */ field->col->max_prefix = field->prefix_len; } + ut_ad(field->col->ord_part == 1); } new_index->stat_n_diff_key_vals = @@ -3051,7 +3088,6 @@ dict_index_add_col( field = dict_index_get_nth_field(index, index->n_def - 1); field->col = col; - /* DATA_POINT is a special type, whose fixed_len should be: 1) DATA_MBR_LEN, when it's indexed in R-TREE. In this case, it must be the first col to be added. @@ -3663,7 +3699,7 @@ dict_foreign_find_index( /*!< out: column number where error happened */ dict_index_t** err_index) - /*!< out: index where error + /*!< out: index where error happened */ { dict_index_t* index; @@ -4645,6 +4681,11 @@ dict_foreign_push_index_error( } /*********************************************************************//** +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 bot participating tables. The indexes are allowed +to contain more fields than mentioned in the constraint. @return error code or DB_SUCCESS */ static dberr_t @@ -4879,6 +4920,10 @@ loop: return(DB_CANNOT_ADD_CONSTRAINT); } + if (dict_foreigns_has_s_base_col(local_fk_set, table)) { + return(DB_NO_FK_ON_S_BASE_COL); + } + /**********************************************************/ /* The following call adds the foreign key constraints to the data dictionary system tables on disk */ @@ -4894,6 +4939,8 @@ loop: local_fk_set.end(), dict_foreign_add_to_referenced_table()); local_fk_set.clear(); + + dict_mem_table_fill_foreign_vcol_set(table); } return(error); } @@ -4919,53 +4966,52 @@ loop: } if (my_isspace(cs, *ptr)) { - ptr1 = dict_accept(cs, ptr, "IF", &success); + ptr1 = dict_accept(cs, ptr, "IF", &success); - if (success) { - if (!my_isspace(cs, *ptr1)) { - goto loop; - } - ptr1 = dict_accept(cs, ptr1, "NOT", &success); - if (!success) { - goto loop; - } - ptr1 = dict_accept(cs, ptr1, "EXISTS", &success); - if (!success) { - goto loop; - } - ptr = ptr1; - } + if (success) { + if (!my_isspace(cs, *ptr1)) { + goto loop; + } + ptr1 = dict_accept(cs, ptr1, "NOT", &success); + if (!success) { + goto loop; + } + ptr1 = dict_accept(cs, ptr1, "EXISTS", &success); + if (!success) { + goto loop; + } + ptr = ptr1; + } } orig = ptr; ptr = dict_accept(cs, ptr, "(", &success); if (!success) { - if (constraint_name) { - /* MySQL allows also an index id before the '('; we - skip it */ - ptr = dict_skip_word(cs, ptr, &success); - if (!success) { - dict_foreign_report_syntax_err( - "%s table %s with foreign key constraint" - " failed. Parse error in '%s'" - " near '%s'.\n", - operation, create_name, start_of_latest_foreign, orig); - - ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, - "%s table %s with foreign key constraint" - " failed. Parse error in '%s'" - " near '%s'.", - operation, create_name, start_of_latest_foreign, orig); - return(DB_CANNOT_ADD_CONSTRAINT); - } - } - else { - while (my_isspace(cs, *ptr)) { - ptr++; - } + if (constraint_name) { + /* MySQL allows also an index id before the '('; we + skip it */ + ptr = dict_skip_word(cs, ptr, &success); + if (!success) { + dict_foreign_report_syntax_err( + "%s table %s with foreign key constraint" + " failed. Parse error in '%s'" + " near '%s'.\n", + operation, create_name, start_of_latest_foreign, orig); + + ib_push_warning(trx, DB_CANNOT_ADD_CONSTRAINT, + "%s table %s with foreign key constraint" + " failed. Parse error in '%s'" + " near '%s'.", + operation, create_name, start_of_latest_foreign, orig); + return(DB_CANNOT_ADD_CONSTRAINT); + } + } else { + while (my_isspace(cs, *ptr)) { + ptr++; + } - ptr = dict_scan_id(cs, ptr, heap, + ptr = dict_scan_id(cs, ptr, heap, &constraint_name, FALSE, FALSE); } @@ -5095,6 +5141,23 @@ col_loop1: return(DB_CANNOT_ADD_CONSTRAINT); } + /* Don't allow foreign keys on partitioned tables yet. */ + ptr1 = dict_scan_to(ptr, "PARTITION"); + if (ptr1) { + ptr1 = dict_accept(cs, ptr1, "PARTITION", &success); + if (success && my_isspace(cs, *ptr1)) { + ptr2 = dict_accept(cs, ptr1, "BY", &success); + if (success) { + my_error(ER_FOREIGN_KEY_ON_PARTITIONED,MYF(0)); + return(DB_CANNOT_ADD_CONSTRAINT); + } + } + } + if (dict_table_is_partition(table)) { + my_error(ER_FOREIGN_KEY_ON_PARTITIONED,MYF(0)); + return(DB_CANNOT_ADD_CONSTRAINT); + } + /* Let us create a constraint struct */ foreign = dict_mem_foreign_create(); @@ -5602,7 +5665,7 @@ dict_foreign_parse_drop_constraints( char* str; size_t len; const char* ptr; - const char* ptr1; + const char* ptr1; const char* id; CHARSET_INFO* cs; @@ -5656,11 +5719,10 @@ loop: ptr1 = dict_accept(cs, ptr, "IF", &success); if (success && my_isspace(cs, *ptr1)) { - ptr1 = dict_accept(cs, ptr1, "EXISTS", &success); - if (success) { - - ptr = ptr1; - } + ptr1 = dict_accept(cs, ptr1, "EXISTS", &success); + if (success) { + ptr = ptr1; + } } ptr = dict_scan_id(cs, ptr, heap, &id, FALSE, TRUE); @@ -5875,6 +5937,12 @@ dict_index_copy_rec_order_prefix( n = dict_index_get_n_unique_in_tree(index); } else { n = dict_index_get_n_unique_in_tree_nonleaf(index); + /* For internal node of R-tree, since we need to + compare the page no field, so, we need to copy this + field as well. */ + if (dict_index_is_spatial(index)) { + n++; + } } } @@ -5992,11 +6060,11 @@ dict_print_info_on_foreign_key_in_create_format( str.append(" CONSTRAINT "); - str.append(ut_get_name(trx, FALSE, stripped_id)); + str.append(innobase_quote_identifier(trx, stripped_id)); str.append(" FOREIGN KEY ("); for (i = 0;;) { - str.append(ut_get_name(trx, FALSE, foreign->foreign_col_names[i])); + str.append(innobase_quote_identifier(trx, foreign->foreign_col_names[i])); if (++i < foreign->n_fields) { str.append(", "); @@ -6010,18 +6078,18 @@ dict_print_info_on_foreign_key_in_create_format( 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 */ - str.append(ut_get_name(trx, TRUE, + str.append(ut_get_name(trx, dict_remove_db_name( foreign->referenced_table_name))); } else { - str.append(ut_get_name(trx, TRUE, + str.append(ut_get_name(trx, foreign->referenced_table_name)); } str.append(" ("); for (i = 0;;) { - str.append(ut_get_name(trx, FALSE, + str.append(innobase_quote_identifier(trx, foreign->referenced_col_names[i])); if (++i < foreign->n_fields) { @@ -6096,12 +6164,12 @@ dict_print_info_on_foreign_keys( str.append(" "); } - str.append(ut_get_name(trx, FALSE, + str.append(innobase_quote_identifier(trx, foreign->foreign_col_names[i])); } str.append(") REFER "); - str.append(ut_get_name(trx, TRUE, + str.append(ut_get_name(trx, foreign->referenced_table_name)); str.append(")"); @@ -6109,8 +6177,8 @@ dict_print_info_on_foreign_keys( if (i) { str.append(" "); } - str.append(ut_get_name( - trx, FALSE, + str.append(innobase_quote_identifier( + trx, foreign->referenced_col_names[i])); } @@ -6143,7 +6211,6 @@ dict_print_info_on_foreign_keys( } mutex_exit(&dict_sys->mutex); - return str; } @@ -6262,6 +6329,13 @@ dict_set_corrupted( goto func_exit; } + /* If this is read only mode, do not update SYS_INDEXES, just + mark it as corrupted in memory */ + if (srv_read_only_mode) { + index->type |= DICT_CORRUPT; + goto func_exit; + } + heap = mem_heap_create(sizeof(dtuple_t) + 2 * (sizeof(dfield_t) + sizeof(que_fork_t) + sizeof(upd_node_t) + sizeof(upd_t) + 12)); @@ -6451,6 +6525,7 @@ dict_set_merge_threshold_all_debug( mutex_exit(&dict_sys->mutex); } + #endif /* UNIV_DEBUG */ #endif /* !UNIV_HOTBACKUP */ @@ -6914,7 +6989,8 @@ dict_fs2utf8( errors = 0; strconvert( - &my_charset_filename, buf, (uint) (buf_p - buf), system_charset_info, + &my_charset_filename, buf, (uint) (buf_p - buf), + system_charset_info, table_utf8, table_utf8_size, &errors); @@ -7380,11 +7456,13 @@ dict_table_t::flags | 0 | 1 | 1 | 1 fil_space_t::flags | 0 | 0 | 1 | 1 @param[in] table_flags dict_table_t::flags @param[in] is_temp whether the tablespace is temporary +@param[in] is_encrypted whether the tablespace is encrypted @return tablespace flags (fil_space_t::flags) */ ulint dict_tf_to_fsp_flags( ulint table_flags, - bool is_temp) + bool is_temp, + bool is_encrypted) { DBUG_EXECUTE_IF("dict_tf_to_fsp_flags_failure", return(ULINT_UNDEFINED);); @@ -7411,9 +7489,30 @@ dict_tf_to_fsp_flags( has_data_dir, is_shared, is_temp, - page_compression, - page_compression_level, - atomic_writes); + 0, + 0, + 0, + is_encrypted); + + /* In addition, tablespace flags also contain if the page + compression is used for this table. */ + if (page_compression) { + fsp_flags |= FSP_FLAGS_SET_PAGE_COMPRESSION(fsp_flags, page_compression); + } + + /* In addition, tablespace flags also contain page compression level + if page compression is used for this table. */ + if (page_compression && page_compression_level) { + fsp_flags |= FSP_FLAGS_SET_PAGE_COMPRESSION_LEVEL(fsp_flags, page_compression_level); + } + + /* In addition, tablespace flags also contain flag if atomic writes + is used for this table */ + if (atomic_writes) { + fsp_flags |= FSP_FLAGS_SET_ATOMIC_WRITES(fsp_flags, atomic_writes); + } + + ut_ad(fsp_flags_is_valid(fsp_flags)); return(fsp_flags); } @@ -7442,10 +7541,10 @@ dict_tf_to_row_format_string( } /** Look for any dictionary objects that are found in the given tablespace. -@param[in] space Tablespace ID to search for. +@param[in] space_id Tablespace ID to search for. @return true if tablespace is empty. */ bool -dict_tablespace_is_empty( +dict_space_is_empty( ulint space_id) { btr_pcur_t pcur; @@ -7480,6 +7579,55 @@ dict_tablespace_is_empty( return(!found); } + +/** Find the space_id for the given name in sys_tablespaces. +@param[in] name Tablespace name to search for. +@return the tablespace ID. */ +ulint +dict_space_get_id( + const char* name) +{ + btr_pcur_t pcur; + const rec_t* rec; + mtr_t mtr; + ulint name_len = strlen(name); + ulint id = ULINT_UNDEFINED; + + rw_lock_x_lock(dict_operation_lock); + mutex_enter(&dict_sys->mutex); + mtr_start(&mtr); + + for (rec = dict_startscan_system(&pcur, &mtr, SYS_TABLESPACES); + rec != NULL; + rec = dict_getnext_system(&pcur, &mtr)) { + const byte* field; + ulint len; + + field = rec_get_nth_field_old( + rec, DICT_FLD__SYS_TABLESPACES__NAME, &len); + ut_ad(len > 0); + ut_ad(len < OS_FILE_MAX_PATH); + + if (len == name_len && ut_memcmp(name, field, len) == 0) { + + field = rec_get_nth_field_old( + rec, DICT_FLD__SYS_TABLESPACES__SPACE, &len); + ut_ad(len == 4); + id = mach_read_from_4(field); + + /* This is normally called by dict_getnext_system() + at the end of the index. */ + btr_pcur_close(&pcur); + break; + } + } + + mtr_commit(&mtr); + mutex_exit(&dict_sys->mutex); + rw_lock_x_unlock(dict_operation_lock); + + return(id); +} #endif /* !UNIV_HOTBACKUP */ /** Determine the extent size (in pages) for the given table diff --git a/storage/innobase/dict/dict0load.cc b/storage/innobase/dict/dict0load.cc index 5267cf1a199..f38ad85e903 100644 --- a/storage/innobase/dict/dict0load.cc +++ b/storage/innobase/dict/dict0load.cc @@ -733,7 +733,6 @@ err_len: @param[in] space_id Tablespace ID @return First filepath (caller must invoke ut_free() on it) @retval NULL if no SYS_DATAFILES entry was found. */ -static char* dict_get_first_path( ulint space_id) @@ -819,7 +818,7 @@ dict_get_first_path( @retval NULL if no dictionary entry was found. */ static char* -dict_get_space_name( +dict_space_get_name( ulint space_id, mem_heap_t* callers_heap) { @@ -1127,7 +1126,7 @@ dict_sys_tablespaces_rec_read( rec, DICT_FLD__SYS_TABLESPACES__NAME, &len); if (len == 0 || len == UNIV_SQL_NULL) { ib::error() << "Wrong field length in SYS_TABLESPACES.NAME: " - << len; + << len; return(false); } strncpy(name, reinterpret_cast<const char*>(field), NAME_LEN); @@ -1137,7 +1136,7 @@ dict_sys_tablespaces_rec_read( rec, DICT_FLD__SYS_TABLESPACES__FLAGS, &len); if (len != 4) { ib::error() << "Wrong field length in SYS_TABLESPACES.FLAGS: " - << len; + << len; return(false); } *flags = mach_read_from_4(field); @@ -1313,32 +1312,16 @@ dict_sys_tables_rec_read( *flags = dict_sys_tables_type_to_tf(type, *n_cols); - /* For tables created with old versions of InnoDB, there may be - garbage in SYS_TABLES.MIX_LEN where flags2 are found. Such tables - would always be in ROW_FORMAT=REDUNDANT which do not have the - high bit set in n_cols, and flags would be zero. */ - if (*flags != 0 || *n_cols & DICT_N_COLS_COMPACT) { - - /* Get flags2 from SYS_TABLES.MIX_LEN */ - field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_TABLES__MIX_LEN, &len); - *flags2 = mach_read_from_4(field); - - if (!dict_tf2_is_valid(*flags, *flags2)) { - ib::error() << "Table " << table_name << " in InnoDB" - " data dictionary contains invalid flags." - " SYS_TABLES.MIX_LEN=" << *flags2; - *flags2 = ULINT_UNDEFINED; - return(false); - } - - /* DICT_TF2_FTS will be set when indexes are being loaded */ - *flags2 &= ~DICT_TF2_FTS; + /* Get flags2 from SYS_TABLES.MIX_LEN */ + field = rec_get_nth_field_old( + rec, DICT_FLD__SYS_TABLES__MIX_LEN, &len); + *flags2 = mach_read_from_4(field); - /* Now that we have used this bit, unset it. */ - *n_cols &= ~DICT_N_COLS_COMPACT; - } + /* DICT_TF2_FTS will be set when indexes are being loaded */ + *flags2 &= ~DICT_TF2_FTS; + /* Now that we have used this bit, unset it. */ + *n_cols &= ~DICT_N_COLS_COMPACT; return(true); } @@ -1431,10 +1414,10 @@ dict_check_sys_tables( and the tablespace_name are the same. Some hidden tables like FTS AUX tables may not be found in the dictionary since they can always be found in the default - location. If so, then dict_get_space_name() will return NULL, + location. If so, then dict_space_get_name() will return NULL, the space name must be the table_name, and the filepath can be discovered in the default location.*/ - char* shared_space_name = dict_get_space_name(space_id, NULL); + char* shared_space_name = dict_space_get_name(space_id, NULL); space_name = shared_space_name == NULL ? table_name.m_name : shared_space_name; @@ -1468,17 +1451,13 @@ dict_check_sys_tables( opened. */ char* filepath = dict_get_first_path(space_id); - /* We need to read page 0 to get (optional) IV - regardless if encryptions is turned on or not, - since if it's off we should decrypt a potentially - already encrypted table */ - bool read_page_0 = true; - /* Check that the .ibd file exists. */ bool is_temp = flags2 & DICT_TF2_TEMPORARY; - ulint fsp_flags = dict_tf_to_fsp_flags(flags, is_temp); - - validate = true; + bool is_encrypted = flags2 & DICT_TF2_ENCRYPTION; + ulint fsp_flags = dict_tf_to_fsp_flags(flags, + is_temp, + is_encrypted); + validate = true; /* Encryption */ dberr_t err = fil_ibd_open( validate, @@ -2601,7 +2580,7 @@ dict_load_indexes( dictionary cache for such metadata corruption, since we would always be able to set it when loading the dictionary cache */ - ut_ad(index->table == table); + index->table = table; dict_set_corrupted_index_cache_only(index); ib::info() << "Index is corrupt but forcing" @@ -2848,7 +2827,7 @@ dict_get_and_save_space_name( dict_mutex_enter_for_mysql(); } - table->tablespace = dict_get_space_name( + table->tablespace = dict_space_get_name( table->space, table->heap); if (!dict_mutex_own) { @@ -2948,7 +2927,7 @@ dict_load_tablespace( if (DICT_TF_HAS_SHARED_SPACE(table->flags)) { if (srv_sys_tablespaces_open) { shared_space_name = - dict_get_space_name(table->space, NULL); + dict_space_get_name(table->space, NULL); } else { /* Make the temporary tablespace name. */ @@ -3012,7 +2991,9 @@ dict_load_tablespace( /* Try to open the tablespace. We set the 2nd param (fix_dict) to false because we do not have an x-lock on dict_operation_lock */ - ulint fsp_flags = dict_tf_to_fsp_flags(table->flags, false); + ulint fsp_flags = dict_tf_to_fsp_flags(table->flags, + false, + dict_table_is_encrypted(table)); dberr_t err = fil_ibd_open( true, false, FIL_TYPE_TABLESPACE, table->space, fsp_flags, space_name, filepath, table); @@ -3179,6 +3160,32 @@ err_exit: } } + /* We don't trust the table->flags2(retrieved from SYS_TABLES.MIX_LEN + field) if the datafiles are from 3.23.52 version. To identify this + version, we do the below check and reset the flags. */ + if (!DICT_TF2_FLAG_IS_SET(table, DICT_TF2_FTS_HAS_DOC_ID) + && table->space == srv_sys_space.space_id() + && table->flags == 0) { + table->flags2 = 0; + } + + DBUG_EXECUTE_IF("ib_table_invalid_flags", + if(strcmp(table->name.m_name, "test/t1") == 0) { + table->flags2 = 255; + table->flags = 255; + }); + + if (!dict_tf2_is_valid(table->flags, table->flags2)) { + ib::error() << "Table " << table->name << " in InnoDB" + " data dictionary contains invalid flags." + " SYS_TABLES.MIX_LEN=" << table->flags2; + table->flags2 &= ~(DICT_TF2_TEMPORARY|DICT_TF2_INTRINSIC); + dict_table_remove_from_cache(table); + table = NULL; + err = DB_FAIL; + goto func_exit; + } + /* Initialize table foreign_child value. Its value could be changed when dict_load_foreigns() is called below */ table->fk_max_recusive_level = 0; @@ -3203,6 +3210,7 @@ err_exit: dict_table_remove_from_cache(table); table = NULL; } else { + dict_mem_table_fill_foreign_vcol_set(table); table->fk_max_recusive_level = 0; } } else { @@ -3353,99 +3361,6 @@ check_rec: return(table); } -/***********************************************************************//** -Loads a table id based on the index id. -@return true if found */ -static -bool -dict_load_table_id_on_index_id( -/*==================*/ - index_id_t index_id, /*!< in: index id */ - table_id_t* table_id) /*!< out: table id */ -{ - /* check hard coded indexes */ - switch(index_id) { - case DICT_TABLES_ID: - case DICT_COLUMNS_ID: - case DICT_INDEXES_ID: - case DICT_FIELDS_ID: - *table_id = index_id; - return true; - case DICT_TABLE_IDS_ID: - /* The following is a secondary index on SYS_TABLES */ - *table_id = DICT_TABLES_ID; - return true; - } - - bool found = false; - mtr_t mtr; - - ut_ad(mutex_own(&(dict_sys->mutex))); - - /* NOTE that the operation of this function is protected by - the dictionary mutex, and therefore no deadlocks can occur - with other dictionary operations. */ - - mtr_start(&mtr); - - btr_pcur_t pcur; - const rec_t* rec = dict_startscan_system(&pcur, &mtr, SYS_INDEXES); - - while (rec) { - ulint len; - const byte* field = rec_get_nth_field_old( - rec, DICT_FLD__SYS_INDEXES__ID, &len); - ut_ad(len == 8); - - /* Check if the index id is the one searched for */ - if (index_id == mach_read_from_8(field)) { - found = true; - /* Now we get the table id */ - const byte* field = rec_get_nth_field_old( - rec, - DICT_FLD__SYS_INDEXES__TABLE_ID, - &len); - *table_id = mach_read_from_8(field); - break; - } - mtr_commit(&mtr); - mtr_start(&mtr); - rec = dict_getnext_system(&pcur, &mtr); - } - - btr_pcur_close(&pcur); - mtr_commit(&mtr); - - return(found); -} - -UNIV_INTERN -dict_table_t* -dict_table_open_on_index_id( -/*==================*/ - index_id_t index_id, /*!< in: index id */ - bool dict_locked) /*!< in: dict locked */ -{ - if (!dict_locked) { - mutex_enter(&dict_sys->mutex); - } - - ut_ad(mutex_own(&dict_sys->mutex)); - table_id_t table_id; - dict_table_t * table = NULL; - if (dict_load_table_id_on_index_id(index_id, &table_id)) { - bool local_dict_locked = true; - table = dict_table_open_on_id(table_id, - local_dict_locked, - DICT_TABLE_OP_LOAD_TABLESPACE); - } - - if (!dict_locked) { - mutex_exit(&dict_sys->mutex); - } - return table; -} - /********************************************************************//** This function is called when the database is booted. Loads system table index definitions except for the clustered index which is added to the @@ -3953,3 +3868,96 @@ load_next_index: DBUG_RETURN(DB_SUCCESS); } + +/***********************************************************************//** +Loads a table id based on the index id. +@return true if found */ +static +bool +dict_load_table_id_on_index_id( +/*===========================*/ + index_id_t index_id, /*!< in: index id */ + table_id_t* table_id) /*!< out: table id */ +{ + /* check hard coded indexes */ + switch(index_id) { + case DICT_TABLES_ID: + case DICT_COLUMNS_ID: + case DICT_INDEXES_ID: + case DICT_FIELDS_ID: + *table_id = index_id; + return true; + case DICT_TABLE_IDS_ID: + /* The following is a secondary index on SYS_TABLES */ + *table_id = DICT_TABLES_ID; + return true; + } + + bool found = false; + mtr_t mtr; + + ut_ad(mutex_own(&(dict_sys->mutex))); + + /* NOTE that the operation of this function is protected by + the dictionary mutex, and therefore no deadlocks can occur + with other dictionary operations. */ + + mtr_start(&mtr); + + btr_pcur_t pcur; + const rec_t* rec = dict_startscan_system(&pcur, &mtr, SYS_INDEXES); + + while (rec) { + ulint len; + const byte* field = rec_get_nth_field_old( + rec, DICT_FLD__SYS_INDEXES__ID, &len); + ut_ad(len == 8); + + /* Check if the index id is the one searched for */ + if (index_id == mach_read_from_8(field)) { + found = true; + /* Now we get the table id */ + const byte* field = rec_get_nth_field_old( + rec, + DICT_FLD__SYS_INDEXES__TABLE_ID, + &len); + *table_id = mach_read_from_8(field); + break; + } + mtr_commit(&mtr); + mtr_start(&mtr); + rec = dict_getnext_system(&pcur, &mtr); + } + + btr_pcur_close(&pcur); + mtr_commit(&mtr); + + return(found); +} + +UNIV_INTERN +dict_table_t* +dict_table_open_on_index_id( +/*========================*/ + index_id_t index_id, /*!< in: index id */ + bool dict_locked) /*!< in: dict locked */ +{ + if (!dict_locked) { + mutex_enter(&dict_sys->mutex); + } + + ut_ad(mutex_own(&dict_sys->mutex)); + table_id_t table_id; + dict_table_t * table = NULL; + if (dict_load_table_id_on_index_id(index_id, &table_id)) { + bool local_dict_locked = true; + table = dict_table_open_on_id(table_id, + local_dict_locked, + DICT_TABLE_OP_LOAD_TABLESPACE); + } + + if (!dict_locked) { + mutex_exit(&dict_sys->mutex); + } + return table; +} diff --git a/storage/innobase/dict/dict0mem.cc b/storage/innobase/dict/dict0mem.cc index 89e9861db45..b0d679d4619 100644 --- a/storage/innobase/dict/dict0mem.cc +++ b/storage/innobase/dict/dict0mem.cc @@ -190,36 +190,6 @@ dict_mem_table_create( } /****************************************************************//** -Determines if a table belongs to a system database -@return */ -UNIV_INTERN -bool -dict_mem_table_is_system( -/*================*/ - char *name) /*!< in: table name */ -{ - ut_ad(name); - - /* table has the following format: database/table - and some system table are of the form SYS_* */ - if (strchr(name, '/')) { - int table_len = strlen(name); - const char *system_db; - int i = 0; - while ((system_db = innobase_system_databases[i++]) - && (system_db != NullS)) { - int len = strlen(system_db); - if (table_len > len && !strncmp(name, system_db, len)) { - return true; - } - } - return false; - } else { - return true; - } -} - -/****************************************************************//** Free a table memory object. */ void dict_mem_table_free( @@ -243,6 +213,7 @@ dict_mem_table_free( dict_table_autoinc_destroy(table); #endif /* UNIV_HOTBACKUP */ + dict_mem_table_free_foreign_vcol_set(table); dict_table_stats_latch_destroy(table); table->foreign_set.~dict_foreign_set(); @@ -260,6 +231,10 @@ dict_mem_table_free( UT_DELETE(vcol->v_indexes); } + if (table->s_cols != NULL) { + UT_DELETE(table->s_cols); + } + mem_heap_free(table->heap); } @@ -433,6 +408,39 @@ dict_mem_table_add_v_col( return(v_col); } +/** Adds a stored column definition to a table. +@param[in] table table +@param[in] num_base number of base columns. */ +void +dict_mem_table_add_s_col( + dict_table_t* table, + ulint num_base) +{ + ulint i = table->n_def - 1; + dict_col_t* col = dict_table_get_nth_col(table, i); + dict_s_col_t s_col; + + ut_ad(col != NULL); + + if (table->s_cols == NULL) { + table->s_cols = UT_NEW_NOKEY(dict_s_col_list()); + } + + s_col.m_col = col; + s_col.s_pos = i + table->n_v_def; + + if (num_base != 0) { + s_col.base_col = static_cast<dict_col_t**>(mem_heap_zalloc( + table->heap, num_base * sizeof(dict_col_t*))); + } else { + s_col.base_col = NULL; + } + + s_col.num_base = num_base; + table->s_cols->push_back(s_col); +} + + /**********************************************************************//** Renames a column of a table in the data dictionary cache. */ static MY_ATTRIBUTE((nonnull)) @@ -452,7 +460,9 @@ dict_mem_table_col_rename_low( size_t from_len = strlen(s), to_len = strlen(to); - ut_ad(i < table->n_def); + ut_ad(i < table->n_def || is_virtual); + ut_ad(i < table->n_v_def || !is_virtual); + ut_ad(from_len <= NAME_LEN); ut_ad(to_len <= NAME_LEN); @@ -592,7 +602,7 @@ void dict_mem_table_col_rename( /*======================*/ dict_table_t* table, /*!< in/out: table */ - unsigned nth_col,/*!< in: column index */ + ulint nth_col,/*!< in: column index */ const char* from, /*!< in: old column name */ const char* to, /*!< in: new column name */ bool is_virtual) @@ -603,7 +613,7 @@ dict_mem_table_col_rename( ut_ad((!is_virtual && nth_col < table->n_def) || (is_virtual && nth_col < table->n_v_def)); - for (unsigned i = 0; i < nth_col; i++) { + for (ulint i = 0; i < nth_col; i++) { size_t len = strlen(s); ut_ad(len > 0); s += len + 1; @@ -613,7 +623,8 @@ dict_mem_table_col_rename( Proceed with the renaming anyway. */ ut_ad(!strcmp(from, s)); - dict_mem_table_col_rename_low(table, nth_col, to, s, is_virtual); + dict_mem_table_col_rename_low(table, static_cast<unsigned>(nth_col), + to, s, is_virtual); } /**********************************************************************//** @@ -709,6 +720,8 @@ dict_mem_foreign_create(void) foreign->heap = heap; + foreign->v_cols = NULL; + DBUG_PRINT("dict_mem_foreign_create", ("heap: %p", heap)); DBUG_RETURN(foreign); @@ -773,6 +786,181 @@ dict_mem_referenced_table_name_lookup_set( = foreign->referenced_table_name; } } + +/** Fill the virtual column set with virtual column information +present in the given virtual index. +@param[in] index virtual index +@param[out] v_cols virtual column set. */ +static +void +dict_mem_fill_vcol_has_index( + const dict_index_t* index, + dict_vcol_set** v_cols) +{ + for (ulint i = 0; i < index->table->n_v_cols; i++) { + dict_v_col_t* v_col = dict_table_get_nth_v_col( + index->table, i); + if (!v_col->m_col.ord_part) { + continue; + } + + dict_v_idx_list::iterator it; + for (it = v_col->v_indexes->begin(); + it != v_col->v_indexes->end(); ++it) { + dict_v_idx_t v_idx = *it; + + if (v_idx.index != index) { + continue; + } + + if (*v_cols == NULL) { + *v_cols = UT_NEW_NOKEY(dict_vcol_set()); + } + + (*v_cols)->insert(v_col); + } + } +} + +/** Fill the virtual column set with the virtual column of the index +if the index contains given column name. +@param[in] col_name column name +@param[in] table innodb table object +@param[out] v_cols set of virtual column information. */ +static +void +dict_mem_fill_vcol_from_v_indexes( + const char* col_name, + const dict_table_t* table, + dict_vcol_set** v_cols) +{ + /* virtual column can't be Primary Key, so start with + secondary index */ + for (dict_index_t* index = dict_table_get_next_index( + dict_table_get_first_index(table)); + index; + index = dict_table_get_next_index(index)) { + + if (!dict_index_has_virtual(index)) { + continue; + } + + for (ulint i = 0; i < index->n_fields; i++) { + dict_field_t* field = + dict_index_get_nth_field(index, i); + + if (strcmp(field->name, col_name) == 0) { + dict_mem_fill_vcol_has_index( + index, v_cols); + } + } + } +} + +/** Fill the virtual column set with virtual columns which have base columns +as the given col_name +@param[in] col_name column name +@param[in] table table object +@param[out] v_cols set of virtual columns. */ +static +void +dict_mem_fill_vcol_set_for_base_col( + const char* col_name, + const dict_table_t* table, + dict_vcol_set** v_cols) +{ + for (ulint i = 0; i < table->n_v_cols; i++) { + dict_v_col_t* v_col = dict_table_get_nth_v_col(table, i); + + if (!v_col->m_col.ord_part) { + continue; + } + + for (ulint j = 0; j < v_col->num_base; j++) { + if (strcmp(col_name, dict_table_get_col_name( + table, + v_col->base_col[j]->ind)) == 0) { + + if (*v_cols == NULL) { + *v_cols = UT_NEW_NOKEY(dict_vcol_set()); + } + + (*v_cols)->insert(v_col); + } + } + } +} + +/** Fills the dependent virtual columns in a set. +Reason for being dependent are +1) FK can be present on base column of virtual columns +2) FK can be present on column which is a part of virtual index +@param[in,out] foreign foreign key information. */ +void +dict_mem_foreign_fill_vcol_set( + dict_foreign_t* foreign) +{ + ulint type = foreign->type; + + if (type == 0) { + return; + } + + for (ulint i = 0; i < foreign->n_fields; i++) { + /** FK can be present on base columns + of virtual columns. */ + dict_mem_fill_vcol_set_for_base_col( + foreign->foreign_col_names[i], + foreign->foreign_table, + &foreign->v_cols); + + /** FK can be present on the columns + which can be a part of virtual index. */ + dict_mem_fill_vcol_from_v_indexes( + foreign->foreign_col_names[i], + foreign->foreign_table, + &foreign->v_cols); + } +} + +/** Fill virtual columns set in each fk constraint present in the table. +@param[in,out] table innodb table object. */ +void +dict_mem_table_fill_foreign_vcol_set( + dict_table_t* table) +{ + dict_foreign_set fk_set = table->foreign_set; + dict_foreign_t* foreign; + + dict_foreign_set::iterator it; + for (it = fk_set.begin(); it != fk_set.end(); ++it) { + foreign = *it; + + dict_mem_foreign_fill_vcol_set(foreign); + } +} + +/** Free the vcol_set from all foreign key constraint on the table. +@param[in,out] table innodb table object. */ +void +dict_mem_table_free_foreign_vcol_set( + dict_table_t* table) +{ + dict_foreign_set fk_set = table->foreign_set; + dict_foreign_t* foreign; + + dict_foreign_set::iterator it; + for (it = fk_set.begin(); it != fk_set.end(); ++it) { + + foreign = *it; + + if (foreign->v_cols != NULL) { + UT_DELETE(foreign->v_cols); + foreign->v_cols = NULL; + } + } +} + #endif /* !UNIV_HOTBACKUP */ /**********************************************************************//** @@ -942,3 +1130,32 @@ operator<< (std::ostream& out, const dict_foreign_set& fk_set) return(out); } +/****************************************************************//** +Determines if a table belongs to a system database +@return */ +bool +dict_mem_table_is_system( +/*================*/ + char *name) /*!< in: table name */ +{ + ut_ad(name); + + /* table has the following format: database/table + and some system table are of the form SYS_* */ + if (strchr(name, '/')) { + int table_len = strlen(name); + const char *system_db; + int i = 0; + while ((system_db = innobase_system_databases[i++]) + && (system_db != NullS)) { + int len = strlen(system_db); + if (table_len > len && !strncmp(name, system_db, len)) { + return true; + } + } + return false; + } else { + return true; + } +} + diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index 67bf672ab49..3c25e37506b 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -930,14 +930,14 @@ dict_stats_update_transient( if (dict_table_is_discarded(table)) { /* Nothing to do. */ - dict_stats_empty_table(table, false); + dict_stats_empty_table(table, true); return; } else if (index == NULL) { /* Table definition is corrupt */ ib::warn() << "Table " << table->name << " has no indexes. Cannot calculate statistics."; - dict_stats_empty_table(table, false); + dict_stats_empty_table(table, true); return; } @@ -2316,7 +2316,6 @@ storage. allocate and free the trx object. If it is not NULL then it will be rolled back only in the case of error, but not freed. @return DB_SUCCESS or error code */ -static dberr_t dict_stats_save_index_stat( dict_index_t* index, @@ -3257,15 +3256,15 @@ dict_stats_update( if (innodb_table_stats_not_found == false && table->stats_error_printed == false) { - ib::error() << "Fetch of persistent statistics" - " requested for table " - << table->name - << " but the required system tables " - << TABLE_STATS_NAME_PRINT - << " and " << INDEX_STATS_NAME_PRINT - << " are not present or have unexpected" - " structure. Using transient stats instead."; - table->stats_error_printed = true; + ib::error() << "Fetch of persistent statistics" + " requested for table " + << table->name + << " but the required system tables " + << TABLE_STATS_NAME_PRINT + << " and " << INDEX_STATS_NAME_PRINT + << " are not present or have unexpected" + " structure. Using transient stats instead."; + table->stats_error_printed = true; } goto transient; @@ -3337,12 +3336,12 @@ dict_stats_update( if (innodb_table_stats_not_found == false && table->stats_error_printed == false) { - ib::error() << "Error fetching persistent statistics" - " for table " - << table->name - << " from " TABLE_STATS_NAME_PRINT " and " - INDEX_STATS_NAME_PRINT ": " << ut_strerr(err) - << ". Using transient stats method instead."; + ib::error() << "Error fetching persistent statistics" + " for table " + << table->name + << " from " TABLE_STATS_NAME_PRINT " and " + INDEX_STATS_NAME_PRINT ": " << ut_strerr(err) + << ". Using transient stats method instead."; } goto transient; @@ -3842,120 +3841,6 @@ dict_stats_rename_table( } /*********************************************************************//** -Save defragmentation result. -@return DB_SUCCESS or error code */ -UNIV_INTERN -dberr_t -dict_stats_save_defrag_summary( - dict_index_t* index) /*!< in: index */ -{ - dberr_t ret; - lint now = (lint) ut_time(); - - if (dict_stats_should_ignore_index(index)) { - return DB_SUCCESS; - } - - rw_lock_x_lock(dict_operation_lock); - mutex_enter(&dict_sys->mutex); - ret = dict_stats_save_index_stat(index, now, "n_pages_freed", - index->stat_defrag_n_pages_freed, - NULL, - "Number of pages freed during" - " last defragmentation run.", - NULL); - - mutex_exit(&dict_sys->mutex); - rw_lock_x_unlock(dict_operation_lock); - return (ret); -} - -/*********************************************************************//** -Save defragmentation stats for a given index. -@return DB_SUCCESS or error code */ -UNIV_INTERN -dberr_t -dict_stats_save_defrag_stats( - dict_index_t* index) /*!< in: index */ -{ - dberr_t ret; - - if (index->table->ibd_file_missing) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Cannot save defragment stats because " - ".ibd file is missing.\n"); - return (DB_TABLESPACE_DELETED); - } - if (dict_index_is_corrupted(index)) { - ut_print_timestamp(stderr); - fprintf(stderr, - " InnoDB: Cannot save defragment stats because " - "index is corrupted.\n"); - return(DB_CORRUPTION); - } - - if (dict_stats_should_ignore_index(index)) { - return DB_SUCCESS; - } - - lint now = (lint) ut_time(); - mtr_t mtr; - ulint n_leaf_pages=0; - ulint n_leaf_reserved=0; - mtr_start(&mtr); - mtr_s_lock(dict_index_get_lock(index), &mtr); - - n_leaf_reserved = btr_get_size_and_reserved(index, BTR_N_LEAF_PAGES, - &n_leaf_pages, &mtr); - mtr_commit(&mtr); - - if (n_leaf_reserved == ULINT_UNDEFINED) { - // The index name is different during fast index creation, - // so the stats won't be associated with the right index - // for later use. We just return without saving. - return DB_SUCCESS; - } - - rw_lock_x_lock(dict_operation_lock); - - mutex_enter(&dict_sys->mutex); - ret = dict_stats_save_index_stat(index, now, "n_page_split", - index->stat_defrag_n_page_split, - NULL, - "Number of new page splits on leaves" - " since last defragmentation.", - NULL); - if (ret != DB_SUCCESS) { - goto end; - } - - ret = dict_stats_save_index_stat( - index, now, "n_leaf_pages_defrag", - n_leaf_pages, - NULL, - "Number of leaf pages when this stat is saved to disk", - NULL); - if (ret != DB_SUCCESS) { - goto end; - } - - ret = dict_stats_save_index_stat( - index, now, "n_leaf_pages_reserved", - n_leaf_reserved, - NULL, - "Number of pages reserved for this index leaves when this stat " - "is saved to disk", - NULL); - -end: - mutex_exit(&dict_sys->mutex); - rw_lock_x_unlock(dict_operation_lock); - - return (ret); -} - -/*********************************************************************//** Renames an index in InnoDB persistent stats storage. This function creates its own transaction and commits it. @return DB_SUCCESS or error code. DB_STATS_DO_NOT_EXIST will be returned diff --git a/storage/innobase/dict/dict0stats_bg.cc b/storage/innobase/dict/dict0stats_bg.cc index eca3756152a..dbc8e90bed6 100644 --- a/storage/innobase/dict/dict0stats_bg.cc +++ b/storage/innobase/dict/dict0stats_bg.cc @@ -24,9 +24,9 @@ Created Apr 25, 2012 Vasil Dimov *******************************************************/ #include "dict0dict.h" -#include "dict0dict.h" #include "dict0stats.h" #include "dict0stats_bg.h" +#include "dict0defrag_bg.h" #include "row0mysql.h" #include "srv0start.h" #include "ut0new.h" @@ -45,15 +45,27 @@ Created Apr 25, 2012 Vasil Dimov /** Event to wake up the stats thread */ os_event_t dict_stats_event = NULL; +/** Variable to initiate shutdown the dict stats thread. Note we don't +use 'srv_shutdown_state' because we want to shutdown dict stats thread +before purge thread. */ +bool dict_stats_start_shutdown = false; + +/** Event to wait for shutdown of the dict stats thread */ +os_event_t dict_stats_shutdown_event = NULL; + +#ifdef UNIV_DEBUG +/** Used by SET GLOBAL innodb_dict_stats_disabled_debug = 1; */ +my_bool innodb_dict_stats_disabled_debug; + +static os_event_t dict_stats_disabled_event; +#endif /* UNIV_DEBUG */ + /** This mutex protects the "recalc_pool" variable. */ static ib_mutex_t recalc_pool_mutex; -static ib_mutex_t defrag_pool_mutex; -static mysql_pfs_key_t defrag_pool_mutex_key; /** The number of tables that can be added to "recalc_pool" before it is enlarged */ static const ulint RECALC_POOL_INITIAL_SLOTS = 128; -static const ulint DEFRAG_POOL_INITIAL_SLOTS = 128; /** Allocator type, used by std::vector */ typedef ut_allocator<table_id_t> @@ -73,39 +85,23 @@ typedef recalc_pool_t::iterator by background statistics gathering. */ static recalc_pool_t* recalc_pool; -/** Indices whose defrag stats need to be saved to persistent storage.*/ -struct defrag_pool_item_t { - table_id_t table_id; - index_id_t index_id; -}; - -typedef ut_allocator<defrag_pool_item_t> - defrag_pool_allocator_t; -typedef std::vector<defrag_pool_item_t, defrag_pool_allocator_t> - defrag_pool_t; -static defrag_pool_t* defrag_pool; -typedef defrag_pool_t::iterator defrag_pool_iterator_t; /*****************************************************************//** Initialize the recalc pool, called once during thread initialization. */ static void -dict_stats_pool_init() +dict_stats_recalc_pool_init() /*=========================*/ { ut_ad(!srv_read_only_mode); /* JAN: TODO: MySQL 5.7 PSI const PSI_memory_key key = mem_key_dict_stats_bg_recalc_pool_t; - const PSI_memory_key key2 = mem_key_dict_defrag_pool_t; recalc_pool = UT_NEW(recalc_pool_t(recalc_pool_allocator_t(key)), key); - defrag_pool = UT_NEW(defrag_pool_t(defrag_pool_allocator_t(key2)), key2); - defrag_pool->reserve(DEFRAG_POOL_INITIAL_SLOTS); recalc_pool->reserve(RECALC_POOL_INITIAL_SLOTS); */ recalc_pool = new std::vector<table_id_t, recalc_pool_allocator_t>(); - defrag_pool = new std::vector<defrag_pool_item_t, defrag_pool_allocator_t>(); } /*****************************************************************//** @@ -113,16 +109,14 @@ Free the resources occupied by the recalc pool, called once during thread de-initialization. */ static void -dict_stats_pool_deinit() -/*====================*/ +dict_stats_recalc_pool_deinit() +/*===========================*/ { ut_ad(!srv_read_only_mode); recalc_pool->clear(); - defrag_pool->clear(); UT_DELETE(recalc_pool); - UT_DELETE(defrag_pool); } /*****************************************************************//** @@ -217,111 +211,6 @@ dict_stats_recalc_pool_del( } /*****************************************************************//** -Add an index in a table to the defrag pool, which is processed by the -background stats gathering thread. Only the table id and index id are -added to the list, so the table can be closed after being enqueued and -it will be opened when needed. If the table or index does not exist later -(has been DROPped), then it will be removed from the pool and skipped. */ -UNIV_INTERN -void -dict_stats_defrag_pool_add( -/*=======================*/ - const dict_index_t* index) /*!< in: table to add */ -{ - defrag_pool_item_t item; - - ut_ad(!srv_read_only_mode); - - mutex_enter(&defrag_pool_mutex); - - /* quit if already in the list */ - for (defrag_pool_iterator_t iter = defrag_pool->begin(); - iter != defrag_pool->end(); - ++iter) { - if ((*iter).table_id == index->table->id - && (*iter).index_id == index->id) { - mutex_exit(&defrag_pool_mutex); - return; - } - } - - item.table_id = index->table->id; - item.index_id = index->id; - defrag_pool->push_back(item); - - mutex_exit(&defrag_pool_mutex); - - os_event_set(dict_stats_event); -} - -/*****************************************************************//** -Get an index from the auto defrag pool. The returned index id is removed -from the pool. -@return true if the pool was non-empty and "id" was set, false otherwise */ -static -bool -dict_stats_defrag_pool_get( -/*=======================*/ - table_id_t* table_id, /*!< out: table id, or unmodified if - list is empty */ - index_id_t* index_id) /*!< out: index id, or unmodified if - list is empty */ -{ - ut_ad(!srv_read_only_mode); - - mutex_enter(&defrag_pool_mutex); - - if (defrag_pool->empty()) { - mutex_exit(&defrag_pool_mutex); - return(false); - } - - defrag_pool_item_t& item = defrag_pool->back(); - *table_id = item.table_id; - *index_id = item.index_id; - - defrag_pool->pop_back(); - - mutex_exit(&defrag_pool_mutex); - - return(true); -} - -/*****************************************************************//** -Delete a given index from the auto defrag pool. */ -UNIV_INTERN -void -dict_stats_defrag_pool_del( -/*=======================*/ - const dict_table_t* table, /*!<in: if given, remove - all entries for the table */ - const dict_index_t* index) /*!< in: if given, remove this index */ -{ - ut_a((table && !index) || (!table && index)); - ut_ad(!srv_read_only_mode); - ut_ad(mutex_own(&dict_sys->mutex)); - - mutex_enter(&defrag_pool_mutex); - - defrag_pool_iterator_t iter = defrag_pool->begin(); - while (iter != defrag_pool->end()) { - if ((table && (*iter).table_id == table->id) - || (index - && (*iter).table_id == index->table->id - && (*iter).index_id == index->id)) { - /* erase() invalidates the iterator */ - iter = defrag_pool->erase(iter); - if (index) - break; - } else { - iter++; - } - } - - mutex_exit(&defrag_pool_mutex); -} - -/*****************************************************************//** Wait until background stats thread has stopped using the specified table. The caller must have locked the data dictionary using row_mysql_lock_data_dictionary() and this function may unlock it temporarily @@ -352,6 +241,9 @@ dict_stats_thread_init() ut_a(!srv_read_only_mode); dict_stats_event = os_event_create(0); + dict_stats_shutdown_event = os_event_create(0); + + ut_d(dict_stats_disabled_event = os_event_create(0)); /* The recalc_pool_mutex is acquired from: 1) the background stats gathering thread before any other latch @@ -369,10 +261,9 @@ dict_stats_thread_init() mutex_create(LATCH_ID_RECALC_POOL, &recalc_pool_mutex); - /* We choose SYNC_STATS_DEFRAG to be below SYNC_FSP_PAGE. */ - mutex_create(LATCH_ID_DEFRAGMENT_MUTEX, &defrag_pool_mutex); + dict_stats_recalc_pool_init(); + dict_defrag_pool_init(); - dict_stats_pool_init(); } /*****************************************************************//** @@ -385,13 +276,21 @@ dict_stats_thread_deinit() ut_a(!srv_read_only_mode); ut_ad(!srv_dict_stats_thread_active); - dict_stats_pool_deinit(); + dict_stats_recalc_pool_deinit(); + dict_defrag_pool_deinit(); mutex_free(&recalc_pool_mutex); - mutex_free(&defrag_pool_mutex); + +#ifdef UNIV_DEBUG + os_event_destroy(dict_stats_disabled_event); + dict_stats_disabled_event = NULL; +#endif /* UNIV_DEBUG */ os_event_destroy(dict_stats_event); + os_event_destroy(dict_stats_shutdown_event); dict_stats_event = NULL; + dict_stats_shutdown_event = NULL; + dict_stats_start_shutdown = false; } /*****************************************************************//** @@ -459,69 +358,43 @@ dict_stats_process_entry_from_recalc_pool() mutex_enter(&dict_sys->mutex); - table->stats_bg_flag &= ~BG_STAT_IN_PROGRESS; + table->stats_bg_flag = BG_STAT_NONE; dict_table_close(table, TRUE, FALSE); mutex_exit(&dict_sys->mutex); } -/*****************************************************************//** -Get the first index that has been added for updating persistent defrag -stats and eventually save its stats. */ -static +#ifdef UNIV_DEBUG +/** Disables dict stats thread. It's used by: + SET GLOBAL innodb_dict_stats_disabled_debug = 1 (0). +@param[in] thd thread handle +@param[in] var pointer to system variable +@param[out] var_ptr where the formal string goes +@param[in] save immediate result from check function */ void -dict_stats_process_entry_from_defrag_pool() -/*=======================================*/ +dict_stats_disabled_debug_update( + THD* thd, + struct st_mysql_sys_var* var, + void* var_ptr, + const void* save) { - table_id_t table_id; - index_id_t index_id; - - ut_ad(!srv_read_only_mode); - - /* pop the first index from the auto defrag pool */ - if (!dict_stats_defrag_pool_get(&table_id, &index_id)) { - /* no index in defrag pool */ - return; - } - - dict_table_t* table; - - mutex_enter(&dict_sys->mutex); - - /* If the table is no longer cached, we've already lost the in - memory stats so there's nothing really to write to disk. */ - table = dict_table_open_on_id(table_id, TRUE, - DICT_TABLE_OP_OPEN_ONLY_IF_CACHED); - - if (table == NULL) { - mutex_exit(&dict_sys->mutex); - return; - } + /* This method is protected by mutex, as every SET GLOBAL .. */ + ut_ad(dict_stats_disabled_event != NULL); - /* Check whether table is corrupted */ - if (table->corrupted) { - dict_table_close(table, TRUE, FALSE); - mutex_exit(&dict_sys->mutex); - return; - } - mutex_exit(&dict_sys->mutex); + const bool disable = *static_cast<const my_bool*>(save); - dict_index_t* index = dict_table_find_index_on_id(table, index_id); + const int64_t sig_count = os_event_reset(dict_stats_disabled_event); - if (index == NULL) { - return; - } + innodb_dict_stats_disabled_debug = disable; - /* Check whether index is corrupted */ - if (dict_index_is_corrupted(index)) { - dict_table_close(table, FALSE, FALSE); - return; + if (disable) { + os_event_set(dict_stats_event); + os_event_wait_low(dict_stats_disabled_event, sig_count); } - - dict_stats_save_defrag_stats(index); - dict_table_close(table, FALSE, FALSE); } +#endif /* UNIV_DEBUG */ + /*****************************************************************//** This is the thread for background stats gathering. It pops tables, from @@ -545,7 +418,7 @@ DECLARE_THREAD(dict_stats_thread)( srv_dict_stats_thread_active = TRUE; - while (!SHUTTING_DOWN()) { + while (!dict_stats_start_shutdown) { /* Wake up periodically even if not signaled. This is because we may lose an event - if the below call to @@ -555,23 +428,44 @@ DECLARE_THREAD(dict_stats_thread)( os_event_wait_time( dict_stats_event, MIN_RECALC_INTERVAL * 1000000); - if (SHUTTING_DOWN()) { +#ifdef UNIV_DEBUG + while (innodb_dict_stats_disabled_debug) { + os_event_set(dict_stats_disabled_event); + if (dict_stats_start_shutdown) { + break; + } + os_event_wait_time( + dict_stats_event, 100000); + } +#endif /* UNIV_DEBUG */ + + if (dict_stats_start_shutdown) { break; } dict_stats_process_entry_from_recalc_pool(); - - while (defrag_pool->size()) - dict_stats_process_entry_from_defrag_pool(); + dict_defrag_process_entries_from_defrag_pool(); os_event_reset(dict_stats_event); } srv_dict_stats_thread_active = FALSE; + os_event_set(dict_stats_shutdown_event); + my_thread_end(); + /* We count the number of threads in os_thread_exit(). A created thread should always use that to exit instead of return(). */ - os_thread_exit(NULL); + os_thread_exit(); OS_THREAD_DUMMY_RETURN; } + +/** Shutdown the dict stats thread. */ +void +dict_stats_shutdown() +{ + dict_stats_start_shutdown = true; + os_event_set(dict_stats_event); + os_event_wait(dict_stats_shutdown_event); +} |