1 /* Copyright (c) 2015, 2021, Oracle and/or its affiliates.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 /**
24 @file storage/perfschema/table_status_by_host.cc
25 Table STATUS_BY_HOST (implementation).
26 */
27
28 #include "my_global.h"
29 #include "table_status_by_host.h"
30 #include "my_thread.h"
31 #include "pfs_instr_class.h"
32 #include "pfs_column_types.h"
33 #include "pfs_column_values.h"
34 #include "pfs_global.h"
35 #include "pfs_account.h"
36
37 THR_LOCK table_status_by_host::m_table_lock;
38
39 static const TABLE_FIELD_TYPE field_types[]=
40 {
41 {
42 { C_STRING_WITH_LEN("HOST") },
43 { C_STRING_WITH_LEN("char(60)") },
44 { NULL, 0}
45 },
46 {
47 { C_STRING_WITH_LEN("VARIABLE_NAME") },
48 { C_STRING_WITH_LEN("varchar(64)") },
49 { NULL, 0}
50 },
51 {
52 { C_STRING_WITH_LEN("VARIABLE_VALUE") },
53 { C_STRING_WITH_LEN("varchar(1024)") },
54 { NULL, 0}
55 }
56 };
57
58 TABLE_FIELD_DEF
59 table_status_by_host::m_field_def=
60 { 3, field_types };
61
62 PFS_engine_table_share
63 table_status_by_host::m_share=
64 {
65 { C_STRING_WITH_LEN("status_by_host") },
66 &pfs_truncatable_acl,
67 table_status_by_host::create,
68 NULL, /* write_row */
69 table_status_by_host::delete_all_rows,
70 table_status_by_host::get_row_count,
71 sizeof(pos_t),
72 &m_table_lock,
73 &m_field_def,
74 false, /* checked */
75 false /* perpetual */
76 };
77
78 PFS_engine_table*
create(void)79 table_status_by_host::create(void)
80 {
81 return new table_status_by_host();
82 }
83
delete_all_rows(void)84 int table_status_by_host::delete_all_rows(void)
85 {
86 mysql_mutex_lock(&LOCK_status);
87 reset_status_by_thread();
88 reset_status_by_account();
89 reset_status_by_host();
90 mysql_mutex_unlock(&LOCK_status);
91 return 0;
92 }
93
get_row_count(void)94 ha_rows table_status_by_host::get_row_count(void)
95 {
96 mysql_mutex_lock(&LOCK_status);
97 size_t status_var_count= all_status_vars.size();
98 mysql_mutex_unlock(&LOCK_status);
99 return (global_host_container.get_row_count() * status_var_count);
100 }
101
table_status_by_host()102 table_status_by_host::table_status_by_host()
103 : PFS_engine_table(&m_share, &m_pos),
104 m_status_cache(true), m_row_exists(false), m_pos(), m_next_pos()
105 {}
106
reset_position(void)107 void table_status_by_host::reset_position(void)
108 {
109 m_pos.reset();
110 m_next_pos.reset();
111 }
112
rnd_init(bool scan)113 int table_status_by_host::rnd_init(bool scan)
114 {
115 if (show_compatibility_56)
116 return 0;
117
118 /*
119 Build array of SHOW_VARs from the global status array prior to materializing
120 threads in rnd_next() or rnd_pos().
121 */
122 m_status_cache.initialize_client_session();
123
124 /* Use the current number of status variables to detect changes. */
125 ulonglong status_version= m_status_cache.get_status_array_version();
126
127 /*
128 The table context holds the current version of the global status array
129 and a record of which hosts were materialized. If scan == true, then
130 allocate a new context from mem_root and store in TLS. If scan == false,
131 then restore from TLS.
132 */
133 m_context= (table_status_by_host_context *)current_thd->alloc(sizeof(table_status_by_host_context));
134 new(m_context) table_status_by_host_context(status_version, !scan);
135 return 0;
136 }
137
rnd_next(void)138 int table_status_by_host::rnd_next(void)
139 {
140 if (show_compatibility_56)
141 return HA_ERR_END_OF_FILE;
142
143 /* If status array changes, exit with warning. */ // TODO: Issue warning
144 if (!m_context->versions_match())
145 return HA_ERR_END_OF_FILE;
146
147 /*
148 For each user, build a cache of status variables using totals from all
149 threads associated with the host.
150 */
151 bool has_more_host= true;
152
153 for (m_pos.set_at(&m_next_pos);
154 has_more_host;
155 m_pos.next_host())
156 {
157 PFS_host *pfs_host= global_host_container.get(m_pos.m_index_1, &has_more_host);
158
159 if (m_status_cache.materialize_host(pfs_host) == 0)
160 {
161 /* Mark this host as materialized. */
162 m_context->set_item(m_pos.m_index_1);
163
164 /* Get the next status variable. */
165 const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2);
166 if (stat_var != NULL)
167 {
168 make_row(pfs_host, stat_var);
169 m_next_pos.set_after(&m_pos);
170 return 0;
171 }
172 }
173 }
174 return HA_ERR_END_OF_FILE;
175 }
176
177 int
rnd_pos(const void * pos)178 table_status_by_host::rnd_pos(const void *pos)
179 {
180 if (show_compatibility_56)
181 return 0;
182
183 /* If status array changes, exit with warning. */ // TODO: Issue warning
184 if (!m_context->versions_match())
185 return HA_ERR_END_OF_FILE;
186
187 set_position(pos);
188 assert(m_pos.m_index_1 < global_host_container.get_row_count());
189
190 PFS_host *pfs_host= global_host_container.get(m_pos.m_index_1);
191
192 /*
193 Only materialize threads that were previously materialized by rnd_next().
194 If a host cannot be rematerialized, then do nothing.
195 */
196 if (m_context->is_item_set(m_pos.m_index_1) &&
197 m_status_cache.materialize_host(pfs_host) == 0)
198 {
199 const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2);
200 if (stat_var != NULL)
201 {
202 make_row(pfs_host, stat_var);
203 return 0;
204 }
205 }
206 return HA_ERR_RECORD_DELETED;
207 }
208
209 void table_status_by_host
make_row(PFS_host * pfs_host,const Status_variable * status_var)210 ::make_row(PFS_host *pfs_host, const Status_variable *status_var)
211 {
212 pfs_optimistic_state lock;
213 m_row_exists= false;
214 pfs_host->m_lock.begin_optimistic_lock(&lock);
215
216 if (m_row.m_host.make_row(pfs_host))
217 return;
218
219 m_row.m_variable_name.make_row(status_var->m_name, status_var->m_name_length);
220 m_row.m_variable_value.make_row(status_var);
221
222 if (!pfs_host->m_lock.end_optimistic_lock(&lock))
223 return;
224
225 m_row_exists= true;
226 }
227
228 int table_status_by_host
read_row_values(TABLE * table,unsigned char * buf,Field ** fields,bool read_all)229 ::read_row_values(TABLE *table,
230 unsigned char *buf,
231 Field **fields,
232 bool read_all)
233 {
234 Field *f;
235
236 if (unlikely(! m_row_exists))
237 return HA_ERR_RECORD_DELETED;
238
239 /* Set the null bits */
240 assert(table->s->null_bytes == 1);
241 buf[0]= 0;
242
243 for (; (f= *fields) ; fields++)
244 {
245 if (read_all || bitmap_is_set(table->read_set, f->field_index))
246 {
247 switch(f->field_index)
248 {
249 case 0: /* HOST */
250 m_row.m_host.set_field(f);
251 break;
252 case 1: /* VARIABLE_NAME */
253 set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length);
254 break;
255 case 2: /* VARIABLE_VALUE */
256 m_row.m_variable_value.set_field(f);
257 break;
258 default:
259 assert(false);
260 }
261 }
262 }
263
264 return 0;
265 }
266
267