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

System variables tracking
This commit is contained in:
Oleksandr Byelkin 2016-04-15 20:47:45 +02:00
parent e7608a78ef
commit c8948b0d0d
22 changed files with 1486 additions and 156 deletions

View File

@ -288,7 +288,7 @@ enum enum_server_command
CLIENT_MULTI_RESULTS | \ CLIENT_MULTI_RESULTS | \
CLIENT_PS_MULTI_RESULTS | \ CLIENT_PS_MULTI_RESULTS | \
CLIENT_SSL_VERIFY_SERVER_CERT | \ CLIENT_SSL_VERIFY_SERVER_CERT | \
CLIENT_REMEMBER_OPTIONS | \ CLIENT_REMEMBER_OPTIONS | \
MARIADB_CLIENT_PROGRESS | \ MARIADB_CLIENT_PROGRESS | \
CLIENT_PLUGIN_AUTH | \ CLIENT_PLUGIN_AUTH | \
CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \ CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA | \
@ -556,9 +556,9 @@ enum enum_mysql_set_option
*/ */
enum enum_session_state_type enum enum_session_state_type
{ {
SESSION_TRACK_SYSTEM_VARIABLES, /* Session system variables */ SESSION_TRACK_SYSTEM_VARIABLES, /* Session system variables */
SESSION_TRACK_SCHEMA, /* Current schema */ SESSION_TRACK_SCHEMA, /* Current schema */
SESSION_TRACK_STATE_CHANGE, /* track session state changes */ SESSION_TRACK_STATE_CHANGE, /* track session state changes */
SESSION_TRACK_GTIDS, SESSION_TRACK_GTIDS,
SESSION_TRACK_TRANSACTION_CHARACTERISTICS, /* Transaction chistics */ SESSION_TRACK_TRANSACTION_CHARACTERISTICS, /* Transaction chistics */
SESSION_TRACK_TRANSACTION_STATE /* Transaction state */ SESSION_TRACK_TRANSACTION_STATE /* Transaction state */

View File

@ -908,6 +908,8 @@ The following options may be given as the first argument:
(Defaults to on; use --skip-session-track-schema to disable.) (Defaults to on; use --skip-session-track-schema to disable.)
--session-track-state-change --session-track-state-change
Track changes to the 'session state'. Track changes to the 'session state'.
--session-track-system-variables=name
Track changes in registered system variables.
--show-slave-auth-info --show-slave-auth-info
Show user and password in SHOW SLAVE HOSTS on this Show user and password in SHOW SLAVE HOSTS on this
master. master.
@ -1392,6 +1394,7 @@ secure-file-priv (No default value)
server-id 1 server-id 1
session-track-schema TRUE session-track-schema TRUE
session-track-state-change FALSE 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 show-slave-auth-info FALSE
silent-startup FALSE silent-startup FALSE
skip-grant-tables TRUE skip-grant-tables TRUE

View File

@ -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.

View File

@ -3817,6 +3817,20 @@ NUMERIC_BLOCK_SIZE NULL
ENUM_VALUE_LIST OFF,ON ENUM_VALUE_LIST OFF,ON
READ_ONLY NO READ_ONLY NO
COMMAND_LINE_ARGUMENT OPTIONAL 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 VARIABLE_NAME SKIP_EXTERNAL_LOCKING
SESSION_VALUE NULL SESSION_VALUE NULL
GLOBAL_VALUE ON GLOBAL_VALUE ON

View File

@ -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.

View File

@ -690,6 +690,14 @@ THD *next_global_thread(THD *thd)
} }
struct system_variables global_system_variables; 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_variables max_system_variables;
struct system_status_var global_status_var; struct system_status_var global_status_var;
@ -1463,7 +1471,6 @@ my_bool plugins_are_initialized= FALSE;
#ifndef DBUG_OFF #ifndef DBUG_OFF
static const char* default_dbug_option; static const char* default_dbug_option;
#endif #endif
const char *current_dbug_option="";
#ifdef HAVE_LIBWRAP #ifdef HAVE_LIBWRAP
const char *libwrapName= NULL; const char *libwrapName= NULL;
int allow_severity = LOG_INFO; int allow_severity = LOG_INFO;
@ -5278,6 +5285,17 @@ static int init_server_components()
} }
plugins_are_initialized= TRUE; /* Don't separate from init function */ 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 */ /* we do want to exit if there are any other unknown options */
if (remaining_argc > 1) if (remaining_argc > 1)
{ {

View File

@ -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_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs;
extern my_bool opt_secure_auth; extern my_bool opt_secure_auth;
extern const char *current_dbug_option; extern const char *current_dbug_option;
extern const char *current_session_track_system_variables;
extern char* opt_secure_file_priv; extern char* opt_secure_file_priv;
extern char* opt_secure_backup_file_priv; extern char* opt_secure_backup_file_priv;
extern size_t opt_secure_backup_file_priv_len; extern size_t opt_secure_backup_file_priv_len;

View File

@ -276,7 +276,6 @@ net_send_ok(THD *thd,
/* the info field */ /* the info field */
if (state_changed || (message && message[0])) if (state_changed || (message && message[0]))
{ {
DBUG_ASSERT(strlen(message) <= MYSQL_ERRMSG_SIZE);
store.q_net_store_data((uchar*) message, message ? strlen(message) : 0); store.q_net_store_data((uchar*) message, message ? strlen(message) : 0);
} }

View File

@ -15,6 +15,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "sql_plugin.h"
#include "session_tracker.h" #include "session_tracker.h"
#include "hash.h" #include "hash.h"
@ -23,6 +24,7 @@
#include "sql_class.h" #include "sql_class.h"
#include "sql_show.h" #include "sql_show.h"
#include "sql_plugin.h" #include "sql_plugin.h"
#include "set_var.h"
class Not_implemented_tracker : public State_tracker 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, Current_schema_tracker,
@ -108,6 +286,540 @@ public:
/* To be used in expanding the buffer. */ /* To be used in expanding the buffer. */
static const unsigned int EXTRA_ALLOC= 1024; 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,18 +994,11 @@ bool Session_state_change_tracker::is_state_changed(THD *)
Session_tracker::Session_tracker() Session_tracker::Session_tracker()
{ {
m_trackers[SESSION_SYSVARS_TRACKER]= for (int i= 0; i <= SESSION_TRACKER_END; i ++)
new (std::nothrow) Not_implemented_tracker; m_trackers[i]= NULL;
m_trackers[CURRENT_SCHEMA_TRACKER]=
new (std::nothrow) Current_schema_tracker;
m_trackers[SESSION_STATE_CHANGE_TRACKER]=
new (std::nothrow) Session_state_change_tracker;
m_trackers[SESSION_GTIDS_TRACKER]=
new (std::nothrow) Not_implemented_tracker;
m_trackers[TRANSACTION_INFO_TRACKER]=
new (std::nothrow) Not_implemented_tracker;
} }
/** /**
@brief Enables the tracker objects. @brief Enables the tracker objects.
@ -301,13 +1006,57 @@ Session_tracker::Session_tracker()
@return void @return void
*/ */
void Session_tracker::enable(THD *thd) 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) Session_sysvars_tracker();
m_trackers[CURRENT_SCHEMA_TRACKER]=
new (std::nothrow) Current_schema_tracker;
m_trackers[SESSION_STATE_CHANGE_TRACKER]=
new (std::nothrow) Session_state_change_tracker;
m_trackers[SESSION_GTIDS_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 ++) for (int i= 0; i <= SESSION_TRACKER_END; i ++)
m_trackers[i]->enable(thd); m_trackers[i]->enable(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. @brief Store all change information in the specified buffer.

View File

@ -104,6 +104,12 @@ public:
virtual void mark_as_changed(THD *thd, LEX_CSTRING *name)= 0; 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 Session_tracker
@ -134,10 +140,22 @@ public:
Session_tracker(); Session_tracker();
~Session_tracker() ~Session_tracker()
{ {
for (int i= 0; i <= SESSION_TRACKER_END; i ++) deinit();
delete m_trackers[i];
} }
/* trick to make happy memory accounting system */
void deinit()
{
for (int i= 0; i <= SESSION_TRACKER_END; i ++)
{
if (m_trackers[i])
delete m_trackers[i];
m_trackers[i]= NULL;
}
}
void enable(THD *thd); void enable(THD *thd);
bool server_boot_verify(const CHARSET_INFO *char_set);
/** Returns the pointer to the tracker object for the specified tracker. */ /** Returns the pointer to the tracker object for the specified tracker. */
inline State_tracker *get_tracker(enum_session_tracker tracker) const inline State_tracker *get_tracker(enum_session_tracker tracker) const

View File

@ -115,6 +115,9 @@ void sys_var_end()
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
static bool static_test_load= TRUE;
/** /**
sys_var constructor sys_var constructor
@ -184,6 +187,8 @@ sys_var::sys_var(sys_var_chain *chain, const char *name_arg,
else else
chain->first= this; chain->first= this;
chain->last= this; chain->last= this;
test_load= &static_test_load;
} }
bool sys_var::update(THD *thd, set_var *var) 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)) 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 Here MySQL sends variable name to avoid reporting change of
the tracker itself, but we decided that it is not needed the tracker itself, but we decided that it is not needed
*/ */
thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, thd->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER,
NULL); NULL);
} }
return ret; return ret;
@ -995,7 +1001,30 @@ int set_var_collation_client::update(THD *thd)
thd->update_charset(character_set_client, collation_connection, thd->update_charset(character_set_client, collation_connection,
character_set_results); 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->session_tracker.mark_as_changed(thd, SESSION_STATE_CHANGE_TRACKER, NULL);
thd->protocol_text.init(thd); thd->protocol_text.init(thd);
thd->protocol_binary.init(thd); thd->protocol_binary.init(thd);
return 0; return 0;

View File

@ -48,6 +48,9 @@ struct sys_var_chain
int mysql_add_sys_var_chain(sys_var *chain); int mysql_add_sys_var_chain(sys_var *chain);
int mysql_del_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 A class representing one system variable - that is something
that can be accessed as @@global.variable_name or @@session.variable_name, 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: public:
sys_var *next; sys_var *next;
LEX_CSTRING name; LEX_CSTRING name;
bool *test_load;
enum flag_enum { GLOBAL, SESSION, ONLY_SESSION, SCOPE_MASK=1023, enum flag_enum { GLOBAL, SESSION, ONLY_SESSION, SCOPE_MASK=1023,
READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096, READONLY=1024, ALLOCATED=2048, PARSE_EARLY=4096,
NO_SET_STATEMENT=8192, AUTO_SET=16384}; NO_SET_STATEMENT=8192, AUTO_SET=16384};
@ -240,6 +244,9 @@ protected:
uchar *global_var_ptr() uchar *global_var_ptr()
{ return ((uchar*)&global_system_variables) + offset; } { 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 */ #include "sql_plugin.h" /* SHOW_HA_ROWS, SHOW_MY_BOOL */

View File

@ -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" swe "Kan inte utföra kommandot emedan du har en låst tabell eller an aktiv transaktion"
ukr "Не можу виконати подану команду тому, що таблиця заблокована або виконується транзакція" ukr "Не можу виконати подану команду тому, що таблиця заблокована або виконується транзакція"
ER_UNKNOWN_SYSTEM_VARIABLE ER_UNKNOWN_SYSTEM_VARIABLE
cze "Neznámá systémová proměnná '%-.64s'" cze "Neznámá systémová proměnná '%-.*s'"
dan "Ukendt systemvariabel '%-.64s'" dan "Ukendt systemvariabel '%-.*s'"
nla "Onbekende systeem variabele '%-.64s'" nla "Onbekende systeem variabele '%-.*s'"
eng "Unknown system variable '%-.64s'" eng "Unknown system variable '%-.*s'"
est "Tundmatu süsteemne muutuja '%-.64s'" est "Tundmatu süsteemne muutuja '%-.*s'"
fre "Variable système '%-.64s' inconnue" fre "Variable système '%-.*s' inconnue"
ger "Unbekannte Systemvariable '%-.64s'" ger "Unbekannte Systemvariable '%-.*s'"
ita "Variabile di sistema '%-.64s' sconosciuta" ita "Variabile di sistema '%-.*s' sconosciuta"
jpn "'%-.64s' は不明なシステム変数です。" jpn "'%-.*s' は不明なシステム変数です。"
por "Variável de sistema '%-.64s' desconhecida" por "Variável de sistema '%-.*s' desconhecida"
rus "Неизвестная системная переменная '%-.64s'" rus "Неизвестная системная переменная '%-.*s'"
serbian "Nepoznata sistemska promenljiva '%-.64s'" serbian "Nepoznata sistemska promenljiva '%-.*s'"
spa "Desconocida variable de sistema '%-.64s'" spa "Desconocida variable de sistema '%-.*s'"
swe "Okänd systemvariabel: '%-.64s'" swe "Okänd systemvariabel: '%-.*s'"
ukr "Невідома системна змінна '%-.64s'" ukr "Невідома системна змінна '%-.*s'"
ER_CRASHED_ON_USAGE ER_CRASHED_ON_USAGE
cze "Tabulka '%-.192s' je označena jako porušená a měla by být opravena" 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" dan "Tabellen '%-.192s' er markeret med fejl og bør repareres"

View File

@ -1766,6 +1766,10 @@ THD::~THD()
lf_hash_put_pins(xid_hash_pins); lf_hash_put_pins(xid_hash_pins);
/* Ensure everything is freed */ /* Ensure everything is freed */
status_var.local_memory_used-= sizeof(THD); status_var.local_memory_used-= sizeof(THD);
/* trick to make happy memory accounting system */
session_tracker.deinit();
if (status_var.local_memory_used != 0) if (status_var.local_memory_used != 0)
{ {
DBUG_PRINT("error", ("memory_used: %lld", status_var.local_memory_used)); DBUG_PRINT("error", ("memory_used: %lld", status_var.local_memory_used));

View File

@ -691,6 +691,8 @@ typedef struct system_variables
my_bool session_track_schema; my_bool session_track_schema;
my_bool session_track_state_change; my_bool session_track_state_change;
char *session_track_system_variables;
} SV; } SV;
/** /**

View File

@ -269,6 +269,7 @@ struct st_bookmark
uint name_len; uint name_len;
int offset; int offset;
uint version; uint version;
bool loaded;
char key[1]; 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 cleanup_variables(struct system_variables *vars);
static void plugin_vars_free_values(sys_var *vars); static void plugin_vars_free_values(sys_var *vars);
static void restore_ptr_backup(uint n, st_ptr_backup *backup); 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 plugin_ref intern_plugin_lock(LEX *lex, plugin_ref plugin);
static void intern_plugin_unlock(LEX *lex, plugin_ref plugin); static void intern_plugin_unlock(LEX *lex, plugin_ref plugin);
static void reap_plugins(void); static void reap_plugins(void);
@ -1175,6 +1178,13 @@ err:
DBUG_RETURN(errs > 0 || oks + dupes == 0); 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) 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) if (ref_check && plugin->ref_count)
sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.", sql_print_error("Plugin '%s' has ref_count=%d after deinitialization.",
plugin->name.str, plugin->ref_count); plugin->name.str, plugin->ref_count);
plugin_variables_deinit(plugin);
mysql_del_sys_var_chain(plugin->system_vars);
} }
static void plugin_del(struct st_plugin_int *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: err:
if (ret) if (ret)
mysql_del_sys_var_chain(plugin->system_vars); plugin_variables_deinit(plugin);
mysql_mutex_lock(&LOCK_plugin); mysql_mutex_lock(&LOCK_plugin);
plugin->state= state; plugin->state= state;
@ -2780,22 +2789,24 @@ static void update_func_double(THD *thd, struct st_mysql_sys_var *var,
System Variables support System Variables support
****************************************************************************/ ****************************************************************************/
sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length,
sys_var *find_sys_var(THD *thd, const char *str, size_t length) bool throw_error, bool locked)
{ {
sys_var *var; sys_var *var;
sys_var_pluginvar *pi= NULL; sys_var_pluginvar *pi= NULL;
plugin_ref plugin; 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); mysql_rwlock_rdlock(&LOCK_system_variables_hash);
if ((var= intern_find_sys_var(str, length)) && if ((var= intern_find_sys_var(str, length)) &&
(pi= var->cast_pluginvar())) (pi= var->cast_pluginvar()))
{ {
mysql_rwlock_unlock(&LOCK_system_variables_hash); mysql_rwlock_unlock(&LOCK_system_variables_hash);
LEX *lex= thd ? thd->lex : 0; 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 */ var= NULL; /* failed to lock it, it must be uninstalling */
else else
if (!(plugin_state(plugin) & PLUGIN_IS_READY)) 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 else
mysql_rwlock_unlock(&LOCK_system_variables_hash); mysql_rwlock_unlock(&LOCK_system_variables_hash);
mysql_mutex_unlock(&LOCK_plugin); if (!locked)
mysql_mutex_unlock(&LOCK_plugin);
if (!var) if (!throw_error && !var)
my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (char*) str); my_error(ER_UNKNOWN_SYSTEM_VARIABLE, MYF(0), (int)length, (char*) str);
DBUG_RETURN(var); 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. called by register_var, construct_options and test_plugin_options.
Returns the 'bookmark' for the named variable. Returns the 'bookmark' for the named variable.
@ -3940,6 +3957,14 @@ my_bool mark_changed(int, const struct my_option *opt, char *)
return 0; return 0;
} }
/**
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 Create and register system variables supplied from the plugin and
assigns initial values from corresponding command line arguments. 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); tmp_backup[tmp->nbackups++].save(&o->name);
if ((var= find_bookmark(tmp->name.str, o->name, o->flags))) if ((var= find_bookmark(tmp->name.str, o->name, o->flags)))
{
varname= var->key + 1; varname= var->key + 1;
var->loaded= TRUE;
}
else else
{ {
var= NULL;
len= tmp->name.length + strlen(o->name) + 2; len= tmp->name.length + strlen(o->name) + 2;
varname= (char*) alloc_root(mem_root, len); varname= (char*) alloc_root(mem_root, len);
strxmov(varname, tmp->name.str, "-", o->name, NullS); 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); convert_dash_to_underscore(varname, len-1);
} }
v= new (mem_root) sys_var_pluginvar(&chain, varname, tmp, o); 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)) if (!(o->flags & PLUGIN_VAR_NOCMDOPT))
{ {
// update app_type, used for I_S.SYSTEM_VARIABLES // update app_type, used for I_S.SYSTEM_VARIABLES

View File

@ -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, extern bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl,
plugin_foreach_func *func, void *arg); 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 #endif

View File

@ -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, static bool show_status_array(THD *thd, const char *wild,
SHOW_VAR *variables, SHOW_VAR *variables,
enum enum_var_type scope, enum enum_var_type scope,
@ -3324,109 +3450,21 @@ static bool show_status_array(THD *thd, const char *wild,
name_buffer, wild))) && name_buffer, wild))) &&
(!cond || cond->val_int())) (!cond || cond->val_int()))
{ {
void *value=var->value; const char *pos; // We assign a lot of const's
const char *pos, *end; // We assign a lot of const's size_t length;
if (show_type == SHOW_SYS) if (show_type == SHOW_SYS)
{
sys_var *var= (sys_var *) value;
show_type= var->show_type();
mysql_mutex_lock(&LOCK_global_system_variables); mysql_mutex_lock(&LOCK_global_system_variables);
value= var->value_ptr(thd, scope, &null_lex_str); pos= get_one_variable(thd, var, scope, show_type, status_var,
charset= var->charset(thd); &charset, buff, &length);
}
pos= end= buff; table->field[1]->store(pos, (uint32) length, charset);
/* thd->count_cuted_fields= CHECK_FIELD_IGNORE;
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]->set_notnull(); table->field[1]->set_notnull();
if (show_type == SHOW_SYS)
if (var->type == SHOW_SYS)
mysql_mutex_unlock(&LOCK_global_system_variables); mysql_mutex_unlock(&LOCK_global_system_variables);
if (schema_table_store_record(thd, table)) if (schema_table_store_record(thd, table))
{ {
res= TRUE; res= TRUE;

View File

@ -131,6 +131,12 @@ bool get_schema_tables_result(JOIN *join,
enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table); enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list); 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 */ /* These functions were under INNODB_COMPATIBILITY_HOOKS */
int get_quote_char_for_identifier(THD *thd, const char *name, uint length); int get_quote_char_for_identifier(THD *thd, const char *name, uint length);
THD *find_thread_by_id(longlong id, bool query_id= false); THD *find_thread_by_id(longlong id, bool query_id= false);

View File

@ -359,7 +359,9 @@ public:
if (ALIGN_SIZE(arg_length+1) < Alloced_length) if (ALIGN_SIZE(arg_length+1) < Alloced_length)
{ {
char *new_ptr; 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; Alloced_length = 0;
real_alloc(arg_length); real_alloc(arg_length);

View File

@ -5375,6 +5375,16 @@ static Sys_var_ulong Sys_log_tc_size(
BLOCK_SIZE(my_getpagesize())); BLOCK_SIZE(my_getpagesize()));
#endif #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, static bool update_session_track_schema(sys_var *self, THD *thd,
enum_var_type type) enum_var_type type)

View File

@ -438,10 +438,10 @@ public:
does not destroy individual members of SV, there's no way to free does not destroy individual members of SV, there's no way to free
allocated string variables for every thread. allocated string variables for every thread.
*/ */
class Sys_var_charptr: public sys_var class Sys_var_charptr_base: public sys_var
{ {
public: 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, const char *comment, int flag_args, ptrdiff_t off, size_t size,
CMD_LINE getopt, CMD_LINE getopt,
enum charset_enum is_os_charset_arg, enum charset_enum is_os_charset_arg,
@ -463,8 +463,6 @@ public:
*/ */
option.var_type|= (flags & ALLOCATED) ? GET_STR_ALLOC : GET_STR; option.var_type|= (flags & ALLOCATED) ? GET_STR_ALLOC : GET_STR;
global_var(const char*)= def_val; global_var(const char*)= def_val;
SYSVAR_ASSERT(scope() == GLOBAL);
SYSVAR_ASSERT(size == sizeof(char *));
} }
void cleanup() void cleanup()
{ {
@ -503,31 +501,35 @@ public:
} }
bool do_check(THD *thd, set_var *var) bool do_check(THD *thd, set_var *var)
{ return do_string_check(thd, var, charset(thd)); } { return do_string_check(thd, var, charset(thd)); }
bool session_update(THD *thd, set_var *var) bool session_update(THD *thd, set_var *var)= 0;
{ char *global_update_prepare(THD *thd, set_var *var)
DBUG_ASSERT(FALSE);
return true;
}
bool global_update(THD *thd, set_var *var)
{ {
char *new_val, *ptr= var->save_result.string_value.str; char *new_val, *ptr= var->save_result.string_value.str;
size_t len=var->save_result.string_value.length; size_t len=var->save_result.string_value.length;
if (ptr) if (ptr)
{ {
new_val= (char*)my_memdup(ptr, len+1, MYF(MY_WME)); 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; new_val[len]=0;
} }
else else
new_val= 0; new_val= 0;
return new_val;
}
void global_update_finish(char *new_val)
{
if (flags & ALLOCATED) if (flags & ALLOCATED)
my_free(global_var(char*)); my_free(global_var(char*));
flags|= ALLOCATED; flags|= ALLOCATED;
global_var(char*)= new_val; global_var(char*)= new_val;
return false;
} }
void session_save_default(THD *thd, set_var *var) bool global_update(THD *thd, set_var *var)
{ DBUG_ASSERT(FALSE); } {
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) void global_save_default(THD *thd, set_var *var)
{ {
char *ptr= (char*)(intptr)option.def_value; 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 class Sys_var_proxy_user: public sys_var
{ {