[Commits] 3611fdf: # This is a combination of 2 commits.

Oleksandr Byelkin sanja at mariadb.com
Thu May 26 20:15:50 EEST 2016


revision-id: 3611fdf3754feeff5e68cdff5433f38077d63882 (mariadb-10.2.0-44-g3611fdf)
parent(s): 4cf74d8976c18e3a871ce3696f20ab6a08a77ba2
committer: Oleksandr Byelkin
timestamp: 2016-05-26 19:15:50 +0200
message:

# This is a combination of 2 commits.
# The first commit's message is:

MDEV-8931: (server part of) session state tracking

System variables tracking

# The 2nd commit message will be skipped:

#	Fix of global value

---
 include/mysql_com.h                                |   8 +-
 mysql-test/r/mysqld--help.result                   |   3 +
 .../r/session_track_system_variables_basic.result  | 162 +++++
 .../sys_vars/r/sysvars_server_notembedded.result   |  14 +
 .../t/session_track_system_variables_basic.test    | 133 ++++
 sql/mysqld.cc                                      |  20 +-
 sql/mysqld.h                                       |   1 +
 sql/session_tracker.cc                             | 741 ++++++++++++++++++++-
 sql/session_tracker.h                              |   7 +
 sql/set_var.cc                                     |  31 +-
 sql/set_var.h                                      |   7 +
 sql/share/errmsg-utf8.txt                          |  30 +-
 sql/sql_class.h                                    |   2 +
 sql/sql_plugin.cc                                  |  54 +-
 sql/sql_plugin.h                                   |   2 +
 sql/sql_show.cc                                    | 230 ++++---
 sql/sql_show.h                                     |   6 +
 sql/sql_string.h                                   |   4 +-
 sql/sys_vars.cc                                    |  10 +
 sql/sys_vars.ic                                    | 129 +++-
 20 files changed, 1450 insertions(+), 144 deletions(-)

diff --git a/include/mysql_com.h b/include/mysql_com.h
index c6608b7..954c173 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -285,7 +285,7 @@ enum enum_server_command
                            CLIENT_MULTI_RESULTS | \
                            CLIENT_PS_MULTI_RESULTS | \
                            CLIENT_SSL_VERIFY_SERVER_CERT | \
-                            CLIENT_REMEMBER_OPTIONS | \
+                           CLIENT_REMEMBER_OPTIONS | \
                            MARIADB_CLIENT_PROGRESS | \
                            CLIENT_PLUGIN_AUTH | \
                            CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \
@@ -553,9 +553,9 @@ enum enum_mysql_set_option
 */
 enum enum_session_state_type
 {
-  SESSION_TRACK_SYSTEM_VARIABLES,                       /* Session system variables */
-  SESSION_TRACK_SCHEMA,                          /* Current schema */
-  SESSION_TRACK_STATE_CHANGE,                  /* track session state changes */
+  SESSION_TRACK_SYSTEM_VARIABLES,             /* Session system variables */
+  SESSION_TRACK_SCHEMA,                       /* Current schema */
+  SESSION_TRACK_STATE_CHANGE,                 /* track session state changes */
   SESSION_TRACK_GTIDS,
   SESSION_TRACK_TRANSACTION_CHARACTERISTICS,  /* Transaction chistics */
   SESSION_TRACK_TRANSACTION_STATE             /* Transaction state */
diff --git a/mysql-test/r/mysqld--help.result b/mysql-test/r/mysqld--help.result
index 487ccd3..6a8a4e8 100644
--- a/mysql-test/r/mysqld--help.result
+++ b/mysql-test/r/mysqld--help.result
@@ -908,6 +908,8 @@ The following options may be given as the first argument:
  (Defaults to on; use --skip-session-track-schema to disable.)
  --session-track-state-change 
  Track changes to the 'session state'.
+ --session-track-system-variables=name 
+ Track changes in registered system variables.
  --show-slave-auth-info 
  Show user and password in SHOW SLAVE HOSTS on this
  master.
@@ -1394,6 +1396,7 @@ secure-file-priv (No default value)
 server-id 0
 session-track-schema TRUE
 session-track-state-change FALSE
+session-track-system-variables autocommit,character_set_client,character_set_connection,character_set_results,time_zone
 show-slave-auth-info FALSE
 silent-startup FALSE
 skip-grant-tables TRUE
diff --git a/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result b/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result
new file mode 100644
index 0000000..e451a22
--- /dev/null
+++ b/mysql-test/suite/sys_vars/r/session_track_system_variables_basic.result
@@ -0,0 +1,162 @@
+#
+# Variable name : session_track_system_variables
+# Scope         : Global & Session
+#
+# Global - default
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+# Session - default
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+
+# via INFORMATION_SCHEMA.GLOBAL_VARIABLES
+SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME;
+VARIABLE_NAME	VARIABLE_VALUE
+SESSION_TRACK_SCHEMA	ON
+SESSION_TRACK_STATE_CHANGE	OFF
+SESSION_TRACK_SYSTEM_VARIABLES	autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+# via INFORMATION_SCHEMA.SESSION_VARIABLES
+SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME;
+VARIABLE_NAME	VARIABLE_VALUE
+SESSION_TRACK_SCHEMA	ON
+SESSION_TRACK_STATE_CHANGE	OFF
+SESSION_TRACK_SYSTEM_VARIABLES	autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+SET @global_saved_tmp =  @@global.session_track_system_variables;
+
+# Altering global variable's value
+SET @@global.session_track_system_variables='autocommit';
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+autocommit
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+
+# Altering session variable's value
+SET @@session.session_track_system_variables='autocommit';
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+autocommit
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+autocommit
+
+# Variables' values in a new session.
+connect  con1,"127.0.0.1",root,,test,$MASTER_MYPORT,;
+# Global - expect "autocommit"
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+autocommit
+
+# Session - expect "autocommit"
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+autocommit
+
+# Switching to the default connection.
+connection default;
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+autocommit
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+autocommit
+
+# Test if DEFAULT is working as expected.
+SET @@global.session_track_system_variables = DEFAULT;
+SET @@session.session_track_system_variables = DEFAULT;
+
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+
+# Variables' values in a new session (con2).
+connect  con2,"127.0.0.1",root,,test,$MASTER_MYPORT,;
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+
+# Altering session should not affect global.
+SET @@session.session_track_system_variables = 'sql_mode';
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+sql_mode
+
+# Variables' values in a new session (con3).
+connect  con3,"127.0.0.1",root,,test,$MASTER_MYPORT,;
+# Altering global should not affect session.
+SET @@global.session_track_system_variables = 'sql_mode';
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+sql_mode
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+
+# Switching to the default connection.
+connection default;
+# Testing NULL
+SET @@global.session_track_system_variables = NULL;
+SET @@session.session_track_system_variables = NULL;
+# Global - expect "" instead of NULL
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+NULL
+# Session - expect "" instead of NULL
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+
+# testing with duplicate entries.
+SET @@global.session_track_system_variables= "time_zone";
+SET @@session.session_track_system_variables= "time_zone";
+SET @@global.session_track_system_variables= "sql_mode,sql_mode";
+SET @@session.session_track_system_variables= "sql_mode,sql_mode";
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+sql_mode
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+sql_mode
+
+# testing ordering
+SET @@global.session_track_system_variables= "time_zone,sql_mode";
+SET @@session.session_track_system_variables= "time_zone,sql_mode";
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+sql_mode,time_zone
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+sql_mode,time_zone
+
+# special values
+SET @@global.session_track_system_variables= "*";
+SET @@session.session_track_system_variables= "*";
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+*
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+*
+SET @@global.session_track_system_variables= "";
+SET @@session.session_track_system_variables= "";
+SELECT @@global.session_track_system_variables;
+@@global.session_track_system_variables
+
+SELECT @@session.session_track_system_variables;
+@@session.session_track_system_variables
+
+
+# Restoring the original values.
+SET @@global.session_track_system_variables = @global_saved_tmp;
+# End of tests.
diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
index a7a265f..89999a3 100644
--- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
+++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result
@@ -3803,6 +3803,20 @@ NUMERIC_BLOCK_SIZE	NULL
 ENUM_VALUE_LIST	OFF,ON
 READ_ONLY	NO
 COMMAND_LINE_ARGUMENT	OPTIONAL
+VARIABLE_NAME	SESSION_TRACK_SYSTEM_VARIABLES
+SESSION_VALUE	autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+GLOBAL_VALUE	autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+GLOBAL_VALUE_ORIGIN	COMPILE-TIME
+DEFAULT_VALUE	autocommit,character_set_client,character_set_connection,character_set_results,time_zone
+VARIABLE_SCOPE	SESSION
+VARIABLE_TYPE	VARCHAR
+VARIABLE_COMMENT	Track changes in registered system variables.
+NUMERIC_MIN_VALUE	NULL
+NUMERIC_MAX_VALUE	NULL
+NUMERIC_BLOCK_SIZE	NULL
+ENUM_VALUE_LIST	NULL
+READ_ONLY	NO
+COMMAND_LINE_ARGUMENT	REQUIRED
 VARIABLE_NAME	SKIP_EXTERNAL_LOCKING
 SESSION_VALUE	NULL
 GLOBAL_VALUE	ON
diff --git a/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test b/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test
new file mode 100644
index 0000000..bbb32bb
--- /dev/null
+++ b/mysql-test/suite/sys_vars/t/session_track_system_variables_basic.test
@@ -0,0 +1,133 @@
+--source include/not_embedded.inc
+
+--echo #
+--echo # Variable name : session_track_system_variables
+--echo # Scope         : Global & Session
+--echo #
+
+--echo # Global - default
+SELECT @@global.session_track_system_variables;
+--echo # Session - default
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # via INFORMATION_SCHEMA.GLOBAL_VARIABLES
+--disable_warnings
+SELECT * FROM INFORMATION_SCHEMA.GLOBAL_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME;
+--enable_warnings
+
+--echo # via INFORMATION_SCHEMA.SESSION_VARIABLES
+--disable_warnings
+SELECT * FROM INFORMATION_SCHEMA.SESSION_VARIABLES WHERE VARIABLE_NAME LIKE 'session_track%' ORDER BY VARIABLE_NAME;
+--enable_warnings
+
+# Save the global value to be used to restore the original value.
+SET @global_saved_tmp =  @@global.session_track_system_variables;
+--echo
+
+--echo # Altering global variable's value
+SET @@global.session_track_system_variables='autocommit';
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # Altering session variable's value
+SET @@session.session_track_system_variables='autocommit';
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # Variables' values in a new session.
+connect (con1,"127.0.0.1",root,,test,$MASTER_MYPORT,);
+
+--echo # Global - expect "autocommit"
+SELECT @@global.session_track_system_variables;
+--echo
+--echo # Session - expect "autocommit"
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # Switching to the default connection.
+connection default;
+
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # Test if DEFAULT is working as expected.
+SET @@global.session_track_system_variables = DEFAULT;
+SET @@session.session_track_system_variables = DEFAULT;
+--echo
+
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # Variables' values in a new session (con2).
+connect (con2,"127.0.0.1",root,,test,$MASTER_MYPORT,);
+
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # Altering session should not affect global.
+SET @@session.session_track_system_variables = 'sql_mode';
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # Variables' values in a new session (con3).
+connect (con3,"127.0.0.1",root,,test,$MASTER_MYPORT,);
+
+--echo # Altering global should not affect session.
+SET @@global.session_track_system_variables = 'sql_mode';
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # Switching to the default connection.
+connection default;
+
+--echo # Testing NULL
+SET @@global.session_track_system_variables = NULL;
+SET @@session.session_track_system_variables = NULL;
+
+--echo # Global - expect "" instead of NULL
+SELECT @@global.session_track_system_variables;
+--echo # Session - expect "" instead of NULL
+SELECT @@session.session_track_system_variables;
+
+--echo # testing with duplicate entries.
+# Lets first set it to some valid value.
+SET @@global.session_track_system_variables= "time_zone";
+SET @@session.session_track_system_variables= "time_zone";
+# Now set with duplicate entries (must pass)
+SET @@global.session_track_system_variables= "sql_mode,sql_mode";
+SET @@session.session_track_system_variables= "sql_mode,sql_mode";
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # testing ordering
+SET @@global.session_track_system_variables= "time_zone,sql_mode";
+SET @@session.session_track_system_variables= "time_zone,sql_mode";
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+--echo
+
+--echo # special values
+SET @@global.session_track_system_variables= "*";
+SET @@session.session_track_system_variables= "*";
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+SET @@global.session_track_system_variables= "";
+SET @@session.session_track_system_variables= "";
+SELECT @@global.session_track_system_variables;
+SELECT @@session.session_track_system_variables;
+--echo
+
+
+--echo # Restoring the original values.
+SET @@global.session_track_system_variables = @global_saved_tmp;
+
+--echo # End of tests.
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 845d114..e3aba86 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -691,6 +691,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;
 
@@ -1464,7 +1472,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;
@@ -5313,6 +5320,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 e538cbd..4173752 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/session_tracker.cc b/sql/session_tracker.cc
index ad9906d..7d3b90e 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 @@ class Not_implemented_tracker : public State_tracker
 
 };
 
+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,539 @@ class Session_state_change_tracker : public State_tracker
 /* 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_lock(&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_lock(&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;
+  }
+}
+
+
+/**
+  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);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
@@ -283,7 +994,7 @@ bool Session_state_change_tracker::is_state_changed(THD *)
 Session_tracker::Session_tracker()
 {
   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]=
@@ -294,6 +1005,7 @@ Session_tracker::Session_tracker()
     new (std::nothrow) Not_implemented_tracker;
 }
 
+
 /**
   @brief Enables the tracker objects.
 
@@ -301,6 +1013,7 @@ Session_tracker::Session_tracker()
 
   @return            void
 */
+
 void Session_tracker::enable(THD *thd)
 {
   for (int i= 0; i <= SESSION_TRACKER_END; i ++)
@@ -309,6 +1022,32 @@ void Session_tracker::enable(THD *thd)
 
 
 /**
+  Method called during the server startup to verify the contents
+  of @@session_track_system_variables.
+
+  @retval false Success
+  @retval true  Failure
+*/
+
+bool Session_tracker::server_boot_verify(const CHARSET_INFO *char_set)
+{
+  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;
+}
+
+
+/**
   @brief Store all change information in the specified buffer.
 
   @param thd [IN]           The thd handle.
diff --git a/sql/session_tracker.h b/sql/session_tracker.h
index ec24d5a..f6a3f89 100644
--- a/sql/session_tracker.h
+++ b/sql/session_tracker.h
@@ -104,6 +104,12 @@ class State_tracker
   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
@@ -138,6 +144,7 @@ class Session_tracker
       delete m_trackers[i];
   }
   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 8e7f723..5f2bc93 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;
@@ -997,7 +1003,30 @@ int set_var_collation_client::update(THD *thd)
   thd->variables.collation_connection= collation_connection;
   thd->update_charset();
 
+  /* 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 6a650f2..16111ad 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 @@ class sys_var: protected Value_source // for double_from_string_with_check
 
   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 14ccc81..5af3875 100644
--- a/sql/share/errmsg-utf8.txt
+++ b/sql/share/errmsg-utf8.txt
@@ -4066,21 +4066,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.h b/sql/sql_class.h
index 26b7eda..bd289fd 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -684,6 +684,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 d916eb3..0342a88 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 @@ static bool plugin_add(MEM_ROOT *tmp_root,
   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 f9de682..7ce6661 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 f41fb39..b6ed6e6 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -3160,6 +3160,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,
@@ -3272,109 +3398,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 dbae2a4..e93b855 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 10f3c4a..feab807 100644
--- a/sql/sql_string.h
+++ b/sql/sql_string.h
@@ -359,7 +359,9 @@ class String
     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 447449c..9e58643 100644
--- a/sql/sys_vars.cc
+++ b/sql/sys_vars.cc
@@ -5366,6 +5366,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 2488e80..45ab6b2 100644
--- a/sql/sys_vars.ic
+++ b/sql/sys_vars.ic
@@ -437,10 +437,10 @@ class Sys_var_mybool: public Sys_var_typelib
   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,
@@ -462,8 +462,6 @@ class Sys_var_charptr: public sys_var
     */
     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()
   {
@@ -502,31 +500,35 @@ class Sys_var_charptr: public sys_var
   }
   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;
@@ -535,6 +537,105 @@ class Sys_var_charptr: public sys_var
   }
 };
 
+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
 {


More information about the commits mailing list