diff options
Diffstat (limited to 'storage/innobase/dict/dict0stats.cc')
-rw-r--r-- | storage/innobase/dict/dict0stats.cc | 716 |
1 files changed, 293 insertions, 423 deletions
diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index e3d0effb5ac..5fa049f6b2b 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -1,6 +1,6 @@ /***************************************************************************** -Copyright (c) 2009, 2016, Oracle and/or its affiliates. All Rights Reserved. +Copyright (c) 2009, 2017, Oracle and/or its affiliates. All Rights Reserved. Copyright (c) 2015, 2017, MariaDB Corporation. This program is free software; you can redistribute it and/or modify it under @@ -276,9 +276,7 @@ This function will free the pinfo object. @param[in,out] pinfo pinfo to pass to que_eval_sql() must already have any literals bound to it @param[in] sql SQL string to execute -@param[in,out] trx in case of NULL the function will 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. +@param[in,out] trx transaction @return DB_SUCCESS or error code */ static dberr_t @@ -288,53 +286,12 @@ dict_stats_exec_sql( trx_t* trx) { dberr_t err; - bool trx_started = false; - - ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X)); - ut_ad(mutex_own(&dict_sys->mutex)); - - if (!dict_stats_persistent_storage_check(true)) { - pars_info_free(pinfo); - return(DB_STATS_DO_NOT_EXIST); - } - - if (trx == NULL) { - trx = trx_allocate_for_background(); - trx_started = true; - - if (srv_read_only_mode) { - trx_start_internal_read_only(trx); - } else { - trx_start_internal(trx); - } - } err = que_eval_sql(pinfo, sql, FALSE, trx); /* pinfo is freed here */ DBUG_EXECUTE_IF("stats_index_error", - if (!trx_started) { err = DB_STATS_DO_NOT_EXIST; - trx->error_state = DB_STATS_DO_NOT_EXIST; - }); - - if (!trx_started && err == DB_SUCCESS) { - return(DB_SUCCESS); - } - - if (err == DB_SUCCESS) { - trx_commit_for_mysql(trx); - } else { - trx->op_info = "rollback of internal trx on stats tables"; - trx->dict_operation_lock_mode = RW_X_LATCH; - trx_rollback_to_savepoint(trx, NULL); - trx->dict_operation_lock_mode = 0; - trx->op_info = ""; - ut_a(trx->error_state == DB_SUCCESS); - } - - if (trx_started) { - trx_free_for_background(trx); - } + trx->error_state = DB_STATS_DO_NOT_EXIST;); return(err); } @@ -543,39 +500,12 @@ dict_stats_empty_index( } } -/**********************************************************************//** -Clear defragmentation summary. */ -UNIV_INTERN -void -dict_stats_empty_defrag_summary( -/*==================*/ - dict_index_t* index) /*!< in: index to clear defragmentation stats */ -{ - index->stat_defrag_n_pages_freed = 0; -} - -/**********************************************************************//** -Clear defragmentation related index stats. */ -UNIV_INTERN -void -dict_stats_empty_defrag_stats( -/*==================*/ - dict_index_t* index) /*!< in: index to clear defragmentation stats */ -{ - index->stat_defrag_modified_counter = 0; - index->stat_defrag_n_page_split = 0; -} - -/*********************************************************************//** -Write all zeros (or 1 where it makes sense) into a table and its indexes' -statistics members. The resulting stats correspond to an empty table. */ -static +/** Reset the table and index statsistics, corresponding to an empty table. +@param[in,out] table table whose statistics are to be reset +@param[in] empty_defrag_stats whether to empty the defrag statistics +*/ void -dict_stats_empty_table( -/*===================*/ - dict_table_t* table, /*!< in/out: table */ - bool empty_defrag_stats) - /*!< in: whether to empty defrag stats */ +dict_stats_empty_table(dict_table_t* table, bool empty_defrag_stats) { /* Zero the stats members */ @@ -2329,9 +2259,7 @@ storage. @param[in] stat_value value of the stat @param[in] sample_size n pages sampled or NULL @param[in] stat_description description of the stat -@param[in,out] trx in case of NULL the function will -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. +@param[in,out] trx transaction @return DB_SUCCESS or error code */ dberr_t dict_stats_save_index_stat( @@ -2348,8 +2276,7 @@ dict_stats_save_index_stat( char db_utf8[MAX_DB_UTF8_LEN]; char table_utf8[MAX_TABLE_UTF8_LEN]; - ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X)); - ut_ad(mutex_own(&dict_sys->mutex)); + ut_ad(trx->persistent_stats || trx->in_mysql_trx_list); dict_fs2utf8(index->table->name.m_name, db_utf8, sizeof(db_utf8), table_utf8, sizeof(table_utf8)); @@ -2375,6 +2302,8 @@ dict_stats_save_index_stat( pars_info_add_str_literal(pinfo, "stat_description", stat_description); + mutex_enter(&dict_sys->mutex); + ret = dict_stats_exec_sql( pinfo, "PROCEDURE INDEX_STATS_SAVE () IS\n" @@ -2401,6 +2330,8 @@ dict_stats_save_index_stat( ");\n" "END;", trx); + mutex_exit(&dict_sys->mutex); + if (ret != DB_SUCCESS) { if (innodb_index_stats_not_found == false && index->stats_error_printed == false) { @@ -2453,6 +2384,7 @@ dict_stats_report_error(dict_table_t* table, bool defragment) /** Save the table's statistics into the persistent statistics storage. @param[in] table_orig table whose stats to save +@param[in,out] trx transaction @param[in] only_for_index if this is non-NULL, then stats for indexes that are not equal to it will not be saved, if NULL, then all indexes' stats are saved @@ -2461,7 +2393,8 @@ static dberr_t dict_stats_save( dict_table_t* table_orig, - const index_id_t* only_for_index) + trx_t* trx, + const index_id_t* only_for_index = NULL) { pars_info_t* pinfo; lint now; @@ -2470,6 +2403,8 @@ dict_stats_save( char db_utf8[MAX_DB_UTF8_LEN]; char table_utf8[MAX_TABLE_UTF8_LEN]; + ut_ad(trx->persistent_stats || trx->in_mysql_trx_list); + if (table_orig->is_readable()) { } else { return (dict_stats_report_error(table_orig)); @@ -2480,9 +2415,6 @@ dict_stats_save( dict_fs2utf8(table->name.m_name, db_utf8, sizeof(db_utf8), table_utf8, sizeof(table_utf8)); - rw_lock_x_lock(dict_operation_lock); - mutex_enter(&dict_sys->mutex); - /* MySQL's timestamp is 4 byte, so we use pars_info_add_int4_literal() which takes a lint arg, so "now" is lint */ @@ -2499,6 +2431,8 @@ dict_stats_save( pars_info_add_ull_literal(pinfo, "sum_of_other_index_sizes", table->stat_sum_of_other_index_sizes); + mutex_enter(&dict_sys->mutex); + ret = dict_stats_exec_sql( pinfo, "PROCEDURE TABLE_STATS_SAVE () IS\n" @@ -2519,33 +2453,18 @@ dict_stats_save( ":clustered_index_size,\n" ":sum_of_other_index_sizes\n" ");\n" - "END;", NULL); - - if (ret != DB_SUCCESS) { - ib::error() << "Cannot save table statistics for table " - << table->name << ": " << ut_strerr(ret); - - mutex_exit(&dict_sys->mutex); - rw_lock_x_unlock(dict_operation_lock); - - dict_stats_snapshot_free(table); - - return(ret); - } - - trx_t* trx = trx_allocate_for_background(); + "END;", trx); - if (srv_read_only_mode) { - trx_start_internal_read_only(trx); - } else { - trx_start_internal(trx); - } + mutex_exit(&dict_sys->mutex); - dict_index_t* index; index_map_t indexes( (ut_strcmp_functor()), index_map_t_allocator(mem_key_dict_stats_index_map_t)); + if (ret != DB_SUCCESS) { + goto end; + } + /* Below we do all the modifications in innodb_index_stats in a single transaction for performance reasons. Modifying more than one row in a single transaction may deadlock with other transactions if they @@ -2558,18 +2477,17 @@ dict_stats_save( stat_name). This is why below we sort the indexes by name and then for each index, do the mods ordered by stat_name. */ - for (index = dict_table_get_first_index(table); + for (dict_index_t* index = dict_table_get_first_index(table); index != NULL; index = dict_table_get_next_index(index)) { indexes[index->name] = index; } - index_map_t::const_iterator it; - - for (it = indexes.begin(); it != indexes.end(); ++it) { + for (index_map_t::const_iterator it = indexes.begin(); + it != indexes.end(); ++it) { - index = it->second; + dict_index_t* index = it->second; if (only_for_index != NULL && index->id != *only_for_index) { continue; @@ -2586,21 +2504,20 @@ dict_stats_save( char stat_name[16]; char stat_description[1024]; - ut_snprintf(stat_name, sizeof(stat_name), - "n_diff_pfx%02u", i + 1); + snprintf(stat_name, sizeof(stat_name), + "n_diff_pfx%02u", i + 1); /* craft a string that contains the column names */ - ut_snprintf(stat_description, - sizeof(stat_description), - "%s", index->fields[0].name()); + snprintf(stat_description, sizeof(stat_description), + "%s", index->fields[0].name()); for (unsigned j = 1; j <= i; j++) { size_t len; len = strlen(stat_description); - ut_snprintf(stat_description + len, - sizeof(stat_description) - len, - ",%s", index->fields[j].name()); + snprintf(stat_description + len, + sizeof(stat_description) - len, + ",%s", index->fields[j].name()); } ret = dict_stats_save_index_stat( @@ -2633,13 +2550,11 @@ dict_stats_save( } } - trx_commit_for_mysql(trx); - + if (ret != DB_SUCCESS) { end: - trx_free_for_background(trx); - - mutex_exit(&dict_sys->mutex); - rw_lock_x_unlock(dict_operation_lock); + ib::error() << "Cannot save table statistics for table " + << table->name << ": " << ut_strerr(ret); + } dict_stats_snapshot_free(table); @@ -3137,12 +3052,13 @@ dict_stats_empty_defrag_modified_counter( } } -/*********************************************************************//** -Fetches or calculates new estimates for index statistics. */ -void -dict_stats_update_for_index( -/*========================*/ - dict_index_t* index) /*!< in/out: index */ +/** Calculate index statistics. +@param[in,out] index index tree +@param[in,out] trx transaction (for persistent statistics) +@return DB_SUCCESS or error code */ +UNIV_INTERN +dberr_t +dict_stats_update_for_index(dict_index_t* index, trx_t* trx) { DBUG_ENTER("dict_stats_update_for_index"); @@ -3154,8 +3070,8 @@ dict_stats_update_for_index( dict_table_stats_lock(index->table, RW_X_LATCH); dict_stats_analyze_index(index); dict_table_stats_unlock(index->table, RW_X_LATCH); - dict_stats_save(index->table, &index->id); - DBUG_VOID_RETURN; + DBUG_RETURN(dict_stats_save(index->table, trx, + &index->id)); } /* else */ @@ -3178,22 +3094,20 @@ dict_stats_update_for_index( dict_stats_update_transient_for_index(index); dict_table_stats_unlock(index->table, RW_X_LATCH); - DBUG_VOID_RETURN; + DBUG_RETURN(DB_SUCCESS); } -/*********************************************************************//** -Calculates new estimates for table and index statistics. The statistics -are used in query optimization. -@return DB_SUCCESS or error code */ +/** Calculate new estimates for table and index statistics. +@param[in,out] table table +@param[in] stats_upd_option how to update statistics +@param[in,out] trx transaction +@return DB_* error code or DB_SUCCESS */ +UNIV_INTERN dberr_t dict_stats_update( -/*==============*/ - dict_table_t* table, /*!< in/out: table */ - dict_stats_upd_option_t stats_upd_option) - /*!< in: whether to (re) calc - the stats or to fetch them from - the persistent statistics - storage */ + dict_table_t* table, + dict_stats_upd_option_t stats_upd_option, + trx_t* trx) { ut_ad(!mutex_own(&dict_sys->mutex)); @@ -3238,7 +3152,7 @@ dict_stats_update( return(err); } - err = dict_stats_save(table, NULL); + err = dict_stats_save(table, trx); return(err); } @@ -3274,7 +3188,7 @@ dict_stats_update( if (dict_stats_persistent_storage_check(false)) { - return(dict_stats_save(table, NULL)); + return(dict_stats_save(table, trx)); } return(DB_STATS_DO_NOT_EXIST); @@ -3353,9 +3267,9 @@ dict_stats_update( } if (dict_stats_auto_recalc_is_enabled(table)) { - return(dict_stats_update( - table, - DICT_STATS_RECALC_PERSISTENT)); + return dict_stats_update( + table, DICT_STATS_RECALC_PERSISTENT, + trx); } ib::info() << "Trying to use table " << table->name @@ -3403,25 +3317,20 @@ transient: return(DB_SUCCESS); } -/*********************************************************************//** -Removes the information for a particular index's stats from the persistent -storage if it exists and if there is data stored for this index. -This function creates its own trx and commits it. -A note from Marko why we cannot edit user and sys_* tables in one trx: -marko: The problem is that ibuf merges should be disabled while we are -rolling back dict transactions. -marko: If ibuf merges are not disabled, we need to scan the *.ibd files. -But we shouldn't open *.ibd files before we have rolled back dict -transactions and opened the SYS_* records for the *.ibd files. +/** Remove the persistent statistics for an index. +@param[in] db_and_table schema and table name, e.g., 'db/table' +@param[in] iname index name +@param[out] errstr error message (when not returning DB_SUCCESS) +@param[in] errstr_sz sizeof errstr +@param[in,out] trx transaction @return DB_SUCCESS or error code */ dberr_t dict_stats_drop_index( -/*==================*/ - const char* db_and_table,/*!< in: db and table, e.g. 'db/table' */ - const char* iname, /*!< in: index name */ - char* errstr, /*!< out: error message if != DB_SUCCESS - is returned */ - ulint errstr_sz)/*!< in: size of the errstr buffer */ + const char* db_and_table, + const char* iname, + char* errstr, + size_t errstr_sz, + trx_t* trx) { char db_utf8[MAX_DB_UTF8_LEN]; char table_utf8[MAX_TABLE_UTF8_LEN]; @@ -3437,6 +3346,11 @@ dict_stats_drop_index( return(DB_SUCCESS); } + if (!dict_stats_persistent_storage_check(false)) { + /* Statistics tables do not exist. */ + return(DB_SUCCESS); + } + dict_fs2utf8(db_and_table, db_utf8, sizeof(db_utf8), table_utf8, sizeof(table_utf8)); @@ -3448,7 +3362,6 @@ dict_stats_drop_index( pars_info_add_str_literal(pinfo, "index_name", iname); - rw_lock_x_lock(dict_operation_lock); mutex_enter(&dict_sys->mutex); ret = dict_stats_exec_sql( @@ -3459,33 +3372,35 @@ dict_stats_drop_index( "database_name = :database_name AND\n" "table_name = :table_name AND\n" "index_name = :index_name;\n" - "END;\n", NULL); + "END;\n", trx); mutex_exit(&dict_sys->mutex); - rw_lock_x_unlock(dict_operation_lock); - if (ret == DB_STATS_DO_NOT_EXIST) { - ret = DB_SUCCESS; - } - - if (ret != DB_SUCCESS) { - ut_snprintf(errstr, errstr_sz, - "Unable to delete statistics for index %s" - " from %s%s: %s. They can be deleted later using" - " DELETE FROM %s WHERE" - " database_name = '%s' AND" - " table_name = '%s' AND" - " index_name = '%s';", - iname, - INDEX_STATS_NAME_PRINT, - (ret == DB_LOCK_WAIT_TIMEOUT - ? " because the rows are locked" - : ""), - ut_strerr(ret), - INDEX_STATS_NAME_PRINT, - db_utf8, - table_utf8, - iname); + switch (ret) { + case DB_STATS_DO_NOT_EXIST: + case DB_SUCCESS: + return(DB_SUCCESS); + case DB_QUE_THR_SUSPENDED: + ret = DB_LOCK_WAIT; + /* fall through */ + default: + snprintf(errstr, errstr_sz, + "Unable to delete statistics for index %s" + " from %s%s: %s. They can be deleted later using" + " DELETE FROM %s WHERE" + " database_name = '%s' AND" + " table_name = '%s' AND" + " index_name = '%s';", + iname, + INDEX_STATS_NAME_PRINT, + (ret == DB_LOCK_WAIT_TIMEOUT + ? " because the rows are locked" + : ""), + ut_strerr(ret), + INDEX_STATS_NAME_PRINT, + db_utf8, + table_utf8, + iname); ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: %s\n", errstr); @@ -3494,98 +3409,71 @@ dict_stats_drop_index( return(ret); } -/*********************************************************************//** -Executes -DELETE FROM mysql.innodb_table_stats -WHERE database_name = '...' AND table_name = '...'; -Creates its own transaction and commits it. +/** Delete table statistics. +@param[in] db schema name +@param[in] t table name +@param[in,out] trx transaction @return DB_SUCCESS or error code */ UNIV_INLINE dberr_t -dict_stats_delete_from_table_stats( -/*===============================*/ - const char* database_name, /*!< in: database name, e.g. 'db' */ - const char* table_name) /*!< in: table name, e.g. 'table' */ +dict_stats_delete_from_table_stats(const char* db, const char* t, trx_t* trx) { - pars_info_t* pinfo; - dberr_t ret; + pars_info_t* pinfo = pars_info_create(); - ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X)); - ut_ad(mutex_own(&dict_sys->mutex)); - - pinfo = pars_info_create(); + pars_info_add_str_literal(pinfo, "database_name", db); + pars_info_add_str_literal(pinfo, "table_name", t); - pars_info_add_str_literal(pinfo, "database_name", database_name); - pars_info_add_str_literal(pinfo, "table_name", table_name); - - ret = dict_stats_exec_sql( + return dict_stats_exec_sql( pinfo, "PROCEDURE DELETE_FROM_TABLE_STATS () IS\n" "BEGIN\n" "DELETE FROM \"" TABLE_STATS_NAME "\" WHERE\n" "database_name = :database_name AND\n" "table_name = :table_name;\n" - "END;\n", NULL); - - return(ret); + "END;\n", trx); } -/*********************************************************************//** -Executes -DELETE FROM mysql.innodb_index_stats -WHERE database_name = '...' AND table_name = '...'; -Creates its own transaction and commits it. +/** Delete index statistics. +@param[in] db schema name +@param[in] t table name +@param[in,out] trx transaction @return DB_SUCCESS or error code */ UNIV_INLINE dberr_t -dict_stats_delete_from_index_stats( -/*===============================*/ - const char* database_name, /*!< in: database name, e.g. 'db' */ - const char* table_name) /*!< in: table name, e.g. 'table' */ +dict_stats_delete_from_index_stats(const char* db, const char* t, trx_t* trx) { - pars_info_t* pinfo; - dberr_t ret; - - ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X)); - ut_ad(mutex_own(&dict_sys->mutex)); + pars_info_t* pinfo = pars_info_create(); - pinfo = pars_info_create(); - - pars_info_add_str_literal(pinfo, "database_name", database_name); - pars_info_add_str_literal(pinfo, "table_name", table_name); + pars_info_add_str_literal(pinfo, "database_name", db); + pars_info_add_str_literal(pinfo, "table_name", t); - ret = dict_stats_exec_sql( + return dict_stats_exec_sql( pinfo, "PROCEDURE DELETE_FROM_INDEX_STATS () IS\n" "BEGIN\n" "DELETE FROM \"" INDEX_STATS_NAME "\" WHERE\n" "database_name = :database_name AND\n" "table_name = :table_name;\n" - "END;\n", NULL); - - return(ret); + "END;\n", trx); } -/*********************************************************************//** -Removes the statistics for a table and all of its indexes from the -persistent statistics storage if it exists and if there is data stored for -the table. This function creates its own transaction and commits it. +/** Remove the persistent statistics for a table and all of its indexes. +@param[in] db_and_table schema and table name, e.g., 'db/table' +@param[out] errstr error message (when not returning DB_SUCCESS) +@param[in] errstr_sz sizeof errstr +@param[in,out] trx transaction @return DB_SUCCESS or error code */ dberr_t dict_stats_drop_table( -/*==================*/ - const char* db_and_table, /*!< in: db and table, e.g. 'db/table' */ - char* errstr, /*!< out: error message - if != DB_SUCCESS is returned */ - ulint errstr_sz) /*!< in: size of errstr buffer */ + const char* db_and_table, + char* errstr, + size_t errstr_sz, + trx_t* trx) { char db_utf8[MAX_DB_UTF8_LEN]; char table_utf8[MAX_TABLE_UTF8_LEN]; dberr_t ret; - ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X)); - ut_ad(mutex_own(&dict_sys->mutex)); - /* skip tables that do not contain a database name e.g. if we are dropping SYS_TABLES */ if (strchr(db_and_table, '/') == NULL) { @@ -3600,76 +3488,78 @@ dict_stats_drop_table( return(DB_SUCCESS); } + if (!dict_stats_persistent_storage_check(true)) { + /* Statistics tables do not exist. */ + return(DB_SUCCESS); + } + dict_fs2utf8(db_and_table, db_utf8, sizeof(db_utf8), table_utf8, sizeof(table_utf8)); - ret = dict_stats_delete_from_table_stats(db_utf8, table_utf8); + ret = dict_stats_delete_from_table_stats(db_utf8, table_utf8, trx); if (ret == DB_SUCCESS) { - ret = dict_stats_delete_from_index_stats(db_utf8, table_utf8); + ret = dict_stats_delete_from_index_stats( + db_utf8, table_utf8, trx); } - if (ret == DB_STATS_DO_NOT_EXIST) { - ret = DB_SUCCESS; - } - - if (ret != DB_SUCCESS) { - - ut_snprintf(errstr, errstr_sz, - "Unable to delete statistics for table %s.%s: %s." - " They can be deleted later using" - - " DELETE FROM %s WHERE" - " database_name = '%s' AND" - " table_name = '%s';" - - " DELETE FROM %s WHERE" - " database_name = '%s' AND" - " table_name = '%s';", - - db_utf8, table_utf8, - ut_strerr(ret), - - INDEX_STATS_NAME_PRINT, - db_utf8, table_utf8, - - TABLE_STATS_NAME_PRINT, - db_utf8, table_utf8); + switch (ret) { + case DB_SUCCESS: + case DB_STATS_DO_NOT_EXIST: + return(DB_SUCCESS); + case DB_QUE_THR_SUSPENDED: + ret = DB_LOCK_WAIT; + /* fall through */ + default: + snprintf(errstr, errstr_sz, + "Unable to delete statistics for table %s.%s: %s. " + "They can be deleted later using " + + " DELETE FROM %s WHERE" + " database_name = '%s' AND" + " table_name = '%s';" + + " DELETE FROM %s WHERE" + " database_name = '%s' AND" + " table_name = '%s';", + + db_utf8, table_utf8, + ut_strerr(ret), + + INDEX_STATS_NAME_PRINT, + db_utf8, table_utf8, + + TABLE_STATS_NAME_PRINT, + db_utf8, table_utf8); } return(ret); } -/*********************************************************************//** -Executes -UPDATE mysql.innodb_table_stats SET -database_name = '...', table_name = '...' -WHERE database_name = '...' AND table_name = '...'; -Creates its own transaction and commits it. +/** Rename table statistics. +@param[in] old_dbname_utf8 old schema name +@param[in] old_tablename_utf8 old table name +@param[in] new_dbname_utf8 new schema name +@param[in] new_tablename_utf8 new schema name +@param[in,out] trx transaction @return DB_SUCCESS or error code */ UNIV_INLINE dberr_t -dict_stats_rename_table_in_table_stats( -/*===================================*/ - const char* old_dbname_utf8,/*!< in: database name, e.g. 'olddb' */ - const char* old_tablename_utf8,/*!< in: table name, e.g. 'oldtable' */ - const char* new_dbname_utf8,/*!< in: database name, e.g. 'newdb' */ - const char* new_tablename_utf8)/*!< in: table name, e.g. 'newtable' */ +dict_stats_rename_in_table_stats( + const char* old_dbname_utf8, + const char* old_tablename_utf8, + const char* new_dbname_utf8, + const char* new_tablename_utf8, + trx_t* trx) { - pars_info_t* pinfo; - dberr_t ret; - - ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X)); - ut_ad(mutex_own(&dict_sys->mutex)); - - pinfo = pars_info_create(); + pars_info_t* pinfo = pars_info_create(); pars_info_add_str_literal(pinfo, "old_dbname_utf8", old_dbname_utf8); pars_info_add_str_literal(pinfo, "old_tablename_utf8", old_tablename_utf8); pars_info_add_str_literal(pinfo, "new_dbname_utf8", new_dbname_utf8); pars_info_add_str_literal(pinfo, "new_tablename_utf8", new_tablename_utf8); - ret = dict_stats_exec_sql( + return dict_stats_exec_sql( pinfo, "PROCEDURE RENAME_TABLE_IN_TABLE_STATS () IS\n" "BEGIN\n" @@ -3679,41 +3569,33 @@ dict_stats_rename_table_in_table_stats( "WHERE\n" "database_name = :old_dbname_utf8 AND\n" "table_name = :old_tablename_utf8;\n" - "END;\n", NULL); - - return(ret); + "END;\n", trx); } -/*********************************************************************//** -Executes -UPDATE mysql.innodb_index_stats SET -database_name = '...', table_name = '...' -WHERE database_name = '...' AND table_name = '...'; -Creates its own transaction and commits it. +/** Rename index statistics. +@param[in] old_dbname_utf8 old schema name +@param[in] old_tablename_utf8 old table name +@param[in] new_dbname_utf8 new schema name +@param[in] new_tablename_utf8 new schema name +@param[in,out] trx transaction @return DB_SUCCESS or error code */ UNIV_INLINE dberr_t -dict_stats_rename_table_in_index_stats( -/*===================================*/ - const char* old_dbname_utf8,/*!< in: database name, e.g. 'olddb' */ - const char* old_tablename_utf8,/*!< in: table name, e.g. 'oldtable' */ - const char* new_dbname_utf8,/*!< in: database name, e.g. 'newdb' */ - const char* new_tablename_utf8)/*!< in: table name, e.g. 'newtable' */ +dict_stats_rename_in_index_stats( + const char* old_dbname_utf8, + const char* old_tablename_utf8, + const char* new_dbname_utf8, + const char* new_tablename_utf8, + trx_t* trx) { - pars_info_t* pinfo; - dberr_t ret; - - ut_ad(rw_lock_own(dict_operation_lock, RW_LOCK_X)); - ut_ad(mutex_own(&dict_sys->mutex)); - - pinfo = pars_info_create(); + pars_info_t* pinfo = pars_info_create(); pars_info_add_str_literal(pinfo, "old_dbname_utf8", old_dbname_utf8); pars_info_add_str_literal(pinfo, "old_tablename_utf8", old_tablename_utf8); pars_info_add_str_literal(pinfo, "new_dbname_utf8", new_dbname_utf8); pars_info_add_str_literal(pinfo, "new_tablename_utf8", new_tablename_utf8); - ret = dict_stats_exec_sql( + return dict_stats_exec_sql( pinfo, "PROCEDURE RENAME_TABLE_IN_INDEX_STATS () IS\n" "BEGIN\n" @@ -3723,23 +3605,23 @@ dict_stats_rename_table_in_index_stats( "WHERE\n" "database_name = :old_dbname_utf8 AND\n" "table_name = :old_tablename_utf8;\n" - "END;\n", NULL); - - return(ret); + "END;\n", trx); } -/*********************************************************************//** -Renames a table in InnoDB persistent stats storage. -This function creates its own transaction and commits it. +/** Rename a table in the InnoDB persistent statistics storage. +@param[in] old_name old schema and table name, e.g., 'db/table' +@param[in] new_name new schema and table name, e.g., 'db/table' +@param[out] errstr error message (when not returning DB_SUCCESS) +@param[in] errstr_sz sizeof errstr +@param[in,out] trx transaction @return DB_SUCCESS or error code */ dberr_t dict_stats_rename_table( -/*====================*/ - const char* old_name, /*!< in: old name, e.g. 'db/table' */ - const char* new_name, /*!< in: new name, e.g. 'db/table' */ - char* errstr, /*!< out: error string if != DB_SUCCESS - is returned */ - size_t errstr_sz) /*!< in: errstr size */ + const char* old_name, + const char* new_name, + char* errstr, + size_t errstr_sz, + trx_t* trx) { char old_db_utf8[MAX_DB_UTF8_LEN]; char new_db_utf8[MAX_DB_UTF8_LEN]; @@ -3747,9 +3629,6 @@ dict_stats_rename_table( char new_table_utf8[MAX_TABLE_UTF8_LEN]; dberr_t ret; - ut_ad(!rw_lock_own(dict_operation_lock, RW_LOCK_X)); - ut_ad(!mutex_own(&dict_sys->mutex)); - /* skip innodb_table_stats and innodb_index_stats themselves */ if (strcmp(old_name, TABLE_STATS_NAME) == 0 || strcmp(old_name, INDEX_STATS_NAME) == 0 @@ -3759,124 +3638,115 @@ dict_stats_rename_table( return(DB_SUCCESS); } + if (!dict_stats_persistent_storage_check(false)) { + /* Statistics tables do not exist. */ + return(DB_SUCCESS); + } + dict_fs2utf8(old_name, old_db_utf8, sizeof(old_db_utf8), old_table_utf8, sizeof(old_table_utf8)); dict_fs2utf8(new_name, new_db_utf8, sizeof(new_db_utf8), new_table_utf8, sizeof(new_table_utf8)); - rw_lock_x_lock(dict_operation_lock); - mutex_enter(&dict_sys->mutex); - ulint n_attempts = 0; do { - n_attempts++; + trx_savept_t savept = trx_savept_take(trx); - ret = dict_stats_rename_table_in_table_stats( - old_db_utf8, old_table_utf8, - new_db_utf8, new_table_utf8); + mutex_enter(&dict_sys->mutex); - if (ret == DB_DUPLICATE_KEY) { - dict_stats_delete_from_table_stats( - new_db_utf8, new_table_utf8); - } + ret = dict_stats_rename_in_table_stats( + old_db_utf8, old_table_utf8, + new_db_utf8, new_table_utf8, trx); - if (ret == DB_STATS_DO_NOT_EXIST) { - ret = DB_SUCCESS; - } + mutex_exit(&dict_sys->mutex); - if (ret != DB_SUCCESS) { + switch (ret) { + case DB_DUPLICATE_KEY: + trx_rollback_to_savepoint(trx, &savept); + mutex_enter(&dict_sys->mutex); + dict_stats_delete_from_table_stats( + new_db_utf8, new_table_utf8, trx); mutex_exit(&dict_sys->mutex); - rw_lock_x_unlock(dict_operation_lock); + /* fall through */ + case DB_DEADLOCK: + case DB_LOCK_WAIT_TIMEOUT: os_thread_sleep(200000 /* 0.2 sec */); - rw_lock_x_lock(dict_operation_lock); - mutex_enter(&dict_sys->mutex); + continue; + case DB_STATS_DO_NOT_EXIST: + ret = DB_SUCCESS; + break; + default: + break; } - } while ((ret == DB_DEADLOCK - || ret == DB_DUPLICATE_KEY - || ret == DB_LOCK_WAIT_TIMEOUT) - && n_attempts < 5); + + break; + } while (++n_attempts < 5); + + const char* table_name = TABLE_STATS_NAME_PRINT; if (ret != DB_SUCCESS) { - ut_snprintf(errstr, errstr_sz, - "Unable to rename statistics from" - " %s.%s to %s.%s in %s: %s." - " They can be renamed later using" - - " UPDATE %s SET" - " database_name = '%s'," - " table_name = '%s'" - " WHERE" - " database_name = '%s' AND" - " table_name = '%s';", - - old_db_utf8, old_table_utf8, - new_db_utf8, new_table_utf8, - TABLE_STATS_NAME_PRINT, - ut_strerr(ret), - - TABLE_STATS_NAME_PRINT, - new_db_utf8, new_table_utf8, - old_db_utf8, old_table_utf8); - mutex_exit(&dict_sys->mutex); - rw_lock_x_unlock(dict_operation_lock); - return(ret); + goto err_exit; } - /* else */ + + table_name = INDEX_STATS_NAME_PRINT; n_attempts = 0; do { - n_attempts++; + trx_savept_t savept = trx_savept_take(trx); - ret = dict_stats_rename_table_in_index_stats( - old_db_utf8, old_table_utf8, - new_db_utf8, new_table_utf8); + mutex_enter(&dict_sys->mutex); - if (ret == DB_DUPLICATE_KEY) { - dict_stats_delete_from_index_stats( - new_db_utf8, new_table_utf8); - } + ret = dict_stats_rename_in_index_stats( + old_db_utf8, old_table_utf8, + new_db_utf8, new_table_utf8, trx); - if (ret == DB_STATS_DO_NOT_EXIST) { - ret = DB_SUCCESS; - } + mutex_exit(&dict_sys->mutex); - if (ret != DB_SUCCESS) { + switch (ret) { + case DB_DUPLICATE_KEY: + trx_rollback_to_savepoint(trx, &savept); + mutex_enter(&dict_sys->mutex); + dict_stats_delete_from_index_stats( + new_db_utf8, new_table_utf8, trx); mutex_exit(&dict_sys->mutex); - rw_lock_x_unlock(dict_operation_lock); + /* fall through */ + case DB_DEADLOCK: + case DB_LOCK_WAIT_TIMEOUT: os_thread_sleep(200000 /* 0.2 sec */); - rw_lock_x_lock(dict_operation_lock); - mutex_enter(&dict_sys->mutex); + continue; + case DB_STATS_DO_NOT_EXIST: + ret = DB_SUCCESS; + break; + default: + break; } - } while ((ret == DB_DEADLOCK - || ret == DB_DUPLICATE_KEY - || ret == DB_LOCK_WAIT_TIMEOUT) - && n_attempts < 5); - mutex_exit(&dict_sys->mutex); - rw_lock_x_unlock(dict_operation_lock); + break; + } while (++n_attempts < 5); if (ret != DB_SUCCESS) { - ut_snprintf(errstr, errstr_sz, - "Unable to rename statistics from" - " %s.%s to %s.%s in %s: %s." - " They can be renamed later using" - - " UPDATE %s SET" - " database_name = '%s'," - " table_name = '%s'" - " WHERE" - " database_name = '%s' AND" - " table_name = '%s';", - - old_db_utf8, old_table_utf8, - new_db_utf8, new_table_utf8, - INDEX_STATS_NAME_PRINT, - ut_strerr(ret), - - INDEX_STATS_NAME_PRINT, - new_db_utf8, new_table_utf8, - old_db_utf8, old_table_utf8); +err_exit: + snprintf(errstr, errstr_sz, + "Unable to rename statistics from" + " %s.%s to %s.%s in %s: %s." + " They can be renamed later using" + + " UPDATE %s SET" + " database_name = '%s'," + " table_name = '%s'" + " WHERE" + " database_name = '%s' AND" + " table_name = '%s';", + + old_db_utf8, old_table_utf8, + new_db_utf8, new_table_utf8, + table_name, + ut_strerr(ret), + + table_name, + new_db_utf8, new_table_utf8, + old_db_utf8, old_table_utf8); } return(ret); @@ -3979,7 +3849,7 @@ test_dict_table_schema_check() }; char errstr[512]; - ut_snprintf(errstr, sizeof(errstr), "Table not found"); + snprintf(errstr, sizeof(errstr), "Table not found"); /* prevent any data dictionary modifications while we are checking the tables' structure */ |