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_account.cc
25 Table STATUS_BY_ACCOUNT (implementation).
26 */
27
28 #include "my_global.h"
29 #include "table_status_by_account.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_account::m_table_lock;
38
39 static const TABLE_FIELD_TYPE field_types[]=
40 {
41 {
42 { C_STRING_WITH_LEN("USER") },
43 { C_STRING_WITH_LEN("char(" USERNAME_CHAR_LENGTH_STR ")") },
44 { NULL, 0}
45 },
46 {
47 { C_STRING_WITH_LEN("HOST") },
48 { C_STRING_WITH_LEN("char(60)") },
49 { NULL, 0}
50 },
51 {
52 { C_STRING_WITH_LEN("VARIABLE_NAME") },
53 { C_STRING_WITH_LEN("varchar(64)") },
54 { NULL, 0}
55 },
56 {
57 { C_STRING_WITH_LEN("VARIABLE_VALUE") },
58 { C_STRING_WITH_LEN("varchar(1024)") },
59 { NULL, 0}
60 }
61 };
62
63 TABLE_FIELD_DEF
64 table_status_by_account::m_field_def=
65 { 4, field_types };
66
67 PFS_engine_table_share
68 table_status_by_account::m_share=
69 {
70 { C_STRING_WITH_LEN("status_by_account") },
71 &pfs_truncatable_acl,
72 table_status_by_account::create,
73 NULL, /* write_row */
74 table_status_by_account::delete_all_rows,
75 table_status_by_account::get_row_count,
76 sizeof(pos_t),
77 &m_table_lock,
78 &m_field_def,
79 false, /* checked */
80 false /* perpetual */
81 };
82
83 PFS_engine_table*
create(void)84 table_status_by_account::create(void)
85 {
86 return new table_status_by_account();
87 }
88
delete_all_rows(void)89 int table_status_by_account::delete_all_rows(void)
90 {
91 mysql_mutex_lock(&LOCK_status);
92 reset_status_by_thread();
93 reset_status_by_account();
94 mysql_mutex_unlock(&LOCK_status);
95 return 0;
96 }
97
get_row_count(void)98 ha_rows table_status_by_account::get_row_count(void)
99 {
100 mysql_mutex_lock(&LOCK_status);
101 size_t status_var_count= all_status_vars.size();
102 mysql_mutex_unlock(&LOCK_status);
103 return (global_account_container.get_row_count() * status_var_count);
104 }
105
table_status_by_account()106 table_status_by_account::table_status_by_account()
107 : PFS_engine_table(&m_share, &m_pos),
108 m_status_cache(true), m_row_exists(false), m_pos(), m_next_pos()
109 {}
110
reset_position(void)111 void table_status_by_account::reset_position(void)
112 {
113 m_pos.reset();
114 m_next_pos.reset();
115 }
116
rnd_init(bool scan)117 int table_status_by_account::rnd_init(bool scan)
118 {
119 if (show_compatibility_56)
120 return 0;
121
122 /*
123 Build array of SHOW_VARs from the global status array prior to materializing
124 threads in rnd_next() or rnd_pos().
125 */
126 m_status_cache.initialize_client_session();
127
128 /* Use the current number of status variables to detect changes. */
129 ulonglong status_version= m_status_cache.get_status_array_version();
130
131 /*
132 The table context holds the current version of the global status array
133 and a record of which accounts were materialized. If scan == true, then
134 allocate a new context from mem_root and store in TLS. If scan == false,
135 then restore from TLS.
136 */
137 m_context= (table_status_by_account_context *)current_thd->alloc(sizeof(table_status_by_account_context));
138 new(m_context) table_status_by_account_context(status_version, !scan);
139 return 0;
140 }
141
rnd_next(void)142 int table_status_by_account::rnd_next(void)
143 {
144 if (show_compatibility_56)
145 return HA_ERR_END_OF_FILE;
146
147 /* If status array changes, exit with warning. */ // TODO: Issue warning
148 if (!m_context->versions_match())
149 return HA_ERR_END_OF_FILE;
150
151 /*
152 For each account, build a cache of status variables using totals from all
153 threads associated with the account.
154 */
155 bool has_more_account= true;
156
157 for (m_pos.set_at(&m_next_pos);
158 has_more_account;
159 m_pos.next_account())
160 {
161 PFS_account *pfs_account= global_account_container.get(m_pos.m_index_1, &has_more_account);
162
163 if (m_status_cache.materialize_account(pfs_account) == 0)
164 {
165 /* Mark this account as materialized. */
166 m_context->set_item(m_pos.m_index_1);
167
168 /* Get the next status variable. */
169 const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2);
170 if (stat_var != NULL)
171 {
172 make_row(pfs_account, stat_var);
173 m_next_pos.set_after(&m_pos);
174 return 0;
175 }
176 }
177 }
178 return HA_ERR_END_OF_FILE;
179 }
180
181 int
rnd_pos(const void * pos)182 table_status_by_account::rnd_pos(const void *pos)
183 {
184 if (show_compatibility_56)
185 return HA_ERR_RECORD_DELETED;
186
187 /* If status array changes, exit with warning. */ // TODO: Issue warning
188 if (!m_context->versions_match())
189 return HA_ERR_END_OF_FILE;
190
191 set_position(pos);
192 assert(m_pos.m_index_1 < global_account_container.get_row_count());
193
194 PFS_account *pfs_account= global_account_container.get(m_pos.m_index_1);
195
196 /*
197 Only materialize threads that were previously materialized by rnd_next().
198 If a account cannot be rematerialized, then do nothing.
199 */
200 if (m_context->is_item_set(m_pos.m_index_1) &&
201 m_status_cache.materialize_account(pfs_account) == 0)
202 {
203 const Status_variable *stat_var= m_status_cache.get(m_pos.m_index_2);
204 if (stat_var != NULL)
205 {
206 make_row(pfs_account, stat_var);
207 return 0;
208 }
209 }
210 return HA_ERR_RECORD_DELETED;
211 }
212
213 void table_status_by_account
make_row(PFS_account * pfs_account,const Status_variable * status_var)214 ::make_row(PFS_account *pfs_account, const Status_variable *status_var)
215 {
216 pfs_optimistic_state lock;
217 m_row_exists= false;
218 pfs_account->m_lock.begin_optimistic_lock(&lock);
219
220 if (m_row.m_account.make_row(pfs_account))
221 return;
222
223 m_row.m_variable_name.make_row(status_var->m_name, status_var->m_name_length);
224 m_row.m_variable_value.make_row(status_var);
225
226 if (!pfs_account->m_lock.end_optimistic_lock(&lock))
227 return;
228
229 m_row_exists= true;
230 }
231
232 int table_status_by_account
read_row_values(TABLE * table,unsigned char * buf,Field ** fields,bool read_all)233 ::read_row_values(TABLE *table,
234 unsigned char *buf,
235 Field **fields,
236 bool read_all)
237 {
238 Field *f;
239
240 if (unlikely(! m_row_exists))
241 return HA_ERR_RECORD_DELETED;
242
243 /* Set the null bits */
244 assert(table->s->null_bytes == 1);
245 buf[0]= 0;
246
247 for (; (f= *fields) ; fields++)
248 {
249 if (read_all || bitmap_is_set(table->read_set, f->field_index))
250 {
251 switch(f->field_index)
252 {
253 case 0: /* USER */
254 case 1: /* HOST */
255 m_row.m_account.set_field(f->field_index, f);
256 break;
257 case 2: /* VARIABLE_NAME */
258 set_field_varchar_utf8(f, m_row.m_variable_name.m_str, m_row.m_variable_name.m_length);
259 break;
260 case 3: /* VARIABLE_VALUE */
261 m_row.m_variable_value.set_field(f);
262 break;
263 default:
264 assert(false);
265 }
266 }
267 }
268
269 return 0;
270 }
271
272