#ifndef SET_VAR_INCLUDED #define SET_VAR_INCLUDED /* Copyright (c) 2002, 2024, 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 designed to work 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 either included with the program or referenced in the documentation. 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 St, Fifth Floor, Boston, MA 02110-1301 USA */ /** @file "public" interface to sys_var - server configuration variables. */ #include "my_config.h" #include #include #include #include #include #include #include #include #include #include "lex_string.h" #include "my_getopt.h" // get_opt_arg_type #include "my_hostname.h" // HOSTNAME_LENGTH #include "my_inttypes.h" #include "my_sys.h" #include "my_systime.h" // my_micro_time() #include "mysql/components/services/system_variable_source_type.h" #include "mysql/status_var.h" #include "mysql/strings/m_ctype.h" #include "mysql/udf_registration_types.h" #include "mysql_com.h" // Item_result #include "prealloced_array.h" // Prealloced_array #include "sql/sql_const.h" // SHOW_COMP_OPTION #include "sql/sql_plugin_ref.h" // plugin_ref #include "typelib.h" // TYPELIB class Item; class Item_func_set_user_var; class PolyLock; class String; class THD; class Time_zone; class set_var; class sys_var; class sys_var_pluginvar; struct LEX_USER; template class collation_unordered_map; using sql_mode_t = uint64_t; typedef enum enum_mysql_show_type SHOW_TYPE; typedef enum enum_mysql_show_scope SHOW_SCOPE; template class List; extern TYPELIB bool_typelib; struct sys_var_chain { sys_var *first; sys_var *last; }; bool add_static_system_variable_chain(sys_var *chain); bool add_dynamic_system_variable_chain(sys_var *chain); void delete_dynamic_system_variable_chain(sys_var *chain); enum enum_var_type : int { OPT_DEFAULT = 0, OPT_SESSION, OPT_GLOBAL, OPT_PERSIST, OPT_PERSIST_ONLY }; /** A class representing one system variable - that is something that can be accessed as @@global.variable_name or @@session.variable_name, visible in SHOW xxx VARIABLES and in INFORMATION_SCHEMA.xxx_VARIABLES, optionally it can be assigned to, optionally it can have a command-line counterpart with the same name. */ class sys_var { public: sys_var *next; LEX_CSTRING name; /** If the variable has an alias in the persisted variables file, this should point to it. This has the following consequences: - A SET PERSIST statement that sets either of the variables will persist both variables in the file. - When loading persisted variables, an occurrence of any one of the variables will initialize both variables. */ sys_var *m_persisted_alias; /** If m_persist_alias is set, and the current variable is deprecated and m_persist_alias is the recommended substitute, then this flag should be set to true. This has the consequence that the code that loads persisted variables will generate a warning if it encounters this variable but does not encounter the alias. */ bool m_is_persisted_deprecated; enum flag_enum { GLOBAL = 0x0001, SESSION = 0x0002, ONLY_SESSION = 0x0004, SCOPE_MASK = 0x03FF, // 1023 READONLY = 0x0400, // 1024 ALLOCATED = 0x0800, // 2048 INVISIBLE = 0x1000, // 4096 TRI_LEVEL = 0x2000, // 8192 - default is neither GLOBAL nor SESSION NOTPERSIST = 0x4000, HINT_UPDATEABLE = 0x8000, // Variable is updateable using SET_VAR hint /** There can be some variables which needs to be set before plugin is loaded. ex: binlog_checksum needs to be set before GR plugin is loaded. Also, there are some variables which needs to be set before some server internal component initialization. ex: binlog_encryption needs to be set before binary and relay log files generation. */ PERSIST_AS_READ_ONLY = 0x10000, /** Sensitive variable. If keyring is available, the variable will be persisted in mysqld-auto.cnf in encrypted format */ SENSITIVE = 0x20000 }; static const int PARSE_EARLY = 1; static const int PARSE_NORMAL = 2; /** Enumeration type to indicate for a system variable whether it will be written to the binlog or not. */ enum binlog_status_enum { VARIABLE_NOT_IN_BINLOG, SESSION_VARIABLE_IN_BINLOG } binlog_status; protected: typedef bool (*on_check_function)(sys_var *self, THD *thd, set_var *var); typedef bool (*pre_update_function)(sys_var *self, THD *thd, set_var *var); typedef bool (*on_update_function)(sys_var *self, THD *thd, enum_var_type type); int flags; ///< or'ed flag_enum values int m_parse_flag; ///< either PARSE_EARLY or PARSE_NORMAL. const SHOW_TYPE show_val_type; ///< what value_ptr() returns for sql_show.cc my_option option; ///< min, max, default values are stored here PolyLock *guard; ///< *second* lock that protects the variable ptrdiff_t offset; ///< offset to the value from global_system_variables on_check_function on_check; /** Pointer to function to be invoked before updating system variable (but after calling on_check hook), while we do not hold any locks yet. */ pre_update_function pre_update; on_update_function on_update; const char *const deprecation_substitute; bool is_os_charset; ///< true if the value is in character_set_filesystem struct get_opt_arg_source source; char user[USERNAME_CHAR_LENGTH + 1]; /* which user has set this variable */ char host[HOSTNAME_LENGTH + 1]; /* host on which this variable is set */ ulonglong timestamp; /* represents when this variable was set */ public: sys_var(sys_var_chain *chain, const char *name_arg, const char *comment, int flag_args, ptrdiff_t off, int getopt_id, enum get_opt_arg_type getopt_arg_type, SHOW_TYPE show_val_type_arg, longlong def_val, PolyLock *lock, enum binlog_status_enum binlog_status_arg, on_check_function on_check_func, on_update_function on_update_func, const char *substitute, int parse_flag, sys_var *persisted_alias = nullptr, bool is_persisted_deprecated = false); virtual ~sys_var() = default; const char *get_deprecation_substitute() { return deprecation_substitute; } /** All the cleanup procedures should be performed here */ virtual void cleanup() {} /** downcast for sys_var_pluginvar. Returns this if it's an instance of sys_var_pluginvar, and 0 otherwise. */ virtual sys_var_pluginvar *cast_pluginvar() { return nullptr; } bool check(THD *thd, set_var *var); const uchar *value_ptr(THD *running_thd, THD *target_thd, enum_var_type type, std::string_view keycache_name); const uchar *value_ptr(THD *thd, enum_var_type type, std::string_view keycache_name); virtual void update_default(longlong new_def_value) { option.def_value = new_def_value; } virtual longlong get_default() { return option.def_value; } virtual longlong get_min_value() { return option.min_value; } virtual ulonglong get_max_value() { return option.max_value; } /** Returns variable type. @return variable type */ virtual ulong get_var_type() { return (option.var_type & GET_TYPE_MASK); } virtual void set_arg_source(get_opt_arg_source *) {} virtual void set_is_plugin(bool) {} virtual enum_variable_source get_source() { return source.m_source; } virtual const char *get_source_name() { return source.m_path_name; } virtual void set_source(enum_variable_source src) { option.arg_source->m_source = src; } virtual bool set_source_name(const char *path) { return set_and_truncate(option.arg_source->m_path_name, path, sizeof(option.arg_source->m_path_name)); } virtual bool set_user(const char *usr) { return set_and_truncate(user, usr, sizeof(user)); } virtual const char *get_user() { return user; } virtual const char *get_host() { return host; } virtual bool set_host(const char *hst) { return set_and_truncate(host, hst, sizeof(host)); } virtual ulonglong get_timestamp() const { return timestamp; } virtual void set_user_host(THD *thd); my_option *get_option() { return &option; } virtual void set_timestamp() { timestamp = my_micro_time(); } virtual void set_timestamp(ulonglong ts) { timestamp = ts; } virtual bool is_non_persistent() { return flags & NOTPERSIST; } /** Update the system variable with the default value from either session or global scope. The default value is stored in the 'var' argument. Return false when successful. */ bool set_default(THD *thd, set_var *var); bool update(THD *thd, set_var *var); /** This function converts value stored in save_result to string. This function must be called after calling save_default() as save_default() will store default value to save_result. */ virtual void saved_value_to_string(THD *thd, set_var *var, char *def_val) = 0; SHOW_TYPE show_type() { return show_val_type; } int scope() const { return flags & SCOPE_MASK; } const CHARSET_INFO *charset(THD *thd); bool is_readonly() const { return flags & READONLY; } bool not_visible() const { return flags & INVISIBLE; } bool is_trilevel() const { return flags & TRI_LEVEL; } bool is_persist_readonly() const { return flags & PERSIST_AS_READ_ONLY; } bool is_parse_early() const { return (m_parse_flag == PARSE_EARLY); } bool is_sensitive() const { return flags & SENSITIVE; } /** Check if the variable can be set using SET_VAR hint. @return true if the variable can be set using SET_VAR hint, false otherwise. */ bool is_hint_updateable() const { return flags & HINT_UPDATEABLE; } /** the following is only true for keycache variables, that support the syntax @@keycache_name.variable_name */ bool is_struct() { return option.var_type & GET_ASK_ADDR; } /* Indicates whether this system variable is written to the binlog or not. Variables are written to the binlog as part of "status_vars" in Query_log_event, as an Intvar_log_event, or a Rand_log_event. @return true if the variable is written to the binlog, false otherwise. */ bool is_written_to_binlog(enum_var_type type) { return type != OPT_GLOBAL && binlog_status == SESSION_VARIABLE_IN_BINLOG; } virtual bool check_update_type(Item_result type) = 0; /** Return true for success if: Global query and variable scope is GLOBAL or SESSION, or Session query and variable scope is SESSION or ONLY_SESSION. */ bool check_scope(enum_var_type query_type) { switch (query_type) { case OPT_PERSIST: case OPT_PERSIST_ONLY: case OPT_GLOBAL: return scope() & (GLOBAL | SESSION); case OPT_SESSION: return scope() & (SESSION | ONLY_SESSION); case OPT_DEFAULT: return scope() & (SESSION | ONLY_SESSION); } return false; } bool is_global_persist(enum_var_type type) { return (type == OPT_GLOBAL || type == OPT_PERSIST || type == OPT_PERSIST_ONLY); } /** Return true if settable at the command line */ bool is_settable_at_command_line() { return option.id != -1; } bool register_option(std::vector *array, int parse_flags) { return is_settable_at_command_line() && (m_parse_flag & parse_flags) && (array->push_back(option), false); } void do_deprecated_warning(THD *thd); /** Create item from system variable value. @param thd pointer to THD object @return pointer to Item object or NULL if it's impossible to obtain the value. */ Item *copy_value(THD *thd); void save_default(THD *thd, set_var *var) { global_save_default(thd, var); } bool check_if_sensitive_in_context(THD *, bool suppress_errors = true) const; private: /** Like strncpy, but ensures the destination is '\0'-terminated. Is also safe to call if dst==string (but not if they overlap in any other way). @param dst Target string @param string Source string @param sizeof_dst Size of the dst buffer @retval false The entire string was copied to dst @retval true strlen(string) was bigger than or equal to sizeof_dst, so dst contains only the sizeof_dst-1 first characters of string. */ inline static bool set_and_truncate(char *dst, const char *string, size_t sizeof_dst) { if (dst == string) return false; const size_t string_length = strlen(string); const size_t length = std::min(sizeof_dst - 1, string_length); memcpy(dst, string, length); dst[length] = 0; return length < string_length; // truncated } private: virtual bool do_check(THD *thd, set_var *var) = 0; /** save the session default value of the variable in var */ virtual void session_save_default(THD *thd, set_var *var) = 0; /** save the global default value of the variable in var */ virtual void global_save_default(THD *thd, set_var *var) = 0; virtual bool session_update(THD *thd, set_var *var) = 0; virtual bool global_update(THD *thd, set_var *var) = 0; protected: /** A pointer to a value of the variable for SHOW. It must be of show_val_type type (bool for SHOW_BOOL, int for SHOW_INT, longlong for SHOW_LONGLONG, etc). */ virtual const uchar *session_value_ptr(THD *running_thd, THD *target_thd, std::string_view keycache_name); virtual const uchar *global_value_ptr(THD *thd, std::string_view keycache_name); /** A pointer to a storage area of the variable, to the raw data. Typically it's the same as session_value_ptr(), but it's different, for example, for ENUM, that is printed as a string, but stored as a number. */ uchar *session_var_ptr(THD *thd); uchar *global_var_ptr(); friend class Sys_var_alias; }; enum class Suppress_not_found_error { NO, YES }; enum class Force_sensitive_system_variable_access { NO, YES }; enum class Is_already_locked { NO, YES }; enum class Is_single_thread { NO, YES }; /** Wrapper interface for all kinds of system variables. The interface encapsulates parse- and execution-time resolvers for all kinds of sys_var: * regular (static) system variables * MyISAM Multiple Key Cache variables * plugin-registered variables * component-registered variables */ /* There are 4 different sorts of system variables in MySQL: 1. Static system variables. 2. MyISAM Multiple Key Cache variables A.K.A. "Structured Variables" (the latest is easy to confuse with component-registered variables, so here and below the "Multiple Key Cache variable" or just "key cache variable" name is preferred. 3. Plugin-registered variables. 4. Component-registered variables. While they share the same internals/data structures for resolving them by name, they have different naming conventions, lifetimes, and lock policies at the same time. How to differentiate sorts of system variables by their syntax in SQL --------------------------------------------------------------------- Common note: the "@@" prefix is optional in lvalue syntax (left-hand sides of assignments in the SET statement) and mandatory in rvalue syntax (query blocks). 1. Static system variable names have the simplest syntax: ["@@"][ "."] where ::= SESSION | LOCAL | GLOBAL | PERSIST | PERSIST_ONLY Thus, static system variable names can be confused with plugin-registered variables names or reduced forms of key cache variable names. 2. Key cache variables can have either simple (reduced) or structured syntax: structured: ["@@"][ "."][ | DEFAULT "."] simple (reduced): ["@@"][ "."] where ::= GLOBAL | PERSIST | PERSIST_ONLY and ::= key_buffer_size | key_cache_block_size | key_cache_division_limit | key_cache_age_threshold Semantically, a name of the reduced form "cache_name_foo" is equivalent to the structured name "DEFAULT.cache_name_foo". Thus, key cache variable names of the simple form can be confused with static system variable names while names of the structured form can be confused with component-registered variable names. 3. Plugin-registered variables have almost same simple syntax as static system variables: ["@@"][ "."] where ::= GLOBAL | PERSIST | PERSIST_ONLY 4. Component-registered variable name syntax reminds the structured syntax of key cache variables: ["@@"][ "."] "." where ::= GLOBAL | PERSIST | PERSIST_ONLY Note on overlapping names: Component-registered and key cache variable names have a similar structured syntax (dot-separated). Plugin-registered and static system have similar syntax too (plain identifiers). To manage name conflicts, the server rejects registration of plugin-registered variable names coinciding with compiled-in names of static system variables. OTOH, the server silently accepts component-registered variable names like key_buffer_size etc. (conflict with qualified key cache variables), so those newly-registered variables won't be easily accessible via SQL. API --- All 4 sorts of system variables share the same interface: sys_var. See following paragraph "Lifetime" for API return value lifetime details. Common note about using variable names as API parameter: the search key is * case-insensitive * doesn't include the "@@" prefix * doesn't include a scope prefix Lifetimes --------- 1. Static system variable definitions are compiled into the server binary and have a runtime-wide life time, so pointers to their sys_var objects are stable. 2. While key cache variables are dynamic by their nature, their sys_var object pointers are stable, since all key caches (including DEFAULT) do share 4 always existing sys_var objects. So, pointers to key cache sys_var objects are as stable as pointers to sys_var objects of static system variables. 3. Plugin-registered variables can be unregistered by the UNINSTALL PLUGIN statement any time, so pointers to their sys_var objects are unstable between references to them. To make sys_var pointers stable during a single query execution, the plugin library maintains: * internally: a reference counting * in the outer world: the LEX::plugins release list. 4. Component-registered variables can be unregistered by the UNINSTALL COMPONENT statement any time, so pointers to their sys_var objects are unstable. Locking internals ----------------- 1. Static system variables and key cache variables don't need/cause a lock on LOCK_system_variables_hash for resolving their sys_var objects since they use a separate stable dictionary: static_system_variable_hash. 2. Both plugin-registered and component-registered variables share the same dynamic_system_variable_hash dictionary, and, normally, both need to lock LOCK_system_variables_hash while resolving their sys_var objects and accessing variable data. */ /** Encapsulation of system variable resolving and data accessing tasks. */ class System_variable_tracker final { struct Static {}; struct Keycache {}; struct Plugin {}; struct Component {}; System_variable_tracker(Static, sys_var *var); System_variable_tracker(Keycache, std::string_view cache_name, sys_var *var); System_variable_tracker(Plugin, std::string_view name); System_variable_tracker(Component, std::string_view dot_separated_name); System_variable_tracker(Component, std::string_view component_name, std::string_view variable_name); public: enum Lifetime { STATIC, ///< for regular static variables KEYCACHE, ///< for MyISAM Multiple Key Cache variables PLUGIN, ///< for plugin-registered system variables COMPONENT, ///< for component-registered variables }; System_variable_tracker(const System_variable_tracker &); ~System_variable_tracker(); void operator=(System_variable_tracker &&); /** Static "constructor" @note This function requires no locks and allocates nothing on memory heaps. @param prefix One of: component name, MyISAM Multiple Key Cache name, or empty string. @param suffix In a dot-separated syntax (@p prefix is mandatory): component-registered variable name, or MyISAM Multiple Key Cache property name. Otherwise (@p is empty): name of static or plugin-registered variables. @returns System_variable_tracker object */ static System_variable_tracker make_tracker(std::string_view prefix, std::string_view suffix); /** Static "constructor" @note This function requires no locks and allocates nothing on memory heaps. @param multipart_name Variable name, optionally dot-separated. @returns System_variable_tracker object */ static System_variable_tracker make_tracker(std::string_view multipart_name); Lifetime lifetime() const { return m_tag; } bool operator==(const System_variable_tracker &x) const { if (m_tag != x.m_tag) { return false; } switch (m_tag) { case STATIC: return m_static.m_static_var == x.m_static.m_static_var; case KEYCACHE: return m_keycache.m_keycache_var == x.m_keycache.m_keycache_var; case PLUGIN: return names_are_same(m_plugin.m_plugin_var_name, x.m_plugin.m_plugin_var_name); case COMPONENT: return names_are_same(m_component.m_component_var_name, x.m_component.m_component_var_name); } my_abort(); // to make compiler happy } bool is_keycache_var() const { return m_tag == KEYCACHE; } bool eq_static_sys_var(const sys_var *var) const { return m_tag == STATIC && m_static.m_static_var == var; } /** @returns 1) normalized names of regular system variables; 2) user-specified dot-separated names of key cache variables, or user-specified unqualified names of default key cache property names; 3) user-specified names of plugin-registered variables; 4) user-specified dot-separated names of component-registered variables. */ const char *get_var_name() const; /** @returns 1) not normalized names of MyISAM Multiple Key Caches, also can return empty string for the implicit prefix (i.e. implicit "DEFAULT."). 2) empty string for the rest of system variables. */ std::string_view get_keycache_name() const { return m_tag == KEYCACHE ? std::string_view{m_keycache.m_keycache_var_name, m_keycache.m_keycache_name_size} : std::string_view{}; } /** @returns true if the underlying variable can be referenced in the SET_VAR optimizer hint syntax, otherwise false. */ bool is_hint_updateable() const { return m_tag == STATIC && m_static.m_static_var->is_hint_updateable(); } /** Safely pass a sys_var object to a function. Non-template variant. access_system_variable() is the most important interface: it does necessary locks, if a dynamic variable is inaccessible it exits, otherwise it calls @p function. access_system_variable() is reentrant: being called recursively it suppresses double locks. Simplified pseudo code of access_system_variable() implementation: if system variable is @@session_track_system_variables { // a special case: the system variable is static, // but it references a comma-separated list of // other system variables if function is not no-op { process @@session_track_system_variables as plugin-registered one } else { return false // success } } if system variable is static or key cache variable { // no dictionary/plugin locks needed call function(cached sys_var pointer) return false // success } if system variable is plugin-registered one { if need to hold LOCK_system_variables_hash { acquire read-only LOCK_system_variables_hash } if need to hold LOCK_plugin { acquire LOCK_plugin, unlock on exit } find sys_var if found { intern_plugin_lock } if this function acquired LOCK_plugin { release LOCK_plugin } if this function acquired LOCK_system_variables_hash { release LOCK_system_variables_hash } if not found or plugin is not in the PLUGIN_IS_READY state { return true // error: variable not found or } call function(found sys_var pointer) return false // success } if system variable is component-registered one { if need to hold LOCK_system_variables_hash(**) { acquire read-only LOCK_system_variables_hash, unlock on exit } find sys_var if not found { return true } call function(found sys_var pointer) return false // success } @param thd A connection handler or nullptr. @param function An optional anonymous function to pass a sys_var object if the latest is accessible. @param suppress_not_found_error Suppress or output ER_UNKNOWN_SYSTEM_VARIABLE if a dynamic variable is unavailable. @param force_sensitive_variable_access Suppress privilege check for SENSITIVE variables. @param is_already_locked YES means LOCK_system_variables_hash has already been taken. @param is_single_thread YES means we are called from mysqld_main, during bootstrap. @returns True if the dynamic variable is unavailable, otherwise false. */ bool access_system_variable( THD *thd, std::function function = {}, Suppress_not_found_error suppress_not_found_error = Suppress_not_found_error::NO, Force_sensitive_system_variable_access force_sensitive_variable_access = Force_sensitive_system_variable_access::NO, Is_already_locked is_already_locked = Is_already_locked::NO, Is_single_thread is_single_thread = Is_single_thread::NO) const; /** Safely pass a sys_var object to a function. Template variant. access_system_variable() is the most important interface: it does necessary locks, if a dynamic variable is inaccessible then access_system_variable() exits returning empty std::option, otherwise it calls @p function and returns the result value of @p function to the caller. See the pseudo code part at the comment above a signature of the non-template variant of access_system_variable() for details (replace "return true" with "return std::option{}" and "return false" with "return result value of anonymous function call"). @tparam T A type of a return value of the callback function. @param thd A connection handler or nullptr. @param function A function to pass a sys_var object if the latest is accessible. @param suppress_not_found_error Suppress or output ER_UNKNOWN_SYSTEM_VARIABLE if a dynamic variable is unavailable. @param force_sensitive_variable_access Suppress privilege check for SENSITIVE variables. @param is_already_locked YES means LOCK_system_variables_hash has already been taken. @param is_single_thread YES means we are called from mysqld_main, during bootstrap. @returns No value if the dynamic variable is unavailable, otherwise a value of type T returned by the callback function. */ template std::optional access_system_variable( THD *thd, std::function function, Suppress_not_found_error suppress_not_found_error = Suppress_not_found_error::NO, Force_sensitive_system_variable_access force_sensitive_variable_access = Force_sensitive_system_variable_access::NO, Is_already_locked is_already_locked = Is_already_locked::NO, Is_single_thread is_single_thread = Is_single_thread::NO) const { T result; auto wrapper = [function, &result](const System_variable_tracker &t, sys_var *v) { result = function(t, v); // clang-format }; return access_system_variable(thd, wrapper, suppress_not_found_error, force_sensitive_variable_access, is_already_locked, is_single_thread) ? std::optional{} : std::optional{result}; } SHOW_TYPE cached_show_type() const { if (!m_cache.has_value()) my_abort(); return m_cache.value().m_cached_show_type; } bool cached_is_sensitive() const { if (!m_cache.has_value()) my_abort(); return m_cache.value().m_cached_is_sensitive; } bool cached_is_applied_as_command_line() const { if (!m_cache.has_value()) my_abort(); return m_cache.value().m_cached_is_applied_as_command_line; } /** Number of system variable elements to preallocate. */ static constexpr size_t SYSTEM_VARIABLE_PREALLOC = 200; typedef Prealloced_array Array; friend Array; // for Array::emplace_back() static bool enumerate_sys_vars(bool sort, enum enum_var_type type, bool strict, Array *output); private: static bool enumerate_sys_vars_in_hash( collation_unordered_map *hash, enum enum_var_type query_scope, bool strict, System_variable_tracker::Array *output); static bool names_are_same(const char *, const char *); sys_var *get_stable_var() const { switch (m_tag) { case STATIC: return m_static.m_static_var; case KEYCACHE: return m_keycache.m_keycache_var; default: assert(false); return nullptr; // should never happen } } private: bool visit_plugin_variable(THD *, std::function, Suppress_not_found_error, Is_already_locked, Is_single_thread) const; bool visit_component_variable(THD *, std::function, Suppress_not_found_error, Is_already_locked, Is_single_thread) const; void cache_metadata(THD *thd, sys_var *v) const; private: Lifetime m_tag; struct Cache { SHOW_TYPE m_cached_show_type; bool m_cached_is_sensitive; bool m_cached_is_applied_as_command_line; }; mutable std::optional m_cache; /** A non-zero value suppresses LOCK_system_variables_hash guards in System_variable_tracker::access_system_variable */ thread_local static int m_hash_lock_recursion_depth; union { struct { sys_var *m_static_var; } m_static; // when m_tag == STATIC struct { /// A dot-separated key cache name or, for a reduced form of key cache /// variable names, a key cache property name as specified by the /// caller of System_variable_tracker::make_traker(). char m_keycache_var_name[NAME_LEN + sizeof(".key_cache_division_limit")]; size_t m_keycache_name_size; sys_var *m_keycache_var; } m_keycache; // when m_tag == KEYCACHE struct { /// A "_"-separated plugin-registered variables name. char m_plugin_var_name[NAME_LEN + 1]; mutable sys_var *m_plugin_var_cache; } m_plugin; // when m_tag == PLUGIN struct { /// A dot-separated component-registered variable name. char m_component_var_name[NAME_LEN + sizeof('.') + NAME_LEN + 1]; mutable sys_var *m_component_var_cache; } m_component; }; }; /**************************************************************************** Classes for parsing of the SET command ****************************************************************************/ /** A base class for everything that can be set with SET command. It's similar to Items, an instance of this is created by the parser for every assignment in SET (or elsewhere, e.g. in SELECT). */ class set_var_base { public: set_var_base() = default; virtual ~set_var_base() = default; virtual int resolve(THD *thd) = 0; ///< Check privileges & fix_fields virtual int check(THD *thd) = 0; ///< Evaluate the expression virtual int update(THD *thd) = 0; ///< Set the value virtual bool print(const THD *thd, String *str) = 0; ///< To self-print /** @returns whether this variable is @@@@optimizer_trace. */ virtual bool is_var_optimizer_trace() const { return false; } virtual void cleanup() {} /** Used only by prepared statements to resolve and check. No locking of tables between the two phases. */ virtual int light_check(THD *thd) { return (resolve(thd) || check(thd)); } /** Used to identify if variable is sensitive or not */ virtual bool is_sensitive() const { return false; } }; /** set_var_base descendant for assignments to the system variables. */ class set_var : public set_var_base { public: Item *value; ///< the expression that provides the new value of the variable const enum_var_type type; union ///< temp storage to hold a value between sys_var::check and ::update { ulonglong ulonglong_value; ///< for all integer, set, enum sysvars double double_value; ///< for Sys_var_double plugin_ref plugin; ///< for Sys_var_plugin Time_zone *time_zone; ///< for Sys_var_tz LEX_STRING string_value; ///< for Sys_var_charptr and others const void *ptr; ///< for Sys_var_struct } save_result; ///< Resolver of the variable at the left hand side of the assignment. const System_variable_tracker m_var_tracker; public: set_var(enum_var_type type_arg, const System_variable_tracker &var_arg, Item *value_arg) : value{value_arg}, type{type_arg}, m_var_tracker(var_arg) {} int resolve(THD *thd) override; int check(THD *thd) override; int update(THD *thd) override; int light_check(THD *thd) override; /** Print variable in short form. @param thd Thread handle. @param str String buffer to append the partial assignment to. */ void print_short(const THD *thd, String *str); bool print(const THD *, String *str) override; /* To self-print */ bool is_global_persist() { return (type == OPT_GLOBAL || type == OPT_PERSIST || type == OPT_PERSIST_ONLY); } bool is_var_optimizer_trace() const override { extern sys_var *Sys_optimizer_trace_ptr; return m_var_tracker.eq_static_sys_var(Sys_optimizer_trace_ptr); } bool is_sensitive() const override; void update_source_user_host_timestamp(THD *thd, sys_var *var); }; /* User variables like @my_own_variable */ class set_var_user : public set_var_base { Item_func_set_user_var *user_var_item; public: set_var_user(Item_func_set_user_var *item) : user_var_item(item) {} int resolve(THD *thd) override; int check(THD *thd) override; int update(THD *thd) override; int light_check(THD *thd) override; bool print(const THD *thd, String *str) override; /* To self-print */ }; class set_var_password : public set_var_base { LEX_USER *user; char *password; const char *current_password; bool retain_current_password; bool generate_password; char *str_generated_password; public: set_var_password(LEX_USER *user_arg, char *password_arg, char *current_password_arg, bool retain_current, bool generate_password); const LEX_USER *get_user(void) { return user; } bool has_generated_password(void) { return generate_password; } const char *get_generated_password(void) { return str_generated_password; } int resolve(THD *) override { return 0; } int check(THD *thd) override; int update(THD *thd) override; bool print(const THD *thd, String *str) override; /* To self-print */ ~set_var_password() override; }; /* For SET NAMES and SET CHARACTER SET */ class set_var_collation_client : public set_var_base { int set_cs_flags; const CHARSET_INFO *character_set_client; const CHARSET_INFO *character_set_results; const CHARSET_INFO *collation_connection; public: enum set_cs_flags_enum { SET_CS_NAMES = 1, SET_CS_DEFAULT = 2, SET_CS_COLLATE = 4 }; set_var_collation_client(int set_cs_flags_arg, const CHARSET_INFO *client_coll_arg, const CHARSET_INFO *connection_coll_arg, const CHARSET_INFO *result_coll_arg) : set_cs_flags(set_cs_flags_arg), character_set_client(client_coll_arg), character_set_results(result_coll_arg), collation_connection(connection_coll_arg) {} int resolve(THD *) override { return 0; } int check(THD *thd) override; int update(THD *thd) override; bool print(const THD *thd, String *str) override; /* To self-print */ }; /* optional things, have_* variables */ extern SHOW_COMP_OPTION have_profiling; extern SHOW_COMP_OPTION have_symlink, have_dlopen; extern SHOW_COMP_OPTION have_query_cache; extern SHOW_COMP_OPTION have_geometry, have_rtree_keys; extern SHOW_COMP_OPTION have_compress; extern SHOW_COMP_OPTION have_statement_timeout; /* Helper functions */ ulong get_system_variable_count(void); ulonglong get_dynamic_system_variable_hash_version(void); collation_unordered_map *get_static_system_variable_hash(void); collation_unordered_map *get_dynamic_system_variable_hash(void); extern bool get_sysvar_source(const char *name, uint length, enum enum_variable_source *source); int sql_set_variables(THD *thd, List *var_list, bool opened); bool keyring_access_test(); bool fix_delay_key_write(sys_var *self, THD *thd, enum_var_type type); sql_mode_t expand_sql_mode(sql_mode_t sql_mode, THD *thd); bool sql_mode_string_representation(THD *thd, sql_mode_t sql_mode, LEX_STRING *ls); bool sql_mode_quoted_string_representation(THD *thd, sql_mode_t sql_mode, LEX_STRING *ls); extern sys_var *Sys_autocommit_ptr; extern sys_var *Sys_gtid_next_ptr; extern sys_var *Sys_gtid_next_list_ptr; extern sys_var *Sys_gtid_purged_ptr; extern ulonglong dynamic_system_variable_hash_version; const CHARSET_INFO *get_old_charset_by_name(const char *old_name); int sys_var_init(); int sys_var_add_options(std::vector *long_options, int parse_flags); void sys_var_end(void); /* check needed privileges to perform SET PERSIST[_only] or RESET PERSIST */ bool check_priv(THD *thd, bool static_variable); #define PERSIST_ONLY_ADMIN_X509_SUBJECT "persist_only_admin_x509_subject" #define PERSISTED_GLOBALS_LOAD "persisted_globals_load" extern char *sys_var_persist_only_admin_x509_subject; #endif