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