Bug#32757: hang with sql_mode set when setting some global variables

If setting a system-variable provided by a plug-in failed, no OK or
error was sent in some cases, hanging the client. We now send an error
in the case from the ticket (integer-argument out of range in STRICT
mode). We also provide a semi-generic fallback message for possible
future cases like this where an error is signalled, but no message is
sent to the client. The error/warning handling is unified so it's the
same again for variables provided by plugins and those in the server
proper.
parent 60fc9157
...@@ -27,3 +27,30 @@ SET GLOBAL example_enum_var= e2; ...@@ -27,3 +27,30 @@ SET GLOBAL example_enum_var= e2;
SET GLOBAL example_enum_var= impossible; SET GLOBAL example_enum_var= impossible;
ERROR 42000: Variable 'enum_var' can't be set to the value of 'impossible' ERROR 42000: Variable 'enum_var' can't be set to the value of 'impossible'
UNINSTALL PLUGIN example; UNINSTALL PLUGIN example;
INSTALL PLUGIN example SONAME 'ha_example.so';
select @@session.sql_mode into @old_sql_mode;
set session sql_mode='';
set global example_ulong_var=500;
select @@global.example_ulong_var;
@@global.example_ulong_var
500
set global example_ulong_var=1111;
Warnings:
Warning 1292 Truncated incorrect ulong_var value: '1111'
select @@global.example_ulong_var;
@@global.example_ulong_var
1000
set session sql_mode='STRICT_ALL_TABLES';
set global example_ulong_var=500;
select @@global.example_ulong_var;
@@global.example_ulong_var
500
set global example_ulong_var=1111;
ERROR 42000: Variable 'ulong_var' can't be set to the value of '1111'
select @@global.example_ulong_var;
@@global.example_ulong_var
500
set session sql_mode=@old_sql_mode;
set session old=bla;
ERROR HY000: Variable 'old' is a read only variable
UNINSTALL PLUGIN example;
...@@ -39,3 +39,38 @@ SET GLOBAL example_enum_var= e2; ...@@ -39,3 +39,38 @@ SET GLOBAL example_enum_var= e2;
SET GLOBAL example_enum_var= impossible; SET GLOBAL example_enum_var= impossible;
UNINSTALL PLUGIN example; UNINSTALL PLUGIN example;
#
# Bug #32757 hang with sql_mode set when setting some global variables
#
INSTALL PLUGIN example SONAME 'ha_example.so';
select @@session.sql_mode into @old_sql_mode;
# first, try normal sql_mode (no error, send OK)
set session sql_mode='';
set global example_ulong_var=500;
select @@global.example_ulong_var;
# overflow -- correct value, but throw warning
set global example_ulong_var=1111;
select @@global.example_ulong_var;
# now, try STRICT (error occurrs, no message is sent, so send default)
set session sql_mode='STRICT_ALL_TABLES';
set global example_ulong_var=500;
select @@global.example_ulong_var;
# overflow -- throw warning, do NOT change value
--error ER_WRONG_VALUE_FOR_VAR
set global example_ulong_var=1111;
select @@global.example_ulong_var;
set session sql_mode=@old_sql_mode;
# finally, show that conditions that already raised an error are not
# adversely affected (error was already sent, do nothing)
--error ER_INCORRECT_GLOBAL_LOCAL_VAR
set session old=bla;
UNINSTALL PLUGIN example;
...@@ -123,7 +123,8 @@ static void fix_server_id(THD *thd, enum_var_type type); ...@@ -123,7 +123,8 @@ static void fix_server_id(THD *thd, enum_var_type type);
static ulonglong fix_unsigned(THD *thd, ulonglong num, static ulonglong fix_unsigned(THD *thd, ulonglong num,
const struct my_option *option_limits); const struct my_option *option_limits);
static bool get_unsigned(THD *thd, set_var *var); static bool get_unsigned(THD *thd, set_var *var);
static void throw_bounds_warning(THD *thd, const char *name, ulonglong num); bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
const char *name, longlong val);
static KEY_CACHE *create_key_cache(const char *name, uint length); static KEY_CACHE *create_key_cache(const char *name, uint length);
void fix_sql_mode_var(THD *thd, enum_var_type type); void fix_sql_mode_var(THD *thd, enum_var_type type);
static uchar *get_error_count(THD *thd); static uchar *get_error_count(THD *thd);
...@@ -1106,13 +1107,29 @@ static void fix_server_id(THD *thd, enum_var_type type) ...@@ -1106,13 +1107,29 @@ static void fix_server_id(THD *thd, enum_var_type type)
} }
static void throw_bounds_warning(THD *thd, const char *name, ulonglong num) bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
const char *name, longlong val)
{ {
char buf[22]; if (fixed)
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, {
ER_TRUNCATED_WRONG_VALUE, char buf[22];
ER(ER_TRUNCATED_WRONG_VALUE), name,
ullstr(num, buf)); if (unsignd)
ullstr((ulonglong) val, buf);
else
llstr(val, 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, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), name, buf);
}
return FALSE;
} }
static ulonglong fix_unsigned(THD *thd, ulonglong num, static ulonglong fix_unsigned(THD *thd, ulonglong num,
...@@ -1121,8 +1138,7 @@ static ulonglong fix_unsigned(THD *thd, ulonglong num, ...@@ -1121,8 +1138,7 @@ static ulonglong fix_unsigned(THD *thd, ulonglong num,
bool fixed= FALSE; bool fixed= FALSE;
ulonglong out= getopt_ull_limit_value(num, option_limits, &fixed); ulonglong out= getopt_ull_limit_value(num, option_limits, &fixed);
if (fixed) throw_bounds_warning(thd, fixed, TRUE, option_limits->name, (longlong) num);
throw_bounds_warning(thd, option_limits->name, num);
return out; return out;
} }
...@@ -1165,7 +1181,8 @@ bool sys_var_long_ptr_global::update(THD *thd, set_var *var) ...@@ -1165,7 +1181,8 @@ bool sys_var_long_ptr_global::update(THD *thd, set_var *var)
if (tmp > ULONG_MAX) if (tmp > ULONG_MAX)
{ {
tmp= ULONG_MAX; tmp= ULONG_MAX;
throw_bounds_warning(thd, name, var->save_result.ulonglong_value); throw_bounds_warning(thd, TRUE, TRUE, name,
(longlong) var->save_result.ulonglong_value);
} }
#endif #endif
*value= (ulong) tmp; *value= (ulong) tmp;
...@@ -1250,7 +1267,7 @@ bool sys_var_thd_ulong::update(THD *thd, set_var *var) ...@@ -1250,7 +1267,7 @@ bool sys_var_thd_ulong::update(THD *thd, set_var *var)
/* Don't use bigger value than given with --maximum-variable-name=.. */ /* Don't use bigger value than given with --maximum-variable-name=.. */
if ((ulong) tmp > max_system_variables.*offset) if ((ulong) tmp > max_system_variables.*offset)
{ {
throw_bounds_warning(thd, name, tmp); throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) tmp);
tmp= max_system_variables.*offset; tmp= max_system_variables.*offset;
} }
...@@ -1260,7 +1277,7 @@ bool sys_var_thd_ulong::update(THD *thd, set_var *var) ...@@ -1260,7 +1277,7 @@ bool sys_var_thd_ulong::update(THD *thd, set_var *var)
else if (tmp > ULONG_MAX) else if (tmp > ULONG_MAX)
{ {
tmp= ULONG_MAX; tmp= ULONG_MAX;
throw_bounds_warning(thd, name, var->save_result.ulonglong_value); throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) var->save_result.ulonglong_value);
} }
#endif #endif
......
...@@ -3185,6 +3185,18 @@ mysql_execute_command(THD *thd) ...@@ -3185,6 +3185,18 @@ mysql_execute_command(THD *thd)
thd->one_shot_set|= lex->one_shot_set; thd->one_shot_set|= lex->one_shot_set;
send_ok(thd); send_ok(thd);
} }
else
{
/*
We encountered some sort of error, but no message was sent.
Send something semi-generic here since we don't know which
assignment in the list caused the error.
*/
if (!thd->is_error())
my_error(ER_WRONG_ARGUMENTS,MYF(0),"SET");
goto error;
}
break; break;
} }
......
...@@ -210,6 +210,8 @@ static void reap_plugins(void); ...@@ -210,6 +210,8 @@ static void reap_plugins(void);
/* declared in set_var.cc */ /* declared in set_var.cc */
extern sys_var *intern_find_sys_var(const char *str, uint length, bool no_error); extern sys_var *intern_find_sys_var(const char *str, uint length, bool no_error);
extern bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
const char *name, longlong val);
#ifdef EMBEDDED_LIBRARY #ifdef EMBEDDED_LIBRARY
/* declared in sql_base.cc */ /* declared in sql_base.cc */
...@@ -1888,16 +1890,8 @@ static int check_func_int(THD *thd, struct st_mysql_sys_var *var, ...@@ -1888,16 +1890,8 @@ static int check_func_int(THD *thd, struct st_mysql_sys_var *var,
else else
*(int *)save= (int) getopt_ll_limit_value(tmp, &options, &fixed); *(int *)save= (int) getopt_ll_limit_value(tmp, &options, &fixed);
if (fixed) return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
{ var->name, (longlong) tmp);
char buf[22];
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), var->name,
ullstr(tmp, buf));
}
return (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) &&
(*(int *)save != (int) tmp);
} }
...@@ -1916,16 +1910,8 @@ static int check_func_long(THD *thd, struct st_mysql_sys_var *var, ...@@ -1916,16 +1910,8 @@ static int check_func_long(THD *thd, struct st_mysql_sys_var *var,
else else
*(long *)save= (long) getopt_ll_limit_value(tmp, &options, &fixed); *(long *)save= (long) getopt_ll_limit_value(tmp, &options, &fixed);
if (fixed) return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
{ var->name, (longlong) tmp);
char buf[22];
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), var->name,
ullstr(tmp, buf));
}
return (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) &&
(*(long *)save != (long) tmp);
} }
...@@ -1937,7 +1923,6 @@ static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var, ...@@ -1937,7 +1923,6 @@ static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var,
struct my_option options; struct my_option options;
value->val_int(value, &tmp); value->val_int(value, &tmp);
plugin_opt_set_limits(&options, var); plugin_opt_set_limits(&options, var);
*(ulonglong *)save= getopt_ull_limit_value(tmp, &options, &fixed);
if (var->flags & PLUGIN_VAR_UNSIGNED) if (var->flags & PLUGIN_VAR_UNSIGNED)
*(ulonglong *)save= getopt_ull_limit_value((ulonglong) tmp, &options, *(ulonglong *)save= getopt_ull_limit_value((ulonglong) tmp, &options,
...@@ -1945,16 +1930,8 @@ static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var, ...@@ -1945,16 +1930,8 @@ static int check_func_longlong(THD *thd, struct st_mysql_sys_var *var,
else else
*(longlong *)save= getopt_ll_limit_value(tmp, &options, &fixed); *(longlong *)save= getopt_ll_limit_value(tmp, &options, &fixed);
if (fixed) return throw_bounds_warning(thd, fixed, var->flags & PLUGIN_VAR_UNSIGNED,
{ var->name, (longlong) tmp);
char buf[22];
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE,
ER(ER_TRUNCATED_WRONG_VALUE), var->name,
ullstr(tmp, buf));
}
return (thd->variables.sql_mode & MODE_STRICT_ALL_TABLES) &&
(*(long long *)save != tmp);
} }
static int check_func_str(THD *thd, struct st_mysql_sys_var *var, static int check_func_str(THD *thd, struct st_mysql_sys_var *var,
......
...@@ -849,6 +849,7 @@ struct st_mysql_storage_engine example_storage_engine= ...@@ -849,6 +849,7 @@ struct st_mysql_storage_engine example_storage_engine=
{ MYSQL_HANDLERTON_INTERFACE_VERSION }; { MYSQL_HANDLERTON_INTERFACE_VERSION };
static ulong srv_enum_var= 0; static ulong srv_enum_var= 0;
static ulong srv_ulong_var= 0;
const char *enum_var_names[]= const char *enum_var_names[]=
{ {
...@@ -871,8 +872,21 @@ static MYSQL_SYSVAR_ENUM( ...@@ -871,8 +872,21 @@ static MYSQL_SYSVAR_ENUM(
0, // def 0, // def
&enum_var_typelib); // typelib &enum_var_typelib); // typelib
static MYSQL_SYSVAR_ULONG(
ulong_var,
srv_ulong_var,
PLUGIN_VAR_RQCMDARG,
"0..1000",
NULL,
NULL,
8,
0,
1000,
0);
static struct st_mysql_sys_var* example_system_variables[]= { static struct st_mysql_sys_var* example_system_variables[]= {
MYSQL_SYSVAR(enum_var), MYSQL_SYSVAR(enum_var),
MYSQL_SYSVAR(ulong_var),
NULL NULL
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment