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 Street, Fifth Floor, Boston, MA 02110-1301, USA */
22
23 /**
24 @file storage/perfschema/pfs_variable.cc
25 Performance schema system variable and status variable (implementation).
26 */
27 #include "my_global.h"
28 #include "pfs_variable.h"
29 #include "my_sys.h"
30 #include "debug_sync.h"
31 #include "pfs.h"
32 #include "pfs_global.h"
33 #include "pfs_visitor.h"
34 #include "sql_audit.h" // audit_global_variable_get
35
36 /**
37 CLASS PFS_system_variable_cache
38 */
39
40 /**
41 Build a sorted list of all system variables from the system variable hash.
42 Filter by scope. Must be called inside of LOCK_plugin_delete.
43 */
init_show_var_array(enum_var_type scope,bool strict)44 bool PFS_system_variable_cache::init_show_var_array(enum_var_type scope, bool strict)
45 {
46 assert(!m_initialized);
47 m_query_scope= scope;
48
49 mysql_rwlock_rdlock(&LOCK_system_variables_hash);
50 DEBUG_SYNC(m_current_thd, "acquired_LOCK_system_variables_hash");
51
52 /* Record the system variable hash version to detect subsequent changes. */
53 m_version= get_system_variable_hash_version();
54
55 /* Build the SHOW_VAR array from the system variable hash. */
56 enumerate_sys_vars(m_current_thd, &m_show_var_array, true, m_query_scope, strict);
57
58 mysql_rwlock_unlock(&LOCK_system_variables_hash);
59
60 /* Increase cache size if necessary. */
61 m_cache.reserve(m_show_var_array.size());
62
63 m_initialized= true;
64 return true;
65 }
66
67 /**
68 Build an array of SHOW_VARs from the system variable hash.
69 Filter for SESSION scope.
70 */
do_initialize_session(void)71 bool PFS_system_variable_cache::do_initialize_session(void)
72 {
73 /* Block plugins from unloading. */
74 mysql_mutex_lock(&LOCK_plugin_delete);
75
76 /* Build the array. */
77 bool ret= init_show_var_array(OPT_SESSION, true);
78
79 mysql_mutex_unlock(&LOCK_plugin_delete);
80 return ret;
81 }
82
83 /**
84 Match system variable scope to desired scope.
85 */
match_scope(int scope)86 bool PFS_system_variable_cache::match_scope(int scope)
87 {
88 switch (scope)
89 {
90 case sys_var::GLOBAL:
91 return m_query_scope == OPT_GLOBAL;
92 break;
93
94 case sys_var::SESSION:
95 return (m_query_scope == OPT_GLOBAL || m_query_scope == OPT_SESSION);
96 break;
97
98 case sys_var::ONLY_SESSION:
99 return m_query_scope == OPT_SESSION;
100 break;
101
102 default:
103 return false;
104 break;
105 }
106 return false;
107 }
108
109 /**
110 Build a GLOBAL system variable cache.
111 */
do_materialize_global(void)112 int PFS_system_variable_cache::do_materialize_global(void)
113 {
114 /* Block plugins from unloading. */
115 mysql_mutex_lock(&LOCK_plugin_delete);
116
117 m_materialized= false;
118
119 /*
120 Build array of SHOW_VARs from system variable hash. Do this within
121 LOCK_plugin_delete to ensure that the hash table remains unchanged
122 during materialization.
123 */
124 if (!m_external_init)
125 init_show_var_array(OPT_GLOBAL, true);
126
127 /* Resolve the value for each SHOW_VAR in the array, add to cache. */
128 for (Show_var_array::iterator show_var= m_show_var_array.begin();
129 show_var->value && (show_var != m_show_var_array.end()); show_var++)
130 {
131 const char* name= show_var->name;
132 sys_var *value= (sys_var *)show_var->value;
133 assert(value);
134
135 if ((m_query_scope == OPT_GLOBAL) &&
136 (!my_strcasecmp(system_charset_info, name, "sql_log_bin")))
137 {
138 /*
139 PLEASE READ:
140 http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-6.html
141
142 SQL_LOG_BIN is:
143 - declared in sys_vars.cc as both GLOBAL and SESSION in 5.7
144 - impossible to SET with SET GLOBAL (raises an error)
145 - and yet can be read with @@global.sql_log_bin
146
147 When show_compatibility_56 = ON,
148 - SHOW GLOBAL VARIABLES does expose a row for SQL_LOG_BIN
149 - INFORMATION_SCHEMA.GLOBAL_VARIABLES also does expose a row,
150 both are for backward compatibility of existing applications,
151 so that no application logic change is required.
152
153 Now, with show_compatibility_56 = OFF (aka, in this code)
154 - SHOW GLOBAL VARIABLES does -- not -- expose a row for SQL_LOG_BIN
155 - PERFORMANCE_SCHEMA.GLOBAL_VARIABLES also does -- not -- expose a row
156 so that a clean interface is exposed to (upgraded and modified) applications.
157
158 The assert below will fail once SQL_LOG_BIN really is defined
159 as SESSION_ONLY (in 5.8), so that this special case can be removed.
160 */
161 assert(value->scope() == sys_var::SESSION);
162 continue;
163 }
164
165 /* Match the system variable scope to the target scope. */
166 if (match_scope(value->scope()))
167 {
168 /* Resolve value, convert to text, add to cache. */
169 System_variable system_var(m_current_thd, show_var, m_query_scope, false);
170 m_cache.push_back(system_var);
171 }
172 }
173
174 m_materialized= true;
175 mysql_mutex_unlock(&LOCK_plugin_delete);
176 return 0;
177 }
178
179 /**
180 Build a GLOBAL and SESSION system variable cache.
181 */
do_materialize_all(THD * unsafe_thd)182 int PFS_system_variable_cache::do_materialize_all(THD *unsafe_thd)
183 {
184 int ret= 1;
185
186 m_unsafe_thd= unsafe_thd;
187 m_safe_thd= NULL;
188 m_materialized= false;
189 m_cache.clear();
190
191 /* Block plugins from unloading. */
192 mysql_mutex_lock(&LOCK_plugin_delete);
193
194 /*
195 Build array of SHOW_VARs from system variable hash. Do this within
196 LOCK_plugin_delete to ensure that the hash table remains unchanged
197 while this thread is materialized.
198 */
199 if (!m_external_init)
200 init_show_var_array(OPT_SESSION, false);
201
202 /* Get and lock a validated THD from the thread manager. */
203 if ((m_safe_thd= get_THD(unsafe_thd)) != NULL)
204 {
205 DEBUG_SYNC(m_current_thd, "materialize_session_variable_array_THD_locked");
206 for (Show_var_array::iterator show_var= m_show_var_array.begin();
207 show_var->value && (show_var != m_show_var_array.end()); show_var++)
208 {
209 const char* name= show_var->name;
210 sys_var *value= (sys_var *)show_var->value;
211 assert(value);
212 bool ignore= false;
213
214 if (value->scope() == sys_var::SESSION &&
215 (!my_strcasecmp(system_charset_info, name, "gtid_executed")))
216 {
217 /*
218 GTID_EXECUTED is:
219 - declared in sys_vars.cc as both GLOBAL and SESSION in 5.7
220 - can be read with @@session.gtid_executed
221
222 When show_compatibility_56 = ON,
223 - SHOW SESSION VARIABLES does expose a row for GTID_EXECUTED
224 - INFORMATION_SCHEMA.SESSION_VARIABLES also does expose a row,
225 both are for backward compatibility of existing applications,
226 so that no application logic change is required.
227
228 Now, with show_compatibility_56 = OFF (aka, in this code)
229 - SHOW SESSION VARIABLES does -- not -- expose a row for GTID_EXECUTED
230 - PERFORMANCE_SCHEMA.SESSION_VARIABLES also does -- not -- expose a row
231 so that a clean interface is exposed to (upgraded and modified)
232 applications.
233
234 This special case needs be removed once @@SESSION.GTID_EXECUTED is
235 deprecated.
236 */
237 ignore= true;
238
239 }
240 /* Resolve value, convert to text, add to cache. */
241 System_variable system_var(m_safe_thd, show_var, m_query_scope, ignore);
242 m_cache.push_back(system_var);
243 }
244
245 /* Release lock taken in get_THD(). */
246 mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data);
247
248 m_materialized= true;
249 ret= 0;
250 }
251
252 mysql_mutex_unlock(&LOCK_plugin_delete);
253 return ret;
254 }
255
256 /**
257 Allocate and assign mem_root for system variable materialization.
258 */
set_mem_root(void)259 void PFS_system_variable_cache::set_mem_root(void)
260 {
261 if (m_mem_sysvar_ptr == NULL)
262 {
263 init_sql_alloc(PSI_INSTRUMENT_ME, &m_mem_sysvar, SYSVAR_MEMROOT_BLOCK_SIZE, 0);
264 m_mem_sysvar_ptr= &m_mem_sysvar;
265 }
266 m_mem_thd= my_thread_get_THR_MALLOC(); /* pointer to current THD mem_root */
267 m_mem_thd_save= *m_mem_thd; /* restore later */
268 *m_mem_thd= &m_mem_sysvar; /* use temporary mem_root */
269 }
270
271 /**
272 Mark memory blocks in the temporary mem_root as free.
273 Restore THD::mem_root.
274 */
clear_mem_root(void)275 void PFS_system_variable_cache::clear_mem_root(void)
276 {
277 if (m_mem_sysvar_ptr)
278 {
279 free_root(&m_mem_sysvar, MYF(MY_MARK_BLOCKS_FREE));
280 *m_mem_thd= m_mem_thd_save; /* restore original mem_root */
281 m_mem_thd= NULL;
282 m_mem_thd_save= NULL;
283 }
284 }
285
286 /**
287 Free the temporary mem_root.
288 Restore THD::mem_root if necessary.
289 */
free_mem_root(void)290 void PFS_system_variable_cache::free_mem_root(void)
291 {
292 if (m_mem_sysvar_ptr)
293 {
294 free_root(&m_mem_sysvar, MYF(0));
295 m_mem_sysvar_ptr= NULL;
296 if (m_mem_thd && m_mem_thd_save)
297 {
298 *m_mem_thd= m_mem_thd_save; /* restore original mem_root */
299 m_mem_thd= NULL;
300 m_mem_thd_save= NULL;
301 }
302 }
303 }
304
305 /**
306 Build a SESSION system variable cache for a pfs_thread.
307 Requires that init_show_var_array() has already been called.
308 Return 0 for success.
309 */
do_materialize_session(PFS_thread * pfs_thread)310 int PFS_system_variable_cache::do_materialize_session(PFS_thread *pfs_thread)
311 {
312 int ret= 1;
313
314 m_pfs_thread= pfs_thread;
315 m_materialized= false;
316 m_cache.clear();
317
318 /* Block plugins from unloading. */
319 mysql_mutex_lock(&LOCK_plugin_delete);
320
321 /* The SHOW_VAR array must be initialized externally. */
322 assert(m_initialized);
323
324 /* Use a temporary mem_root to avoid depleting THD mem_root. */
325 if (m_use_mem_root)
326 set_mem_root();
327
328 /* Get and lock a validated THD from the thread manager. */
329 if ((m_safe_thd= get_THD(pfs_thread)) != NULL)
330 {
331 for (Show_var_array::iterator show_var= m_show_var_array.begin();
332 show_var->value && (show_var != m_show_var_array.end()); show_var++)
333 {
334 sys_var *value= (sys_var *)show_var->value;
335
336 /* Match the system variable scope to the target scope. */
337 if (match_scope(value->scope()))
338 {
339 const char* name= show_var->name;
340 bool ignore= false;
341
342 if (value->scope() == sys_var::SESSION &&
343 (!my_strcasecmp(system_charset_info, name, "gtid_executed")))
344 {
345 /* Deprecated. See PFS_system_variable_cache::do_materialize_all. */
346 ignore= true;
347 }
348 /* Resolve value, convert to text, add to cache. */
349 System_variable system_var(m_safe_thd, show_var, m_query_scope, ignore);
350 m_cache.push_back(system_var);
351 }
352 }
353
354 /* Release lock taken in get_THD(). */
355 mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data);
356
357 m_materialized= true;
358 ret= 0;
359 }
360
361 /* Mark mem_root blocks as free. */
362 if (m_use_mem_root)
363 clear_mem_root();
364
365 mysql_mutex_unlock(&LOCK_plugin_delete);
366 return ret;
367 }
368
369 /**
370 Materialize a single system variable for a pfs_thread.
371 Requires that init_show_var_array() has already been called.
372 Return 0 for success.
373 */
do_materialize_session(PFS_thread * pfs_thread,uint index)374 int PFS_system_variable_cache::do_materialize_session(PFS_thread *pfs_thread, uint index)
375 {
376 int ret= 1;
377
378 m_pfs_thread= pfs_thread;
379 m_materialized= false;
380 m_cache.clear();
381
382 /* Block plugins from unloading. */
383 mysql_mutex_lock(&LOCK_plugin_delete);
384
385 /* The SHOW_VAR array must be initialized externally. */
386 assert(m_initialized);
387
388 /* Get and lock a validated THD from the thread manager. */
389 if ((m_safe_thd= get_THD(pfs_thread)) != NULL)
390 {
391 SHOW_VAR *show_var= &m_show_var_array.at(index);
392
393 if (show_var && show_var->value &&
394 (show_var != m_show_var_array.end()))
395 {
396 sys_var *value= (sys_var *)show_var->value;
397
398 /* Match the system variable scope to the target scope. */
399 if (match_scope(value->scope()))
400 {
401 const char* name= show_var->name;
402 bool ignore= false;
403
404 if (value->scope() == sys_var::SESSION &&
405 (!my_strcasecmp(system_charset_info, name, "gtid_executed")))
406 {
407 /* Deprecated. See PFS_system_variable_cache::do_materialize_all. */
408 ignore= true;
409 }
410 /* Resolve value, convert to text, add to cache. */
411 System_variable system_var(m_safe_thd, show_var, m_query_scope, ignore);
412 m_cache.push_back(system_var);
413 }
414 }
415
416 /* Release lock taken in get_THD(). */
417 mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data);
418
419 m_materialized= true;
420 ret= 0;
421 }
422
423 mysql_mutex_unlock(&LOCK_plugin_delete);
424 return ret;
425 }
426
427 /**
428 Build a SESSION system variable cache for a THD.
429 */
do_materialize_session(THD * unsafe_thd)430 int PFS_system_variable_cache::do_materialize_session(THD *unsafe_thd)
431 {
432 int ret= 1;
433
434 m_unsafe_thd= unsafe_thd;
435 m_safe_thd= NULL;
436 m_materialized= false;
437 m_cache.clear();
438
439 /* Block plugins from unloading. */
440 mysql_mutex_lock(&LOCK_plugin_delete);
441
442 /*
443 Build array of SHOW_VARs from system variable hash. Do this within
444 LOCK_plugin_delete to ensure that the hash table remains unchanged
445 while this thread is materialized.
446 */
447 if (!m_external_init)
448 init_show_var_array(OPT_SESSION, true);
449
450 /* Get and lock a validated THD from the thread manager. */
451 if ((m_safe_thd= get_THD(unsafe_thd)) != NULL)
452 {
453 for (Show_var_array::iterator show_var= m_show_var_array.begin();
454 show_var->value && (show_var != m_show_var_array.end()); show_var++)
455 {
456 sys_var *value = (sys_var *)show_var->value;
457
458 /* Match the system variable scope to the target scope. */
459 if (match_scope(value->scope()))
460 {
461 const char* name= show_var->name;
462 bool ignore= false;
463
464 if (value->scope() == sys_var::SESSION &&
465 (!my_strcasecmp(system_charset_info, name, "gtid_executed")))
466 {
467 /* Deprecated. See PFS_system_variable_cache::do_materialize_all. */
468 ignore= true;
469 }
470 /* Resolve value, convert to text, add to cache. */
471 System_variable system_var(m_safe_thd, show_var, m_query_scope, ignore);
472
473 m_cache.push_back(system_var);
474 }
475 }
476
477 /* Release lock taken in get_THD(). */
478 mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data);
479
480 m_materialized= true;
481 ret= 0;
482 }
483
484 mysql_mutex_unlock(&LOCK_plugin_delete);
485 return ret;
486 }
487
488
489 /**
490 CLASS System_variable
491 */
492
493 /**
494 Empty placeholder.
495 */
System_variable()496 System_variable::System_variable()
497 : m_name(NULL), m_name_length(0), m_value_length(0), m_type(SHOW_UNDEF), m_scope(0),
498 m_ignore(false), m_charset(NULL), m_initialized(false)
499 {
500 m_value_str[0]= '\0';
501 }
502
503 /**
504 GLOBAL or SESSION system variable.
505 */
System_variable(THD * target_thd,const SHOW_VAR * show_var,enum_var_type query_scope,bool ignore)506 System_variable::System_variable(THD *target_thd, const SHOW_VAR *show_var,
507 enum_var_type query_scope, bool ignore)
508 : m_name(NULL), m_name_length(0), m_value_length(0), m_type(SHOW_UNDEF), m_scope(0),
509 m_ignore(ignore), m_charset(NULL), m_initialized(false)
510 {
511 init(target_thd, show_var, query_scope);
512 }
513
514 /**
515 Get sys_var value from global or local source then convert to string.
516 */
init(THD * target_thd,const SHOW_VAR * show_var,enum_var_type query_scope)517 void System_variable::init(THD *target_thd, const SHOW_VAR *show_var,
518 enum_var_type query_scope)
519 {
520 if (show_var == NULL || show_var->name == NULL)
521 return;
522
523 enum_mysql_show_type show_var_type= show_var->type;
524 assert(show_var_type == SHOW_SYS);
525
526 m_name= show_var->name;
527 m_name_length= strlen(m_name);
528
529 /* Deprecated variables are ignored but must still be accounted for. */
530 if (m_ignore)
531 {
532 m_value_str[0]= '\0';
533 m_value_length= 0;
534 m_initialized= true;
535 return;
536 }
537
538 THD *current_thread= current_thd;
539
540 /* Block remote target thread from updating this system variable. */
541 if (target_thd != current_thread)
542 mysql_mutex_lock(&target_thd->LOCK_thd_sysvar);
543 /* Block system variable additions or deletions. */
544 mysql_mutex_lock(&LOCK_global_system_variables);
545
546 sys_var *system_var= (sys_var *)show_var->value;
547 assert(system_var != NULL);
548 m_charset= system_var->charset(target_thd);
549 m_type= system_var->show_type();
550 m_scope= system_var->scope();
551
552 /* Get the value of the system variable. */
553 const char *value;
554 value= get_one_variable_ext(current_thread, target_thd, show_var, query_scope, show_var_type,
555 NULL, &m_charset, m_value_str, &m_value_length);
556
557 m_value_length= MY_MIN(m_value_length, SHOW_VAR_FUNC_BUFF_SIZE);
558
559 /* Returned value may reference a string other than m_value_str. */
560 if (value != m_value_str)
561 memcpy(m_value_str, value, m_value_length);
562 m_value_str[m_value_length]= 0;
563
564 mysql_mutex_unlock(&LOCK_global_system_variables);
565 if (target_thd != current_thread)
566 mysql_mutex_unlock(&target_thd->LOCK_thd_sysvar);
567
568 #ifndef EMBEDDED_LIBRARY
569 if (show_var_type != SHOW_FUNC && query_scope == OPT_GLOBAL &&
570 mysql_audit_notify(current_thread,
571 AUDIT_EVENT(MYSQL_AUDIT_GLOBAL_VARIABLE_GET),
572 m_name, value, m_value_length))
573 return;
574 #endif
575
576 m_initialized= true;
577 }
578
579
580 /**
581 CLASS PFS_status_variable_cache
582 */
583
584 PFS_status_variable_cache::
PFS_status_variable_cache(bool external_init)585 PFS_status_variable_cache(bool external_init) :
586 PFS_variable_cache<Status_variable>(external_init),
587 m_show_command(false), m_sum_client_status(NULL)
588 {
589 /* Determine if the originating query is a SHOW command. */
590 m_show_command= (m_current_thd->lex->sql_command == SQLCOM_SHOW_STATUS);
591 }
592
593 /**
594 Build cache of SESSION status variables for a user.
595 */
materialize_user(PFS_user * pfs_user)596 int PFS_status_variable_cache::materialize_user(PFS_user *pfs_user)
597 {
598 if (!pfs_user)
599 return 1;
600
601 if (is_materialized(pfs_user))
602 return 0;
603
604 if (!pfs_user->m_lock.is_populated())
605 return 1;
606
607 /* Set callback function. */
608 m_sum_client_status= sum_user_status;
609 return do_materialize_client((PFS_client *)pfs_user);
610 }
611
612 /**
613 Build cache of SESSION status variables for a host.
614 */
materialize_host(PFS_host * pfs_host)615 int PFS_status_variable_cache::materialize_host(PFS_host *pfs_host)
616 {
617 if (!pfs_host)
618 return 1;
619
620 if (is_materialized(pfs_host))
621 return 0;
622
623 if (!pfs_host->m_lock.is_populated())
624 return 1;
625
626 /* Set callback function. */
627 m_sum_client_status= sum_host_status;
628 return do_materialize_client((PFS_client *)pfs_host);
629 }
630
631 /**
632 Build cache of SESSION status variables for an account.
633 */
materialize_account(PFS_account * pfs_account)634 int PFS_status_variable_cache::materialize_account(PFS_account *pfs_account)
635 {
636 if (!pfs_account)
637 return 1;
638
639 if (is_materialized(pfs_account))
640 return 0;
641
642 if (!pfs_account->m_lock.is_populated())
643 return 1;
644
645 /* Set callback function. */
646 m_sum_client_status= sum_account_status;
647 return do_materialize_client((PFS_client *)pfs_account);
648 }
649 /**
650 Compare status variable scope to desired scope.
651 @param variable_scope Scope of current status variable
652 @return TRUE if variable matches the query scope
653 */
match_scope(SHOW_SCOPE variable_scope,bool strict)654 bool PFS_status_variable_cache::match_scope(SHOW_SCOPE variable_scope, bool strict)
655 {
656 switch (variable_scope)
657 {
658 case SHOW_SCOPE_GLOBAL:
659 return (m_query_scope == OPT_GLOBAL) || (! strict && (m_query_scope == OPT_SESSION));
660 break;
661 case SHOW_SCOPE_SESSION:
662 /* Ignore session-only vars if aggregating by user, host or account. */
663 if (m_aggregate)
664 return false;
665 else
666 return (m_query_scope == OPT_SESSION);
667 break;
668 case SHOW_SCOPE_ALL:
669 return (m_query_scope == OPT_GLOBAL || m_query_scope == OPT_SESSION);
670 break;
671 case SHOW_SCOPE_UNDEF:
672 default:
673 return false;
674 break;
675 }
676 return false;
677 }
678
679 /*
680 Exclude specific status variables from the query by name or prefix.
681 Return TRUE if variable should be filtered.
682 */
filter_by_name(const SHOW_VAR * show_var)683 bool PFS_status_variable_cache::filter_by_name(const SHOW_VAR *show_var)
684 {
685 assert(show_var);
686 assert(show_var->name);
687
688 if (show_var->type == SHOW_ARRAY)
689 {
690 /* The SHOW_ARRAY name is the prefix for the variables in the subarray. */
691 const char *prefix= show_var->name;
692 /* Exclude COM counters if not a SHOW STATUS command. */
693 if (!my_strcasecmp(system_charset_info, prefix, "Com") && !m_show_command)
694 return true;
695 }
696 else
697 {
698 /*
699 Slave status resides in Performance Schema replication tables. Exclude
700 these slave status variables from the SHOW STATUS command and from the
701 status tables.
702 Assume null prefix to ensure that only server-defined slave status
703 variables are filtered.
704 */
705 const char *name= show_var->name;
706 if (!my_strcasecmp(system_charset_info, name, "Slave_running") ||
707 !my_strcasecmp(system_charset_info, name, "Slave_retried_transactions") ||
708 !my_strcasecmp(system_charset_info, name, "Slave_last_heartbeat") ||
709 !my_strcasecmp(system_charset_info, name, "Slave_received_heartbeats") ||
710 !my_strcasecmp(system_charset_info, name, "Slave_heartbeat_period"))
711 {
712 return true;
713 }
714 }
715
716 return false;
717 }
718
719 /**
720 Check that the variable type is aggregatable.
721
722 @param variable_type Status variable type
723 @return TRUE if variable type can be aggregated
724 */
can_aggregate(enum_mysql_show_type variable_type)725 bool PFS_status_variable_cache::can_aggregate(enum_mysql_show_type variable_type)
726 {
727 switch(variable_type)
728 {
729 /*
730 All server status counters that are totaled across threads are defined in
731 system_status_var as either SHOW_LONGLONG_STATUS or SHOW_LONG_STATUS.
732 These data types are not available to plugins.
733 */
734 case SHOW_LONGLONG_STATUS:
735 case SHOW_LONG_STATUS:
736 return true;
737 break;
738
739 /* Server and plugin */
740 case SHOW_UNDEF:
741 case SHOW_BOOL:
742 case SHOW_CHAR:
743 case SHOW_CHAR_PTR:
744 case SHOW_ARRAY:
745 case SHOW_FUNC:
746 case SHOW_INT:
747 case SHOW_LONG:
748 case SHOW_LONGLONG:
749 case SHOW_DOUBLE:
750 /* Server only */
751 case SHOW_HAVE:
752 case SHOW_MY_BOOL:
753 case SHOW_SYS:
754 case SHOW_LEX_STRING:
755 case SHOW_KEY_CACHE_LONG:
756 case SHOW_KEY_CACHE_LONGLONG:
757 case SHOW_DOUBLE_STATUS:
758 case SHOW_HA_ROWS:
759 case SHOW_LONG_NOFLUSH:
760 case SHOW_SIGNED_INT:
761 case SHOW_SIGNED_LONG:
762 case SHOW_SIGNED_LONGLONG:
763 default:
764 return false;
765 break;
766 }
767 }
768
769 /**
770 Check if a status variable should be excluded from the query.
771 Return TRUE if the variable should be excluded.
772 */
filter_show_var(const SHOW_VAR * show_var,bool strict)773 bool PFS_status_variable_cache::filter_show_var(const SHOW_VAR *show_var, bool strict)
774 {
775 /* Match the variable scope with the query scope. */
776 if (!match_scope(show_var->scope, strict))
777 return true;
778
779 /* Exclude specific status variables by name or prefix. */
780 if (filter_by_name(show_var))
781 return true;
782
783 /* For user, host or account, ignore variables having non-aggregatable types. */
784 if (m_aggregate && !can_aggregate(show_var->type))
785 return true;
786
787 return false;
788 }
789
790
791 /**
792 Build an array of SHOW_VARs from the global status array. Expand nested
793 subarrays, filter unwanted variables.
794 NOTE: Must be done inside of LOCK_status to guard against plugin load/unload.
795 */
init_show_var_array(enum_var_type scope,bool strict)796 bool PFS_status_variable_cache::init_show_var_array(enum_var_type scope, bool strict)
797 {
798 assert(!m_initialized);
799
800 /* Resize if necessary. */
801 m_show_var_array.reserve(all_status_vars.size()+1);
802
803 m_query_scope= scope;
804
805 for (Status_var_array::iterator show_var_iter= all_status_vars.begin();
806 show_var_iter != all_status_vars.end();
807 show_var_iter++)
808 {
809 SHOW_VAR show_var= *show_var_iter;
810
811 /* Check if this status var should be excluded from the query. */
812 if (filter_show_var(&show_var, strict))
813 continue;
814
815 if (show_var.type == SHOW_ARRAY)
816 {
817 /* Expand nested subarray. The name is used as a prefix. */
818 expand_show_var_array((SHOW_VAR *)show_var.value, show_var.name, strict);
819 }
820 else
821 {
822 show_var.name= make_show_var_name(NULL, show_var.name);
823 m_show_var_array.push_back(show_var);
824 }
825 }
826
827 /* Last element is NULL. */
828 m_show_var_array.push_back(st_mysql_show_var());
829
830 /* Get the latest version of all_status_vars. */
831 m_version= get_status_vars_version();
832
833 /* Increase cache size if necessary. */
834 m_cache.reserve(m_show_var_array.size());
835
836 m_initialized= true;
837 return true;
838 }
839
840 /**
841 Expand a nested subarray of status variables, indicated by a type of SHOW_ARRAY.
842 */
expand_show_var_array(const SHOW_VAR * show_var_array,const char * prefix,bool strict)843 void PFS_status_variable_cache::expand_show_var_array(const SHOW_VAR *show_var_array, const char *prefix, bool strict)
844 {
845 for (const SHOW_VAR *show_var_ptr= show_var_array;
846 show_var_ptr && show_var_ptr->name;
847 show_var_ptr++)
848 {
849 SHOW_VAR show_var= *show_var_ptr;
850
851 if (filter_show_var(&show_var, strict))
852 continue;
853
854 if (show_var.type == SHOW_ARRAY)
855 {
856 char name_buf[SHOW_VAR_MAX_NAME_LEN];
857 show_var.name= make_show_var_name(prefix, show_var.name, name_buf, sizeof(name_buf));
858 /* Expand nested subarray. The name is used as a prefix. */
859 expand_show_var_array((SHOW_VAR *)show_var.value, show_var.name, strict);
860 }
861 else
862 {
863 /* Add the SHOW_VAR element. Make a local copy of the name string. */
864 show_var.name= make_show_var_name(prefix, show_var.name);
865 m_show_var_array.push_back(show_var);
866 }
867 }
868 }
869
870 /**
871 Build the complete status variable name, with prefix. Return in buffer provided.
872 */
make_show_var_name(const char * prefix,const char * name,char * name_buf,size_t buf_len)873 char * PFS_status_variable_cache::make_show_var_name(const char* prefix, const char* name,
874 char *name_buf, size_t buf_len)
875 {
876 assert(name_buf != NULL);
877 char *prefix_end= name_buf;
878
879 if (prefix && *prefix)
880 {
881 /* Drop the prefix into the front of the name buffer. */
882 prefix_end= my_stpnmov(name_buf, prefix, buf_len-1);
883 *prefix_end++= '_';
884 }
885
886 /* Restrict name length to remaining buffer size. */
887 size_t max_name_len= name_buf + buf_len - prefix_end;
888
889 /* Load the name into the buffer after the prefix. */
890 my_stpnmov(prefix_end, name, max_name_len);
891 name_buf[buf_len-1]= 0;
892
893 return (name_buf);
894 }
895
896 /**
897 Make a copy of the name string prefixed with the subarray name if necessary.
898 */
make_show_var_name(const char * prefix,const char * name)899 char * PFS_status_variable_cache::make_show_var_name(const char* prefix, const char* name)
900 {
901 char name_buf[SHOW_VAR_MAX_NAME_LEN];
902 size_t buf_len= sizeof(name_buf);
903 make_show_var_name(prefix, name, name_buf, buf_len);
904 return m_current_thd->mem_strdup(name_buf); /* freed at statement end */
905 }
906
907 /**
908 Build an internal SHOW_VAR array from the external status variable array.
909 */
do_initialize_session(void)910 bool PFS_status_variable_cache::do_initialize_session(void)
911 {
912 /* Acquire LOCK_status to guard against plugin load/unload. */
913 if (m_current_thd->fill_status_recursion_level++ == 0)
914 mysql_mutex_lock(&LOCK_status);
915
916 bool ret= init_show_var_array(OPT_SESSION, true);
917
918 if (m_current_thd->fill_status_recursion_level-- == 1)
919 mysql_mutex_unlock(&LOCK_status);
920
921 return ret;
922 }
923
924 /**
925 For the current THD, use initial_status_vars taken from before the query start.
926 */
set_status_vars(void)927 STATUS_VAR *PFS_status_variable_cache::set_status_vars(void)
928 {
929 STATUS_VAR *status_vars;
930 if (m_safe_thd == m_current_thd && m_current_thd->initial_status_var != NULL)
931 status_vars= m_current_thd->initial_status_var;
932 else
933 status_vars= &m_safe_thd->status_var;
934
935 return status_vars;
936 }
937
938 /**
939 Build cache for GLOBAL status variables using values totaled from all threads.
940 */
do_materialize_global(void)941 int PFS_status_variable_cache::do_materialize_global(void)
942 {
943 STATUS_VAR status_totals;
944
945 m_materialized= false;
946 DEBUG_SYNC(m_current_thd, "before_materialize_global_status_array");
947
948 /* Acquire LOCK_status to guard against plugin load/unload. */
949 if (m_current_thd->fill_status_recursion_level++ == 0)
950 mysql_mutex_lock(&LOCK_status);
951
952 /*
953 Build array of SHOW_VARs from global status array. Do this within
954 LOCK_status to ensure that the array remains unchanged during
955 materialization.
956 */
957 if (!m_external_init)
958 init_show_var_array(OPT_GLOBAL, true);
959
960 /*
961 Collect totals for all active threads. Start with global status vars as a
962 baseline.
963 */
964 PFS_connection_status_visitor visitor(&status_totals);
965 PFS_connection_iterator::visit_global(false, /* hosts */
966 false, /* users */
967 false, /* accounts */
968 false, /* threads */
969 true, /* THDs */
970 &visitor);
971 /*
972 Build the status variable cache using the SHOW_VAR array as a reference.
973 Use the status totals collected from all threads.
974 */
975 manifest(m_current_thd, m_show_var_array.begin(), &status_totals, "", false, true);
976
977 if (m_current_thd->fill_status_recursion_level-- == 1)
978 mysql_mutex_unlock(&LOCK_status);
979
980 m_materialized= true;
981 DEBUG_SYNC(m_current_thd, "after_materialize_global_status_array");
982
983 return 0;
984 }
985
986 /**
987 Build GLOBAL and SESSION status variable cache using values for a non-instrumented thread.
988 */
do_materialize_all(THD * unsafe_thd)989 int PFS_status_variable_cache::do_materialize_all(THD* unsafe_thd)
990 {
991 int ret= 1;
992 assert(unsafe_thd != NULL);
993
994 m_unsafe_thd= unsafe_thd;
995 m_materialized= false;
996 m_cache.clear();
997
998 /* Avoid recursive acquisition of LOCK_status. */
999 if (m_current_thd->fill_status_recursion_level++ == 0)
1000 mysql_mutex_lock(&LOCK_status);
1001
1002 /*
1003 Build array of SHOW_VARs from global status array. Do this within
1004 LOCK_status to ensure that the array remains unchanged while this
1005 thread is materialized.
1006 */
1007 if (!m_external_init)
1008 init_show_var_array(OPT_SESSION, false);
1009
1010 /* Get and lock a validated THD from the thread manager. */
1011 if ((m_safe_thd= get_THD(unsafe_thd)) != NULL)
1012 {
1013 DEBUG_SYNC(m_current_thd, "materialize_session_status_array_THD_locked");
1014 /*
1015 Build the status variable cache using the SHOW_VAR array as a reference.
1016 Use the status values from the THD protected by the thread manager lock.
1017 */
1018 STATUS_VAR *status_vars= set_status_vars();
1019 manifest(m_safe_thd, m_show_var_array.begin(), status_vars, "", false, false);
1020
1021 /* Release lock taken in get_THD(). */
1022 mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data);
1023
1024 m_materialized= true;
1025 ret= 0;
1026 }
1027
1028 if (m_current_thd->fill_status_recursion_level-- == 1)
1029 mysql_mutex_unlock(&LOCK_status);
1030 return ret;
1031 }
1032
1033 /**
1034 Build SESSION status variable cache using values for a non-instrumented thread.
1035 */
do_materialize_session(THD * unsafe_thd)1036 int PFS_status_variable_cache::do_materialize_session(THD* unsafe_thd)
1037 {
1038 int ret= 1;
1039 assert(unsafe_thd != NULL);
1040
1041 m_unsafe_thd= unsafe_thd;
1042 m_materialized= false;
1043 m_cache.clear();
1044
1045 /* Avoid recursive acquisition of LOCK_status. */
1046 if (m_current_thd->fill_status_recursion_level++ == 0)
1047 mysql_mutex_lock(&LOCK_status);
1048
1049 /*
1050 Build array of SHOW_VARs from global status array. Do this within
1051 LOCK_status to ensure that the array remains unchanged while this
1052 thread is materialized.
1053 */
1054 if (!m_external_init)
1055 init_show_var_array(OPT_SESSION, true);
1056
1057 /* Get and lock a validated THD from the thread manager. */
1058 if ((m_safe_thd= get_THD(unsafe_thd)) != NULL)
1059 {
1060 /*
1061 Build the status variable cache using the SHOW_VAR array as a reference.
1062 Use the status values from the THD protected by the thread manager lock.
1063 */
1064 STATUS_VAR *status_vars= set_status_vars();
1065 manifest(m_safe_thd, m_show_var_array.begin(), status_vars, "", false, true);
1066
1067 /* Release lock taken in get_THD(). */
1068 mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data);
1069
1070 m_materialized= true;
1071 ret= 0;
1072 }
1073
1074 if (m_current_thd->fill_status_recursion_level-- == 1)
1075 mysql_mutex_unlock(&LOCK_status);
1076 return ret;
1077 }
1078
1079 /**
1080 Build SESSION status variable cache using values for a PFS_thread.
1081 NOTE: Requires that init_show_var_array() has already been called.
1082 */
do_materialize_session(PFS_thread * pfs_thread)1083 int PFS_status_variable_cache::do_materialize_session(PFS_thread *pfs_thread)
1084 {
1085 int ret= 1;
1086 assert(pfs_thread != NULL);
1087
1088 m_pfs_thread= pfs_thread;
1089 m_materialized= false;
1090 m_cache.clear();
1091
1092 /* Acquire LOCK_status to guard against plugin load/unload. */
1093 if (m_current_thd->fill_status_recursion_level++ == 0)
1094 mysql_mutex_lock(&LOCK_status);
1095
1096 /* The SHOW_VAR array must be initialized externally. */
1097 assert(m_initialized);
1098
1099 /* Get and lock a validated THD from the thread manager. */
1100 if ((m_safe_thd= get_THD(pfs_thread)) != NULL)
1101 {
1102 /*
1103 Build the status variable cache using the SHOW_VAR array as a reference.
1104 Use the status values from the THD protected by the thread manager lock.
1105 */
1106 STATUS_VAR *status_vars= set_status_vars();
1107 manifest(m_safe_thd, m_show_var_array.begin(), status_vars, "", false, true);
1108
1109 /* Release lock taken in get_THD(). */
1110 mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data);
1111
1112 m_materialized= true;
1113 ret= 0;
1114 }
1115
1116 if (m_current_thd->fill_status_recursion_level-- == 1)
1117 mysql_mutex_unlock(&LOCK_status);
1118 return ret;
1119 }
1120
1121 /**
1122 Build cache of SESSION status variables using the status values provided.
1123 The cache is associated with a user, host or account, but not with any
1124 particular thread.
1125 NOTE: Requires that init_show_var_array() has already been called.
1126 */
do_materialize_client(PFS_client * pfs_client)1127 int PFS_status_variable_cache::do_materialize_client(PFS_client *pfs_client)
1128 {
1129 assert(pfs_client != NULL);
1130 STATUS_VAR status_totals;
1131
1132 m_pfs_client= pfs_client;
1133 m_materialized= false;
1134 m_cache.clear();
1135
1136 /* Acquire LOCK_status to guard against plugin load/unload. */
1137 if (m_current_thd->fill_status_recursion_level++ == 0)
1138 mysql_mutex_lock(&LOCK_status);
1139
1140 /* The SHOW_VAR array must be initialized externally. */
1141 assert(m_initialized);
1142
1143 /*
1144 Generate status totals from active threads and from totals aggregated
1145 from disconnected threads.
1146 */
1147 m_sum_client_status(pfs_client, &status_totals);
1148
1149 /*
1150 Build the status variable cache using the SHOW_VAR array as a reference and
1151 the status totals collected from threads associated with this client.
1152 */
1153 manifest(m_current_thd, m_show_var_array.begin(), &status_totals, "", false, true);
1154
1155 if (m_current_thd->fill_status_recursion_level-- == 1)
1156 mysql_mutex_unlock(&LOCK_status);
1157
1158 m_materialized= true;
1159 return 0;
1160 }
1161
1162 /*
1163 Build the status variable cache from the expanded and sorted SHOW_VAR array.
1164 Resolve status values using the STATUS_VAR struct provided.
1165 */
manifest(THD * thd,const SHOW_VAR * show_var_array,STATUS_VAR * status_vars,const char * prefix,bool nested_array,bool strict)1166 void PFS_status_variable_cache::manifest(THD *thd, const SHOW_VAR *show_var_array,
1167 STATUS_VAR *status_vars, const char *prefix,
1168 bool nested_array, bool strict)
1169 {
1170 for (const SHOW_VAR *show_var_iter= show_var_array;
1171 show_var_iter && show_var_iter->name;
1172 show_var_iter++)
1173 {
1174 // work buffer, must be aligned to handle long/longlong values
1175 my_aligned_storage<SHOW_VAR_FUNC_BUFF_SIZE+1, MY_ALIGNOF(longlong)>
1176 value_buf;
1177 SHOW_VAR show_var_tmp;
1178 const SHOW_VAR *show_var_ptr= show_var_iter; /* preserve array pointer */
1179
1180 /*
1181 If the value is a function reference, then execute the function and
1182 reevaluate the new SHOW_TYPE and value. Handle nested case where
1183 SHOW_FUNC resolves to another SHOW_FUNC.
1184 */
1185 if (show_var_ptr->type == SHOW_FUNC)
1186 {
1187 show_var_tmp= *show_var_ptr;
1188 /*
1189 Execute the function reference in show_var_tmp->value, which returns
1190 show_var_tmp with a new type and new value.
1191 */
1192 for (const SHOW_VAR *var= show_var_ptr; var->type == SHOW_FUNC; var= &show_var_tmp)
1193 {
1194 ((mysql_show_var_func)(var->value))(thd, &show_var_tmp, value_buf.data);
1195 }
1196 show_var_ptr= &show_var_tmp;
1197 }
1198
1199 /*
1200 If we are expanding a SHOW_ARRAY, filter variables that were not prefiltered by
1201 init_show_var_array().
1202 */
1203 if (nested_array && filter_show_var(show_var_ptr, strict))
1204 continue;
1205
1206 if (show_var_ptr->type == SHOW_ARRAY)
1207 {
1208 /*
1209 Status variables of type SHOW_ARRAY were expanded and filtered by
1210 init_show_var_array(), except where a SHOW_FUNC resolves into a
1211 SHOW_ARRAY, such as with InnoDB. Recurse to expand the subarray.
1212 */
1213 manifest(thd, (SHOW_VAR *)show_var_ptr->value, status_vars, show_var_ptr->name, true, strict);
1214 }
1215 else
1216 {
1217 /* Add the materialized status variable to the cache. */
1218 SHOW_VAR show_var= *show_var_ptr;
1219 /*
1220 For nested array expansions, make a copy of the variable name, just as
1221 done in init_show_var_array().
1222 */
1223 if (nested_array)
1224 show_var.name= make_show_var_name(prefix, show_var_ptr->name);
1225
1226 /* Convert status value to string format. Add to the cache. */
1227 Status_variable status_var(&show_var, status_vars, m_query_scope);
1228 m_cache.push_back(status_var);
1229 }
1230 }
1231 }
1232
1233 /**
1234 CLASS Status_variable
1235 */
Status_variable(const SHOW_VAR * show_var,STATUS_VAR * status_vars,enum_var_type query_scope)1236 Status_variable::Status_variable(const SHOW_VAR *show_var, STATUS_VAR *status_vars, enum_var_type query_scope)
1237 : m_name_length(0), m_value_length(0), m_type(SHOW_UNDEF),
1238 m_scope(SHOW_SCOPE_UNDEF), m_charset(NULL), m_initialized(false)
1239 {
1240 init(show_var, status_vars, query_scope);
1241 }
1242
1243 /**
1244 Resolve status value, convert to string.
1245 show_var->value is an offset into status_vars.
1246 NOTE: Assumes LOCK_status is held.
1247 */
init(const SHOW_VAR * show_var,STATUS_VAR * status_vars,enum_var_type query_scope)1248 void Status_variable::init(const SHOW_VAR *show_var, STATUS_VAR *status_vars, enum_var_type query_scope)
1249 {
1250 if (show_var == NULL || show_var->name == NULL)
1251 return;
1252 m_name= show_var->name;
1253 m_name_length= strlen(m_name);
1254 m_type= show_var->type;
1255 m_scope= show_var->scope;
1256
1257 /* Get the value of the status variable. */
1258 const char *value;
1259 value= get_one_variable(current_thd, show_var, query_scope, m_type,
1260 status_vars, &m_charset, m_value_str, &m_value_length);
1261 m_value_length= MY_MIN(m_value_length, SHOW_VAR_FUNC_BUFF_SIZE);
1262
1263 /* Returned value may reference a string other than m_value_str. */
1264 if (value != m_value_str)
1265 memcpy(m_value_str, value, m_value_length);
1266 m_value_str[m_value_length]= 0;
1267
1268 m_initialized= true;
1269 }
1270
1271 /*
1272 Get status totals for this user from active THDs and related accounts.
1273 */
sum_user_status(PFS_client * pfs_user,STATUS_VAR * status_totals)1274 void sum_user_status(PFS_client *pfs_user, STATUS_VAR *status_totals)
1275 {
1276 PFS_connection_status_visitor visitor(status_totals);
1277 PFS_connection_iterator::visit_user((PFS_user *)pfs_user,
1278 true, /* accounts */
1279 false, /* threads */
1280 true, /* THDs */
1281 &visitor);
1282 }
1283
1284 /*
1285 Get status totals for this host from active THDs and related accounts.
1286 */
sum_host_status(PFS_client * pfs_host,STATUS_VAR * status_totals)1287 void sum_host_status(PFS_client *pfs_host, STATUS_VAR *status_totals)
1288 {
1289 PFS_connection_status_visitor visitor(status_totals);
1290 PFS_connection_iterator::visit_host((PFS_host *)pfs_host,
1291 true, /* accounts */
1292 false, /* threads */
1293 true, /* THDs */
1294 &visitor);
1295 }
1296
1297 /*
1298 Get status totals for this account from active THDs and from totals aggregated
1299 from disconnectd threads.
1300 */
sum_account_status(PFS_client * pfs_account,STATUS_VAR * status_totals)1301 void sum_account_status(PFS_client *pfs_account, STATUS_VAR *status_totals)
1302 {
1303 PFS_connection_status_visitor visitor(status_totals);
1304 PFS_connection_iterator::visit_account((PFS_account *)pfs_account,
1305 false, /* threads */
1306 true, /* THDs */
1307 &visitor);
1308 }
1309
1310 /**
1311 Reset aggregated status counter stats for account, user and host.
1312 NOTE: Assumes LOCK_status is held.
1313 */
reset_pfs_status_stats()1314 void reset_pfs_status_stats()
1315 {
1316 reset_status_by_account();
1317 reset_status_by_user();
1318 reset_status_by_host();
1319 }
1320
1321 /** @} */
1322