1/***************************************************************************** 2 3Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. 4 5This program is free software; you can redistribute it and/or modify it under 6the terms of the GNU General Public License, version 2.0, as published by the 7Free Software Foundation. 8 9This program is also distributed with certain software (including but not 10limited to OpenSSL) that is licensed under separate terms, as designated in a 11particular file or component or in included license documentation. The authors 12of MySQL hereby grant you an additional permission to link the program and 13your derivative works with the separately licensed software that they have 14included with MySQL. 15 16This program is distributed in the hope that it will be useful, but WITHOUT 17ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 18FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, 19for more details. 20 21You should have received a copy of the GNU General Public License along with 22this program; if not, write to the Free Software Foundation, Inc., 2351 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 24 25*****************************************************************************/ 26 27/** @file include/dict0stats.ic 28 Code used for calculating and manipulating table statistics. 29 30 Created Jan 23, 2012 Vasil Dimov 31 *******************************************************/ 32 33#include "dict0dict.h" 34#include "dict0types.h" 35#include "srv0srv.h" 36 37/** Set the persistent statistics flag for a given table. This is set only 38 in the in-memory table object and is not saved on disk. It will be read 39 from the .frm file upon first open from MySQL after a server restart. */ 40UNIV_INLINE 41void dict_stats_set_persistent( 42 dict_table_t *table, /*!< in/out: table */ 43 ibool ps_on, /*!< in: persistent stats explicitly enabled */ 44 ibool ps_off) /*!< in: persistent stats explicitly disabled */ 45{ 46 /* Not allowed to have both flags set, but a CREATE or ALTER 47 statement that contains "STATS_PERSISTENT=0 STATS_PERSISTENT=1" would 48 end up having both set. In this case we clear the OFF flag. */ 49 if (ps_on && ps_off) { 50 ps_off = FALSE; 51 } 52 53 ib_uint32_t stat_persistent = 0; 54 55 if (ps_on) { 56 stat_persistent |= DICT_STATS_PERSISTENT_ON; 57 } 58 59 if (ps_off) { 60 stat_persistent |= DICT_STATS_PERSISTENT_OFF; 61 } 62 63 /* we rely on this assignment to be atomic */ 64 table->stat_persistent = stat_persistent; 65} 66 67/** Check whether persistent statistics is enabled for a given table. 68 @return true if enabled, false otherwise */ 69UNIV_INLINE 70ibool dict_stats_is_persistent_enabled( 71 const dict_table_t *table) /*!< in: table */ 72{ 73 /* Because of the nature of this check (non-locking) it is possible 74 that a table becomes: 75 * PS-disabled immediately after this function has returned TRUE or 76 * PS-enabled immediately after this function has returned FALSE. 77 This means that it is possible that we do: 78 + dict_stats_update(DICT_STATS_RECALC_PERSISTENT) on a table that has 79 just been PS-disabled or 80 + dict_stats_update(DICT_STATS_RECALC_TRANSIENT) on a table that has 81 just been PS-enabled. 82 This is acceptable. Avoiding this would mean that we would have to 83 protect the ::stat_persistent with dict_table_stats_lock() like the 84 other ::stat_ members which would be too big performance penalty, 85 especially when this function is called from 86 row_update_statistics_if_needed(). */ 87 88 /* we rely on this read to be atomic */ 89 ib_uint32_t stat_persistent = table->stat_persistent; 90 91 if (stat_persistent & DICT_STATS_PERSISTENT_ON) { 92 ut_ad(!(stat_persistent & DICT_STATS_PERSISTENT_OFF)); 93 return (TRUE); 94 } else if (stat_persistent & DICT_STATS_PERSISTENT_OFF) { 95 return (FALSE); 96 } else { 97 return (srv_stats_persistent); 98 } 99} 100 101/** Set the auto recalc flag for a given table (only honored for a persistent 102 stats enabled table). The flag is set only in the in-memory table object 103 and is not saved in InnoDB files. It will be read from the .frm file upon 104 first open from MySQL after a server restart. */ 105UNIV_INLINE 106void dict_stats_auto_recalc_set( 107 dict_table_t *table, /*!< in/out: table */ 108 ibool auto_recalc_on, /*!< in: explicitly enabled */ 109 ibool auto_recalc_off) /*!< in: explicitly disabled */ 110{ 111 ut_ad(!auto_recalc_on || !auto_recalc_off); 112 113 ib_uint32_t stats_auto_recalc = 0; 114 115 if (auto_recalc_on) { 116 stats_auto_recalc |= DICT_STATS_AUTO_RECALC_ON; 117 } 118 119 if (auto_recalc_off) { 120 stats_auto_recalc |= DICT_STATS_AUTO_RECALC_OFF; 121 } 122 123 /* we rely on this assignment to be atomic */ 124 table->stats_auto_recalc = stats_auto_recalc; 125} 126 127/** Check whether auto recalc is enabled for a given table. 128 @return true if enabled, false otherwise */ 129UNIV_INLINE 130ibool dict_stats_auto_recalc_is_enabled( 131 const dict_table_t *table) /*!< in: table */ 132{ 133 /* we rely on this read to be atomic */ 134 ib_uint32_t stats_auto_recalc = table->stats_auto_recalc; 135 136 if (stats_auto_recalc & DICT_STATS_AUTO_RECALC_ON) { 137 ut_ad(!(stats_auto_recalc & DICT_STATS_AUTO_RECALC_OFF)); 138 return (TRUE); 139 } else if (stats_auto_recalc & DICT_STATS_AUTO_RECALC_OFF) { 140 return (FALSE); 141 } else { 142 return (srv_stats_auto_recalc); 143 } 144} 145 146/** Initialize table's stats for the first time when opening a table. */ 147UNIV_INLINE 148void dict_stats_init(dict_table_t *table) /*!< in/out: table */ 149{ 150 ut_ad(!mutex_own(&dict_sys->mutex)); 151 152 if (table->stat_initialized) { 153 return; 154 } 155 156 dict_stats_upd_option_t opt; 157 158 if (dict_stats_is_persistent_enabled(table)) { 159 opt = DICT_STATS_FETCH_ONLY_IF_NOT_IN_MEMORY; 160 } else { 161 opt = DICT_STATS_RECALC_TRANSIENT; 162 } 163 164 dict_stats_update(table, opt); 165} 166 167/** Deinitialize table's stats after the last close of the table. This is 168 used to detect "FLUSH TABLE" and refresh the stats upon next open. */ 169UNIV_INLINE 170void dict_stats_deinit(dict_table_t *table) /*!< in/out: table */ 171{ 172 /* Don't assert the table->n_ref_count is 0 here, since there could 173 be some background threads opening the table concurrently. This is 174 not prevented. The cases prevented are in opening table code pathes, 175 to acquire an existing table, table->mutex is required */ 176 177 dict_table_stats_lock(table, RW_X_LATCH); 178 179 if (!table->stat_initialized) { 180 dict_table_stats_unlock(table, RW_X_LATCH); 181 return; 182 } 183 184 table->stat_initialized = FALSE; 185 186#ifdef UNIV_DEBUG_VALGRIND 187 UNIV_MEM_INVALID(&table->stat_n_rows, sizeof(table->stat_n_rows)); 188 UNIV_MEM_INVALID(&table->stat_clustered_index_size, 189 sizeof(table->stat_clustered_index_size)); 190 UNIV_MEM_INVALID(&table->stat_sum_of_other_index_sizes, 191 sizeof(table->stat_sum_of_other_index_sizes)); 192 UNIV_MEM_INVALID(&table->stat_modified_counter, 193 sizeof(table->stat_modified_counter)); 194 195 dict_index_t *index; 196 197 for (index = table->first_index(); index != NULL; index = index->next()) { 198 ulint n_uniq = dict_index_get_n_unique(index); 199 200 UNIV_MEM_INVALID(index->stat_n_diff_key_vals, 201 n_uniq * sizeof(index->stat_n_diff_key_vals[0])); 202 UNIV_MEM_INVALID(index->stat_n_sample_sizes, 203 n_uniq * sizeof(index->stat_n_sample_sizes[0])); 204 UNIV_MEM_INVALID(index->stat_n_non_null_key_vals, 205 n_uniq * sizeof(index->stat_n_non_null_key_vals[0])); 206 UNIV_MEM_INVALID(&index->stat_index_size, sizeof(index->stat_index_size)); 207 UNIV_MEM_INVALID(&index->stat_n_leaf_pages, 208 sizeof(index->stat_n_leaf_pages)); 209 } 210#endif /* UNIV_DEBUG_VALGRIND */ 211 212 dict_table_stats_unlock(table, RW_X_LATCH); 213} 214