/* Copyright (c) 2015, 2023, Oracle and/or its affiliates. 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 */ #ifndef PFS_VARIABLE_H #define PFS_VARIABLE_H /** @file storage/perfschema/pfs_variable.h Performance schema system and status variables (declarations). */ /** OVERVIEW -------- Status and system variables are implemented differently in the server, but the steps to process them in the Performance Schema are essentially the same: 1. INITIALIZE - Build or acquire a sorted list of variables to use for input. Use the SHOW_VAR struct as an intermediate format common to system, status and user vars: SHOW_VAR Name - Text string Value - Pointer to memory location, function, subarray structure Type - Scalar, function, or subarray Scope - SESSION, GLOBAL, BOTH Steps: - Register the server's internal buffer with the class. Acquire locks if necessary, then scan the contents of the input buffer. - For system variables, convert each element to SHOW_VAR format, store in a temporary array. - For status variables, copy existing global status array into a local array that can be used without locks. Expand nested subarrays, indicated by a type of SHOW_ARRAY. 2. MATERIALIZE - Convert the list of SHOW_VAR variables to string format, store in a local cache: - Resolve each variable according to the type. - Recursively process unexpanded nested arrays and callback functions. - Aggregate values across threads for global status. - Convert numeric values to a string. - Prefix variable name with the plugin name. 3. OUTPUT - Iterate the cache for the SHOW command or table query. CLASS OVERVIEW -------------- 1. System_variable - A materialized system variable 2. Status_variable - A materialized status variable 3. PFS_variable_cache - Base class that defines the interface for the operations above. public init_show_var_array() - Build SHOW_VAR list of variables for processing materialize_global() - Materialize global variables, aggregate across sessions materialize_session() - Materialize variables for a given PFS_thread or THD materialize_user() - Materialize variables for a user, aggregate across related threads. materialize_host() - Materialize variables for a host, aggregate across related threads. materialize_account() - Materialize variables for a account, aggregate across related threads. private m_show_var_array - Prealloc_array of SHOW_VARs for input to Materialize m_cache - Prealloc_array of materialized variables for output do_materialize_global() - Implementation of materialize_global() do_materialize_session() - Implementation of materialize_session() do_materialize_client() - Implementation of materialize_user/host/account() 4. PFS_system_variable_cache - System variable implementation of PFS_variable_cache 5. PFS_status_variable_cache - Status variable implementation of PFS_variable_cache 6. Find_THD_variable - Used by the thread manager to find and lock a THD. GLOSSARY -------- Status variable - Server or plugin status counter. Not dynamic. System variable - Server or plugin configuration variable. Usually dynamic. GLOBAL scope - Associated with the server, no context at thread level. SESSION scope - Associated with a connection or thread, but no global context. BOTH scope - Globally defined but applies at the session level. Initialize - Build list of variables in SHOW_VAR format. Materialize - Convert variables in SHOW_VAR list to string, cache for output. Manifest - Substep of Materialize. Resolve variable values according to type. This includes SHOW_FUNC types which are resolved by executing a callback function (possibly recursively), and SHOW_ARRAY types that expand into nested subarrays. LOCK PRIORITIES --------------- System Variables LOCK_plugin_delete (block plugin delete) LOCK_system_variables_hash LOCK_thd_data (block THD delete) LOCK_thd_sysvar (block system variable updates, alloc_and_copy_thd_dynamic_variables) LOCK_global_system_variables (very briefly held) Status Variables LOCK_status LOCK_thd_data (block THD delete) */ /* Iteration on THD from the sql layer. */ #include "mysqld_thd_manager.h" #define PFS_VAR /* Class sys_var */ #include "set_var.h" /* convert_value_to_string */ #include "sql_show.h" /* PFS_thread */ #include "pfs_instr.h" #include "pfs_user.h" #include "pfs_host.h" #include "pfs_account.h" /* Global array of all server and plugin-defined status variables. */ extern DYNAMIC_ARRAY all_status_vars; extern bool status_vars_inited; static const uint SYSVAR_MEMROOT_BLOCK_SIZE = 4096; extern mysql_mutex_t LOCK_plugin_delete; class Find_THD_Impl; class Find_THD_variable; typedef PFS_connection_slice PFS_client; /** CLASS System_variable - System variable derived from sys_var object. */ class System_variable { public: System_variable(); System_variable(THD *target_thd, const SHOW_VAR *show_var, enum_var_type query_scope, bool ignore); ~System_variable() {} bool is_null() const { return !m_initialized; } bool is_ignored() const { return m_ignore; } public: const char *m_name; size_t m_name_length; char m_value_str[SHOW_VAR_FUNC_BUFF_SIZE+1]; size_t m_value_length; enum_mysql_show_type m_type; int m_scope; bool m_ignore; const CHARSET_INFO *m_charset; private: bool m_initialized; void init(THD *thd, const SHOW_VAR *show_var, enum_var_type query_scope); }; /** CLASS Status_variable - Status variable derived from SHOW_VAR. */ class Status_variable { public: Status_variable() : m_name(NULL), m_name_length(0), m_value_length(0), m_type(SHOW_UNDEF), m_charset(NULL), m_initialized(false) {} Status_variable(const SHOW_VAR *show_var, STATUS_VAR *status_array, enum_var_type query_scope); ~Status_variable() {} bool is_null() const {return !m_initialized;}; public: const char *m_name; size_t m_name_length; char m_value_str[SHOW_VAR_FUNC_BUFF_SIZE+1]; size_t m_value_length; SHOW_TYPE m_type; const CHARSET_INFO *m_charset; private: bool m_initialized; void init(const SHOW_VAR *show_var, STATUS_VAR *status_array, enum_var_type query_scope); }; /** CLASS Find_THD_variable - Get and lock a validated THD from the thread manager. */ class Find_THD_variable : public Find_THD_Impl { public: Find_THD_variable() : m_unsafe_thd(NULL) {} Find_THD_variable(THD *unsafe_thd) : m_unsafe_thd(unsafe_thd) {} virtual bool operator()(THD *thd) { //TODO: filter bg threads? if (thd != m_unsafe_thd) return false; /* Hold this lock to keep THD during materialization. But don't lock current_thd (to be able to use set_killed() later */ if (thd != current_thd) mysql_mutex_lock(&thd->LOCK_thd_kill); return true; } void set_unsafe_thd(THD *unsafe_thd) { m_unsafe_thd= unsafe_thd; } private: THD *m_unsafe_thd; }; /** CLASS PFS_variable_cache - Base class for a system or status variable cache. */ template class PFS_variable_cache { public: typedef Dynamic_array Variable_array; PFS_variable_cache(bool external_init) : m_safe_thd(NULL), m_unsafe_thd(NULL), m_current_thd(current_thd), m_pfs_thread(NULL), m_pfs_client(NULL), m_thd_finder(), m_cache(PSI_INSTRUMENT_MEM), m_initialized(false), m_external_init(external_init), m_materialized(false), m_show_var_array(PSI_INSTRUMENT_MEM), m_version(0), m_query_scope(OPT_DEFAULT), m_use_mem_root(false), m_aggregate(false) { } virtual ~PFS_variable_cache()= 0; /** Build array of SHOW_VARs from the external variable source. Filter using session scope. */ bool initialize_session(void); /** Build array of SHOW_VARs suitable for aggregation by user, host or account. Filter using session scope. */ bool initialize_client_session(void); /** Build cache of GLOBAL system or status variables. Aggregate across threads if applicable. */ int materialize_global(); /** Build cache of GLOBAL and SESSION variables for a non-instrumented thread. */ int materialize_all(THD *thd); /** Build cache of SESSION variables for a non-instrumented thread. */ int materialize_session(THD *thd); /** Build cache of SESSION variables for an instrumented thread. */ int materialize_session(PFS_thread *pfs_thread, bool use_mem_root= false); /** Cache a single SESSION variable for an instrumented thread. */ int materialize_session(PFS_thread *pfs_thread, uint index); /** Build cache of SESSION status variables for a user. */ int materialize_user(PFS_user *pfs_user); /** Build cache of SESSION status variables for a host. */ int materialize_host(PFS_host *pfs_host); /** Build cache of SESSION status variables for an account. */ int materialize_account(PFS_account *pfs_account); /** True if variables have been materialized. */ bool is_materialized(void) { return m_materialized; } /** True if variables have been materialized for given THD. */ bool is_materialized(THD *unsafe_thd) { return (unsafe_thd == m_unsafe_thd && m_materialized); } /** True if variables have been materialized for given PFS_thread. */ bool is_materialized(PFS_thread *pfs_thread) { return (pfs_thread == m_pfs_thread && m_materialized); } /** True if variables have been materialized for given PFS_user. */ bool is_materialized(PFS_user *pfs_user) { return (static_cast(pfs_user) == m_pfs_client && m_materialized); } /** True if variables have been materialized for given PFS_host. */ bool is_materialized(PFS_host *pfs_host) { return (static_cast(pfs_host) == m_pfs_client && m_materialized); } /** True if variables have been materialized for given PFS_account. */ bool is_materialized(PFS_account *pfs_account) { return (static_cast(pfs_account) == m_pfs_client && m_materialized); } /** True if variables have been materialized for given PFS_user/host/account. */ bool is_materialized(PFS_client *pfs_client) { return (static_cast(pfs_client) == m_pfs_client && m_materialized); } /** Get a validated THD from the thread manager. Execute callback function while inside of the thread manager locks. */ THD *get_THD(THD *thd); THD *get_THD(PFS_thread *pfs_thread); /** Get a single variable from the cache. Get the first element in the cache by default. */ const Var_type *get(uint index= 0) const { if (index >= m_cache.elements()) return NULL; const Var_type *p= &m_cache.at(index); return p; } /** Number of elements in the cache. */ uint size() { return (uint)m_cache.elements(); } private: virtual bool do_initialize_global(void) { return true; } virtual bool do_initialize_session(void) { return true; } virtual int do_materialize_global(void) { return 1; } virtual int do_materialize_all(THD *thd) { return 1; } virtual int do_materialize_session(THD *thd) { return 1; } virtual int do_materialize_session(PFS_thread *) { return 1; } virtual int do_materialize_session(PFS_thread *, uint index) { return 1; } protected: /* Validated THD */ THD *m_safe_thd; /* Unvalidated THD */ THD *m_unsafe_thd; /* Current THD */ THD *m_current_thd; /* Current PFS_thread. */ PFS_thread *m_pfs_thread; /* Current PFS_user, host or account. */ PFS_client *m_pfs_client; /* Callback for thread iterator. */ Find_THD_variable m_thd_finder; /* Cache of materialized variables. */ Variable_array m_cache; /* True when list of SHOW_VAR is complete. */ bool m_initialized; /* True if the SHOW_VAR array must be initialized externally from the materialization step, such as with aggregations and queries by thread. */ bool m_external_init; /* True when cache is complete. */ bool m_materialized; /* Array of variables to be materialized. Last element must be null. */ Dynamic_array m_show_var_array; /* Version of global hash/array. Changes when vars added/removed. */ ulonglong m_version; /* Query scope: GLOBAL or SESSION. */ enum_var_type m_query_scope; /* True if temporary mem_root should be used for materialization. */ bool m_use_mem_root; /* True if summarizing across users, hosts or accounts. */ bool m_aggregate; }; /** Required implementation for pure virtual destructor of a template class. */ template PFS_variable_cache::~PFS_variable_cache() { } /** Get a validated THD from the thread manager. Execute callback function while while inside the thread manager lock. */ template THD *PFS_variable_cache::get_THD(THD *unsafe_thd) { if (unsafe_thd == NULL) { /* May happen, precisely because the pointer is unsafe (THD just disconnected for example). No need to walk Global_THD_manager for that. */ return NULL; } m_thd_finder.set_unsafe_thd(unsafe_thd); THD* safe_thd= Global_THD_manager::get_instance()->find_thd(&m_thd_finder); return safe_thd; } template THD *PFS_variable_cache::get_THD(PFS_thread *pfs_thread) { assert(pfs_thread != NULL); return get_THD(pfs_thread->m_thd); } /** Build array of SHOW_VARs from external source of system or status variables. Filter using session scope. */ template bool PFS_variable_cache::initialize_session(void) { if (m_initialized) return 0; return do_initialize_session(); } /** Build array of SHOW_VARs suitable for aggregation by user, host or account. Filter using session scope. */ template bool PFS_variable_cache::initialize_client_session(void) { if (m_initialized) return 0; /* Requires aggregation by user, host or account. */ m_aggregate= true; return do_initialize_session(); } /** Build cache of all GLOBAL variables. */ template int PFS_variable_cache::materialize_global() { if (is_materialized()) return 0; return do_materialize_global(); } /** Build cache of GLOBAL and SESSION variables for a non-instrumented thread. */ template int PFS_variable_cache::materialize_all(THD *unsafe_thd) { if (!unsafe_thd) return 1; if (is_materialized(unsafe_thd)) return 0; return do_materialize_all(unsafe_thd); } /** Build cache of SESSION variables for a non-instrumented thread. */ template int PFS_variable_cache::materialize_session(THD *unsafe_thd) { if (!unsafe_thd) return 1; if (is_materialized(unsafe_thd)) return 0; return do_materialize_session(unsafe_thd); } /** Build cache of SESSION variables for a thread. */ template int PFS_variable_cache::materialize_session(PFS_thread *pfs_thread, bool use_mem_root) { if (!pfs_thread) return 1; if (is_materialized(pfs_thread)) return 0; if (!pfs_thread->m_lock.is_populated() || pfs_thread->m_thd == NULL) return 1; m_use_mem_root= use_mem_root; return do_materialize_session(pfs_thread); } /** Materialize a single variable for a thread. */ template int PFS_variable_cache::materialize_session(PFS_thread *pfs_thread, uint index) { /* No check for is_materialized(). */ if (!pfs_thread) return 1; if (!pfs_thread->m_lock.is_populated() || pfs_thread->m_thd == NULL) return 1; return do_materialize_session(pfs_thread, index); } /** CLASS PFS_system_variable_cache - System variable cache. */ class PFS_system_variable_cache : public PFS_variable_cache { public: PFS_system_variable_cache(bool external_init) : PFS_variable_cache(external_init), m_mem_thd(NULL), m_mem_thd_save(NULL), m_mem_sysvar_ptr(NULL) { } bool match_scope(int scope); ulonglong get_sysvar_hash_version(void) { return m_version; } ~PFS_system_variable_cache() { free_mem_root(); } private: /* Build SHOW_var array. */ bool init_show_var_array(enum_var_type scope, bool strict); bool do_initialize_session(void); /* Global */ int do_materialize_global(void); /* Global and Session - THD */ int do_materialize_all(THD* thd); /* Session - THD */ int do_materialize_session(THD* thd); /* Session - PFS_thread */ int do_materialize_session(PFS_thread *thread); /* Single variable - PFS_thread */ int do_materialize_session(PFS_thread *pfs_thread, uint index); /* Temporary mem_root to use for materialization. */ MEM_ROOT m_mem_sysvar; /* Pointer to THD::mem_root. */ MEM_ROOT **m_mem_thd; /* Save THD::mem_root. */ MEM_ROOT *m_mem_thd_save; /* Pointer to temporary mem_root. */ MEM_ROOT *m_mem_sysvar_ptr; /* Allocate and/or assign temporary mem_root. */ void set_mem_root(void); /* Mark all memory blocks as free in temporary mem_root. */ void clear_mem_root(void); /* Free mem_root memory. */ void free_mem_root(void); }; /** CLASS PFS_status_variable_cache - Status variable cache */ class PFS_status_variable_cache : public PFS_variable_cache { public: PFS_status_variable_cache(bool external_init); int materialize_user(PFS_user *pfs_user); int materialize_host(PFS_host *pfs_host); int materialize_account(PFS_account *pfs_account); ulonglong get_status_array_version(void) { return m_version; } protected: /* Get PFS_user, account or host associated with a PFS_thread. Implemented by table class. */ virtual PFS_client *get_pfs(PFS_thread *pfs_thread) { return NULL; } /* True if query is a SHOW command. */ bool m_show_command; private: bool do_initialize_session(void); int do_materialize_global(void); /* Global and Session - THD */ int do_materialize_all(THD* thd); int do_materialize_session(THD *thd); int do_materialize_session(PFS_thread *thread); int do_materialize_session(PFS_thread *thread, uint index) { return 0; } int do_materialize_client(PFS_client *pfs_client); /* Callback to sum user, host or account status variables. */ void (*m_sum_client_status)(PFS_client *pfs_client, STATUS_VAR *status_totals); /* Build SHOW_VAR array from external source. */ bool init_show_var_array(enum_var_type scope, bool strict); /* Recursively expand nested SHOW_VAR arrays. */ void expand_show_var_array(const SHOW_VAR *show_var_array, const char *prefix, bool strict); /* Exclude unwanted variables from the query. */ bool filter_show_var(const SHOW_VAR *show_var, bool strict); /* Check the variable scope against the query scope. */ bool match_scope(SHOW_SCOPE variable_scope, bool strict); /* Exclude specific status variables by name or prefix. */ bool filter_by_name(const SHOW_VAR *show_var); /* Check if a variable has an aggregatable type. */ bool can_aggregate(enum_mysql_show_type variable_type); /* Build status variable name with prefix. Return in the buffer provided. */ char *make_show_var_name(const char* prefix, const char* name, char *name_buf, size_t buf_len); /* Build status variable name with prefix. Return copy of the string. */ char *make_show_var_name(const char* prefix, const char* name); /* For the current THD, use initial_status_vars taken from before the query start. */ STATUS_VAR *set_status_vars(void); /* Build the list of status variables from SHOW_VAR array. */ void manifest(THD *thd, const SHOW_VAR *show_var_array, STATUS_VAR *status_var_array, const char *prefix, bool nested_array, bool strict); }; /* Callback functions to sum status variables for a given user, host or account. */ void sum_user_status(PFS_client *pfs_user, STATUS_VAR *status_totals); void sum_host_status(PFS_client *pfs_host, STATUS_VAR *status_totals); void sum_account_status(PFS_client *pfs_account, STATUS_VAR *status_totals); /** @} */ #endif