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