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-1301 USA */
#include "my_global.h"
#include "sql_class.h"
#include "set_var.h"
#include "sql_priv.h"
#include "unireg.h"
#include "mysqld.h"
#include "sys_vars_shared.h"
#include "sql_locale.h"
#include "strfunc.h"
#include "sql_parse.h"
#include "sql_table.h"
#include "sql_time.h"
#include "derror.h"
#include "sql_acl.h"
#include "sql_show.h"
#include "sql_view.h"
static HASH system_variable_hash;
static PolyLock_mutex PLock_global_system_variables(&LOCK_global_system_variables);
Return variable name and length for hashing of variables.
*/
static uchar *get_sys_var_length(const sys_var *var, size_t *length,
my_bool first)
{
*length= var->name.length;
return (uchar*) var->name.str;
}
sys_var_chain all_sys_vars = { NULL, NULL };
int sys_var_init()
{
DBUG_ENTER("sys_var_init");
DBUG_ASSERT(system_charset_info != NULL);
if (my_hash_init(&system_variable_hash, system_charset_info, 100, 0,
0, (my_hash_get_key) 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);
}
int sys_var_add_options(std::vector<my_option> *long_options, int parse_flags)
{
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");
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;
}
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.
@param parse_flag either PARSE_EARLY or PARSE_NORMAL
*/
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, int parse_flag) :
next(0),
binlog_status(binlog_status_arg),
flags(flags_arg), m_parse_flag(parse_flag), show_val_type(show_val_type_arg),
guard(lock), offset(off), on_check(on_check_func), on_update(on_update_func),
deprecation_substitute(substitute),
is_os_charset(FALSE)
{
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(parse_flag == PARSE_NORMAL || getopt_id <= 0 || getopt_id >= 255);
name.str= name_arg;
name.length= strlen(name_arg);
DBUG_ASSERT(name.length <= NAME_CHAR_LEN);
memset(&option, 0, 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;
if (chain->last)
chain->last->next= this;
else
chain->first= this;
chain->last= this;
}
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);
return global_update(thd, var) ||
(on_update && on_update(this, thd, OPT_GLOBAL));
}
else
return session_update(thd, var) ||
(on_update && on_update(this, thd, OPT_SESSION));
}
uchar *sys_var::session_value_ptr(THD *thd, LEX_STRING *base)
{
return session_var_ptr(thd);
}
uchar *sys_var::global_value_ptr(THD *thd, LEX_STRING *base)
{
return global_var_ptr();
}
uchar *sys_var::value_ptr(THD *thd, enum_var_type type, LEX_STRING *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 update(thd, var);
}
void sys_var::do_deprecated_warning(THD *thd)
{
if (deprecation_substitute != NULL)
{
char buf1[NAME_CHAR_LEN + 3];
strxnmov(buf1, sizeof(buf1)-1, "@@", name.str, 0);
if deprecation_substitute is an empty string,
there is no replacement for the syntax
*/
uint errmsg= deprecation_substitute[0] == '\0'
? ER_WARN_DEPRECATED_SYNTAX_NO_REPLACEMENT
: ER_WARN_DEPRECATED_SYNTAX;
if (thd)
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_WARN_DEPRECATED_SYNTAX, ER(errmsg),
buf1, deprecation_substitute);
else
sql_print_warning(ER_DEFAULT(errmsg), buf1, deprecation_substitute);
}
}
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,
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;
}
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), 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;
}
push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), name, buf);
}
return false;
}
const CHARSET_INFO *sys_var::charset(THD *thd)
{
return is_os_charset ? thd->variables.character_set_filesystem :
system_charset_info;
}
typedef struct old_names_map_st
{
const char *old_name;
const char *new_name;
} my_old_conv;
static my_old_conv old_conv[]=
{
{ "cp1251_koi8" , "cp1251" },
{ "cp1250_latin2" , "cp1250" },
{ "kam_latin2" , "keybcs2" },
{ "mac_latin2" , "MacRoman" },
{ "macce_latin2" , "MacCE" },
{ "pc2_latin2" , "pclatin2" },
{ "vga_latin2" , "pclatin1" },
{ "koi8_cp1251" , "koi8r" },
{ "win1251ukr_koi8_ukr" , "win1251ukr" },
{ "koi8_ukr_win1251ukr" , "koi8u" },
{ NULL , NULL }
};
const CHARSET_INFO *get_old_charset_by_name(const char *name)
{
my_old_conv *conv;
for (conv= old_conv; conv->old_name; conv++)
{
if (!my_strcasecmp(&my_charset_latin1, name, conv->old_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;
for (var= first; var; var= var->next)
{
if (my_hash_insert(&system_variable_hash, (uchar*) var))
{
fprintf(stderr, "*** duplicate variable name '%s' ?\n", var->name.str);
goto error;
}
}
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;
for (sys_var *var= first; var; var= var->next)
result|= my_hash_delete(&system_variable_hash, (uchar*) var);
return result;
}
static int show_cmp(SHOW_VAR *a, SHOW_VAR *b)
{
return strcmp(a->name, b->name);
}
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 type 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 type)
{
int count= system_variable_hash.records, i;
int size= sizeof(SHOW_VAR) * (count + 1);
SHOW_VAR *result= (SHOW_VAR*) thd->alloc(size);
if (result)
{
SHOW_VAR *show= result;
for (i= 0; i < count; i++)
{
sys_var *var= (sys_var*) my_hash_element(&system_variable_hash, i);
const my_bool *hidden=
getopt_constraint_get_hidden_value(var->name.str, var->name.length,
FALSE);
if (hidden && *hidden)
continue;
if (type == OPT_GLOBAL && var->check_type(type))
continue;
if (var->not_visible())
continue;
show->name= var->name.str;
show->value= (char*) var;
show->type= SHOW_SYS;
show++;
}
if (sorted)
my_qsort(result, show-result, sizeof(SHOW_VAR),
(qsort_cmp) show_cmp);
memset(show, 0, 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, uint 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));
if (var && var->not_visible())
return NULL;
return var;
}
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);
}
Self-print assignment
@param str string buffer to append the partial assignment to
*/
void set_var::print(THD *thd, String *str)
{
str->append(type == OPT_GLOBAL ? "GLOBAL " : "SESSION ");
if (base.length)
{
str->append(base.str, base.length);
str->append(STRING_WITH_LEN("."));
}
str->append(var->name.str,var->name.length);
str->append(STRING_WITH_LEN("="));
if (value)
value->print(str, QT_ORDINARY);
else
str->append(STRING_WITH_LEN("DEFAULT"));
}
void set_var_user::print(THD *thd, String *str)
{
user_var_item->print_assignment(str, QT_ORDINARY);
}
Functions to handle SET PASSWORD
*****************************************************************************/
void set_var_password::print(THD *thd, String *str)
{
if (user->user.str != NULL && user->user.length > 0)
{
str->append(STRING_WITH_LEN("PASSWORD FOR "));
append_identifier(thd, str, user->user.str, user->user.length);
if (user->host.str != NULL && user->host.length > 0)
{
str->append(STRING_WITH_LEN("@"));
append_identifier(thd, str, user->host.str, user->host.length);
}
str->append(STRING_WITH_LEN("="));
}
else
str->append(STRING_WITH_LEN("PASSWORD FOR CURRENT_USER()="));
str->append(STRING_WITH_LEN("<secret>"));
}
Functions to handle SET NAMES and SET CHARACTER SET
*****************************************************************************/
int set_var_collation_client::update(THD *thd)
{
thd->variables.character_set_client= character_set_client;
thd->variables.character_set_results= character_set_results;
thd->variables.collation_connection= collation_connection;
thd->update_charset();
return 0;
}
void set_var_collation_client::print(THD *thd, String *str)
{
str->append((set_cs_flags & SET_CS_NAMES) ? "NAMES " : "CHARACTER SET ");
if (set_cs_flags & SET_CS_DEFAULT)
str->append("DEFAULT");
else
{
str->append("'");
str->append(character_set_client->csname);
str->append("'");
if (set_cs_flags & SET_CS_COLLATE)
{
str->append(" COLLATE '");
str->append(collation_connection->name);
str->append("'");
}
}
}