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