mirror of
https://github.com/MariaDB/server.git
synced 2025-01-15 19:42:28 +01:00
1559 lines
44 KiB
C++
1559 lines
44 KiB
C++
/* Copyright (c) 2002, 2013, Oracle and/or its affiliates.
|
|
Copyright (c) 2008, 2014, SkySQL Ab.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
|
|
|
|
/* variable declarations are in sys_vars.cc now !!! */
|
|
|
|
#include "sql_plugin.h"
|
|
#include "sql_class.h" // set_var.h: session_var_ptr
|
|
#include "set_var.h"
|
|
#include "sql_priv.h"
|
|
#include "unireg.h"
|
|
#include "mysqld.h" // lc_messages_dir
|
|
#include "sys_vars_shared.h"
|
|
#include "transaction.h"
|
|
#include "sql_locale.h" // my_locale_by_number,
|
|
// my_locale_by_name
|
|
#include "strfunc.h" // find_set_from_flags, find_set
|
|
#include "sql_parse.h" // check_global_access
|
|
#include "sql_table.h" // reassign_keycache_tables
|
|
#include "sql_time.h" // date_time_format_copy,
|
|
// date_time_format_make
|
|
#include "derror.h"
|
|
#include "tztime.h" // my_tz_find, my_tz_SYSTEM, struct Time_zone
|
|
#include "sql_select.h" // free_underlaid_joins
|
|
#include "sql_i_s.h"
|
|
#include "sql_view.h" // updatable_views_with_limit_typelib
|
|
#include "lock.h" // lock_global_read_lock,
|
|
// make_global_read_lock_block_commit,
|
|
// unlock_global_read_lock
|
|
|
|
static HASH system_variable_hash;
|
|
static PolyLock_mutex PLock_global_system_variables(&LOCK_global_system_variables);
|
|
static ulonglong system_variable_hash_version= 0;
|
|
|
|
/**
|
|
Return variable name and length for hashing of variables.
|
|
*/
|
|
|
|
static const uchar *get_sys_var_length(const void *var_, size_t *length,
|
|
my_bool)
|
|
{
|
|
auto var= static_cast<const sys_var *>(var_);
|
|
*length= var->name.length;
|
|
return reinterpret_cast<const uchar *>(var->name.str);
|
|
}
|
|
|
|
sys_var_chain all_sys_vars = { NULL, NULL };
|
|
|
|
int sys_var_init()
|
|
{
|
|
DBUG_ENTER("sys_var_init");
|
|
|
|
/* Must be already initialized. */
|
|
DBUG_ASSERT(system_charset_info != NULL);
|
|
|
|
if (my_hash_init(PSI_INSTRUMENT_ME, &system_variable_hash,
|
|
Lex_ident_sys_var::charset_info(), 700, 0,
|
|
0, get_sys_var_length, 0, HASH_UNIQUE))
|
|
goto error;
|
|
|
|
if (mysql_add_sys_var_chain(all_sys_vars.first))
|
|
goto error;
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
error:
|
|
fprintf(stderr, "failed to initialize System variables");
|
|
DBUG_RETURN(1);
|
|
}
|
|
|
|
uint sys_var_elements()
|
|
{
|
|
return system_variable_hash.records;
|
|
}
|
|
|
|
int sys_var_add_options(DYNAMIC_ARRAY *long_options, int parse_flags)
|
|
{
|
|
size_t saved_elements= long_options->elements;
|
|
|
|
DBUG_ENTER("sys_var_add_options");
|
|
|
|
for (sys_var *var=all_sys_vars.first; var; var= var->next)
|
|
{
|
|
if (var->register_option(long_options, parse_flags))
|
|
goto error;
|
|
}
|
|
|
|
DBUG_RETURN(0);
|
|
|
|
error:
|
|
fprintf(stderr, "failed to initialize System variables");
|
|
long_options->elements= saved_elements;
|
|
DBUG_RETURN(1);
|
|
}
|
|
|
|
void sys_var_end()
|
|
{
|
|
DBUG_ENTER("sys_var_end");
|
|
|
|
my_hash_free(&system_variable_hash);
|
|
|
|
for (sys_var *var=all_sys_vars.first; var; var= var->next)
|
|
var->cleanup();
|
|
|
|
DBUG_VOID_RETURN;
|
|
}
|
|
|
|
|
|
static bool static_test_load= TRUE;
|
|
|
|
/**
|
|
sys_var constructor
|
|
|
|
@param chain variables are linked into chain for mysql_add_sys_var_chain()
|
|
@param name_arg the name of the variable. Must be 0-terminated and exist
|
|
for the liftime of the sys_var object. @sa my_option::name
|
|
@param comment shown in mysqld --help, @sa my_option::comment
|
|
@param flags_arg or'ed flag_enum values
|
|
@param off offset of the global variable value from the
|
|
&global_system_variables.
|
|
@param getopt_id -1 for no command-line option, otherwise @sa my_option::id
|
|
@param getopt_arg_type @sa my_option::arg_type
|
|
@param show_val_type_arg what value_ptr() returns for sql_show.cc
|
|
@param def_val default value, @sa my_option::def_value
|
|
@param lock mutex or rw_lock that protects the global variable
|
|
*in addition* to LOCK_global_system_variables.
|
|
@param binlog_status_enum @sa binlog_status_enum
|
|
@param on_check_func a function to be called at the end of sys_var::check,
|
|
put your additional checks here
|
|
@param on_update_func a function to be called at the end of sys_var::update,
|
|
any post-update activity should happen here
|
|
@param substitute If non-NULL, this variable is deprecated and the
|
|
string describes what one should use instead. If an empty string,
|
|
the variable is deprecated but no replacement is offered.
|
|
*/
|
|
sys_var::sys_var(sys_var_chain *chain, const char *name_arg,
|
|
const char *comment, int flags_arg, ptrdiff_t off,
|
|
int getopt_id, enum get_opt_arg_type getopt_arg_type,
|
|
SHOW_TYPE show_val_type_arg, longlong def_val,
|
|
PolyLock *lock, enum binlog_status_enum binlog_status_arg,
|
|
on_check_function on_check_func,
|
|
on_update_function on_update_func,
|
|
const char *substitute) :
|
|
next(0), binlog_status(binlog_status_arg), value_origin(COMPILE_TIME),
|
|
flags(flags_arg), show_val_type(show_val_type_arg),
|
|
guard(lock), offset(off), on_check(on_check_func), on_update(on_update_func)
|
|
{
|
|
/*
|
|
There is a limitation in handle_options() related to short options:
|
|
- either all short options should be declared when parsing in multiple stages,
|
|
- or none should be declared.
|
|
Because a lot of short options are used in the normal parsing phase
|
|
for mysqld, we enforce here that no short option is present
|
|
in the first (PARSE_EARLY) stage.
|
|
See handle_options() for details.
|
|
*/
|
|
DBUG_ASSERT(!(flags & PARSE_EARLY) || getopt_id <= 0 || getopt_id >= 255);
|
|
|
|
name.str= name_arg; // ER_NO_DEFAULT relies on 0-termination of name_arg
|
|
name.length= strlen(name_arg); // and so does this.
|
|
DBUG_ASSERT(name.length <= NAME_CHAR_LEN);
|
|
DBUG_ASSERT(!comment || !comment[0] || comment[strlen(comment)-1] != '.');
|
|
DBUG_ASSERT(!comment || !comment[0] || comment[strlen(comment)-1] != ' ');
|
|
|
|
bzero(&option, sizeof(option));
|
|
option.name= name_arg;
|
|
option.id= getopt_id;
|
|
option.comment= comment;
|
|
option.arg_type= getopt_arg_type;
|
|
option.value= (uchar **)global_var_ptr();
|
|
option.def_value= def_val;
|
|
option.app_type= this;
|
|
option.var_type= flags & AUTO_SET ? GET_AUTO : 0;
|
|
option.deprecation_substitute= substitute;
|
|
|
|
if (chain->last)
|
|
chain->last->next= this;
|
|
else
|
|
chain->first= this;
|
|
chain->last= this;
|
|
|
|
test_load= &static_test_load;
|
|
}
|
|
|
|
bool sys_var::update(THD *thd, set_var *var)
|
|
{
|
|
enum_var_type type= var->type;
|
|
if (type == OPT_GLOBAL || scope() == GLOBAL)
|
|
{
|
|
/*
|
|
Yes, both locks need to be taken before an update, just as
|
|
both are taken to get a value. If we'll take only 'guard' here,
|
|
then value_ptr() for strings won't be safe in SHOW VARIABLES anymore,
|
|
to make it safe we'll need value_ptr_unlock().
|
|
*/
|
|
AutoWLock lock1(&PLock_global_system_variables);
|
|
AutoWLock lock2(guard);
|
|
value_origin= SQL;
|
|
return global_update(thd, var) ||
|
|
(on_update && on_update(this, thd, OPT_GLOBAL));
|
|
}
|
|
else
|
|
{
|
|
bool ret= session_update(thd, var) ||
|
|
(on_update && on_update(this, thd, OPT_SESSION));
|
|
|
|
/*
|
|
Make sure we don't session-track variables that are not actually
|
|
part of the session. transaction_isolation and transaction_read_only for
|
|
example exist as GLOBAL, SESSION, and one-shot ("for next transaction
|
|
only").
|
|
*/
|
|
if ((var->type == OPT_SESSION) && (!ret))
|
|
{
|
|
thd->session_tracker.sysvars.mark_as_changed(thd, 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.state_change.mark_as_changed(thd);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
const uchar *sys_var::session_value_ptr(THD *thd, const LEX_CSTRING *base) const
|
|
{
|
|
return session_var_ptr(thd);
|
|
}
|
|
|
|
const uchar *sys_var::global_value_ptr(THD *thd, const LEX_CSTRING *base) const
|
|
{
|
|
return global_var_ptr();
|
|
}
|
|
|
|
bool sys_var::check(THD *thd, set_var *var)
|
|
{
|
|
if (unlikely((var->value && do_check(thd, var)) ||
|
|
(on_check && on_check(this, thd, var))))
|
|
{
|
|
if (likely(!thd->is_error()))
|
|
{
|
|
char buff[STRING_BUFFER_USUAL_SIZE];
|
|
String str(buff, sizeof(buff), system_charset_info), *res;
|
|
|
|
if (!var->value)
|
|
{
|
|
str.set(STRING_WITH_LEN("DEFAULT"), &my_charset_latin1);
|
|
res= &str;
|
|
}
|
|
else if (!(res=var->value->val_str(&str)))
|
|
{
|
|
str.set(STRING_WITH_LEN("NULL"), &my_charset_latin1);
|
|
res= &str;
|
|
}
|
|
ErrConvString err(res);
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name.str, err.ptr());
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const uchar *sys_var::value_ptr(THD *thd, enum_var_type type,
|
|
const LEX_CSTRING *base) const
|
|
{
|
|
DBUG_ASSERT(base);
|
|
if (type == OPT_GLOBAL || scope() == GLOBAL)
|
|
{
|
|
mysql_mutex_assert_owner(&LOCK_global_system_variables);
|
|
AutoRLock lock(guard);
|
|
return global_value_ptr(thd, base);
|
|
}
|
|
else
|
|
return session_value_ptr(thd, base);
|
|
}
|
|
|
|
bool sys_var::set_default(THD *thd, set_var* var)
|
|
{
|
|
if (var->type == OPT_GLOBAL || scope() == GLOBAL)
|
|
global_save_default(thd, var);
|
|
else
|
|
session_save_default(thd, var);
|
|
|
|
return check(thd, var) || update(thd, var);
|
|
}
|
|
|
|
|
|
#define do_num_val(T,CMD) \
|
|
do { \
|
|
T val= *(T*) value; \
|
|
CMD; \
|
|
} while (0)
|
|
|
|
#define case_for_integers(CMD) \
|
|
case SHOW_SINT: do_num_val (int,CMD); \
|
|
case SHOW_SLONG: do_num_val (long,CMD); \
|
|
case SHOW_SLONGLONG:do_num_val (longlong,CMD); \
|
|
case SHOW_UINT: do_num_val (uint,CMD); \
|
|
case SHOW_ULONG: do_num_val (ulong,CMD); \
|
|
case SHOW_ULONGLONG:do_num_val (ulonglong,CMD); \
|
|
case SHOW_HA_ROWS: do_num_val (ha_rows,CMD);
|
|
|
|
#define case_for_double(CMD) \
|
|
case SHOW_DOUBLE: do_num_val (double,CMD);
|
|
|
|
#define case_get_string_as_lex_string \
|
|
case SHOW_CHAR: \
|
|
sval.str= (char*) value; \
|
|
sval.length= sval.str ? strlen(sval.str) : 0; \
|
|
break; \
|
|
case SHOW_CHAR_PTR: \
|
|
sval.str= *(char**) value; \
|
|
sval.length= sval.str ? strlen(sval.str) : 0; \
|
|
break; \
|
|
case SHOW_LEX_STRING: \
|
|
sval= *(LEX_CSTRING *) value; \
|
|
break
|
|
|
|
longlong sys_var::val_int(bool *is_null,
|
|
THD *thd, enum_var_type type,
|
|
const LEX_CSTRING *base)
|
|
{
|
|
LEX_CSTRING sval;
|
|
AutoWLock lock(&PLock_global_system_variables);
|
|
const uchar *value= value_ptr(thd, type, base);
|
|
*is_null= false;
|
|
|
|
switch (show_type())
|
|
{
|
|
case_get_string_as_lex_string;
|
|
case_for_integers(return val);
|
|
case_for_double(return (longlong) val);
|
|
case SHOW_MY_BOOL: return *(my_bool*)value;
|
|
default:
|
|
my_error(ER_VAR_CANT_BE_READ, MYF(0), name.str);
|
|
return 0;
|
|
}
|
|
|
|
longlong ret= 0;
|
|
if (!(*is_null= !sval.str))
|
|
ret= longlong_from_string_with_check(charset(thd),
|
|
sval.str, sval.str + sval.length);
|
|
return ret;
|
|
}
|
|
|
|
|
|
String *sys_var::val_str_nolock(String *str, THD *thd, const uchar *value)
|
|
{
|
|
static LEX_CSTRING bools[]=
|
|
{
|
|
{ STRING_WITH_LEN("OFF") },
|
|
{ STRING_WITH_LEN("ON") }
|
|
};
|
|
|
|
LEX_CSTRING sval;
|
|
switch (show_type())
|
|
{
|
|
case_get_string_as_lex_string;
|
|
case_for_integers(return str->set(val, system_charset_info) ? 0 : str);
|
|
case_for_double(return str->set_real(val, 6, system_charset_info) ? 0 : str);
|
|
case SHOW_MY_BOOL:
|
|
sval= bools[(int)*(my_bool*)value];
|
|
break;
|
|
default:
|
|
my_error(ER_VAR_CANT_BE_READ, MYF(0), name.str);
|
|
return 0;
|
|
}
|
|
|
|
if (!sval.str || str->copy(sval.str, sval.length, charset(thd)))
|
|
str= NULL;
|
|
return str;
|
|
}
|
|
|
|
|
|
String *sys_var::val_str(String *str,
|
|
THD *thd, enum_var_type type, const LEX_CSTRING *base)
|
|
{
|
|
AutoWLock lock(&PLock_global_system_variables);
|
|
const uchar *value= value_ptr(thd, type, base);
|
|
return val_str_nolock(str, thd, value);
|
|
}
|
|
|
|
|
|
double sys_var::val_real(bool *is_null,
|
|
THD *thd, enum_var_type type, const LEX_CSTRING *base)
|
|
{
|
|
LEX_CSTRING sval;
|
|
AutoWLock lock(&PLock_global_system_variables);
|
|
const uchar *value= value_ptr(thd, type, base);
|
|
*is_null= false;
|
|
|
|
switch (show_type())
|
|
{
|
|
case_get_string_as_lex_string;
|
|
case_for_integers(return (double)val);
|
|
case_for_double(return val);
|
|
case SHOW_MY_BOOL: return *(my_bool*)value;
|
|
default:
|
|
my_error(ER_VAR_CANT_BE_READ, MYF(0), name.str);
|
|
return 0;
|
|
}
|
|
|
|
double ret= 0;
|
|
if (!(*is_null= !sval.str))
|
|
ret= double_from_string_with_check(charset(thd),
|
|
sval.str, sval.str + sval.length);
|
|
return ret;
|
|
}
|
|
|
|
/* Marker if the variable is deleted instead of depricated */
|
|
const char *UNUSED_HELP="Unused";
|
|
|
|
void sys_var::do_deprecated_warning(THD *thd)
|
|
{
|
|
if (option.deprecation_substitute != NULL)
|
|
{
|
|
char buf1[NAME_CHAR_LEN + 3];
|
|
strxnmov(buf1, sizeof(buf1)-1, "@@", name.str, 0);
|
|
|
|
if (option.comment == UNUSED_HELP ||
|
|
strcmp(option.comment, UNUSED_HELP) == 0)
|
|
my_error(ER_VARIABLE_IGNORED, MYF(ME_WARNING), buf1);
|
|
else
|
|
{
|
|
char buf2[NAME_CHAR_LEN + 3];
|
|
if (!IS_DEPRECATED_NO_REPLACEMENT(option.deprecation_substitute))
|
|
strxnmov(buf2, sizeof(buf2)-1, "@@", option.deprecation_substitute, 0);
|
|
else
|
|
buf2[0]= 0;
|
|
warn_deprecated<999999>(thd, buf1, buf2);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
Throw warning (error in STRICT mode) if value for variable needed bounding.
|
|
Plug-in interface also uses this.
|
|
|
|
@param thd thread handle
|
|
@param name variable's name
|
|
@param fixed did we have to correct the value? (throw warn/err if so)
|
|
@param is_unsigned is value's type unsigned?
|
|
@param v variable's value
|
|
|
|
@retval true on error, false otherwise (warning or ok)
|
|
*/
|
|
|
|
|
|
bool throw_bounds_warning(THD *thd, const char *name,const char *v)
|
|
{
|
|
if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
|
|
{
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, v);
|
|
return true;
|
|
}
|
|
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
|
|
ER_TRUNCATED_WRONG_VALUE,
|
|
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE), name, v);
|
|
return false;
|
|
}
|
|
|
|
|
|
bool throw_bounds_warning(THD *thd, const char *name,
|
|
bool fixed, bool is_unsigned, longlong v)
|
|
{
|
|
if (fixed)
|
|
{
|
|
char buf[22];
|
|
|
|
if (is_unsigned)
|
|
ullstr((ulonglong) v, buf);
|
|
else
|
|
llstr(v, buf);
|
|
|
|
if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
|
|
{
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
|
|
return true;
|
|
}
|
|
return throw_bounds_warning(thd, name, buf);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool throw_bounds_warning(THD *thd, const char *name, bool fixed, double v)
|
|
{
|
|
if (fixed)
|
|
{
|
|
char buf[64];
|
|
|
|
my_gcvt(v, MY_GCVT_ARG_DOUBLE, sizeof(buf) - 1, buf, NULL);
|
|
|
|
if (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES)
|
|
{
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), name, buf);
|
|
return true;
|
|
}
|
|
return throw_bounds_warning(thd, name, buf);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
typedef struct old_names_map_st
|
|
{
|
|
const Lex_ident_charset old_name;
|
|
const char *new_name;
|
|
} my_old_conv;
|
|
|
|
static my_old_conv old_conv[]=
|
|
{
|
|
{ "cp1251_koi8"_Lex_ident_charset , "cp1251" },
|
|
{ "cp1250_latin2"_Lex_ident_charset , "cp1250" },
|
|
{ "kam_latin2"_Lex_ident_charset , "keybcs2" },
|
|
{ "mac_latin2"_Lex_ident_charset , "MacRoman" },
|
|
{ "macce_latin2"_Lex_ident_charset , "MacCE" },
|
|
{ "pc2_latin2"_Lex_ident_charset , "pclatin2" },
|
|
{ "vga_latin2"_Lex_ident_charset , "pclatin1" },
|
|
{ "koi8_cp1251"_Lex_ident_charset , "koi8r" },
|
|
{ "win1251ukr_koi8_ukr"_Lex_ident_charset , "win1251ukr" },
|
|
{ "koi8_ukr_win1251ukr"_Lex_ident_charset , "koi8u" },
|
|
{ Lex_ident_charset() , NULL }
|
|
};
|
|
|
|
CHARSET_INFO *get_old_charset_by_name(const LEX_CSTRING &name)
|
|
{
|
|
my_old_conv *conv;
|
|
for (conv= old_conv; conv->old_name.str; conv++)
|
|
{
|
|
if (conv->old_name.streq(name))
|
|
return get_charset_by_csname(conv->new_name, MY_CS_PRIMARY, MYF(0));
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/****************************************************************************
|
|
Main handling of variables:
|
|
- Initialisation
|
|
- Searching during parsing
|
|
- Update loop
|
|
****************************************************************************/
|
|
|
|
/**
|
|
Add variables to the dynamic hash of system variables
|
|
|
|
@param first Pointer to first system variable to add
|
|
|
|
@retval
|
|
0 SUCCESS
|
|
@retval
|
|
otherwise FAILURE
|
|
*/
|
|
|
|
|
|
int mysql_add_sys_var_chain(sys_var *first)
|
|
{
|
|
sys_var *var;
|
|
|
|
/* A write lock should be held on LOCK_system_variables_hash */
|
|
|
|
for (var= first; var; var= var->next)
|
|
{
|
|
/* this fails if there is a conflicting variable name. see HASH_UNIQUE */
|
|
if (my_hash_insert(&system_variable_hash, (uchar*) var))
|
|
{
|
|
fprintf(stderr, "*** duplicate variable name '%s' ?\n", var->name.str);
|
|
goto error;
|
|
}
|
|
}
|
|
/* Update system_variable_hash version. */
|
|
system_variable_hash_version++;
|
|
return 0;
|
|
|
|
error:
|
|
for (; first != var; first= first->next)
|
|
my_hash_delete(&system_variable_hash, (uchar*) first);
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
Remove variables to the dynamic hash of system variables
|
|
|
|
SYNOPSIS
|
|
mysql_del_sys_var_chain()
|
|
first Pointer to first system variable to remove
|
|
|
|
RETURN VALUES
|
|
0 SUCCESS
|
|
otherwise FAILURE
|
|
*/
|
|
|
|
int mysql_del_sys_var_chain(sys_var *first)
|
|
{
|
|
int result= 0;
|
|
|
|
mysql_prlock_wrlock(&LOCK_system_variables_hash);
|
|
for (sys_var *var= first; var; var= var->next)
|
|
result|= my_hash_delete(&system_variable_hash, (uchar*) var);
|
|
mysql_prlock_unlock(&LOCK_system_variables_hash);
|
|
|
|
/* Update system_variable_hash version. */
|
|
system_variable_hash_version++;
|
|
return result;
|
|
}
|
|
|
|
|
|
static int show_cmp(const void *a_, const void *b_)
|
|
{
|
|
const SHOW_VAR *a= static_cast<const SHOW_VAR *>(a_);
|
|
const SHOW_VAR *b= static_cast<const SHOW_VAR *>(b_);
|
|
return strcmp(a->name, b->name);
|
|
}
|
|
|
|
|
|
/*
|
|
Number of records in the system_variable_hash.
|
|
Requires lock on LOCK_system_variables_hash.
|
|
*/
|
|
ulong get_system_variable_hash_records(void)
|
|
{
|
|
return system_variable_hash.records;
|
|
}
|
|
|
|
|
|
/**
|
|
Constructs an array of system variables for display to the user.
|
|
|
|
@param thd current thread
|
|
@param sorted If TRUE, the system variables should be sorted
|
|
@param scope OPT_GLOBAL or OPT_SESSION for SHOW GLOBAL|SESSION VARIABLES
|
|
|
|
@retval
|
|
pointer Array of SHOW_VAR elements for display
|
|
@retval
|
|
NULL FAILURE
|
|
*/
|
|
|
|
SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type scope)
|
|
{
|
|
int count= system_variable_hash.records, i;
|
|
SHOW_VAR *result= thd->alloc<SHOW_VAR>(count + 1);
|
|
|
|
if (result)
|
|
{
|
|
SHOW_VAR *show= result;
|
|
|
|
for (i= 0; i < count; i++)
|
|
{
|
|
sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i);
|
|
|
|
// don't show session-only variables in SHOW GLOBAL VARIABLES
|
|
if (scope == OPT_GLOBAL && var->check_type(scope))
|
|
continue;
|
|
|
|
show->name= var->name.str;
|
|
show->value= (char*) var;
|
|
show->type= SHOW_SYS;
|
|
show++;
|
|
}
|
|
|
|
/* sort into order */
|
|
if (sorted)
|
|
my_qsort(result, show-result, sizeof(SHOW_VAR),
|
|
(qsort_cmp) show_cmp);
|
|
|
|
/* make last element empty */
|
|
bzero(show, sizeof(SHOW_VAR));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
Find a user set-table variable.
|
|
|
|
@param str Name of system variable to find
|
|
@param length Length of variable. zero means that we should use strlen()
|
|
on the variable
|
|
|
|
@retval
|
|
pointer pointer to variable definitions
|
|
@retval
|
|
0 Unknown variable (error message is given)
|
|
*/
|
|
|
|
sys_var *intern_find_sys_var(const char *str, size_t length)
|
|
{
|
|
sys_var *var;
|
|
|
|
/*
|
|
This function is only called from the sql_plugin.cc.
|
|
A lock on LOCK_system_variable_hash should be held
|
|
*/
|
|
var= (sys_var*) my_hash_search(&system_variable_hash,
|
|
(uchar*) str, length ? length : strlen(str));
|
|
|
|
return var;
|
|
}
|
|
|
|
|
|
/**
|
|
Execute update of all variables.
|
|
|
|
First run a check of all variables that all updates will go ok.
|
|
If yes, then execute all updates, returning an error if any one failed.
|
|
|
|
This should ensure that in all normal cases none all or variables are
|
|
updated.
|
|
|
|
@param THD Thread id
|
|
@param var_list List of variables to update
|
|
|
|
@retval
|
|
0 ok
|
|
@retval
|
|
1 ERROR, message sent (normally no variables was updated)
|
|
@retval
|
|
-1 ERROR, message not sent
|
|
*/
|
|
|
|
int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free)
|
|
{
|
|
int error= 0;
|
|
bool was_error= thd->is_error();
|
|
List_iterator_fast<set_var_base> it(*var_list);
|
|
DBUG_ENTER("sql_set_variables");
|
|
|
|
set_var_base *var;
|
|
while ((var=it++))
|
|
{
|
|
if (unlikely((error= var->check(thd))))
|
|
goto err;
|
|
}
|
|
if (unlikely(was_error) || likely(!(error= MY_TEST(thd->is_error()))))
|
|
{
|
|
it.rewind();
|
|
while ((var= it++))
|
|
error|= var->update(thd); // Returns 0, -1 or 1
|
|
}
|
|
|
|
err:
|
|
if (free)
|
|
free_underlaid_joins(thd, thd->lex->first_select_lex());
|
|
DBUG_RETURN(error);
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Functions to handle SET mysql_internal_variable=const_expr
|
|
*****************************************************************************/
|
|
|
|
bool sys_var::on_check_access_global(THD *thd) const
|
|
{
|
|
return check_global_access(thd, PRIV_SET_GLOBAL_SYSTEM_VARIABLE);
|
|
}
|
|
|
|
/**
|
|
Verify that the supplied value is correct.
|
|
|
|
@param thd Thread handler
|
|
|
|
@return status code
|
|
@retval -1 Failure
|
|
@retval 0 Success
|
|
*/
|
|
|
|
int set_var::check(THD *thd)
|
|
{
|
|
var->do_deprecated_warning(thd);
|
|
if (var->is_readonly())
|
|
{
|
|
my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), var->name.str, "read only");
|
|
return -1;
|
|
}
|
|
if (var->check_type(type))
|
|
{
|
|
int err= type == OPT_GLOBAL ? ER_LOCAL_VARIABLE : ER_GLOBAL_VARIABLE;
|
|
my_error(err, MYF(0), var->name.str);
|
|
return -1;
|
|
}
|
|
if (type == OPT_GLOBAL && var->on_check_access_global(thd))
|
|
return 1;
|
|
/* value is a NULL pointer if we are using SET ... = DEFAULT */
|
|
if (!value)
|
|
return 0;
|
|
|
|
thd->where= THD_WHERE::SET_LIST;
|
|
if (value->fix_fields_if_needed_for_scalar(thd, &value))
|
|
return -1;
|
|
if (var->check_update_type(value))
|
|
{
|
|
my_error(ER_WRONG_TYPE_FOR_VAR, MYF(0), var->name.str);
|
|
return -1;
|
|
}
|
|
switch (type) {
|
|
case SHOW_OPT_DEFAULT:
|
|
case SHOW_OPT_SESSION:
|
|
DBUG_ASSERT(var->scope() != sys_var::GLOBAL);
|
|
if (var->on_check_access_session(thd))
|
|
return -1;
|
|
break;
|
|
case SHOW_OPT_GLOBAL: // Checked earlier
|
|
break;
|
|
}
|
|
return var->check(thd, this) ? -1 : 0;
|
|
}
|
|
|
|
|
|
/**
|
|
Check variable, but without assigning value (used by PS).
|
|
|
|
@param thd thread handler
|
|
|
|
@retval
|
|
0 ok
|
|
@retval
|
|
1 ERROR, message sent (normally no variables was updated)
|
|
@retval
|
|
-1 ERROR, message not sent
|
|
*/
|
|
int set_var::light_check(THD *thd)
|
|
{
|
|
if (var->is_readonly())
|
|
{
|
|
my_error(ER_INCORRECT_GLOBAL_LOCAL_VAR, MYF(0), var->name.str, "read only");
|
|
return -1;
|
|
}
|
|
if (var->check_type(type))
|
|
{
|
|
int err= type == OPT_GLOBAL ? ER_LOCAL_VARIABLE : ER_GLOBAL_VARIABLE;
|
|
my_error(err, MYF(0), var->name.str);
|
|
return -1;
|
|
}
|
|
|
|
if (type == OPT_GLOBAL && var->on_check_access_global(thd))
|
|
return 1;
|
|
|
|
if (value && value->fix_fields_if_needed_for_scalar(thd, &value))
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
Update variable
|
|
|
|
@param thd thread handler
|
|
@returns 0|1 ok or ERROR
|
|
|
|
@note ERROR can be only due to abnormal operations involving
|
|
the server's execution evironment such as
|
|
out of memory, hard disk failure or the computer blows up.
|
|
Consider set_var::check() method if there is a need to return
|
|
an error due to logics.
|
|
*/
|
|
|
|
int set_var::update(THD *thd)
|
|
{
|
|
return value ? var->update(thd, this) : var->set_default(thd, this);
|
|
}
|
|
|
|
|
|
set_var::set_var(THD *thd, enum_var_type type_arg, sys_var *var_arg,
|
|
const LEX_CSTRING *base_name_arg, Item *value_arg)
|
|
:var(var_arg), type(type_arg), base(*base_name_arg)
|
|
{
|
|
/*
|
|
If the set value is a field, change it to a string to allow things like
|
|
SET table_type=MYISAM;
|
|
*/
|
|
if (value_arg && value_arg->type() == Item::FIELD_ITEM)
|
|
{
|
|
Item_field *item= (Item_field*) value_arg;
|
|
// names are utf8
|
|
if (!(value= new (thd->mem_root) Item_string_sys(thd,
|
|
item->field_name.str,
|
|
(uint)item->field_name.length)))
|
|
value=value_arg; /* Give error message later */
|
|
}
|
|
else
|
|
value=value_arg;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
Functions to handle SET @user_variable=const_expr
|
|
*****************************************************************************/
|
|
|
|
int set_var_user::check(THD *thd)
|
|
{
|
|
/*
|
|
Item_func_set_user_var can't substitute something else on its place =>
|
|
0 can be passed as last argument (reference on item)
|
|
*/
|
|
thd->where= THD_WHERE::SET_LIST;
|
|
return (user_var_item->fix_fields(thd, (Item**) 0) ||
|
|
user_var_item->check(0)) ? -1 : 0;
|
|
}
|
|
|
|
|
|
/**
|
|
Check variable, but without assigning value (used by PS).
|
|
|
|
@param thd thread handler
|
|
|
|
@retval
|
|
0 ok
|
|
@retval
|
|
1 ERROR, message sent (normally no variables was updated)
|
|
@retval
|
|
-1 ERROR, message not sent
|
|
*/
|
|
int set_var_user::light_check(THD *thd)
|
|
{
|
|
/*
|
|
Item_func_set_user_var can't substitute something else on its place =>
|
|
0 can be passed as last argument (reference on item)
|
|
*/
|
|
return (user_var_item->fix_fields(thd, (Item**) 0));
|
|
}
|
|
|
|
|
|
int set_var_user::update(THD *thd)
|
|
{
|
|
if (user_var_item->update())
|
|
{
|
|
/* Give an error if it's not given already */
|
|
my_message(ER_SET_CONSTANTS_ONLY, ER_THD(thd, ER_SET_CONSTANTS_ONLY),
|
|
MYF(0));
|
|
return -1;
|
|
}
|
|
|
|
thd->session_tracker.state_change.mark_as_changed(thd);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
Functions to handle SET PASSWORD
|
|
*****************************************************************************/
|
|
|
|
int set_var_password::check(THD *thd)
|
|
{
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
return check_change_password(thd, user);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int set_var_password::update(THD *thd)
|
|
{
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
|
|
thd->m_reprepare_observer= 0;
|
|
int res= change_password(thd, user);
|
|
thd->m_reprepare_observer= save_reprepare_observer;
|
|
return res;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Functions to handle SET ROLE
|
|
*****************************************************************************/
|
|
|
|
int set_var_role::check(THD *thd)
|
|
{
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
int status= acl_check_setrole(thd, role, &access);
|
|
return status;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int set_var_role::update(THD *thd)
|
|
{
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
int res= acl_setrole(thd, role, access);
|
|
if (!res)
|
|
thd->session_tracker.state_change.mark_as_changed(thd);
|
|
return res;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Functions to handle SET DEFAULT ROLE
|
|
*****************************************************************************/
|
|
|
|
int set_var_default_role::check(THD *thd)
|
|
{
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
real_user= get_current_user(thd, user);
|
|
real_role= role;
|
|
if (role.str == current_role.str)
|
|
{
|
|
if (!thd->security_ctx->priv_role[0])
|
|
real_role= "NONE"_LEX_CSTRING;
|
|
else
|
|
real_role= Lex_cstring_strlen(thd->security_ctx->priv_role);
|
|
}
|
|
|
|
return acl_check_set_default_role(thd, real_user->host,
|
|
real_user->user, real_role);
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
int set_var_default_role::update(THD *thd)
|
|
{
|
|
#ifndef NO_EMBEDDED_ACCESS_CHECKS
|
|
Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
|
|
thd->m_reprepare_observer= 0;
|
|
int res= acl_set_default_role(thd, real_user->host, real_user->user,
|
|
real_role);
|
|
thd->m_reprepare_observer= save_reprepare_observer;
|
|
return res;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
/*****************************************************************************
|
|
Functions to handle SET NAMES and SET CHARACTER SET
|
|
*****************************************************************************/
|
|
|
|
int set_var_collation_client::check(THD *thd)
|
|
{
|
|
/* Currently, UCS-2 cannot be used as a client character set */
|
|
if (!is_supported_parser_charset(character_set_client))
|
|
{
|
|
my_error(ER_WRONG_VALUE_FOR_VAR, MYF(0), "character_set_client",
|
|
character_set_client->cs_name.str);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int set_var_collation_client::update(THD *thd)
|
|
{
|
|
thd->update_charset(character_set_client, collation_connection,
|
|
character_set_results);
|
|
|
|
/* Mark client collation variables as changed */
|
|
thd->session_tracker.sysvars.mark_as_changed(thd,
|
|
Sys_character_set_client_ptr);
|
|
thd->session_tracker.sysvars.mark_as_changed(thd,
|
|
Sys_character_set_results_ptr);
|
|
thd->session_tracker.sysvars.mark_as_changed(thd,
|
|
Sys_character_set_connection_ptr);
|
|
thd->session_tracker.state_change.mark_as_changed(thd);
|
|
|
|
thd->protocol_text.init(thd);
|
|
thd->protocol_binary.init(thd);
|
|
return 0;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
INFORMATION_SCHEMA.SYSTEM_VARIABLES
|
|
*****************************************************************************/
|
|
static void store_value_ptr(Field *field, sys_var *var, String *str,
|
|
const uchar *value_ptr)
|
|
{
|
|
field->set_notnull();
|
|
str= var->val_str_nolock(str, field->table->in_use, value_ptr);
|
|
if (str)
|
|
field->store(str->ptr(), str->length(), str->charset());
|
|
}
|
|
|
|
static void store_var(Field *field, sys_var *var, enum_var_type scope,
|
|
String *str)
|
|
{
|
|
if (var->check_type(scope))
|
|
return;
|
|
|
|
store_value_ptr(field, var, str,
|
|
var->value_ptr(field->table->in_use, scope, &null_clex_str));
|
|
}
|
|
|
|
int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond)
|
|
{
|
|
bool res= 1;
|
|
CHARSET_INFO *scs= system_charset_info;
|
|
StringBuffer<STRING_BUFFER_USUAL_SIZE> strbuf(scs);
|
|
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : 0;
|
|
Field **fields=tables->table->field;
|
|
bool has_file_acl= !check_access(thd, FILE_ACL, any_db.str, NULL,NULL,0,1);
|
|
|
|
DBUG_ASSERT(tables->table->in_use == thd);
|
|
|
|
cond= make_cond_for_info_schema(thd, cond, tables);
|
|
mysql_prlock_rdlock(&LOCK_system_variables_hash);
|
|
|
|
for (uint i= 0; i < system_variable_hash.records; i++)
|
|
{
|
|
sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i);
|
|
CharBuffer<NAME_CHAR_LEN> name_buffer;
|
|
name_buffer.copy_caseup(scs, var->name);
|
|
|
|
/* this must be done before evaluating cond */
|
|
restore_record(tables->table, s->default_values);
|
|
fields[0]->store(name_buffer.to_lex_cstring(), scs);
|
|
|
|
if ((wild && wild_case_compare(scs, name_buffer.ptr(), wild))
|
|
|| (cond && !cond->val_bool()))
|
|
continue;
|
|
|
|
mysql_mutex_lock(&LOCK_global_system_variables);
|
|
|
|
// SESSION_VALUE
|
|
store_var(fields[1], var, OPT_SESSION, &strbuf);
|
|
|
|
// GLOBAL_VALUE
|
|
store_var(fields[2], var, OPT_GLOBAL, &strbuf);
|
|
|
|
// GLOBAL_VALUE_ORIGIN
|
|
static const LEX_CSTRING origins[]=
|
|
{
|
|
{ STRING_WITH_LEN("CONFIG") },
|
|
{ STRING_WITH_LEN("COMMAND-LINE") },
|
|
{ STRING_WITH_LEN("AUTO") },
|
|
{ STRING_WITH_LEN("SQL") },
|
|
{ STRING_WITH_LEN("COMPILE-TIME") },
|
|
{ STRING_WITH_LEN("ENVIRONMENT") }
|
|
};
|
|
const LEX_CSTRING *origin= origins + var->value_origin;
|
|
fields[3]->store(origin->str, origin->length, scs);
|
|
|
|
// DEFAULT_VALUE
|
|
const uchar *def= var->is_readonly() && var->option.id < 0
|
|
? 0 : var->default_value_ptr(thd);
|
|
if (def)
|
|
store_value_ptr(fields[4], var, &strbuf, def);
|
|
|
|
mysql_mutex_unlock(&LOCK_global_system_variables);
|
|
|
|
// VARIABLE_SCOPE
|
|
static const LEX_CSTRING scopes[]=
|
|
{
|
|
{ STRING_WITH_LEN("GLOBAL") },
|
|
{ STRING_WITH_LEN("SESSION") },
|
|
{ STRING_WITH_LEN("SESSION ONLY") }
|
|
};
|
|
const LEX_CSTRING *scope= scopes + var->scope();
|
|
fields[5]->store(scope->str, scope->length, scs);
|
|
|
|
// VARIABLE_TYPE
|
|
#if SIZEOF_LONG == SIZEOF_INT
|
|
#define LONG_TYPE "INT"
|
|
#else
|
|
#define LONG_TYPE "BIGINT"
|
|
#endif
|
|
|
|
static const LEX_CSTRING types[]=
|
|
{
|
|
{ 0, 0 }, // unused 0
|
|
{ 0, 0 }, // GET_NO_ARG 1
|
|
{ STRING_WITH_LEN("BOOLEAN") }, // GET_BOOL 2
|
|
{ STRING_WITH_LEN("INT") }, // GET_INT 3
|
|
{ STRING_WITH_LEN("INT UNSIGNED") }, // GET_UINT 4
|
|
{ STRING_WITH_LEN(LONG_TYPE) }, // GET_LONG 5
|
|
{ STRING_WITH_LEN(LONG_TYPE " UNSIGNED") }, // GET_ULONG 6
|
|
{ STRING_WITH_LEN("BIGINT") }, // GET_LL 7
|
|
{ STRING_WITH_LEN("BIGINT UNSIGNED") }, // GET_ULL 8
|
|
{ STRING_WITH_LEN("VARCHAR") }, // GET_STR 9
|
|
{ STRING_WITH_LEN("VARCHAR") }, // GET_STR_ALLOC 10
|
|
{ 0, 0 }, // GET_DISABLED 11
|
|
{ STRING_WITH_LEN("ENUM") }, // GET_ENUM 12
|
|
{ STRING_WITH_LEN("SET") }, // GET_SET 13
|
|
{ STRING_WITH_LEN("DOUBLE") }, // GET_DOUBLE 14
|
|
{ STRING_WITH_LEN("FLAGSET") }, // GET_FLAGSET 15
|
|
{ STRING_WITH_LEN("BOOLEAN") }, // GET_BIT 16
|
|
};
|
|
const ulong vartype= (var->option.var_type & GET_TYPE_MASK);
|
|
const LEX_CSTRING *type= types + vartype;
|
|
fields[6]->store(type->str, type->length, scs);
|
|
|
|
// VARIABLE_COMMENT
|
|
fields[7]->store(var->option.comment, strlen(var->option.comment),
|
|
scs);
|
|
|
|
// NUMERIC_MIN_VALUE
|
|
// NUMERIC_MAX_VALUE
|
|
// NUMERIC_BLOCK_SIZE
|
|
bool is_unsigned= true;
|
|
switch (vartype)
|
|
{
|
|
case GET_INT:
|
|
case GET_LONG:
|
|
case GET_LL:
|
|
is_unsigned= false;
|
|
/* fall through */
|
|
case GET_UINT:
|
|
case GET_ULONG:
|
|
case GET_ULL:
|
|
fields[8]->set_notnull();
|
|
fields[9]->set_notnull();
|
|
fields[10]->set_notnull();
|
|
fields[8]->store(var->option.min_value, is_unsigned);
|
|
fields[9]->store(var->option.max_value, is_unsigned);
|
|
fields[10]->store(var->option.block_size, is_unsigned);
|
|
break;
|
|
case GET_DOUBLE:
|
|
fields[8]->set_notnull();
|
|
fields[9]->set_notnull();
|
|
fields[8]->store(getopt_ulonglong2double(var->option.min_value));
|
|
fields[9]->store(getopt_ulonglong2double(var->option.max_value));
|
|
}
|
|
|
|
// ENUM_VALUE_LIST
|
|
TYPELIB *tl= var->option.typelib;
|
|
if (tl)
|
|
{
|
|
uint i;
|
|
strbuf.length(0);
|
|
for (i=0; i < tl->count; i++)
|
|
{
|
|
const char *name= tl->type_names[i];
|
|
strbuf.append(name, strlen(name));
|
|
strbuf.append(',');
|
|
}
|
|
if (!strbuf.is_empty())
|
|
strbuf.chop();
|
|
fields[11]->set_notnull();
|
|
fields[11]->store(strbuf.ptr(), strbuf.length(), scs);
|
|
}
|
|
|
|
// READ_ONLY
|
|
static const LEX_CSTRING yesno[]=
|
|
{
|
|
{ STRING_WITH_LEN("NO") },
|
|
{ STRING_WITH_LEN("YES") }
|
|
};
|
|
const LEX_CSTRING *yn = yesno + var->is_readonly();
|
|
fields[12]->store(yn->str, yn->length, scs);
|
|
|
|
// COMMAND_LINE_ARGUMENT
|
|
if (var->option.id >= 0)
|
|
{
|
|
static const LEX_CSTRING args[]=
|
|
{
|
|
{ STRING_WITH_LEN("NONE") }, // NO_ARG
|
|
{ STRING_WITH_LEN("OPTIONAL") }, // OPT_ARG
|
|
{ STRING_WITH_LEN("REQUIRED") } // REQUIRED_ARG
|
|
};
|
|
const LEX_CSTRING *arg= args + var->option.arg_type;
|
|
fields[13]->set_notnull();
|
|
fields[13]->store(arg->str, arg->length, scs);
|
|
}
|
|
|
|
// GLOBAL_VALUE_PATH
|
|
if (var->value_origin == sys_var::CONFIG && has_file_acl)
|
|
{
|
|
fields[14]->set_notnull();
|
|
fields[14]->store(var->origin_filename, strlen(var->origin_filename),
|
|
files_charset_info);
|
|
}
|
|
|
|
if (schema_table_store_record(thd, tables->table))
|
|
goto end;
|
|
thd->get_stmt_da()->inc_current_row_for_warning();
|
|
}
|
|
res= 0;
|
|
end:
|
|
mysql_prlock_unlock(&LOCK_system_variables_hash);
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
This is a simple and inefficient helper that sets sys_var::value_origin
|
|
for a specific sysvar.
|
|
It should *only* be used on server startup, if you need to do this later,
|
|
get yourself a pointer to your sysvar (see e.g. Sys_autocommit_ptr)
|
|
and update it directly.
|
|
*/
|
|
|
|
void set_sys_var_value_origin(void *ptr, enum sys_var::where here,
|
|
const char *filename)
|
|
{
|
|
bool found __attribute__((unused))= false;
|
|
DBUG_ASSERT(!mysqld_server_started); // only to be used during startup
|
|
|
|
for (uint i= 0; i < system_variable_hash.records; i++)
|
|
{
|
|
sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i);
|
|
if (var->option.value == ptr)
|
|
{
|
|
found= true;
|
|
var->origin_filename= filename;
|
|
var->value_origin= here;
|
|
/* don't break early, search for all matches */
|
|
}
|
|
}
|
|
|
|
DBUG_ASSERT(found); // variable must have been found
|
|
}
|
|
|
|
enum sys_var::where get_sys_var_value_origin(void *ptr)
|
|
{
|
|
DBUG_ASSERT(!mysqld_server_started); // only to be used during startup
|
|
|
|
for (uint i= 0; i < system_variable_hash.records; i++)
|
|
{
|
|
sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i);
|
|
if (var->option.value == ptr)
|
|
{
|
|
return var->value_origin; //first match
|
|
}
|
|
}
|
|
|
|
DBUG_ASSERT(0); // variable must have been found
|
|
return sys_var::CONFIG;
|
|
}
|
|
|
|
|
|
/*
|
|
Find the next item in string of comma-separated items.
|
|
END_POS points at the end of the string.
|
|
ITEM_START and ITEM_END return the limits of the next item.
|
|
Returns true while items are available, false at the end.
|
|
*/
|
|
static bool
|
|
engine_list_next_item(const char **pos, const char *end_pos,
|
|
const char **item_start, const char **item_end)
|
|
{
|
|
if (*pos >= end_pos)
|
|
return false;
|
|
*item_start= *pos;
|
|
while (*pos < end_pos && **pos != ',')
|
|
++*pos;
|
|
*item_end= *pos;
|
|
++*pos;
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool
|
|
resolve_engine_list_item(THD *thd, plugin_ref *list, uint32 *idx,
|
|
const char *pos, const char *pos_end,
|
|
bool error_on_unknown_engine, bool temp_copy)
|
|
{
|
|
LEX_CSTRING item_str;
|
|
plugin_ref ref;
|
|
uint32 i;
|
|
THD *thd_or_null = (temp_copy ? thd : NULL);
|
|
|
|
item_str.str= pos;
|
|
item_str.length= pos_end-pos;
|
|
ref= ha_resolve_by_name(thd_or_null, &item_str, false);
|
|
if (!ref)
|
|
{
|
|
if (error_on_unknown_engine)
|
|
{
|
|
ErrConvString err(pos, pos_end-pos, system_charset_info);
|
|
my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), err.ptr());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
/* Ignore duplicates, like --plugin-load does. */
|
|
for (i= 0; i < *idx; ++i)
|
|
{
|
|
if (plugin_hton(list[i]) == plugin_hton(ref))
|
|
{
|
|
if (!temp_copy)
|
|
plugin_unlock(NULL, ref);
|
|
return false;
|
|
}
|
|
}
|
|
list[*idx]= ref;
|
|
++*idx;
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
Helper for class Sys_var_pluginlist.
|
|
Resolve a comma-separated list of storage engine names to a null-terminated
|
|
array of plugin_ref.
|
|
|
|
If TEMP_COPY is true, a THD must be given as well. In this case, the
|
|
allocated memory and locked plugins are registered in the THD and will
|
|
be freed / unlocked automatically. If TEMP_COPY is true, THD can be
|
|
passed as NULL, and resources must be freed explicitly later with
|
|
free_engine_list().
|
|
*/
|
|
plugin_ref *
|
|
resolve_engine_list(THD *thd, const char *str_arg, size_t str_arg_len,
|
|
bool error_on_unknown_engine, bool temp_copy)
|
|
{
|
|
uint32 count, idx;
|
|
const char *pos, *item_start, *item_end;
|
|
const char *str_arg_end= str_arg + str_arg_len;
|
|
plugin_ref *res;
|
|
|
|
count= 0;
|
|
pos= str_arg;
|
|
for (;;)
|
|
{
|
|
if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end))
|
|
break;
|
|
++count;
|
|
}
|
|
|
|
if (temp_copy)
|
|
res= thd->calloc<plugin_ref>(count+1);
|
|
else
|
|
res= (plugin_ref *)my_malloc(PSI_INSTRUMENT_ME, (count+1)*sizeof(*res), MYF(MY_ZEROFILL|MY_WME));
|
|
if (!res)
|
|
{
|
|
my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*res)));
|
|
goto err;
|
|
}
|
|
|
|
idx= 0;
|
|
pos= str_arg;
|
|
for (;;)
|
|
{
|
|
if (!engine_list_next_item(&pos, str_arg_end, &item_start, &item_end))
|
|
break;
|
|
DBUG_ASSERT(idx < count);
|
|
if (idx >= count)
|
|
break;
|
|
if (resolve_engine_list_item(thd, res, &idx, item_start, item_end,
|
|
error_on_unknown_engine, temp_copy))
|
|
goto err;
|
|
}
|
|
|
|
return res;
|
|
|
|
err:
|
|
if (!temp_copy)
|
|
free_engine_list(res);
|
|
return NULL;
|
|
}
|
|
|
|
|
|
void
|
|
free_engine_list(plugin_ref *list)
|
|
{
|
|
plugin_ref *p;
|
|
|
|
if (!list)
|
|
return;
|
|
for (p= list; *p; ++p)
|
|
plugin_unlock(NULL, *p);
|
|
my_free(list);
|
|
}
|
|
|
|
|
|
plugin_ref *
|
|
copy_engine_list(plugin_ref *list)
|
|
{
|
|
plugin_ref *p;
|
|
uint32 count, i;
|
|
|
|
for (p= list, count= 0; *p; ++p, ++count)
|
|
;
|
|
p= (plugin_ref *)my_malloc(PSI_INSTRUMENT_ME, (count+1)*sizeof(*p), MYF(0));
|
|
if (!p)
|
|
{
|
|
my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p)));
|
|
return NULL;
|
|
}
|
|
for (i= 0; i < count; ++i)
|
|
p[i]= my_plugin_lock(NULL, list[i]);
|
|
p[i] = NULL;
|
|
return p;
|
|
}
|
|
|
|
|
|
/*
|
|
Create a temporary copy of an engine list. The memory will be freed
|
|
(and the plugins unlocked) automatically, on the passed THD.
|
|
*/
|
|
plugin_ref *
|
|
temp_copy_engine_list(THD *thd, plugin_ref *list)
|
|
{
|
|
plugin_ref *p;
|
|
uint32 count, i;
|
|
|
|
for (p= list, count= 0; *p; ++p, ++count)
|
|
;
|
|
p= thd->alloc<plugin_ref>(count+1);
|
|
if (!p)
|
|
{
|
|
my_error(ER_OUTOFMEMORY, MYF(0), (int)((count+1)*sizeof(*p)));
|
|
return NULL;
|
|
}
|
|
for (i= 0; i < count; ++i)
|
|
p[i]= my_plugin_lock(thd, list[i]);
|
|
p[i] = NULL;
|
|
return p;
|
|
}
|
|
|
|
|
|
char *
|
|
pretty_print_engine_list(THD *thd, plugin_ref *list)
|
|
{
|
|
plugin_ref *p;
|
|
size_t size;
|
|
char *buf, *pos;
|
|
|
|
if (!list || !*list)
|
|
return thd->strmake("", 0);
|
|
|
|
size= 0;
|
|
for (p= list; *p; ++p)
|
|
size+= plugin_name(*p)->length + 1;
|
|
buf= thd->alloc(size);
|
|
if (!buf)
|
|
return NULL;
|
|
pos= buf;
|
|
for (p= list; *p; ++p)
|
|
{
|
|
LEX_CSTRING *name;
|
|
size_t remain;
|
|
|
|
remain= buf + size - pos;
|
|
DBUG_ASSERT(remain > 0);
|
|
if (remain <= 1)
|
|
break;
|
|
if (pos != buf)
|
|
{
|
|
pos= strmake(pos, ",", remain-1);
|
|
--remain;
|
|
}
|
|
name= plugin_name(*p);
|
|
pos= strmake(pos, name->str, MY_MIN(name->length, remain-1));
|
|
}
|
|
*pos= '\0';
|
|
return buf;
|
|
}
|
|
|
|
/*
|
|
Current version of the system_variable_hash.
|
|
Requires lock on LOCK_system_variables_hash.
|
|
*/
|
|
ulonglong get_system_variable_hash_version(void)
|
|
{
|
|
return system_variable_hash_version;
|
|
}
|