diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-04 18:00:34 +0000 |
commit | 3f619478f796eddbba6e39502fe941b285dd97b1 (patch) | |
tree | e2c7b5777f728320e5b5542b6213fd3591ba51e2 /storage/perfschema/pfs_variable.cc | |
parent | Initial commit. (diff) | |
download | mariadb-upstream.tar.xz mariadb-upstream.zip |
Adding upstream version 1:10.11.6.upstream/1%10.11.6upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | storage/perfschema/pfs_variable.cc | 1291 |
1 files changed, 1291 insertions, 0 deletions
diff --git a/storage/perfschema/pfs_variable.cc b/storage/perfschema/pfs_variable.cc new file mode 100644 index 00000000..239c55b6 --- /dev/null +++ b/storage/perfschema/pfs_variable.cc @@ -0,0 +1,1291 @@ +/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. + Copyright (c) 2020, MariaDB Corporation. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License, version 2.0, + as published by the Free Software Foundation. + + This program is also distributed with certain software (including + but not limited to OpenSSL) that is licensed under separate terms, + as designated in a particular file or component or in included license + documentation. The authors of MySQL hereby grant you an additional + permission to link the program and your derivative works with the + separately licensed software that they have included with MySQL. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License, version 2.0, for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + +/** + @file storage/perfschema/pfs_variable.cc + Performance schema system variable and status variable (implementation). +*/ +#include "sql_plugin.h" +#include "pfs_variable.h" +#include "my_sys.h" +#include "debug_sync.h" +#include "pfs.h" +#include "pfs_global.h" +#include "pfs_visitor.h" +#include "sql_audit.h" // audit_global_variable_get + +static inline SHOW_SCOPE show_scope_from_type(enum enum_mysql_show_type type) +{ + switch(type) { + case SHOW_BOOL: + case SHOW_CHAR: + case SHOW_CHAR_PTR: + case SHOW_DOUBLE: + case SHOW_HAVE: + case SHOW_HA_ROWS: + case SHOW_KEY_CACHE_LONG: + case SHOW_LEX_STRING: + case SHOW_LONG_NOFLUSH: + case SHOW_MY_BOOL: + case SHOW_SINT: + case SHOW_SLONG: + case SHOW_SLONGLONG: + case SHOW_SYS: + case SHOW_UINT: + case SHOW_ULONG: + case SHOW_ULONGLONG: + return SHOW_SCOPE_GLOBAL; + + case SHOW_DOUBLE_STATUS: + case SHOW_LONGLONG_STATUS: + case SHOW_LONG_STATUS: + return SHOW_SCOPE_ALL; + + case SHOW_ARRAY: + case SHOW_FUNC: + case SHOW_SIMPLE_FUNC: + case SHOW_UNDEF: + default: + return SHOW_SCOPE_UNDEF; + } + return SHOW_SCOPE_UNDEF; +} + + +/** + CLASS PFS_system_variable_cache +*/ + +/** + Build a sorted list of all system variables from the system variable hash. + Filter by scope. Must be called inside of LOCK_plugin_delete. +*/ +bool PFS_system_variable_cache::init_show_var_array(enum_var_type scope, bool strict) +{ + assert(!m_initialized); + m_query_scope= scope; + + mysql_prlock_rdlock(&LOCK_system_variables_hash); + DEBUG_SYNC(m_current_thd, "acquired_LOCK_system_variables_hash"); + + /* Record the system variable hash version to detect subsequent changes. */ + m_version= get_system_variable_hash_version(); + + /* Build the SHOW_VAR array from the system variable hash. */ + SHOW_VAR *vars= enumerate_sys_vars(m_current_thd, true, m_query_scope/*, strict */); + m_show_var_array.reserve(get_system_variable_hash_records()); + for (int i=0; vars[i].name; i++) + m_show_var_array.set(i, vars[i]); + + mysql_prlock_unlock(&LOCK_system_variables_hash); + + /* Increase cache size if necessary. */ + m_cache.reserve(m_show_var_array.elements()); + + m_initialized= true; + return true; +} + +/** + Build an array of SHOW_VARs from the system variable hash. + Filter for SESSION scope. +*/ +bool PFS_system_variable_cache::do_initialize_session(void) +{ + /* Block plugins from unloading. */ + mysql_mutex_lock(&LOCK_plugin_delete); + + /* Build the array. */ + bool ret= init_show_var_array(OPT_SESSION, true); + + mysql_mutex_unlock(&LOCK_plugin_delete); + return ret; +} + +/** + Match system variable scope to desired scope. +*/ +bool PFS_system_variable_cache::match_scope(int scope) +{ + switch (scope) + { + case sys_var::GLOBAL: + return m_query_scope == OPT_GLOBAL; + break; + + case sys_var::SESSION: + return (m_query_scope == OPT_GLOBAL || m_query_scope == OPT_SESSION); + break; + + case sys_var::ONLY_SESSION: + return m_query_scope == OPT_SESSION; + break; + + default: + return false; + break; + } + return false; +} + +/** + Build a GLOBAL system variable cache. +*/ +int PFS_system_variable_cache::do_materialize_global(void) +{ + /* Block system variable additions or deletions. */ + mysql_mutex_lock(&LOCK_global_system_variables); + + m_materialized= false; + + /* + Build array of SHOW_VARs from system variable hash. Do this within + LOCK_plugin_delete to ensure that the hash table remains unchanged + during materialization. + */ + if (!m_external_init) + init_show_var_array(OPT_GLOBAL, true); + + /* Resolve the value for each SHOW_VAR in the array, add to cache. */ + for (SHOW_VAR *show_var= m_show_var_array.front(); + show_var->value && (show_var != m_show_var_array.end()); show_var++) + { + const char* name= show_var->name; + sys_var *value= (sys_var *)show_var->value; + assert(value); + + if ((m_query_scope == OPT_GLOBAL) && + (!my_strcasecmp(system_charset_info, name, "sql_log_bin"))) + { + /* + PLEASE READ: + http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-6.html + + SQL_LOG_BIN is: + - declared in sys_vars.cc as both GLOBAL and SESSION in 5.7 + - impossible to SET with SET GLOBAL (raises an error) + - and yet can be read with @@global.sql_log_bin + + When show_compatibility_56 = ON, + - SHOW GLOBAL VARIABLES does expose a row for SQL_LOG_BIN + - INFORMATION_SCHEMA.GLOBAL_VARIABLES also does expose a row, + both are for backward compatibility of existing applications, + so that no application logic change is required. + + Now, with show_compatibility_56 = OFF (aka, in this code) + - SHOW GLOBAL VARIABLES does -- not -- expose a row for SQL_LOG_BIN + - PERFORMANCE_SCHEMA.GLOBAL_VARIABLES also does -- not -- expose a row + so that a clean interface is exposed to (upgraded and modified) applications. + + The assert below will fail once SQL_LOG_BIN really is defined + as SESSION_ONLY (in 5.8), so that this special case can be removed. + */ + assert(value->scope() == sys_var::SESSION); + continue; + } + + /* Match the system variable scope to the target scope. */ + if (match_scope(value->scope())) + { + /* Resolve value, convert to text, add to cache. */ + System_variable system_var(m_current_thd, show_var, m_query_scope, false); + m_cache.push(system_var); + } + } + + m_materialized= true; + mysql_mutex_unlock(&LOCK_global_system_variables); + return 0; +} + +/** + Build a GLOBAL and SESSION system variable cache. +*/ +int PFS_system_variable_cache::do_materialize_all(THD *unsafe_thd) +{ + int ret= 1; + + m_unsafe_thd= unsafe_thd; + m_safe_thd= NULL; + m_materialized= false; + m_cache.clear(); + + /* Block plugins from unloading. */ + mysql_mutex_lock(&LOCK_plugin_delete); + + /* + Build array of SHOW_VARs from system variable hash. Do this within + LOCK_plugin_delete to ensure that the hash table remains unchanged + while this thread is materialized. + */ + if (!m_external_init) + init_show_var_array(OPT_SESSION, false); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(unsafe_thd)) != NULL) + { + DEBUG_SYNC(m_current_thd, "materialize_session_variable_array_THD_locked"); + for (SHOW_VAR *show_var= m_show_var_array.front(); + show_var->value && (show_var != m_show_var_array.end()); show_var++) + { + /* Resolve value, convert to text, add to cache. */ + System_variable system_var(m_safe_thd, show_var, m_query_scope, false); + m_cache.push(system_var); + } + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + mysql_mutex_unlock(&LOCK_plugin_delete); + return ret; +} + +/** + Allocate and assign mem_root for system variable materialization. +*/ +void PFS_system_variable_cache::set_mem_root(void) +{ + if (m_mem_sysvar_ptr == NULL) + { + init_sql_alloc(PSI_INSTRUMENT_ME, &m_mem_sysvar, SYSVAR_MEMROOT_BLOCK_SIZE, 0, 0); + m_mem_sysvar_ptr= &m_mem_sysvar; + } + m_mem_thd= ¤t_thd->mem_root; /* pointer to current THD mem_root */ + m_mem_thd_save= *m_mem_thd; /* restore later */ + *m_mem_thd= &m_mem_sysvar; /* use temporary mem_root */ +} + +/** + Mark memory blocks in the temporary mem_root as free. + Restore THD::mem_root. +*/ +void PFS_system_variable_cache::clear_mem_root(void) +{ + if (m_mem_sysvar_ptr) + { + free_root(&m_mem_sysvar, MYF(MY_MARK_BLOCKS_FREE)); + *m_mem_thd= m_mem_thd_save; /* restore original mem_root */ + m_mem_thd= NULL; + m_mem_thd_save= NULL; + } +} + +/** + Free the temporary mem_root. + Restore THD::mem_root if necessary. +*/ +void PFS_system_variable_cache::free_mem_root(void) +{ + if (m_mem_sysvar_ptr) + { + free_root(&m_mem_sysvar, MYF(0)); + m_mem_sysvar_ptr= NULL; + if (m_mem_thd && m_mem_thd_save) + { + *m_mem_thd= m_mem_thd_save; /* restore original mem_root */ + m_mem_thd= NULL; + m_mem_thd_save= NULL; + } + } +} + +/** + Build a SESSION system variable cache for a pfs_thread. + Requires that init_show_var_array() has already been called. + Return 0 for success. +*/ +int PFS_system_variable_cache::do_materialize_session(PFS_thread *pfs_thread) +{ + int ret= 1; + + m_pfs_thread= pfs_thread; + m_materialized= false; + m_cache.clear(); + + /* Block plugins from unloading. */ + mysql_mutex_lock(&LOCK_plugin_delete); + + /* The SHOW_VAR array must be initialized externally. */ + assert(m_initialized); + + /* Use a temporary mem_root to avoid depleting THD mem_root. */ + if (m_use_mem_root) + set_mem_root(); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(pfs_thread)) != NULL) + { + for (SHOW_VAR *show_var= m_show_var_array.front(); + show_var->value && (show_var != m_show_var_array.end()); show_var++) + { + sys_var *value= (sys_var *)show_var->value; + + /* Match the system variable scope to the target scope. */ + if (match_scope(value->scope())) + { + /* Resolve value, convert to text, add to cache. */ + System_variable system_var(m_safe_thd, show_var, m_query_scope, false); + m_cache.push(system_var); + } + } + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + /* Mark mem_root blocks as free. */ + if (m_use_mem_root) + clear_mem_root(); + + mysql_mutex_unlock(&LOCK_plugin_delete); + return ret; +} + +/** + Materialize a single system variable for a pfs_thread. + Requires that init_show_var_array() has already been called. + Return 0 for success. +*/ +int PFS_system_variable_cache::do_materialize_session(PFS_thread *pfs_thread, uint index) +{ + int ret= 1; + + m_pfs_thread= pfs_thread; + m_materialized= false; + m_cache.clear(); + + /* Block plugins from unloading. */ + mysql_mutex_lock(&LOCK_plugin_delete); + + /* The SHOW_VAR array must be initialized externally. */ + assert(m_initialized); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(pfs_thread)) != NULL) + { + SHOW_VAR *show_var= &m_show_var_array.at(index); + + if (show_var && show_var->value && + (show_var != m_show_var_array.end())) + { + sys_var *value= (sys_var *)show_var->value; + + /* Match the system variable scope to the target scope. */ + if (match_scope(value->scope())) + { + /* Resolve value, convert to text, add to cache. */ + System_variable system_var(m_safe_thd, show_var, m_query_scope, false); + m_cache.push(system_var); + } + } + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + mysql_mutex_unlock(&LOCK_plugin_delete); + return ret; +} + +/** + Build a SESSION system variable cache for a THD. +*/ +int PFS_system_variable_cache::do_materialize_session(THD *unsafe_thd) +{ + int ret= 1; + + m_unsafe_thd= unsafe_thd; + m_safe_thd= NULL; + m_materialized= false; + m_cache.clear(); + + /* Block plugins from unloading. */ + mysql_mutex_lock(&LOCK_plugin_delete); + + /* + Build array of SHOW_VARs from system variable hash. Do this within + LOCK_plugin_delete to ensure that the hash table remains unchanged + while this thread is materialized. + */ + if (!m_external_init) + init_show_var_array(OPT_SESSION, true); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(unsafe_thd)) != NULL) + { + for (SHOW_VAR *show_var= m_show_var_array.front(); + show_var->value && (show_var != m_show_var_array.end()); show_var++) + { + sys_var *value = (sys_var *)show_var->value; + + /* Match the system variable scope to the target scope. */ + if (match_scope(value->scope())) + { + /* Resolve value, convert to text, add to cache. */ + System_variable system_var(m_safe_thd, show_var, m_query_scope, false); + m_cache.push(system_var); + } + } + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + mysql_mutex_unlock(&LOCK_plugin_delete); + return ret; +} + + +/** + CLASS System_variable +*/ + +/** + Empty placeholder. +*/ +System_variable::System_variable() + : m_name(NULL), m_name_length(0), m_value_length(0), m_type(SHOW_UNDEF), m_scope(0), + m_ignore(false), m_charset(NULL), m_initialized(false) +{ + m_value_str[0]= '\0'; +} + +/** + GLOBAL or SESSION system variable. +*/ +System_variable::System_variable(THD *target_thd, const SHOW_VAR *show_var, + enum_var_type query_scope, bool ignore) + : m_name(NULL), m_name_length(0), m_value_length(0), m_type(SHOW_UNDEF), m_scope(0), + m_ignore(ignore), m_charset(NULL), m_initialized(false) +{ + init(target_thd, show_var, query_scope); +} + +/** + Get sys_var value from global or local source then convert to string. +*/ +void System_variable::init(THD *target_thd, const SHOW_VAR *show_var, + enum_var_type query_scope) +{ + if (show_var == NULL || show_var->name == NULL) + return; + + DBUG_ASSERT(show_var->type == SHOW_SYS); + + m_name= show_var->name; + m_name_length= strlen(m_name); + + /* Deprecated variables are ignored but must still be accounted for. */ + if (m_ignore) + { + m_value_str[0]= '\0'; + m_value_length= 0; + m_initialized= true; + return; + } + + /* Block remote target thread from updating this system variable. */ + /*XXX + THD *current_thread= current_thd; + if (target_thd != current_thread) + mysql_mutex_lock(&target_thd->LOCK_thd_sysvar);*/ + + sys_var *system_var= (sys_var *)show_var->value; + assert(system_var != NULL); + m_charset= system_var->charset(target_thd); + m_type= system_var->show_type(); + m_scope= system_var->scope(); + + /* Get the value of the system variable. */ + String buf(m_value_str, sizeof(m_value_str) - 1, system_charset_info); + if (!system_var->val_str_nolock(&buf, target_thd, + system_var->value_ptr(target_thd, query_scope, &null_clex_str))) + buf.length(0); + + m_value_length= MY_MIN(buf.length(), SHOW_VAR_FUNC_BUFF_SIZE); + + /* Returned value may reference a string other than m_value_str. */ + if (buf.ptr() != m_value_str) + memcpy(m_value_str, buf.ptr(), m_value_length); + m_value_str[m_value_length]= 0; + + /*XXX + if (target_thd != current_thread) + mysql_mutex_unlock(&target_thd->LOCK_thd_sysvar);*/ + + m_initialized= true; +} + + +/** + CLASS PFS_status_variable_cache +*/ + +PFS_status_variable_cache:: +PFS_status_variable_cache(bool external_init) : + PFS_variable_cache<Status_variable>(external_init), + m_show_command(false), m_sum_client_status(NULL) +{ + /* Determine if the originating query is a SHOW command. */ + m_show_command= (m_current_thd->lex->sql_command == SQLCOM_SHOW_STATUS); +} + +/** + Build cache of SESSION status variables for a user. +*/ +int PFS_status_variable_cache::materialize_user(PFS_user *pfs_user) +{ + if (!pfs_user) + return 1; + + if (is_materialized(pfs_user)) + return 0; + + if (!pfs_user->m_lock.is_populated()) + return 1; + + /* Set callback function. */ + m_sum_client_status= sum_user_status; + return do_materialize_client((PFS_client *)pfs_user); +} + +/** + Build cache of SESSION status variables for a host. +*/ +int PFS_status_variable_cache::materialize_host(PFS_host *pfs_host) +{ + if (!pfs_host) + return 1; + + if (is_materialized(pfs_host)) + return 0; + + if (!pfs_host->m_lock.is_populated()) + return 1; + + /* Set callback function. */ + m_sum_client_status= sum_host_status; + return do_materialize_client((PFS_client *)pfs_host); +} + +/** + Build cache of SESSION status variables for an account. +*/ +int PFS_status_variable_cache::materialize_account(PFS_account *pfs_account) +{ + if (!pfs_account) + return 1; + + if (is_materialized(pfs_account)) + return 0; + + if (!pfs_account->m_lock.is_populated()) + return 1; + + /* Set callback function. */ + m_sum_client_status= sum_account_status; + return do_materialize_client((PFS_client *)pfs_account); +} +/** + Compare status variable scope to desired scope. + @param variable_scope Scope of current status variable + @return TRUE if variable matches the query scope +*/ +bool PFS_status_variable_cache::match_scope(SHOW_SCOPE variable_scope, bool strict) +{ + switch (variable_scope) + { + case SHOW_SCOPE_GLOBAL: + return (m_query_scope == OPT_GLOBAL) || (! strict && (m_query_scope == OPT_SESSION)); + break; + case SHOW_SCOPE_SESSION: + /* Ignore session-only vars if aggregating by user, host or account. */ + if (m_aggregate) + return false; + else + return (m_query_scope == OPT_SESSION); + break; + case SHOW_SCOPE_ALL: + return (m_query_scope == OPT_GLOBAL || m_query_scope == OPT_SESSION); + break; + case SHOW_SCOPE_UNDEF: + default: + return false; + break; + } + return false; +} + +/* + Exclude specific status variables from the query by name or prefix. + Return TRUE if variable should be filtered. +*/ +bool PFS_status_variable_cache::filter_by_name(const SHOW_VAR *show_var) +{ + assert(show_var); + assert(show_var->name); + + if (show_var->type == SHOW_ARRAY) + { + /* The SHOW_ARRAY name is the prefix for the variables in the subarray. */ + const char *prefix= show_var->name; + /* Exclude COM counters if not a SHOW STATUS command. */ + if (!my_strcasecmp(system_charset_info, prefix, "Com") && !m_show_command) + return true; + } + else + { + /* + Slave status resides in Performance Schema replication tables. Exclude + these slave status variables from the SHOW STATUS command and from the + status tables. + Assume null prefix to ensure that only server-defined slave status + variables are filtered. + */ + const char *name= show_var->name; + if (!my_strcasecmp(system_charset_info, name, "Slave_running") || + !my_strcasecmp(system_charset_info, name, "Slave_retried_transactions") || + !my_strcasecmp(system_charset_info, name, "Slave_last_heartbeat") || + !my_strcasecmp(system_charset_info, name, "Slave_received_heartbeats") || + !my_strcasecmp(system_charset_info, name, "Slave_heartbeat_period")) + { + return true; + } + } + + return false; +} + +/** + Check that the variable type is aggregatable. + + @param variable_type Status variable type + @return TRUE if variable type can be aggregated +*/ +bool PFS_status_variable_cache::can_aggregate(enum_mysql_show_type variable_type) +{ + switch(variable_type) + { + /* + All server status counters that are totaled across threads are defined in + system_status_var as either SHOW_LONGLONG_STATUS or SHOW_LONG_STATUS. + These data types are not available to plugins. + */ + case SHOW_LONGLONG_STATUS: + case SHOW_LONG_STATUS: + return true; + break; + + /* Server and plugin */ + case SHOW_UNDEF: + case SHOW_BOOL: + case SHOW_CHAR: + case SHOW_CHAR_PTR: + case SHOW_ARRAY: + case SHOW_FUNC: + case SHOW_INT: + case SHOW_LONG: + case SHOW_LONGLONG: + case SHOW_DOUBLE: + /* Server only */ + case SHOW_HAVE: + case SHOW_MY_BOOL: + case SHOW_SYS: + case SHOW_LEX_STRING: + case SHOW_KEY_CACHE_LONG: + case SHOW_DOUBLE_STATUS: + case SHOW_HA_ROWS: + case SHOW_LONG_NOFLUSH: + case SHOW_SLONG: + default: + return false; + break; + } +} + +/** + Check if a status variable should be excluded from the query. + Return TRUE if the variable should be excluded. +*/ +bool PFS_status_variable_cache::filter_show_var(const SHOW_VAR *show_var, bool strict) +{ + /* Match the variable scope with the query scope. */ + if (!match_scope(show_scope_from_type(show_var->type), strict)) + return true; + + /* Exclude specific status variables by name or prefix. */ + if (filter_by_name(show_var)) + return true; + + /* For user, host or account, ignore variables having non-aggregatable types. */ + if (m_aggregate && !can_aggregate(show_var->type)) + return true; + + return false; +} + + +/** + Build an array of SHOW_VARs from the global status array. Expand nested + subarrays, filter unwanted variables. + NOTE: Must be done inside of LOCK_status to guard against plugin load/unload. +*/ +bool PFS_status_variable_cache::init_show_var_array(enum_var_type scope, bool strict) +{ + assert(!m_initialized); + + /* Resize if necessary. */ + m_show_var_array.reserve(all_status_vars.elements + 1); + + m_query_scope= scope; + + for (SHOW_VAR *show_var_iter= dynamic_element(&all_status_vars, 0, SHOW_VAR *); + show_var_iter != dynamic_element(&all_status_vars, all_status_vars.elements, SHOW_VAR *); + show_var_iter++) + { + SHOW_VAR show_var= *show_var_iter; + + /* Check if this status var should be excluded from the query. */ + if (filter_show_var(&show_var, strict)) + continue; + + if (show_var.type == SHOW_ARRAY) + { + /* Expand nested subarray. The name is used as a prefix. */ + expand_show_var_array((SHOW_VAR *)show_var.value, show_var.name, strict); + } + else + { + show_var.name= make_show_var_name(NULL, show_var.name); + m_show_var_array.push(show_var); + } + } + + /* Last element is NULL. */ + st_mysql_show_var empty= {0,0,SHOW_UNDEF}; + m_show_var_array.push(empty); + + /* Get the latest version of all_status_vars. */ + m_version= get_status_vars_version(); + + /* Increase cache size if necessary. */ + m_cache.reserve(m_show_var_array.elements()); + + m_initialized= true; + return true; +} + +/** + Expand a nested subarray of status variables, indicated by a type of SHOW_ARRAY. +*/ +void PFS_status_variable_cache::expand_show_var_array(const SHOW_VAR *show_var_array, const char *prefix, bool strict) +{ + for (const SHOW_VAR *show_var_ptr= show_var_array; + show_var_ptr && show_var_ptr->name; + show_var_ptr++) + { + SHOW_VAR show_var= *show_var_ptr; + + if (filter_show_var(&show_var, strict)) + continue; + + if (show_var.type == SHOW_ARRAY) + { + char name_buf[SHOW_VAR_MAX_NAME_LEN]; + show_var.name= make_show_var_name(prefix, show_var.name, name_buf, sizeof(name_buf)); + /* Expand nested subarray. The name is used as a prefix. */ + expand_show_var_array((SHOW_VAR *)show_var.value, show_var.name, strict); + } + else + { + /* Add the SHOW_VAR element. Make a local copy of the name string. */ + show_var.name= make_show_var_name(prefix, show_var.name); + m_show_var_array.push(show_var); + } + } +} + +/** + Build the complete status variable name, with prefix. Return in buffer provided. +*/ +char * PFS_status_variable_cache::make_show_var_name(const char* prefix, const char* name, + char *name_buf, size_t buf_len) +{ + assert(name_buf != NULL); + char *prefix_end= name_buf; + + if (prefix && *prefix) + { + /* Drop the prefix into the front of the name buffer. */ + prefix_end= my_stpnmov(name_buf, prefix, buf_len-1); + *prefix_end++= '_'; + } + + /* Restrict name length to remaining buffer size. */ + size_t max_name_len= name_buf + buf_len - prefix_end; + + /* Load the name into the buffer after the prefix. */ + my_stpnmov(prefix_end, name, max_name_len); + name_buf[buf_len-1]= 0; + + return (name_buf); +} + +/** + Make a copy of the name string prefixed with the subarray name if necessary. +*/ +char * PFS_status_variable_cache::make_show_var_name(const char* prefix, const char* name) +{ + char name_buf[SHOW_VAR_MAX_NAME_LEN]; + size_t buf_len= sizeof(name_buf); + make_show_var_name(prefix, name, name_buf, buf_len); + return m_current_thd->strdup(name_buf); /* freed at statement end */ +} + +/** + Build an internal SHOW_VAR array from the external status variable array. +*/ +bool PFS_status_variable_cache::do_initialize_session(void) +{ + /* Acquire LOCK_status to guard against plugin load/unload. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + bool ret= init_show_var_array(OPT_SESSION, true); + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + + return ret; +} + +/** + For the current THD, use initial_status_vars taken from before the query start. +*/ +STATUS_VAR *PFS_status_variable_cache::set_status_vars(void) +{ + STATUS_VAR *status_vars; + if (m_safe_thd == m_current_thd && m_current_thd->initial_status_var != NULL) + status_vars= m_current_thd->initial_status_var; + else + status_vars= &m_safe_thd->status_var; + + return status_vars; +} + +/** + Build cache for GLOBAL status variables using values totaled from all threads. +*/ +int PFS_status_variable_cache::do_materialize_global(void) +{ + STATUS_VAR status_totals; + + m_materialized= false; + DEBUG_SYNC(m_current_thd, "before_materialize_global_status_array"); + + /* Acquire LOCK_status to guard against plugin load/unload. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + /* + Build array of SHOW_VARs from global status array. Do this within + LOCK_status to ensure that the array remains unchanged during + materialization. + */ + if (!m_external_init) + init_show_var_array(OPT_GLOBAL, true); + + /* + Collect totals for all active threads. Start with global status vars as a + baseline. + */ + PFS_connection_status_visitor visitor(&status_totals); + PFS_connection_iterator::visit_global(false, /* hosts */ + false, /* users */ + false, /* accounts */ + false, /* threads */ + true, /* THDs */ + &visitor); + /* + Build the status variable cache using the SHOW_VAR array as a reference. + Use the status totals collected from all threads. + */ + manifest(m_current_thd, m_show_var_array.front(), &status_totals, "", false, true); + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + + m_materialized= true; + DEBUG_SYNC(m_current_thd, "after_materialize_global_status_array"); + + return 0; +} + +/** + Build GLOBAL and SESSION status variable cache using values for a non-instrumented thread. +*/ +int PFS_status_variable_cache::do_materialize_all(THD* unsafe_thd) +{ + int ret= 1; + assert(unsafe_thd != NULL); + + m_unsafe_thd= unsafe_thd; + m_materialized= false; + m_cache.clear(); + + /* Avoid recursive acquisition of LOCK_status. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + /* + Build array of SHOW_VARs from global status array. Do this within + LOCK_status to ensure that the array remains unchanged while this + thread is materialized. + */ + if (!m_external_init) + init_show_var_array(OPT_SESSION, false); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(unsafe_thd)) != NULL) + { + /* + Build the status variable cache using the SHOW_VAR array as a reference. + Use the status values from the THD protected by the thread manager lock. + */ + STATUS_VAR *status_vars= set_status_vars(); + manifest(m_safe_thd, m_show_var_array.front(), status_vars, "", false, false); + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + return ret; +} + +/** + Build SESSION status variable cache using values for a non-instrumented thread. +*/ +int PFS_status_variable_cache::do_materialize_session(THD* unsafe_thd) +{ + int ret= 1; + assert(unsafe_thd != NULL); + + m_unsafe_thd= unsafe_thd; + m_materialized= false; + m_cache.clear(); + + /* Avoid recursive acquisition of LOCK_status. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + /* + Build array of SHOW_VARs from global status array. Do this within + LOCK_status to ensure that the array remains unchanged while this + thread is materialized. + */ + if (!m_external_init) + init_show_var_array(OPT_SESSION, true); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(unsafe_thd)) != NULL) + { + /* + Build the status variable cache using the SHOW_VAR array as a reference. + Use the status values from the THD protected by the thread manager lock. + */ + STATUS_VAR *status_vars= set_status_vars(); + manifest(m_safe_thd, m_show_var_array.front(), status_vars, "", false, true); + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + return ret; +} + +/** + Build SESSION status variable cache using values for a PFS_thread. + NOTE: Requires that init_show_var_array() has already been called. +*/ +int PFS_status_variable_cache::do_materialize_session(PFS_thread *pfs_thread) +{ + int ret= 1; + assert(pfs_thread != NULL); + + m_pfs_thread= pfs_thread; + m_materialized= false; + m_cache.clear(); + + /* Acquire LOCK_status to guard against plugin load/unload. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + /* The SHOW_VAR array must be initialized externally. */ + assert(m_initialized); + + /* Get and lock a validated THD from the thread manager. */ + if ((m_safe_thd= get_THD(pfs_thread)) != NULL) + { + /* + Build the status variable cache using the SHOW_VAR array as a reference. + Use the status values from the THD protected by the thread manager lock. + */ + STATUS_VAR *status_vars= set_status_vars(); + manifest(m_safe_thd, m_show_var_array.front(), status_vars, "", false, true); + + /* Release lock taken in get_THD(). */ + mysql_mutex_unlock(&m_safe_thd->LOCK_thd_data); + + m_materialized= true; + ret= 0; + } + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + return ret; +} + +/** + Build cache of SESSION status variables using the status values provided. + The cache is associated with a user, host or account, but not with any + particular thread. + NOTE: Requires that init_show_var_array() has already been called. +*/ +int PFS_status_variable_cache::do_materialize_client(PFS_client *pfs_client) +{ + assert(pfs_client != NULL); + STATUS_VAR status_totals; + + m_pfs_client= pfs_client; + m_materialized= false; + m_cache.clear(); + + /* Acquire LOCK_status to guard against plugin load/unload. */ + //if (m_current_thd->fill_status_recursion_level++ == 0) + mysql_mutex_lock(&LOCK_status); + + /* The SHOW_VAR array must be initialized externally. */ + assert(m_initialized); + + /* + Generate status totals from active threads and from totals aggregated + from disconnected threads. + */ + m_sum_client_status(pfs_client, &status_totals); + + /* + Build the status variable cache using the SHOW_VAR array as a reference and + the status totals collected from threads associated with this client. + */ + manifest(m_current_thd, m_show_var_array.front(), &status_totals, "", false, true); + + //if (m_current_thd->fill_status_recursion_level-- == 1) + mysql_mutex_unlock(&LOCK_status); + + m_materialized= true; + return 0; +} + +/* + Build the status variable cache from the expanded and sorted SHOW_VAR array. + Resolve status values using the STATUS_VAR struct provided. +*/ +void PFS_status_variable_cache::manifest(THD *thd, const SHOW_VAR *show_var_array, + STATUS_VAR *status_vars, const char *prefix, + bool nested_array, bool strict) +{ + for (const SHOW_VAR *show_var_iter= show_var_array; + show_var_iter && show_var_iter->name; + show_var_iter++) + { + // work buffer, must be aligned to handle long/longlong values + my_aligned_storage<SHOW_VAR_FUNC_BUFF_SIZE+1, MY_ALIGNOF(longlong)> + value_buf; + SHOW_VAR show_var_tmp; + const SHOW_VAR *show_var_ptr= show_var_iter; /* preserve array pointer */ + + /* + If the value is a function reference, then execute the function and + reevaluate the new SHOW_TYPE and value. Handle nested case where + SHOW_FUNC resolves to another SHOW_FUNC. + */ + if (show_var_ptr->type == SHOW_FUNC) + { + show_var_tmp= *show_var_ptr; + /* + Execute the function reference in show_var_tmp->value, which returns + show_var_tmp with a new type and new value. + */ + for (const SHOW_VAR *var= show_var_ptr; var->type == SHOW_FUNC; var= &show_var_tmp) + { + ((mysql_show_var_func)(var->value))(thd, &show_var_tmp, value_buf.data, NULL, m_query_scope); + } + show_var_ptr= &show_var_tmp; + } + + /* + If we are expanding a SHOW_ARRAY, filter variables that were not prefiltered by + init_show_var_array(). + */ + if (nested_array && filter_show_var(show_var_ptr, strict)) + continue; + + if (show_var_ptr->type == SHOW_ARRAY) + { + /* + Status variables of type SHOW_ARRAY were expanded and filtered by + init_show_var_array(), except where a SHOW_FUNC resolves into a + SHOW_ARRAY, such as with InnoDB. Recurse to expand the subarray. + */ + manifest(thd, (SHOW_VAR *)show_var_ptr->value, status_vars, show_var_ptr->name, true, strict); + } + else + { + /* Add the materialized status variable to the cache. */ + SHOW_VAR show_var= *show_var_ptr; + /* + For nested array expansions, make a copy of the variable name, just as + done in init_show_var_array(). + */ + if (nested_array) + show_var.name= make_show_var_name(prefix, show_var_ptr->name); + + /* Convert status value to string format. Add to the cache. */ + Status_variable status_var(&show_var, status_vars, m_query_scope); + m_cache.push(status_var); + } + } +} + +/** + CLASS Status_variable +*/ +Status_variable::Status_variable(const SHOW_VAR *show_var, STATUS_VAR *status_vars, enum_var_type query_scope) + : m_name_length(0), m_value_length(0), m_type(SHOW_UNDEF), + m_charset(NULL), m_initialized(false) +{ + init(show_var, status_vars, query_scope); +} + +/** + Resolve status value, convert to string. + show_var->value is an offset into status_vars. + NOTE: Assumes LOCK_status is held. +*/ +void Status_variable::init(const SHOW_VAR *show_var, STATUS_VAR *status_vars, enum_var_type query_scope) +{ + if (show_var == NULL || show_var->name == NULL) + return; + m_name= show_var->name; + m_name_length= strlen(m_name); + m_type= show_var->type; + + /* Get the value of the status variable. */ + const char *value; + value= get_one_variable(current_thd, show_var, query_scope, m_type, + status_vars, &m_charset, m_value_str, &m_value_length); + m_value_length= MY_MIN(m_value_length, SHOW_VAR_FUNC_BUFF_SIZE); + m_charset= system_charset_info; + + /* Returned value may reference a string other than m_value_str. */ + if (value != m_value_str) + memcpy(m_value_str, value, m_value_length); + m_value_str[m_value_length]= 0; + + m_initialized= true; +} + +/* + Get status totals for this user from active THDs and related accounts. +*/ +void sum_user_status(PFS_client *pfs_user, STATUS_VAR *status_totals) +{ + PFS_connection_status_visitor visitor(status_totals); + PFS_connection_iterator::visit_user((PFS_user *)pfs_user, + true, /* accounts */ + false, /* threads */ + true, /* THDs */ + &visitor); +} + +/* + Get status totals for this host from active THDs and related accounts. +*/ +void sum_host_status(PFS_client *pfs_host, STATUS_VAR *status_totals) +{ + PFS_connection_status_visitor visitor(status_totals); + PFS_connection_iterator::visit_host((PFS_host *)pfs_host, + true, /* accounts */ + false, /* threads */ + true, /* THDs */ + &visitor); +} + +/* + Get status totals for this account from active THDs and from totals aggregated + from disconnectd threads. +*/ +void sum_account_status(PFS_client *pfs_account, STATUS_VAR *status_totals) +{ + PFS_connection_status_visitor visitor(status_totals); + PFS_connection_iterator::visit_account((PFS_account *)pfs_account, + false, /* threads */ + true, /* THDs */ + &visitor); +} + +/** + Reset aggregated status counter stats for account, user and host. + NOTE: Assumes LOCK_status is held. +*/ +void reset_pfs_status_stats() +{ + reset_status_by_account(); + reset_status_by_user(); + reset_status_by_host(); +} + +/** @} */ |