Safe session_track_system_variables snapshot
When enabling system variables tracker, make a copy of global_system_variables.session_track_system_variables under LOCK_global_system_variables. This protects from concurrent variable updates and potential freed memory access, as well as from variable reconstruction (which was previously protected by LOCK_plugin). We can also use this copy as a session variable pointer, so that we don't have to allocate memory and reconstruct it every time it is referenced. For this very reason we don't need buffer_length stuff anymore. As well as don't we need to take LOCK_plugin early in ::enable(). Unified ::parse_var_list() to acquire LOCK_plugin unconditionally. For no apparent reason it wasn't previously acquired for global variable update. Part of MDEV-14984 - regression in connect performance
This commit is contained in:
parent
554ac6f393
commit
1b5cf2f7e7
@ -38,7 +38,6 @@ static const unsigned int EXTRA_ALLOC= 1024;
|
|||||||
|
|
||||||
void Session_sysvars_tracker::vars_list::reinit()
|
void Session_sysvars_tracker::vars_list::reinit()
|
||||||
{
|
{
|
||||||
buffer_length= 0;
|
|
||||||
track_all= 0;
|
track_all= 0;
|
||||||
if (m_registered_sysvars.records)
|
if (m_registered_sysvars.records)
|
||||||
my_hash_reset(&m_registered_sysvars);
|
my_hash_reset(&m_registered_sysvars);
|
||||||
@ -58,7 +57,6 @@ void Session_sysvars_tracker::vars_list::copy(vars_list* from, THD *thd)
|
|||||||
{
|
{
|
||||||
track_all= from->track_all;
|
track_all= from->track_all;
|
||||||
free_hash();
|
free_hash();
|
||||||
buffer_length= from->buffer_length;
|
|
||||||
m_registered_sysvars= from->m_registered_sysvars;
|
m_registered_sysvars= from->m_registered_sysvars;
|
||||||
from->init();
|
from->init();
|
||||||
}
|
}
|
||||||
@ -111,7 +109,6 @@ bool Session_sysvars_tracker::vars_list::insert(const sys_var *svar)
|
|||||||
in case of invalid/duplicate values.
|
in case of invalid/duplicate values.
|
||||||
@param char_set [IN] charecter set information used for string
|
@param char_set [IN] charecter set information used for string
|
||||||
manipulations.
|
manipulations.
|
||||||
@param take_mutex [IN] take LOCK_plugin
|
|
||||||
|
|
||||||
@return
|
@return
|
||||||
true Error
|
true Error
|
||||||
@ -120,27 +117,21 @@ bool Session_sysvars_tracker::vars_list::insert(const sys_var *svar)
|
|||||||
bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
|
bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
|
||||||
LEX_STRING var_list,
|
LEX_STRING var_list,
|
||||||
bool throw_error,
|
bool throw_error,
|
||||||
CHARSET_INFO *char_set,
|
CHARSET_INFO *char_set)
|
||||||
bool take_mutex)
|
|
||||||
{
|
{
|
||||||
const char separator= ',';
|
const char separator= ',';
|
||||||
char *token, *lasts= NULL;
|
char *token, *lasts= NULL;
|
||||||
size_t rest= var_list.length;
|
size_t rest= var_list.length;
|
||||||
|
|
||||||
if (!var_list.str || var_list.length == 0)
|
if (!var_list.str || var_list.length == 0)
|
||||||
{
|
|
||||||
buffer_length= 1;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if(!strcmp(var_list.str, "*"))
|
if(!strcmp(var_list.str, "*"))
|
||||||
{
|
{
|
||||||
track_all= true;
|
track_all= true;
|
||||||
buffer_length= 2;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer_length= var_list.length + 1;
|
|
||||||
token= var_list.str;
|
token= var_list.str;
|
||||||
|
|
||||||
track_all= false;
|
track_all= false;
|
||||||
@ -150,7 +141,6 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
|
|||||||
token value. Hence the mutex is handled here to avoid a performance
|
token value. Hence the mutex is handled here to avoid a performance
|
||||||
overhead.
|
overhead.
|
||||||
*/
|
*/
|
||||||
if (!thd || take_mutex)
|
|
||||||
mysql_mutex_lock(&LOCK_plugin);
|
mysql_mutex_lock(&LOCK_plugin);
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@ -196,13 +186,11 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd,
|
|||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!thd || take_mutex)
|
|
||||||
mysql_mutex_unlock(&LOCK_plugin);
|
mysql_mutex_unlock(&LOCK_plugin);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
if (!thd || take_mutex)
|
|
||||||
mysql_mutex_unlock(&LOCK_plugin);
|
mysql_mutex_unlock(&LOCK_plugin);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -361,6 +349,25 @@ bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Session_sysvars_tracker::init(THD *thd)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(thd->variables.session_track_system_variables ==
|
||||||
|
global_system_variables.session_track_system_variables);
|
||||||
|
DBUG_ASSERT(global_system_variables.session_track_system_variables);
|
||||||
|
thd->variables.session_track_system_variables=
|
||||||
|
my_strdup(global_system_variables.session_track_system_variables,
|
||||||
|
MYF(MY_WME | MY_THREAD_SPECIFIC));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Session_sysvars_tracker::deinit(THD *thd)
|
||||||
|
{
|
||||||
|
my_free(thd->variables.session_track_system_variables);
|
||||||
|
thd->variables.session_track_system_variables= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Enable session tracker by parsing global value of tracked variables.
|
Enable session tracker by parsing global value of tracked variables.
|
||||||
|
|
||||||
@ -372,21 +379,16 @@ bool Session_sysvars_tracker::vars_list::construct_var_list(char *buf,
|
|||||||
|
|
||||||
bool Session_sysvars_tracker::enable(THD *thd)
|
bool Session_sysvars_tracker::enable(THD *thd)
|
||||||
{
|
{
|
||||||
|
LEX_STRING tmp= { thd->variables.session_track_system_variables,
|
||||||
|
safe_strlen(thd->variables.session_track_system_variables) };
|
||||||
orig_list.reinit();
|
orig_list.reinit();
|
||||||
mysql_mutex_lock(&LOCK_plugin);
|
if (orig_list.parse_var_list(thd, tmp, true, thd->charset()) == true)
|
||||||
LEX_STRING tmp;
|
|
||||||
tmp.str= global_system_variables.session_track_system_variables;
|
|
||||||
tmp.length= safe_strlen(tmp.str);
|
|
||||||
if (orig_list.parse_var_list(thd, tmp, true, thd->charset(), false) == true)
|
|
||||||
{
|
{
|
||||||
mysql_mutex_unlock(&LOCK_plugin);
|
|
||||||
orig_list.reinit();
|
orig_list.reinit();
|
||||||
m_enabled= false;
|
m_enabled= false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
mysql_mutex_unlock(&LOCK_plugin);
|
|
||||||
m_enabled= true;
|
m_enabled= true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,10 +414,28 @@ bool Session_sysvars_tracker::enable(THD *thd)
|
|||||||
bool Session_sysvars_tracker::update(THD *thd, set_var *var)
|
bool Session_sysvars_tracker::update(THD *thd, set_var *var)
|
||||||
{
|
{
|
||||||
vars_list tool_list;
|
vars_list tool_list;
|
||||||
if (tool_list.parse_var_list(thd, var->save_result.string_value, true,
|
void *copy= var->save_result.string_value.str ?
|
||||||
thd->charset(), true))
|
my_memdup(var->save_result.string_value.str,
|
||||||
|
var->save_result.string_value.length + 1,
|
||||||
|
MYF(MY_WME | MY_THREAD_SPECIFIC)) :
|
||||||
|
my_strdup("", MYF(MY_WME | MY_THREAD_SPECIFIC));
|
||||||
|
|
||||||
|
if (!copy)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (tool_list.parse_var_list(thd, var->save_result.string_value, true,
|
||||||
|
thd->charset()))
|
||||||
|
{
|
||||||
|
my_free(copy);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
my_free(thd->variables.session_track_system_variables);
|
||||||
|
thd->variables.session_track_system_variables= static_cast<char*>(copy);
|
||||||
|
|
||||||
orig_list.copy(&tool_list, thd);
|
orig_list.copy(&tool_list, thd);
|
||||||
|
orig_list.construct_var_list(thd->variables.session_track_system_variables,
|
||||||
|
var->save_result.string_value.length + 1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -562,7 +582,7 @@ bool sysvartrack_global_update(THD *thd, char *str, size_t len)
|
|||||||
{
|
{
|
||||||
LEX_STRING tmp= { str, len };
|
LEX_STRING tmp= { str, len };
|
||||||
Session_sysvars_tracker::vars_list dummy;
|
Session_sysvars_tracker::vars_list dummy;
|
||||||
if (!dummy.parse_var_list(thd, tmp, false, system_charset_info, false))
|
if (!dummy.parse_var_list(thd, tmp, false, system_charset_info))
|
||||||
{
|
{
|
||||||
dummy.construct_var_list(str, len + 1);
|
dummy.construct_var_list(str, len + 1);
|
||||||
return false;
|
return false;
|
||||||
@ -571,27 +591,12 @@ bool sysvartrack_global_update(THD *thd, char *str, size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uchar *sysvartrack_session_value_ptr(THD *thd, const LEX_CSTRING *base)
|
|
||||||
{
|
|
||||||
Session_sysvars_tracker *tracker= static_cast<Session_sysvars_tracker*>
|
|
||||||
(thd->session_tracker.get_tracker(SESSION_SYSVARS_TRACKER));
|
|
||||||
size_t len= tracker->get_buffer_length();
|
|
||||||
char *res= (char*) thd->alloc(len + sizeof(char*));
|
|
||||||
if (res)
|
|
||||||
{
|
|
||||||
char *buf= res + sizeof(char*);
|
|
||||||
*((char**) res)= buf;
|
|
||||||
tracker->construct_var_list(buf, len);
|
|
||||||
}
|
|
||||||
return (uchar*) res;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int session_tracker_init()
|
int session_tracker_init()
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(global_system_variables.session_track_system_variables);
|
||||||
if (sysvartrack_validate_value(0,
|
if (sysvartrack_validate_value(0,
|
||||||
global_system_variables.session_track_system_variables,
|
global_system_variables.session_track_system_variables,
|
||||||
safe_strlen(global_system_variables.session_track_system_variables)))
|
strlen(global_system_variables.session_track_system_variables)))
|
||||||
{
|
{
|
||||||
sql_print_error("The variable session_track_system_variables has "
|
sql_print_error("The variable session_track_system_variables has "
|
||||||
"invalid values.");
|
"invalid values.");
|
||||||
|
@ -131,8 +131,6 @@ class Session_sysvars_tracker: public State_tracker
|
|||||||
user.
|
user.
|
||||||
*/
|
*/
|
||||||
HASH m_registered_sysvars;
|
HASH m_registered_sysvars;
|
||||||
/** Size of buffer for string representation */
|
|
||||||
size_t buffer_length;
|
|
||||||
/**
|
/**
|
||||||
If TRUE then we want to check all session variable.
|
If TRUE then we want to check all session variable.
|
||||||
*/
|
*/
|
||||||
@ -165,15 +163,9 @@ class Session_sysvars_tracker: public State_tracker
|
|||||||
my_hash_element(&m_registered_sysvars, i));
|
my_hash_element(&m_registered_sysvars, i));
|
||||||
}
|
}
|
||||||
public:
|
public:
|
||||||
vars_list(): buffer_length(0), track_all(false) { init(); }
|
vars_list(): track_all(false) { init(); }
|
||||||
void deinit() { free_hash(); }
|
void deinit() { free_hash(); }
|
||||||
|
|
||||||
size_t get_buffer_length()
|
|
||||||
{
|
|
||||||
DBUG_ASSERT(buffer_length != 0); // asked earlier then should
|
|
||||||
return buffer_length;
|
|
||||||
}
|
|
||||||
|
|
||||||
sysvar_node_st *insert_or_search(const sys_var *svar)
|
sysvar_node_st *insert_or_search(const sys_var *svar)
|
||||||
{
|
{
|
||||||
sysvar_node_st *res= search(svar);
|
sysvar_node_st *res= search(svar);
|
||||||
@ -197,7 +189,7 @@ class Session_sysvars_tracker: public State_tracker
|
|||||||
}
|
}
|
||||||
void copy(vars_list* from, THD *thd);
|
void copy(vars_list* from, THD *thd);
|
||||||
bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
|
bool parse_var_list(THD *thd, LEX_STRING var_list, bool throw_error,
|
||||||
CHARSET_INFO *char_set, bool take_mutex);
|
CHARSET_INFO *char_set);
|
||||||
bool construct_var_list(char *buf, size_t buf_len);
|
bool construct_var_list(char *buf, size_t buf_len);
|
||||||
bool store(THD *thd, String *buf);
|
bool store(THD *thd, String *buf);
|
||||||
};
|
};
|
||||||
@ -208,15 +200,8 @@ class Session_sysvars_tracker: public State_tracker
|
|||||||
vars_list orig_list;
|
vars_list orig_list;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
size_t get_buffer_length()
|
void init(THD *thd);
|
||||||
{
|
void deinit(THD *thd);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool enable(THD *thd);
|
bool enable(THD *thd);
|
||||||
bool update(THD *thd, set_var *var);
|
bool update(THD *thd, set_var *var);
|
||||||
bool store(THD *thd, String *buf);
|
bool store(THD *thd, String *buf);
|
||||||
@ -232,7 +217,6 @@ public:
|
|||||||
|
|
||||||
bool sysvartrack_validate_value(THD *thd, const char *str, size_t len);
|
bool sysvartrack_validate_value(THD *thd, const char *str, size_t len);
|
||||||
bool sysvartrack_global_update(THD *thd, char *str, size_t len);
|
bool sysvartrack_global_update(THD *thd, char *str, size_t len);
|
||||||
uchar *sysvartrack_session_value_ptr(THD *thd, const LEX_CSTRING *base);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -444,12 +428,6 @@ public:
|
|||||||
m_trackers[i]->enable(thd);
|
m_trackers[i]->enable(thd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the pointer to the tracker object for the specified tracker. */
|
|
||||||
inline State_tracker *get_tracker(enum_session_tracker tracker) const
|
|
||||||
{
|
|
||||||
return m_trackers[tracker];
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void mark_as_changed(THD *thd, enum enum_session_tracker tracker,
|
inline void mark_as_changed(THD *thd, enum enum_session_tracker tracker,
|
||||||
LEX_CSTRING *data)
|
LEX_CSTRING *data)
|
||||||
{
|
{
|
||||||
|
@ -3149,6 +3149,11 @@ void plugin_thdvar_init(THD *thd)
|
|||||||
thd->variables.enforced_table_plugin= NULL;
|
thd->variables.enforced_table_plugin= NULL;
|
||||||
cleanup_variables(&thd->variables);
|
cleanup_variables(&thd->variables);
|
||||||
|
|
||||||
|
/* This and all other variable cleanups are here for COM_CHANGE_USER :( */
|
||||||
|
#ifndef EMBEDDED_LIBRARY
|
||||||
|
thd->session_tracker.sysvars.deinit(thd);
|
||||||
|
#endif
|
||||||
|
|
||||||
thd->variables= global_system_variables;
|
thd->variables= global_system_variables;
|
||||||
|
|
||||||
/* we are going to allocate these lazily */
|
/* we are going to allocate these lazily */
|
||||||
@ -3170,6 +3175,9 @@ void plugin_thdvar_init(THD *thd)
|
|||||||
intern_plugin_unlock(NULL, old_enforced_table_plugin);
|
intern_plugin_unlock(NULL, old_enforced_table_plugin);
|
||||||
mysql_mutex_unlock(&LOCK_plugin);
|
mysql_mutex_unlock(&LOCK_plugin);
|
||||||
|
|
||||||
|
#ifndef EMBEDDED_LIBRARY
|
||||||
|
thd->session_tracker.sysvars.init(thd);
|
||||||
|
#endif
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3235,6 +3243,10 @@ void plugin_thdvar_cleanup(THD *thd)
|
|||||||
plugin_ref *list;
|
plugin_ref *list;
|
||||||
DBUG_ENTER("plugin_thdvar_cleanup");
|
DBUG_ENTER("plugin_thdvar_cleanup");
|
||||||
|
|
||||||
|
#ifndef EMBEDDED_LIBRARY
|
||||||
|
thd->session_tracker.sysvars.deinit(thd);
|
||||||
|
#endif
|
||||||
|
|
||||||
mysql_mutex_lock(&LOCK_plugin);
|
mysql_mutex_lock(&LOCK_plugin);
|
||||||
|
|
||||||
unlock_variables(thd, &thd->variables);
|
unlock_variables(thd, &thd->variables);
|
||||||
|
@ -641,8 +641,6 @@ public:
|
|||||||
DBUG_ASSERT(res == 0);
|
DBUG_ASSERT(res == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base)
|
|
||||||
{ return sysvartrack_session_value_ptr(thd, base); }
|
|
||||||
};
|
};
|
||||||
#endif //EMBEDDED_LIBRARY
|
#endif //EMBEDDED_LIBRARY
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user