summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/mysqld.cc20
-rw-r--r--sql/mysqld.h1
-rw-r--r--sql/protocol.cc1
-rw-r--r--sql/session_tracker.cc765
-rw-r--r--sql/session_tracker.h20
-rw-r--r--sql/set_var.cc31
-rw-r--r--sql/set_var.h7
-rw-r--r--sql/share/errmsg-utf8.txt30
-rw-r--r--sql/sql_class.cc4
-rw-r--r--sql/sql_class.h2
-rw-r--r--sql/sql_plugin.cc54
-rw-r--r--sql/sql_plugin.h2
-rw-r--r--sql/sql_show.cc230
-rw-r--r--sql/sql_show.h6
-rw-r--r--sql/sql_string.h4
-rw-r--r--sql/sys_vars.cc10
-rw-r--r--sql/sys_vars.ic129
17 files changed, 1167 insertions, 149 deletions
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 8fa8f01b894..b59c6c7048f 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -690,6 +690,14 @@ THD *next_global_thread(THD *thd)
}
struct system_variables global_system_variables;
+/**
+ Following is just for options parsing, used with a difference against
+ global_system_variables.
+
+ TODO: something should be done to get rid of following variables
+*/
+const char *current_dbug_option="";
+
struct system_variables max_system_variables;
struct system_status_var global_status_var;
@@ -1463,7 +1471,6 @@ my_bool plugins_are_initialized= FALSE;
#ifndef DBUG_OFF
static const char* default_dbug_option;
#endif
-const char *current_dbug_option="";
#ifdef HAVE_LIBWRAP
const char *libwrapName= NULL;
int allow_severity = LOG_INFO;
@@ -5278,6 +5285,17 @@ static int init_server_components()
}
plugins_are_initialized= TRUE; /* Don't separate from init function */
+ {
+ Session_tracker session_track_system_variables_check;
+ if (session_track_system_variables_check.
+ server_boot_verify(system_charset_info))
+ {
+ sql_print_error("The variable session_track_system_variables has "
+ "invalid values.");
+ unireg_abort(1);
+ }
+ }
+
/* we do want to exit if there are any other unknown options */
if (remaining_argc > 1)
{
diff --git a/sql/mysqld.h b/sql/mysqld.h
index 846a01a9427..68eab815564 100644
--- a/sql/mysqld.h
+++ b/sql/mysqld.h
@@ -135,6 +135,7 @@ extern my_bool lower_case_file_system;
extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
extern my_bool opt_secure_auth;
extern const char *current_dbug_option;
+extern const char *current_session_track_system_variables;
extern char* opt_secure_file_priv;
extern char* opt_secure_backup_file_priv;
extern size_t opt_secure_backup_file_priv_len;
diff --git a/sql/protocol.cc b/sql/protocol.cc
index e12c72dd988..77dedfbc7d2 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -276,7 +276,6 @@ net_send_ok(THD *thd,
/* the info field */
if (state_changed || (message && message[0]))
{
- DBUG_ASSERT(strlen(message) <= MYSQL_ERRMSG_SIZE);
store.q_net_store_data((uchar*) message, message ? strlen(message) : 0);
}
diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc
index ad9906d7159..cfbb1704318 100644
--- a/sql/session_tracker.cc
+++ b/sql/session_tracker.cc
@@ -15,6 +15,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+#include "sql_plugin.h"
#include "session_tracker.h"
#include "hash.h"
@@ -23,6 +24,7 @@
#include "sql_class.h"
#include "sql_show.h"
#include "sql_plugin.h"
+#include "set_var.h"
class Not_implemented_tracker : public State_tracker
{
@@ -40,6 +42,182 @@ public:
};
+static my_bool name_array_filler(void *ptr, void *data_ptr);
+/**
+ Session_sysvars_tracker
+
+ This is a tracker class that enables & manages the tracking of session
+ system variables. It internally maintains a hash of user supplied variable
+ references and a boolean field to store if the variable was changed by the
+ last statement.
+*/
+
+class Session_sysvars_tracker : public State_tracker
+{
+private:
+
+ struct sysvar_node_st {
+ sys_var *m_svar;
+ bool *test_load;
+ bool m_changed;
+ };
+
+ class vars_list
+ {
+ private:
+ /**
+ Registered system variables. (@@session_track_system_variables)
+ A hash to store the name of all the system variables specified by the
+ user.
+ */
+ HASH m_registered_sysvars;
+ /** Size of buffer for string representation */
+ size_t buffer_length;
+ myf m_mem_flag;
+ /**
+ If TRUE then we want to check all session variable.
+ */
+ bool track_all;
+ void init()
+ {
+ my_hash_init(&m_registered_sysvars,
+ &my_charset_bin,
+ 4, 0, 0, (my_hash_get_key) sysvars_get_key,
+ my_free, MYF(HASH_UNIQUE |
+ ((m_mem_flag & MY_THREAD_SPECIFIC) ?
+ HASH_THREAD_SPECIFIC : 0)));
+ }
+ void free_hash()
+ {
+ if (my_hash_inited(&m_registered_sysvars))
+ {
+ my_hash_free(&m_registered_sysvars);
+ }
+ }
+
+ uchar* search(const sys_var *svar)
+ {
+ return (my_hash_search(&m_registered_sysvars, (const uchar *)&svar,
+ sizeof(sys_var *)));
+ }
+
+ public:
+ vars_list() :
+ buffer_length(0)
+ {
+ m_mem_flag= current_thd ? MY_THREAD_SPECIFIC : 0;
+ init();
+ }
+
+ size_t get_buffer_length()
+ {
+ DBUG_ASSERT(buffer_length != 0); // asked earlier then should
+ return buffer_length;
+ }
+ ~vars_list()
+ {
+ /* free the allocated hash. */
+ if (my_hash_inited(&m_registered_sysvars))
+ {
+ my_hash_free(&m_registered_sysvars);
+ }
+ }
+
+ uchar* search(sysvar_node_st *node, const sys_var *svar)
+ {
+ uchar *res;
+ res= search(svar);
+ if (!res)
+ {
+ if (track_all)
+ {
+ insert(node, svar, m_mem_flag);
+ return search(svar);
+ }
+ }
+ return res;
+ }
+
+ uchar* operator[](ulong idx)
+ {
+ return my_hash_element(&m_registered_sysvars, idx);
+ }
+ bool insert(sysvar_node_st *node, const sys_var *svar, myf mem_flag);
+ void reset();
+ void copy(vars_list* from, THD *thd);
+ bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
+ const CHARSET_INFO *char_set, bool session_created);
+ bool construct_var_list(char *buf, size_t buf_len);
+ };
+ /**
+ Two objects of vars_list type are maintained to manage
+ various operations.
+ */
+ vars_list *orig_list, *tool_list;
+
+public:
+ Session_sysvars_tracker()
+ {
+ orig_list= new (std::nothrow) vars_list();
+ tool_list= new (std::nothrow) vars_list();
+ }
+
+ ~Session_sysvars_tracker()
+ {
+ if (orig_list)
+ delete orig_list;
+ if (tool_list)
+ delete tool_list;
+ }
+
+ size_t get_buffer_length()
+ {
+ return orig_list->get_buffer_length();
+ }
+ bool construct_var_list(char *buf, size_t buf_len)
+ {
+ return orig_list->construct_var_list(buf, buf_len);
+ }
+
+ /**
+ Method used to check the validity of string provided
+ for session_track_system_variables during the server
+ startup.
+ */
+ static bool server_init_check(THD *thd, const CHARSET_INFO *char_set,
+ LEX_STRING var_list)
+ {
+ vars_list dummy;
+ bool result;
+ result= dummy.parse_var_list(thd, var_list, false, char_set, false);
+ return result;
+ }
+ static bool server_init_process(THD *thd, const CHARSET_INFO *char_set,
+ LEX_STRING var_list)
+ {
+ vars_list dummy;
+ bool result;
+ result= dummy.parse_var_list(thd, var_list, false, char_set, false);
+ if (!result)
+ dummy.construct_var_list(var_list.str, var_list.length + 1);
+ return result;
+ }
+
+ void reset();
+ bool enable(THD *thd);
+ bool check(THD *thd, set_var *var);
+ bool check_str(THD *thd, LEX_STRING val);
+ bool update(THD *thd);
+ bool store(THD *thd, String *buf);
+ void mark_as_changed(THD *thd, LEX_CSTRING *tracked_item_name);
+ /* callback */
+ static uchar *sysvars_get_key(const char *entry, size_t *length,
+ my_bool not_used __attribute__((unused)));
+
+ friend my_bool name_array_filler(void *ptr, void *data_ptr);
+};
+
+
/**
Current_schema_tracker,
@@ -108,6 +286,540 @@ public:
/* To be used in expanding the buffer. */
static const unsigned int EXTRA_ALLOC= 1024;
+
+void Session_sysvars_tracker::vars_list::reset()
+{
+ buffer_length= 0;
+ track_all= 0;
+ if (m_registered_sysvars.records)
+ my_hash_reset(&m_registered_sysvars);
+}
+
+/**
+ Copy the given list.
+
+ @param from Source vars_list object.
+ @param thd THD handle to retrive the charset in use.
+
+ @retval true there is something to track
+ @retval false nothing to track
+*/
+
+void Session_sysvars_tracker::vars_list::copy(vars_list* from, THD *thd)
+{
+ reset();
+ track_all= from->track_all;
+ free_hash();
+ buffer_length= from->buffer_length;
+ m_registered_sysvars= from->m_registered_sysvars;
+ from->init();
+}
+
+/**
+ Inserts the variable to be tracked into m_registered_sysvars hash.
+
+ @param node Node to be inserted.
+ @param svar address of the system variable
+
+ @retval false success
+ @retval true error
+*/
+
+bool Session_sysvars_tracker::vars_list::insert(sysvar_node_st *node,
+ const sys_var *svar,
+ myf mem_flag)
+{
+ if (!node)
+ {
+ if (!(node= (sysvar_node_st *) my_malloc(sizeof(sysvar_node_st),
+ MYF(MY_WME | mem_flag))))
+ {
+ reset();
+ return true;
+ }
+ }
+
+ node->m_svar= (sys_var *)svar;
+ node->test_load= node->m_svar->test_load;
+ node->m_changed= false;
+ if (my_hash_insert(&m_registered_sysvars, (uchar *) node))
+ {
+ my_free(node);
+ if (!search((sys_var *)svar))
+ {
+ //EOF (error is already reported)
+ reset();
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ Parse the specified system variables list.
+
+ @Note In case of invalid entry a warning is raised per invalid entry.
+ This is done in order to handle 'potentially' valid system
+ variables from uninstalled plugins which might get installed in
+ future.
+
+
+ @param thd [IN] The thd handle.
+ @param var_list [IN] System variable list.
+ @param throw_error [IN] bool when set to true, returns an error
+ in case of invalid/duplicate values.
+ @param char_set [IN] charecter set information used for string
+ manipulations.
+ @param session_created [IN] bool variable which says if the parse is
+ already executed once. The mutex on variables
+ is not acquired if this variable is false.
+
+ @return
+ true Error
+ false Success
+*/
+bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
+ LEX_STRING var_list,
+ bool throw_error,
+ const CHARSET_INFO *char_set,
+ bool session_created)
+{
+ const char separator= ',';
+ char *token, *lasts= NULL;
+ size_t rest= var_list.length;
+
+ if (!var_list.str || var_list.length == 0)
+ {
+ buffer_length= 1;
+ return false;
+ }
+
+ if(!strcmp(var_list.str,(const char *)"*"))
+ {
+ track_all= true;
+ buffer_length= 2;
+ return false;
+ }
+
+ buffer_length= var_list.length + 1;
+ token= var_list.str;
+
+ track_all= false;
+ /*
+ If Lock to the plugin mutex is not acquired here itself, it results
+ in having to acquire it multiple times in find_sys_var_ex for each
+ token value. Hence the mutex is handled here to avoid a performance
+ overhead.
+ */
+ if (!thd || session_created)
+ mysql_mutex_lock(&LOCK_plugin);
+ for (;;)
+ {
+ sys_var *svar;
+ LEX_STRING var;
+
+ lasts= (char *) memchr(token, separator, rest);
+
+ var.str= token;
+ if (lasts)
+ {
+ var.length= (lasts - token);
+ rest-= var.length + 1;
+ }
+ else
+ var.length= rest;
+
+ /* Remove leading/trailing whitespace. */
+ trim_whitespace(char_set, &var);
+
+ if ((svar= find_sys_var_ex(thd, var.str, var.length, throw_error, true)))
+ {
+ if (insert(NULL, svar, m_mem_flag) == TRUE)
+ goto error;
+ }
+ else if (throw_error && session_created && thd)
+ {
+ push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
+ ER_WRONG_VALUE_FOR_VAR,
+ "%.*s is not a valid system variable and will"
+ "be ignored.", (int)var.length, token);
+ }
+ else
+ goto error;
+
+ if (lasts)
+ token= lasts + 1;
+ else
+ break;
+ }
+ if (!thd || session_created)
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ return false;
+
+error:
+ if (!thd || session_created)
+ mysql_mutex_unlock(&LOCK_plugin);
+ return true;
+}
+
+struct name_array_filler_data
+{
+ LEX_CSTRING **names;
+ uint idx;
+
+};
+
+/** Collects variable references into array */
+static my_bool name_array_filler(void *ptr, void *data_ptr)
+{
+ Session_sysvars_tracker::sysvar_node_st *node=
+ (Session_sysvars_tracker::sysvar_node_st *)ptr;
+ name_array_filler_data *data= (struct name_array_filler_data *)data_ptr;
+ if (*node->test_load)
+ data->names[data->idx++]= &node->m_svar->name;
+ return FALSE;
+}
+
+/* Sorts variable references array */
+static int name_array_sorter(const void *a, const void *b)
+{
+ LEX_CSTRING **an= (LEX_CSTRING **)a, **bn=(LEX_CSTRING **)b;
+ size_t min= MY_MIN((*an)->length, (*bn)->length);
+ int res= strncmp((*an)->str, (*bn)->str, min);
+ if (res == 0)
+ res= ((int)(*bn)->length)- ((int)(*an)->length);
+ return res;
+}
+
+/**
+ Construct variable list by internal hash with references
+*/
+
+bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf,
+ size_t buf_len)
+{
+ struct name_array_filler_data data;
+ size_t left= buf_len;
+ size_t names_size= m_registered_sysvars.records * sizeof(LEX_CSTRING *);
+ const char separator= ',';
+
+ if (unlikely(buf_len < 1))
+ return true;
+
+ if (unlikely(track_all))
+ {
+ if (buf_len < 2)
+ return true;
+ buf[0]= '*';
+ buf[1]= '\0';
+ return false;
+ }
+
+ if (m_registered_sysvars.records == 0)
+ {
+ buf[0]= '\0';
+ return false;
+ }
+
+ data.names= (LEX_CSTRING**)my_safe_alloca(names_size);
+
+ if (unlikely(!data.names))
+ return true;
+
+ data.idx= 0;
+
+ mysql_mutex_lock(&LOCK_plugin);
+ my_hash_iterate(&m_registered_sysvars, &name_array_filler, &data);
+ DBUG_ASSERT(data.idx <= m_registered_sysvars.records);
+
+
+ if (m_registered_sysvars.records == 0)
+ {
+ mysql_mutex_unlock(&LOCK_plugin);
+ buf[0]= '\0';
+ return false;
+ }
+
+ my_qsort(data.names, data.idx, sizeof(LEX_CSTRING *),
+ &name_array_sorter);
+
+ for(uint i= 0; i < data.idx; i++)
+ {
+ LEX_CSTRING *nm= data.names[i];
+ size_t ln= nm->length + 1;
+ if (ln > left)
+ {
+ mysql_mutex_unlock(&LOCK_plugin);
+ my_safe_afree(data.names, names_size);
+ return true;
+ }
+ memcpy(buf, nm->str, nm->length);
+ buf[nm->length]= separator;
+ buf+= ln;
+ left-= ln;
+ }
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ buf--; buf[0]= '\0';
+ my_safe_afree(data.names, names_size);
+
+ return false;
+}
+
+/**
+ Enable session tracker by parsing global value of tracked variables.
+
+ @param thd [IN] The thd handle.
+
+ @retval true Error
+ @retval false Success
+*/
+
+bool Session_sysvars_tracker::enable(THD *thd)
+{
+ sys_var *svar;
+
+ mysql_mutex_lock(&LOCK_plugin);
+ svar= find_sys_var_ex(thd, SESSION_TRACK_SYSTEM_VARIABLES_NAME.str,
+ SESSION_TRACK_SYSTEM_VARIABLES_NAME.length,
+ false, true);
+ DBUG_ASSERT(svar);
+
+ set_var tmp(thd, SHOW_OPT_GLOBAL, svar, &null_lex_str, NULL);
+ svar->session_save_default(thd, &tmp);
+
+ if (tool_list->parse_var_list(thd, tmp.save_result.string_value,
+ true, thd->charset(), false) == true)
+ {
+ mysql_mutex_unlock(&LOCK_plugin);
+ return true;
+ }
+ mysql_mutex_unlock(&LOCK_plugin);
+ orig_list->copy(tool_list, thd);
+ m_enabled= true;
+
+ return false;
+}
+
+
+/**
+ Check system variable name(s).
+
+ @note This function is called from the ON_CHECK() function of the
+ session_track_system_variables' sys_var class.
+
+ @param thd [IN] The thd handle.
+ @param var [IN] A pointer to set_var holding the specified list of
+ system variable names.
+
+ @retval true Error
+ @retval false Success
+*/
+
+inline bool Session_sysvars_tracker::check(THD *thd, set_var *var)
+{
+ return check_str(thd, var->save_result.string_value);
+}
+
+inline bool Session_sysvars_tracker::check_str(THD *thd, LEX_STRING val)
+{
+ tool_list->reset();
+ return tool_list->parse_var_list(thd, val, true,
+ thd->charset(), true);
+}
+
+
+/**
+ Once the value of the @@session_track_system_variables has been
+ successfully updated, this function calls
+ Session_sysvars_tracker::vars_list::copy updating the hash in orig_list
+ which represents the system variables to be tracked.
+
+ @note This function is called from the ON_UPDATE() function of the
+ session_track_system_variables' sys_var class.
+
+ @param thd [IN] The thd handle.
+
+ @retval true Error
+ @retval false Success
+*/
+
+bool Session_sysvars_tracker::update(THD *thd)
+{
+ orig_list->copy(tool_list, thd);
+ return false;
+}
+
+
+/**
+ Store the data for changed system variables in the specified buffer.
+ Once the data is stored, we reset the flags related to state-change
+ (see reset()).
+
+ @param thd [IN] The thd handle.
+ @paran buf [INOUT] Buffer to store the information to.
+
+ @retval true Error
+ @retval false Success
+*/
+
+bool Session_sysvars_tracker::store(THD *thd, String *buf)
+{
+ char val_buf[SHOW_VAR_FUNC_BUFF_SIZE];
+ SHOW_VAR show;
+ const char *value;
+ sysvar_node_st *node;
+ const CHARSET_INFO *charset;
+ size_t val_length, length;
+ int idx= 0;
+
+ /* As its always system variable. */
+ show.type= SHOW_SYS;
+
+ while ((node= (sysvar_node_st *) (*orig_list)[idx]))
+ {
+ if (node->m_changed)
+ {
+ mysql_mutex_lock(&LOCK_plugin);
+ if (!*node->test_load)
+ {
+ mysql_mutex_unlock(&LOCK_plugin);
+ continue;
+ }
+ sys_var *svar= node->m_svar;
+ show.name= svar->name.str;
+ show.value= (char *) svar;
+
+ value= get_one_variable(thd, &show, OPT_SESSION, SHOW_SYS, NULL,
+ &charset, val_buf, &val_length);
+ mysql_mutex_unlock(&LOCK_plugin);
+
+ length= net_length_size(svar->name.length) +
+ svar->name.length +
+ net_length_size(val_length) +
+ val_length;
+
+ compile_time_assert(SESSION_TRACK_SYSTEM_VARIABLES < 251);
+ buf->prep_alloc(1 + net_length_size(length) + length, EXTRA_ALLOC);
+
+ /* Session state type (SESSION_TRACK_SYSTEM_VARIABLES) */
+ buf->q_net_store_length((ulonglong)SESSION_TRACK_SYSTEM_VARIABLES);
+
+ /* Length of the overall entity. */
+ buf->q_net_store_length((ulonglong)length);
+
+ /* System variable's name (length-encoded string). */
+ buf->q_net_store_data((const uchar*)svar->name.str, svar->name.length);
+
+ /* System variable's value (length-encoded string). */
+ buf->q_net_store_data((const uchar*)value, val_length);
+ }
+ ++ idx;
+ }
+
+ reset();
+
+ return false;
+}
+
+
+/**
+ Mark the system variable as changed.
+
+ @param [IN] pointer on a variable
+*/
+
+void Session_sysvars_tracker::mark_as_changed(THD *thd,
+ LEX_CSTRING *var)
+{
+ sysvar_node_st *node= NULL;
+ sys_var *svar= (sys_var *)var;
+ /*
+ Check if the specified system variable is being tracked, if so
+ mark it as changed and also set the class's m_changed flag.
+ */
+ if ((node= (sysvar_node_st *) (orig_list->search(node, svar))))
+ {
+ node->m_changed= true;
+ m_changed= true;
+ /* do not cache the statement when there is change in session state */
+ thd->lex->safe_to_cache_query= 0;
+ thd->server_status|= SERVER_SESSION_STATE_CHANGED;
+ }
+}
+
+
+/**
+ Supply key to a hash.
+
+ @param entry [IN] A single entry.
+ @param length [OUT] Length of the key.
+ @param not_used Unused.
+
+ @return Pointer to the key buffer.
+*/
+
+uchar *Session_sysvars_tracker::sysvars_get_key(const char *entry,
+ size_t *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= sizeof(sys_var *);
+ return (uchar *) &(((sysvar_node_st *) entry)->m_svar);
+}
+
+
+/**
+ Prepare/reset the m_registered_sysvars hash for next statement.
+*/
+
+void Session_sysvars_tracker::reset()
+{
+ sysvar_node_st *node;
+ int idx= 0;
+
+ while ((node= (sysvar_node_st *) (*orig_list)[idx]))
+ {
+ node->m_changed= false;
+ ++ idx;
+ }
+ m_changed= false;
+}
+
+static Session_sysvars_tracker* sysvar_tracker(THD *thd)
+{
+ return (Session_sysvars_tracker*)
+ thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER);
+}
+
+bool sysvartrack_validate_value(THD *thd, const char *str, size_t len)
+{
+ LEX_STRING tmp= {(char *)str, len};
+ if (thd && sysvar_tracker(thd)->is_enabled())
+ return sysvar_tracker(thd)->check_str(thd, tmp);
+ return Session_sysvars_tracker::server_init_check(thd, system_charset_info,
+ tmp);
+}
+bool sysvartrack_reprint_value(THD *thd, char *str, size_t len)
+{
+ LEX_STRING tmp= {str, len};
+ return Session_sysvars_tracker::server_init_process(thd,
+ system_charset_info,
+ tmp);
+}
+bool sysvartrack_update(THD *thd)
+{
+ return sysvar_tracker(thd)->update(thd);
+}
+size_t sysvartrack_value_len(THD *thd)
+{
+ return sysvar_tracker(thd)->get_buffer_length();
+}
+bool sysvartrack_value_construct(THD *thd, char *val, size_t len)
+{
+ return sysvar_tracker(thd)->construct_var_list(val, len);
+}
+
///////////////////////////////////////////////////////////////////////////////
/**
@@ -282,8 +994,29 @@ bool Session_state_change_tracker::is_state_changed(THD *)
Session_tracker::Session_tracker()
{
+ for (int i= 0; i <= SESSION_TRACKER_END; i ++)
+ m_trackers[i]= NULL;
+}
+
+
+/**
+ @brief Enables the tracker objects.
+
+ @param thd [IN] The thread handle.
+
+ @return void
+*/
+
+void Session_tracker::enable(THD *thd)
+{
+ /*
+ Originally and correctly this allocation was in the constructor and
+ deallocation in the destructor, but in this case memory counting
+ system works incorrectly (for example in INSERT DELAYED thread)
+ */
+ deinit();
m_trackers[SESSION_SYSVARS_TRACKER]=
- new (std::nothrow) Not_implemented_tracker;
+ new (std::nothrow) Session_sysvars_tracker();
m_trackers[CURRENT_SCHEMA_TRACKER]=
new (std::nothrow) Current_schema_tracker;
m_trackers[SESSION_STATE_CHANGE_TRACKER]=
@@ -292,19 +1025,35 @@ Session_tracker::Session_tracker()
new (std::nothrow) Not_implemented_tracker;
m_trackers[TRANSACTION_INFO_TRACKER]=
new (std::nothrow) Not_implemented_tracker;
+
+ for (int i= 0; i <= SESSION_TRACKER_END; i ++)
+ m_trackers[i]->enable(thd);
}
-/**
- @brief Enables the tracker objects.
- @param thd [IN] The thread handle.
+/**
+ Method called during the server startup to verify the contents
+ of @@session_track_system_variables.
- @return void
+ @retval false Success
+ @retval true Failure
*/
-void Session_tracker::enable(THD *thd)
+
+bool Session_tracker::server_boot_verify(const CHARSET_INFO *char_set)
{
- for (int i= 0; i <= SESSION_TRACKER_END; i ++)
- m_trackers[i]->enable(thd);
+ Session_sysvars_tracker *server_tracker;
+ bool result;
+ sys_var *svar= find_sys_var_ex(NULL, SESSION_TRACK_SYSTEM_VARIABLES_NAME.str,
+ SESSION_TRACK_SYSTEM_VARIABLES_NAME.length,
+ false, true);
+ DBUG_ASSERT(svar);
+ set_var tmp(NULL, SHOW_OPT_GLOBAL, svar, &null_lex_str, NULL);
+ svar->session_save_default(NULL, &tmp);
+ server_tracker= new (std::nothrow) Session_sysvars_tracker();
+ result= server_tracker->server_init_check(NULL, char_set,
+ tmp.save_result.string_value);
+ delete server_tracker;
+ return result;
}
diff --git a/sql/session_tracker.h b/sql/session_tracker.h
index ec24d5a7a00..7025c34967d 100644
--- a/sql/session_tracker.h
+++ b/sql/session_tracker.h
@@ -104,6 +104,12 @@ public:
virtual void mark_as_changed(THD *thd, LEX_CSTRING *name)= 0;
};
+bool sysvartrack_validate_value(THD *thd, const char *str, size_t len);
+bool sysvartrack_reprint_value(THD *thd, char *str, size_t len);
+bool sysvartrack_update(THD *thd);
+size_t sysvartrack_value_len(THD *thd);
+bool sysvartrack_value_construct(THD *thd, char *val, size_t len);
+
/**
Session_tracker
@@ -134,10 +140,22 @@ public:
Session_tracker();
~Session_tracker()
{
+ deinit();
+ }
+
+ /* trick to make happy memory accounting system */
+ void deinit()
+ {
for (int i= 0; i <= SESSION_TRACKER_END; i ++)
- delete m_trackers[i];
+ {
+ if (m_trackers[i])
+ delete m_trackers[i];
+ m_trackers[i]= NULL;
+ }
}
+
void enable(THD *thd);
+ bool server_boot_verify(const CHARSET_INFO *char_set);
/** Returns the pointer to the tracker object for the specified tracker. */
inline State_tracker *get_tracker(enum_session_tracker tracker) const
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 68d57abcdf6..84ed7810650 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -115,6 +115,9 @@ void sys_var_end()
DBUG_VOID_RETURN;
}
+
+static bool static_test_load= TRUE;
+
/**
sys_var constructor
@@ -184,6 +187,8 @@ sys_var::sys_var(sys_var_chain *chain, const char *name_arg,
else
chain->first= this;
chain->last= this;
+
+ test_load= &static_test_load;
}
bool sys_var::update(THD *thd, set_var *var)
@@ -215,13 +220,14 @@ bool sys_var::update(THD *thd, set_var *var)
*/
if ((var->type == OPT_SESSION) && (!ret))
{
+ thd->session_tracker.mark_as_changed(thd, SESSION_SYSVARS_TRACKER,
+ (LEX_CSTRING*)var->var);
/*
Here MySQL sends variable name to avoid reporting change of
the tracker itself, but we decided that it is not needed
*/
thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER,
NULL);
-
}
return ret;
@@ -995,7 +1001,30 @@ int set_var_collation_client::update(THD *thd)
thd->update_charset(character_set_client, collation_connection,
character_set_results);
+ /* Mark client collation variables as changed */
+ if (thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->is_enabled())
+ {
+ sys_var *svar;
+ mysql_mutex_lock(&LOCK_plugin);
+ if ((svar= find_sys_var_ex(thd, "character_set_client",
+ sizeof("character_set_client") - 1,
+ false, true)))
+ thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ mark_as_changed(thd, (LEX_CSTRING*)svar);
+ if ((svar= find_sys_var_ex(thd, "character_set_results",
+ sizeof("character_set_results") - 1,
+ false, true)))
+ thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ mark_as_changed(thd, (LEX_CSTRING*)svar);
+ if ((svar= find_sys_var_ex(thd, "character_set_connection",
+ sizeof("character_set_connection") - 1,
+ false, true)))
+ thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER)->
+ mark_as_changed(thd, (LEX_CSTRING*)svar);
+ mysql_mutex_unlock(&LOCK_plugin);
+ }
thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
+
thd->protocol_text.init(thd);
thd->protocol_binary.init(thd);
return 0;
diff --git a/sql/set_var.h b/sql/set_var.h
index 6a650f2ec8a..16111ad7111 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -48,6 +48,9 @@ struct sys_var_chain
int mysql_add_sys_var_chain(sys_var *chain);
int mysql_del_sys_var_chain(sys_var *chain);
+
+extern const LEX_CSTRING SESSION_TRACK_SYSTEM_VARIABLES_NAME;
+
/**
A class representing one system variable - that is something
that can be accessed as @@global.variable_name or @@session.variable_name,
@@ -60,6 +63,7 @@ class sys_var: protected Value_source // for double_from_string_with_check
public:
sys_var *next;
LEX_CSTRING name;
+ bool *test_load;
enum flag_enum { GLOBAL, SESSION, ONLY_SESSION, SCOPE_MASK=1023,
READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096,
NO_SET_STATEMENT=8192, AUTO_SET=16384};
@@ -240,6 +244,9 @@ protected:
uchar *global_var_ptr()
{ return ((uchar*)&global_system_variables) + offset; }
+
+ friend class Session_sysvars_tracker;
+ friend class Session_tracker;
};
#include "sql_plugin.h" /* SHOW_HA_ROWS, SHOW_MY_BOOL */
diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt
index 3bb1b3a6197..4e301a9df02 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -4064,21 +4064,21 @@ ER_LOCK_OR_ACTIVE_TRANSACTION
swe "Kan inte utföra kommandot emedan du har en låst tabell eller an aktiv transaktion"
ukr "Не можу виконати подану команду тому, що таблиця заблокована або виконується транзакція"
ER_UNKNOWN_SYSTEM_VARIABLE
- cze "Neznámá systémová proměnná '%-.64s'"
- dan "Ukendt systemvariabel '%-.64s'"
- nla "Onbekende systeem variabele '%-.64s'"
- eng "Unknown system variable '%-.64s'"
- est "Tundmatu süsteemne muutuja '%-.64s'"
- fre "Variable système '%-.64s' inconnue"
- ger "Unbekannte Systemvariable '%-.64s'"
- ita "Variabile di sistema '%-.64s' sconosciuta"
- jpn "'%-.64s' は不明なシステム変数です。"
- por "Variável de sistema '%-.64s' desconhecida"
- rus "Неизвестная системная переменная '%-.64s'"
- serbian "Nepoznata sistemska promenljiva '%-.64s'"
- spa "Desconocida variable de sistema '%-.64s'"
- swe "Okänd systemvariabel: '%-.64s'"
- ukr "Невідома системна змінна '%-.64s'"
+ cze "Neznámá systémová proměnná '%-.*s'"
+ dan "Ukendt systemvariabel '%-.*s'"
+ nla "Onbekende systeem variabele '%-.*s'"
+ eng "Unknown system variable '%-.*s'"
+ est "Tundmatu süsteemne muutuja '%-.*s'"
+ fre "Variable système '%-.*s' inconnue"
+ ger "Unbekannte Systemvariable '%-.*s'"
+ ita "Variabile di sistema '%-.*s' sconosciuta"
+ jpn "'%-.*s' は不明なシステム変数です。"
+ por "Variável de sistema '%-.*s' desconhecida"
+ rus "Неизвестная системная переменная '%-.*s'"
+ serbian "Nepoznata sistemska promenljiva '%-.*s'"
+ spa "Desconocida variable de sistema '%-.*s'"
+ swe "Okänd systemvariabel: '%-.*s'"
+ ukr "Невідома системна змінна '%-.*s'"
ER_CRASHED_ON_USAGE
cze "Tabulka '%-.192s' je označena jako porušená a měla by být opravena"
dan "Tabellen '%-.192s' er markeret med fejl og bør repareres"
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index e91c80d3f36..a99e375cfbd 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1766,6 +1766,10 @@ THD::~THD()
lf_hash_put_pins(xid_hash_pins);
/* Ensure everything is freed */
status_var.local_memory_used-= sizeof(THD);
+
+ /* trick to make happy memory accounting system */
+ session_tracker.deinit();
+
if (status_var.local_memory_used != 0)
{
DBUG_PRINT("error", ("memory_used: %lld", status_var.local_memory_used));
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 7a663bf7653..a6af33f7c5a 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -691,6 +691,8 @@ typedef struct system_variables
my_bool session_track_schema;
my_bool session_track_state_change;
+
+ char *session_track_system_variables;
} SV;
/**
diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc
index 98bc5d606af..db6a4b9b15b 100644
--- a/sql/sql_plugin.cc
+++ b/sql/sql_plugin.cc
@@ -269,6 +269,7 @@ struct st_bookmark
uint name_len;
int offset;
uint version;
+ bool loaded;
char key[1];
};
@@ -322,6 +323,8 @@ static void unlock_variables(THD *thd, struct system_variables *vars);
static void cleanup_variables(struct system_variables *vars);
static void plugin_vars_free_values(sys_var *vars);
static void restore_ptr_backup(uint n, st_ptr_backup *backup);
+#define my_intern_plugin_lock(A,B) intern_plugin_lock(A,B)
+#define my_intern_plugin_lock_ci(A,B) intern_plugin_lock(A,B)
static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
static void reap_plugins(void);
@@ -1175,6 +1178,13 @@ err:
DBUG_RETURN(errs > 0 || oks + dupes == 0);
}
+static void plugin_variables_deinit(struct st_plugin_int *plugin)
+{
+
+ for (sys_var *var= plugin->system_vars; var; var= var->next)
+ (*var->test_load)= FALSE;
+ mysql_del_sys_var_chain(plugin->system_vars);
+}
static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
{
@@ -1226,8 +1236,7 @@ static void plugin_deinitialize(struct st_plugin_int *plugin, bool ref_check)
if (ref_check && plugin->ref_count)
sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.",
plugin->name.str, plugin->ref_count);
-
- mysql_del_sys_var_chain(plugin->system_vars);
+ plugin_variables_deinit(plugin);
}
static void plugin_del(struct st_plugin_int *plugin)
@@ -1447,7 +1456,7 @@ static int plugin_initialize(MEM_ROOT *tmp_root, struct st_plugin_int *plugin,
err:
if (ret)
- mysql_del_sys_var_chain(plugin->system_vars);
+ plugin_variables_deinit(plugin);
mysql_mutex_lock(&LOCK_plugin);
plugin->state= state;
@@ -2780,22 +2789,24 @@ static void update_func_double(THD *thd, struct st_mysql_sys_var *var,
System Variables support
****************************************************************************/
-
-sys_var *find_sys_var(THD *thd, const char *str, size_t length)
+sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
+ bool throw_error, bool locked)
{
sys_var *var;
sys_var_pluginvar *pi= NULL;
plugin_ref plugin;
- DBUG_ENTER("find_sys_var");
+ DBUG_ENTER("find_sys_var_ex");
+ DBUG_PRINT("enter", ("var '%.*s'", (int)length, str));
- mysql_mutex_lock(&LOCK_plugin);
+ if (!locked)
+ mysql_mutex_lock(&LOCK_plugin);
mysql_rwlock_rdlock(&LOCK_system_variables_hash);
if ((var= intern_find_sys_var(str, length)) &&
(pi= var->cast_pluginvar()))
{
mysql_rwlock_unlock(&LOCK_system_variables_hash);
LEX *lex= thd ? thd->lex : 0;
- if (!(plugin= intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin))))
+ if (!(plugin= my_intern_plugin_lock(lex, plugin_int_to_ref(pi->plugin))))
var= NULL; /* failed to lock it, it must be uninstalling */
else
if (!(plugin_state(plugin) & PLUGIN_IS_READY))
@@ -2807,14 +2818,20 @@ sys_var *find_sys_var(THD *thd, const char *str, size_t length)
}
else
mysql_rwlock_unlock(&LOCK_system_variables_hash);
- mysql_mutex_unlock(&LOCK_plugin);
+ if (!locked)
+ mysql_mutex_unlock(&LOCK_plugin);
- if (!var)
- my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str);
+ if (!throw_error && !var)
+ my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (int)length, (char*) str);
DBUG_RETURN(var);
}
+sys_var *find_sys_var(THD *thd, const char *str, size_t length)
+{
+ return find_sys_var_ex(thd, str, length, false, false);
+}
+
/*
called by register_var, construct_options and test_plugin_options.
Returns the 'bookmark' for the named variable.
@@ -3941,6 +3958,14 @@ my_bool mark_changed(int, const struct my_option *opt, char *)
}
/**
+ It is always false to mark global plugin variable unloaded just to be
+ safe because we have no way now to know truth about them.
+
+ TODO: make correct mechanism for global plugin variables
+*/
+static bool static_unload= FALSE;
+
+/**
Create and register system variables supplied from the plugin and
assigns initial values from corresponding command line arguments.
@@ -4017,9 +4042,13 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
tmp_backup[tmp->nbackups++].save(&o->name);
if ((var= find_bookmark(tmp->name.str, o->name, o->flags)))
+ {
varname= var->key + 1;
+ var->loaded= TRUE;
+ }
else
{
+ var= NULL;
len= tmp->name.length + strlen(o->name) + 2;
varname= (char*) alloc_root(mem_root, len);
strxmov(varname, tmp->name.str, "-", o->name, NullS);
@@ -4027,6 +4056,9 @@ static int test_plugin_options(MEM_ROOT *tmp_root, struct st_plugin_int *tmp,
convert_dash_to_underscore(varname, len-1);
}
v= new (mem_root) sys_var_pluginvar(&chain, varname, tmp, o);
+ v->test_load= (var ? &var->loaded : &static_unload);
+ DBUG_ASSERT(static_unload == FALSE);
+
if (!(o->flags & PLUGIN_VAR_NOCMDOPT))
{
// update app_type, used for I_S.SYSTEM_VARIABLES
diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h
index 96f8411f8ed..47c3af83bdf 100644
--- a/sql/sql_plugin.h
+++ b/sql/sql_plugin.h
@@ -192,4 +192,6 @@ extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func,
extern bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl,
plugin_foreach_func *func, void *arg);
+sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
+ bool throw_error, bool locked);
#endif
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index a446e05d427..25af6fe07cc 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -3212,6 +3212,132 @@ void remove_status_vars(SHOW_VAR *list)
}
+/**
+ @brief Returns the value of a system or a status variable.
+
+ @param thd [in] The handle of the current THD.
+ @param variable [in] Details of the variable.
+ @param value_type [in] Variable type.
+ @param show_type [in] Variable show type.
+ @param charset [out] Character set of the value.
+ @param buff [in,out] Buffer to store the value.
+ (Needs to have enough memory
+ to hold the value of variable.)
+ @param length [out] Length of the value.
+
+ @return Pointer to the value buffer.
+*/
+
+const char* get_one_variable(THD *thd,
+ const SHOW_VAR *variable,
+ enum_var_type value_type, SHOW_TYPE show_type,
+ system_status_var *status_var,
+ const CHARSET_INFO **charset, char *buff,
+ size_t *length)
+{
+ void *value= variable->value;
+ const char *pos= buff;
+ const char *end= buff;
+
+
+ if (show_type == SHOW_SYS)
+ {
+ sys_var *var= (sys_var *) value;
+ show_type= var->show_type();
+ value= var->value_ptr(thd, value_type, &null_lex_str);
+ *charset= var->charset(thd);
+ }
+
+ /*
+ note that value may be == buff. All SHOW_xxx code below
+ should still work in this case
+ */
+ switch (show_type) {
+ case SHOW_DOUBLE_STATUS:
+ value= ((char *) status_var + (intptr) value);
+ /* fall through */
+ case SHOW_DOUBLE:
+ /* 6 is the default precision for '%f' in sprintf() */
+ end= buff + my_fcvt(*(double *) value, 6, buff, NULL);
+ break;
+ case SHOW_LONG_STATUS:
+ value= ((char *) status_var + (intptr) value);
+ /* fall through */
+ case SHOW_ULONG:
+ case SHOW_LONG_NOFLUSH: // the difference lies in refresh_status()
+ end= int10_to_str(*(long*) value, buff, 10);
+ break;
+ case SHOW_LONGLONG_STATUS:
+ value= ((char *) status_var + (intptr) value);
+ /* fall through */
+ case SHOW_ULONGLONG:
+ end= longlong10_to_str(*(longlong*) value, buff, 10);
+ break;
+ case SHOW_HA_ROWS:
+ end= longlong10_to_str((longlong) *(ha_rows*) value, buff, 10);
+ break;
+ case SHOW_BOOL:
+ end= strmov(buff, *(bool*) value ? "ON" : "OFF");
+ break;
+ case SHOW_MY_BOOL:
+ end= strmov(buff, *(my_bool*) value ? "ON" : "OFF");
+ break;
+ case SHOW_UINT:
+ end= int10_to_str((long) *(uint*) value, buff, 10);
+ break;
+ case SHOW_SINT:
+ end= int10_to_str((long) *(int*) value, buff, -10);
+ break;
+ case SHOW_SLONG:
+ end= int10_to_str(*(long*) value, buff, -10);
+ break;
+ case SHOW_SLONGLONG:
+ end= longlong10_to_str(*(longlong*) value, buff, -10);
+ break;
+ case SHOW_HAVE:
+ {
+ SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value;
+ pos= show_comp_option_name[(int) tmp];
+ end= strend(pos);
+ break;
+ }
+ case SHOW_CHAR:
+ {
+ if (!(pos= (char*)value))
+ pos= "";
+ end= strend(pos);
+ break;
+ }
+ case SHOW_CHAR_PTR:
+ {
+ if (!(pos= *(char**) value))
+ pos= "";
+
+ end= strend(pos);
+ break;
+ }
+ case SHOW_LEX_STRING:
+ {
+ LEX_STRING *ls=(LEX_STRING*)value;
+ if (!(pos= ls->str))
+ end= pos= "";
+ else
+ end= pos + ls->length;
+ break;
+ }
+ case SHOW_UNDEF:
+ break; // Return empty string
+ case SHOW_SYS: // Cannot happen
+ default:
+ DBUG_ASSERT(0);
+ break;
+ }
+
+ *length= (size_t) (end - pos);
+ return pos;
+}
+
+
static bool show_status_array(THD *thd, const char *wild,
SHOW_VAR *variables,
enum enum_var_type scope,
@@ -3324,109 +3450,21 @@ static bool show_status_array(THD *thd, const char *wild,
name_buffer, wild))) &&
(!cond || cond->val_int()))
{
- void *value=var->value;
- const char *pos, *end; // We assign a lot of const's
+ const char *pos; // We assign a lot of const's
+ size_t length;
if (show_type == SHOW_SYS)
- {
- sys_var *var= (sys_var *) value;
- show_type= var->show_type();
mysql_mutex_lock(&LOCK_global_system_variables);
- value= var->value_ptr(thd, scope, &null_lex_str);
- charset= var->charset(thd);
- }
+ pos= get_one_variable(thd, var, scope, show_type, status_var,
+ &charset, buff, &length);
- pos= end= buff;
- /*
- note that value may be == buff. All SHOW_xxx code below
- should still work in this case
- */
- switch (show_type) {
- case SHOW_DOUBLE_STATUS:
- value= ((char *) status_var + (intptr) value);
- /* fall through */
- case SHOW_DOUBLE:
- /* 6 is the default precision for '%f' in sprintf() */
- end= buff + my_fcvt(*(double *) value, 6, buff, NULL);
- break;
- case SHOW_LONG_STATUS:
- value= ((char *) status_var + (intptr) value);
- /* fall through */
- case SHOW_ULONG:
- case SHOW_LONG_NOFLUSH: // the difference lies in refresh_status()
- end= int10_to_str(*(long*) value, buff, 10);
- break;
- case SHOW_LONGLONG_STATUS:
- value= ((char *) status_var + (intptr) value);
- /* fall through */
- case SHOW_ULONGLONG:
- end= longlong10_to_str(*(longlong*) value, buff, 10);
- break;
- case SHOW_HA_ROWS:
- end= longlong10_to_str((longlong) *(ha_rows*) value, buff, 10);
- break;
- case SHOW_BOOL:
- end= strmov(buff, *(bool*) value ? "ON" : "OFF");
- break;
- case SHOW_MY_BOOL:
- end= strmov(buff, *(my_bool*) value ? "ON" : "OFF");
- break;
- case SHOW_UINT:
- end= int10_to_str((long) *(uint*) value, buff, 10);
- break;
- case SHOW_SINT:
- end= int10_to_str((long) *(int*) value, buff, -10);
- break;
- case SHOW_SLONG:
- end= int10_to_str(*(long*) value, buff, -10);
- break;
- case SHOW_SLONGLONG:
- end= longlong10_to_str(*(longlong*) value, buff, -10);
- break;
- case SHOW_HAVE:
- {
- SHOW_COMP_OPTION tmp= *(SHOW_COMP_OPTION*) value;
- pos= show_comp_option_name[(int) tmp];
- end= strend(pos);
- break;
- }
- case SHOW_CHAR:
- {
- if (!(pos= (char*)value))
- pos= "";
- end= strend(pos);
- break;
- }
- case SHOW_CHAR_PTR:
- {
- if (!(pos= *(char**) value))
- pos= "";
-
- end= strend(pos);
- break;
- }
- case SHOW_LEX_STRING:
- {
- LEX_STRING *ls=(LEX_STRING*)value;
- if (!(pos= ls->str))
- end= pos= "";
- else
- end= pos + ls->length;
- break;
- }
- case SHOW_UNDEF:
- break; // Return empty string
- case SHOW_SYS: // Cannot happen
- default:
- DBUG_ASSERT(0);
- break;
- }
- table->field[1]->store(pos, (uint32) (end - pos), charset);
+ table->field[1]->store(pos, (uint32) length, charset);
+ thd->count_cuted_fields= CHECK_FIELD_IGNORE;
table->field[1]->set_notnull();
-
- if (var->type == SHOW_SYS)
+ if (show_type == SHOW_SYS)
mysql_mutex_unlock(&LOCK_global_system_variables);
+
if (schema_table_store_record(thd, table))
{
res= TRUE;
diff --git a/sql/sql_show.h b/sql/sql_show.h
index dbae2a42b39..e93b855450c 100644
--- a/sql/sql_show.h
+++ b/sql/sql_show.h
@@ -131,6 +131,12 @@ bool get_schema_tables_result(JOIN *join,
enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list);
+const char* get_one_variable(THD *thd, const SHOW_VAR *variable,
+ enum_var_type value_type, SHOW_TYPE show_type,
+ system_status_var *status_var,
+ const CHARSET_INFO **charset, char *buff,
+ size_t *length);
+
/* These functions were under INNODB_COMPATIBILITY_HOOKS */
int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
THD *find_thread_by_id(longlong id, bool query_id= false);
diff --git a/sql/sql_string.h b/sql/sql_string.h
index 10f3c4aee43..feab8070cd2 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -359,7 +359,9 @@ public:
if (ALIGN_SIZE(arg_length+1) < Alloced_length)
{
char *new_ptr;
- if (!(new_ptr=(char*) my_realloc(Ptr,arg_length,MYF(0))))
+ if (!(new_ptr=(char*)
+ my_realloc(Ptr, arg_length,MYF((thread_specific ?
+ MY_THREAD_SPECIFIC : 0)))))
{
Alloced_length = 0;
real_alloc(arg_length);
diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc
index ea9f1d14eee..68be0cdbdca 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -5375,6 +5375,16 @@ static Sys_var_ulong Sys_log_tc_size(
BLOCK_SIZE(my_getpagesize()));
#endif
+const LEX_CSTRING SESSION_TRACK_SYSTEM_VARIABLES_NAME=
+ {STRING_WITH_LEN("session_track_system_variables")};
+
+static Sys_var_sesvartrack Sys_track_session_sys_vars(
+ SESSION_TRACK_SYSTEM_VARIABLES_NAME.str,
+ "Track changes in registered system variables.",
+ CMD_LINE(REQUIRED_ARG), IN_SYSTEM_CHARSET,
+ DEFAULT("autocommit,character_set_client,character_set_connection,"
+ "character_set_results,time_zone"),
+ NO_MUTEX_GUARD);
static bool update_session_track_schema(sys_var *self, THD *thd,
enum_var_type type)
diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic
index ca6634849a1..dbe84d3efcc 100644
--- a/sql/sys_vars.ic
+++ b/sql/sys_vars.ic
@@ -438,10 +438,10 @@ public:
does not destroy individual members of SV, there's no way to free
allocated string variables for every thread.
*/
-class Sys_var_charptr: public sys_var
+class Sys_var_charptr_base: public sys_var
{
public:
- Sys_var_charptr(const char *name_arg,
+ Sys_var_charptr_base(const char *name_arg,
const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt,
enum charset_enum is_os_charset_arg,
@@ -463,8 +463,6 @@ public:
*/
option.var_type|= (flags & ALLOCATED) ? GET_STR_ALLOC : GET_STR;
global_var(const char*)= def_val;
- SYSVAR_ASSERT(scope() == GLOBAL);
- SYSVAR_ASSERT(size == sizeof(char *));
}
void cleanup()
{
@@ -503,31 +501,35 @@ public:
}
bool do_check(THD *thd, set_var *var)
{ return do_string_check(thd, var, charset(thd)); }
- bool session_update(THD *thd, set_var *var)
- {
- DBUG_ASSERT(FALSE);
- return true;
- }
- bool global_update(THD *thd, set_var *var)
+ bool session_update(THD *thd, set_var *var)= 0;
+ char *global_update_prepare(THD *thd, set_var *var)
{
char *new_val, *ptr= var->save_result.string_value.str;
size_t len=var->save_result.string_value.length;
if (ptr)
{
new_val= (char*)my_memdup(ptr, len+1, MYF(MY_WME));
- if (!new_val) return true;
+ if (!new_val) return 0;
new_val[len]=0;
}
else
new_val= 0;
+ return new_val;
+ }
+ void global_update_finish(char *new_val)
+ {
if (flags & ALLOCATED)
my_free(global_var(char*));
flags|= ALLOCATED;
global_var(char*)= new_val;
- return false;
}
- void session_save_default(THD *thd, set_var *var)
- { DBUG_ASSERT(FALSE); }
+ bool global_update(THD *thd, set_var *var)
+ {
+ char *new_val= global_update_prepare(thd, var);
+ global_update_finish(new_val);
+ return (new_val == 0 && var->save_result.string_value.str != 0);
+ }
+ void session_save_default(THD *thd, set_var *var)= 0;
void global_save_default(THD *thd, set_var *var)
{
char *ptr= (char*)(intptr)option.def_value;
@@ -536,6 +538,105 @@ public:
}
};
+class Sys_var_charptr: public Sys_var_charptr_base
+{
+public:
+ Sys_var_charptr(const char *name_arg,
+ const char *comment, int flag_args, ptrdiff_t off, size_t size,
+ CMD_LINE getopt,
+ enum charset_enum is_os_charset_arg,
+ const char *def_val, PolyLock *lock=0,
+ enum binlog_status_enum binlog_status_arg=VARIABLE_NOT_IN_BINLOG,
+ on_check_function on_check_func=0,
+ on_update_function on_update_func=0,
+ const char *substitute=0) :
+ Sys_var_charptr_base(name_arg, comment, flag_args, off, size, getopt,
+ is_os_charset_arg, def_val, lock, binlog_status_arg,
+ on_check_func, on_update_func, substitute)
+ {
+ SYSVAR_ASSERT(scope() == GLOBAL);
+ SYSVAR_ASSERT(size == sizeof(char *));
+ }
+
+ bool session_update(THD *thd, set_var *var)
+ {
+ DBUG_ASSERT(FALSE);
+ return true;
+ }
+ void session_save_default(THD *thd, set_var *var)
+ { DBUG_ASSERT(FALSE); }
+};
+
+class Sys_var_sesvartrack: public Sys_var_charptr_base
+{
+public:
+ Sys_var_sesvartrack(const char *name_arg,
+ const char *comment,
+ CMD_LINE getopt,
+ enum charset_enum is_os_charset_arg,
+ const char *def_val, PolyLock *lock) :
+ Sys_var_charptr_base(name_arg, comment,
+ SESSION_VAR(session_track_system_variables),
+ getopt, is_os_charset_arg, def_val, lock,
+ VARIABLE_NOT_IN_BINLOG, 0, 0, 0)
+ {}
+ bool do_check(THD *thd, set_var *var)
+ {
+ if (Sys_var_charptr_base::do_check(thd, var) ||
+ sysvartrack_validate_value(thd, var->save_result.string_value.str,
+ var->save_result.string_value.length))
+ return TRUE;
+ return FALSE;
+ }
+ bool global_update(THD *thd, set_var *var)
+ {
+ char *new_val= global_update_prepare(thd, var);
+ if (new_val)
+ {
+ if (sysvartrack_reprint_value(thd, new_val,
+ var->save_result.string_value.length))
+ new_val= 0;
+ }
+ global_update_finish(new_val);
+ return (new_val == 0 && var->save_result.string_value.str != 0);
+ }
+ bool session_update(THD *thd, set_var *var)
+ {
+ return sysvartrack_update(thd);
+ }
+ void session_save_default(THD *thd, set_var *var)
+ {
+ var->save_result.string_value.str= global_var(char*);
+ var->save_result.string_value.length=
+ strlen(var->save_result.string_value.str);
+ /* parse and feel list with default values */
+ if (thd)
+ {
+ bool res=
+ sysvartrack_validate_value(thd,
+ var->save_result.string_value.str,
+ var->save_result.string_value.length);
+ DBUG_ASSERT(res == 0);
+ }
+ }
+ uchar *session_value_ptr(THD *thd, const LEX_STRING *base)
+ {
+ DBUG_ASSERT(thd != NULL);
+ size_t len= sysvartrack_value_len(thd);
+ char *res= 0;
+ char *buf= (char *)my_safe_alloca(len);
+ if (buf && !sysvartrack_value_construct(thd, buf, len))
+ {
+ size_t len= strlen(buf) + 1;
+ res= (char*) thd->alloc(len + sizeof(char *));
+ if (res)
+ memcpy((*((char**) res)= res + sizeof(char *)), buf, len);
+ my_safe_afree(buf, len);
+ }
+ return (uchar *)res;
+ }
+};
+
class Sys_var_proxy_user: public sys_var
{