1 /* Copyright (c) 2013, 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_uvar_by_thread.cc
25 Table USER_VARIABLES_BY_THREAD (implementation).
26 */
27
28 #include "my_global.h"
29 #include "my_thread.h"
30 #include "pfs_instr_class.h"
31 #include "pfs_column_types.h"
32 #include "pfs_column_values.h"
33 #include "table_uvar_by_thread.h"
34 #include "pfs_global.h"
35 #include "pfs_visitor.h"
36 #include "pfs_buffer_container.h"
37
38 /* Iteration on THD from the sql layer. */
39 #include "sql_class.h"
40 #include "mysqld_thd_manager.h"
41
42 class Find_thd_user_var : public Find_THD_Impl
43 {
44 public:
Find_thd_user_var(THD * unsafe_thd)45 Find_thd_user_var(THD *unsafe_thd)
46 : m_unsafe_thd(unsafe_thd)
47 {}
48
operator ()(THD * thd)49 virtual bool operator()(THD *thd)
50 {
51 if (thd != m_unsafe_thd)
52 return false;
53
54 if (thd->user_vars.records == 0)
55 return false;
56
57 mysql_mutex_lock(&thd->LOCK_thd_data);
58 return true;
59 }
60
61 private:
62 THD *m_unsafe_thd;
63 };
64
materialize(PFS_thread * pfs,THD * thd)65 void User_variables::materialize(PFS_thread *pfs, THD *thd)
66 {
67 reset();
68
69 m_pfs= pfs;
70 m_thread_internal_id= pfs->m_thread_internal_id;
71 m_array.reserve(thd->user_vars.records);
72
73 user_var_entry *sql_uvar;
74
75 uint index= 0;
76 User_variable empty;
77
78 /* Protects thd->user_vars. */
79 mysql_mutex_assert_owner(&thd->LOCK_thd_data);
80
81 for (;;)
82 {
83 sql_uvar= reinterpret_cast<user_var_entry*> (my_hash_element(& thd->user_vars, index));
84 if (sql_uvar == NULL)
85 break;
86
87 /*
88 m_array is a container of objects (not pointers)
89
90 Naive code can:
91 - build locally a new entry
92 - add it to the container
93 but this causes useless object construction, destruction, and deep copies.
94
95 What we do here:
96 - add a dummy (empty) entry
97 - the container does a deep copy on something empty,
98 so that there is nothing to copy.
99 - get a reference to the entry added in the container
100 - complete -- in place -- the entry initialization
101 */
102 m_array.push_back(empty);
103 User_variable & pfs_uvar= m_array.back();
104
105 /* Copy VARIABLE_NAME */
106 const char *name= sql_uvar->entry_name.ptr();
107 size_t name_length= sql_uvar->entry_name.length();
108 assert(name_length <= sizeof(pfs_uvar.m_name));
109 pfs_uvar.m_name.make_row(name, name_length);
110
111 /* Copy VARIABLE_VALUE */
112 my_bool null_value;
113 String *str_value;
114 String str_buffer;
115 uint decimals= 0;
116 str_value= sql_uvar->val_str(& null_value, & str_buffer, decimals);
117 if (str_value != NULL)
118 {
119 pfs_uvar.m_value.make_row(str_value->ptr(), str_value->length());
120 }
121 else
122 {
123 pfs_uvar.m_value.make_row(NULL, 0);
124 }
125
126 index++;
127 }
128 }
129
130 THR_LOCK table_uvar_by_thread::m_table_lock;
131
132 static const TABLE_FIELD_TYPE field_types[]=
133 {
134 {
135 { C_STRING_WITH_LEN("THREAD_ID") },
136 { C_STRING_WITH_LEN("bigint(20)") },
137 { NULL, 0}
138 },
139 {
140 { C_STRING_WITH_LEN("VARIABLE_NAME") },
141 { C_STRING_WITH_LEN("varchar(64)") },
142 { NULL, 0}
143 },
144 {
145 { C_STRING_WITH_LEN("VARIABLE_VALUE") },
146 { C_STRING_WITH_LEN("longblob") },
147 { NULL, 0}
148 }
149 };
150
151 TABLE_FIELD_DEF
152 table_uvar_by_thread::m_field_def=
153 { 3, field_types };
154
155 PFS_engine_table_share
156 table_uvar_by_thread::m_share=
157 {
158 { C_STRING_WITH_LEN("user_variables_by_thread") },
159 &pfs_readonly_acl,
160 table_uvar_by_thread::create,
161 NULL, /* write_row */
162 NULL, /* delete_all_rows */
163 table_uvar_by_thread::get_row_count,
164 sizeof(pos_t),
165 &m_table_lock,
166 &m_field_def,
167 false, /* checked */
168 false /* perpetual */
169 };
170
171 PFS_engine_table*
create(void)172 table_uvar_by_thread::create(void)
173 {
174 return new table_uvar_by_thread();
175 }
176
177 ha_rows
get_row_count(void)178 table_uvar_by_thread::get_row_count(void)
179 {
180 /*
181 This is an estimate only, not a hard limit.
182 The row count is given as a multiple of thread_max,
183 so that a join between:
184 - table performance_schema.threads
185 - table performance_schema.user_variables_by_thread
186 will still evaluate relative table sizes correctly
187 when deciding a join order.
188 */
189 return global_thread_container.get_row_count() * 10;
190 }
191
table_uvar_by_thread()192 table_uvar_by_thread::table_uvar_by_thread()
193 : PFS_engine_table(&m_share, &m_pos),
194 m_row_exists(false), m_pos(), m_next_pos()
195 {}
196
reset_position(void)197 void table_uvar_by_thread::reset_position(void)
198 {
199 m_pos.reset();
200 m_next_pos.reset();
201 }
202
rnd_next(void)203 int table_uvar_by_thread::rnd_next(void)
204 {
205 PFS_thread *thread;
206 bool has_more_thread= true;
207
208 for (m_pos.set_at(&m_next_pos);
209 has_more_thread;
210 m_pos.next_thread())
211 {
212 thread= global_thread_container.get(m_pos.m_index_1, & has_more_thread);
213 if (thread != NULL)
214 {
215 if (materialize(thread) == 0)
216 {
217 const User_variable *uvar= m_THD_cache.get(m_pos.m_index_2);
218 if (uvar != NULL)
219 {
220 make_row(thread, uvar);
221 m_next_pos.set_after(&m_pos);
222 return 0;
223 }
224 }
225 }
226 }
227
228 return HA_ERR_END_OF_FILE;
229 }
230
231 int
rnd_pos(const void * pos)232 table_uvar_by_thread::rnd_pos(const void *pos)
233 {
234 PFS_thread *thread;
235
236 set_position(pos);
237
238 thread= global_thread_container.get(m_pos.m_index_1);
239 if (thread != NULL)
240 {
241 if (materialize(thread) == 0)
242 {
243 const User_variable *uvar= m_THD_cache.get(m_pos.m_index_2);
244 if (uvar != NULL)
245 {
246 make_row(thread, uvar);
247 return 0;
248 }
249 }
250 }
251
252 return HA_ERR_RECORD_DELETED;
253 }
254
materialize(PFS_thread * thread)255 int table_uvar_by_thread::materialize(PFS_thread *thread)
256 {
257 if (m_THD_cache.is_materialized(thread))
258 return 0;
259
260 if (! thread->m_lock.is_populated())
261 return 1;
262
263 THD *unsafe_thd= thread->m_thd;
264 if (unsafe_thd == NULL)
265 return 1;
266
267 Find_thd_user_var finder(unsafe_thd);
268 THD *safe_thd= Global_THD_manager::get_instance()->find_thd(&finder);
269 if (safe_thd == NULL)
270 return 1;
271
272 m_THD_cache.materialize(thread, safe_thd);
273 mysql_mutex_unlock(&safe_thd->LOCK_thd_data);
274 return 0;
275 }
276
277 void table_uvar_by_thread
make_row(PFS_thread * thread,const User_variable * uvar)278 ::make_row(PFS_thread *thread, const User_variable *uvar)
279 {
280 pfs_optimistic_state lock;
281 m_row_exists= false;
282
283 /* Protect this reader against a thread termination */
284 thread->m_lock.begin_optimistic_lock(&lock);
285
286 m_row.m_thread_internal_id= thread->m_thread_internal_id;
287
288 /* uvar is materialized, pointing to it directly. */
289 m_row.m_variable_name= & uvar->m_name;
290 m_row.m_variable_value= & uvar->m_value;
291
292 if (! thread->m_lock.end_optimistic_lock(&lock))
293 return;
294
295 m_row_exists= true;
296 }
297
298 int table_uvar_by_thread
read_row_values(TABLE * table,unsigned char * buf,Field ** fields,bool read_all)299 ::read_row_values(TABLE *table,
300 unsigned char *buf,
301 Field **fields,
302 bool read_all)
303 {
304 Field *f;
305
306 if (unlikely(! m_row_exists))
307 return HA_ERR_RECORD_DELETED;
308
309 /* Set the null bits */
310 assert(table->s->null_bytes == 1);
311 buf[0]= 0;
312
313 assert(m_row.m_variable_name != NULL);
314 assert(m_row.m_variable_value != NULL);
315
316 for (; (f= *fields) ; fields++)
317 {
318 if (read_all || bitmap_is_set(table->read_set, f->field_index))
319 {
320 switch(f->field_index)
321 {
322 case 0: /* THREAD_ID */
323 set_field_ulonglong(f, m_row.m_thread_internal_id);
324 break;
325 case 1: /* VARIABLE_NAME */
326 set_field_varchar_utf8(f,
327 m_row.m_variable_name->m_str,
328 m_row.m_variable_name->m_length);
329 break;
330 case 2: /* VARIABLE_VALUE */
331 if (m_row.m_variable_value->get_value_length() > 0)
332 {
333 set_field_blob(f,
334 m_row.m_variable_value->get_value(),
335 m_row.m_variable_value->get_value_length());
336 }
337 else
338 {
339 f->set_null();
340 }
341 break;
342 default:
343 assert(false);
344 }
345 }
346 }
347
348 return 0;
349 }
350
351