Commit 7099d7bc authored by Tatiana A. Nurnberg's avatar Tatiana A. Nurnberg

Bug#40657: assertion with out of range variables and traditional sql_mode

In STRICT mode, out-of-bounds values caused an error message
to be queued (rather than just a warning), without any further
error-like processing happening. (The error is queued during
update, at which time it's too late. For it to be processed
properly, it would need to be queued during check-stage.)
The assertion rightfully complains that we're trying to send
an OK while having an error queued.

Changeset breaks a lot of tests out into check-stage. This also
allows us to send more correct warnings/error messages.


sql/set_var.cc:
  cleanup: fold get_unsigned() and fix_unsigned() into one,
  as well as all the semi-common code from the ::check
  functions.
parent d1fb6bbc
...@@ -134,8 +134,6 @@ static int check_max_delayed_threads(THD *thd, set_var *var); ...@@ -134,8 +134,6 @@ static int check_max_delayed_threads(THD *thd, set_var *var);
static void fix_thd_mem_root(THD *thd, enum_var_type type); static void fix_thd_mem_root(THD *thd, enum_var_type type);
static void fix_trans_mem_root(THD *thd, enum_var_type type); static void fix_trans_mem_root(THD *thd, enum_var_type type);
static void fix_server_id(THD *thd, enum_var_type type); static void fix_server_id(THD *thd, enum_var_type type);
static int get_unsigned(THD *thd, set_var *var);
static bool fix_unsigned(THD *, ulonglong *, bool, const struct my_option *);
bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd, bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
const char *name, longlong val); 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);
...@@ -1406,42 +1404,49 @@ bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd, ...@@ -1406,42 +1404,49 @@ bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd,
/** /**
check an unsigned user-supplied value for a systemvariable against bounds. check an unsigned user-supplied value for a systemvariable against bounds.
if we needed to adjust the value, throw a warning/error.
@param thd thread handle @param thd thread handle
@param num the value the user gave @param num the value to limit
@param warn throw warning/error (FALSE if we get here from @param option_limits the bounds-record, or NULL
get_unsigned(), so we don't throw two warnings if
user supplies negative value to an unsigned variable)
@param option_limits the bounds-record
@retval TRUE on error, FALSE otherwise (warning or OK) @retval whether or not we needed to bound
*/ */
static bool fix_unsigned(THD *thd, ulonglong *num, bool warn, static my_bool bound_unsigned(THD *thd, ulonglong *num,
const struct my_option *option_limits) const struct my_option *option_limits)
{ {
my_bool fixed = FALSE; my_bool fixed = FALSE;
ulonglong unadjusted= *num; ulonglong unadjusted= *num;
*num= getopt_ull_limit_value(unadjusted, option_limits, &fixed); if (option_limits)
*num= getopt_ull_limit_value(unadjusted, option_limits, &fixed);
return warn && throw_bounds_warning(thd, fixed, TRUE, option_limits->name,
(longlong) unadjusted);
return fixed;
} }
/** /**
Get unsigned system-variable. Get unsigned system-variable.
Negative value does not wrap around, but becomes zero. Negative value does not wrap around, but becomes zero.
Check user-supplied value for a systemvariable against bounds.
If we needed to adjust the value, throw a warning or error depending
on SQL-mode.
@param thd thread handle @param thd thread handle
@param var the system-variable to get @param var the system-variable to get
@param user_max a limit given with --maximum-variable-name=... or 0
@param bound2ulong pass TRUE if size is ulong, not ulonglong. function
will then bound on systems where it's necessary.
@retval 0 - OK, 1 - warning, 2 - error @retval TRUE on error, FALSE otherwise (warning or OK)
*/ */
static int get_unsigned(THD *thd, set_var *var) static bool get_unsigned(THD *thd, set_var *var, ulonglong user_max,
my_bool bound2ulong)
{ {
int warnings= 0;
ulonglong unadjusted;
const struct my_option *limits= var->var->option_limits;
/* get_unsigned() */
if (var->value->unsigned_flag) if (var->value->unsigned_flag)
var->save_result.ulonglong_value= (ulonglong) var->value->val_int(); var->save_result.ulonglong_value= (ulonglong) var->value->val_int();
else else
...@@ -1449,43 +1454,71 @@ static int get_unsigned(THD *thd, set_var *var) ...@@ -1449,43 +1454,71 @@ static int get_unsigned(THD *thd, set_var *var)
longlong v= var->value->val_int(); longlong v= var->value->val_int();
var->save_result.ulonglong_value= (ulonglong) ((v < 0) ? 0 : v); var->save_result.ulonglong_value= (ulonglong) ((v < 0) ? 0 : v);
if (v < 0) if (v < 0)
return throw_bounds_warning(thd, TRUE, FALSE, var->var->name, v) ? 2 : 1; {
warnings++;
if (throw_bounds_warning(thd, TRUE, FALSE, var->var->name, v))
return TRUE; /* warning was promoted to error, give up */
}
} }
return 0;
}
unadjusted= var->save_result.ulonglong_value;
sys_var_long_ptr:: /* max, if any */
sys_var_long_ptr(sys_var_chain *chain, const char *name_arg, ulong *value_ptr_arg,
sys_after_update_func after_update_arg)
:sys_var_long_ptr_global(chain, name_arg, value_ptr_arg,
&LOCK_global_system_variables, after_update_arg)
{}
if ((user_max > 0) && (unadjusted > user_max))
{
var->save_result.ulonglong_value= user_max;
bool sys_var_long_ptr_global::check(THD *thd, set_var *var) if ((warnings == 0) && throw_bounds_warning(thd, TRUE, TRUE,
{ var->var->name,
bool ret = FALSE; (longlong) unadjusted))
int got_warnings= get_unsigned(thd, var); return TRUE;
if (got_warnings == 2) warnings++;
ret= TRUE; }
else if (option_limits)
ret= fix_unsigned(thd, &var->save_result.ulonglong_value, /* fix_unsigned() */
(got_warnings == 0), option_limits); if (limits)
else {
my_bool fixed;
var->save_result.ulonglong_value= getopt_ull_limit_value(unadjusted,
limits, &fixed);
if ((warnings == 0) && throw_bounds_warning(thd, fixed, TRUE, limits->name,
(longlong) unadjusted))
return TRUE;
}
else if (bound2ulong)
{ {
#if SIZEOF_LONG < SIZEOF_LONG_LONG #if SIZEOF_LONG < SIZEOF_LONG_LONG
/* Avoid overflows on 32 bit systems */ /* Avoid overflows on 32 bit systems */
if (var->save_result.ulonglong_value > ULONG_MAX) if (var->save_result.ulonglong_value > ULONG_MAX)
{ {
ret= throw_bounds_warning(thd, TRUE, TRUE, name,
(longlong) var->save_result.ulonglong_value);
var->save_result.ulonglong_value= ULONG_MAX; var->save_result.ulonglong_value= ULONG_MAX;
if ((warnings == 0) && throw_bounds_warning(thd, TRUE, TRUE,
var->var->name,
(longlong) unadjusted))
return TRUE;
} }
#endif #endif
} }
return ret;
return FALSE;
}
sys_var_long_ptr::
sys_var_long_ptr(sys_var_chain *chain, const char *name_arg, ulong *value_ptr_arg,
sys_after_update_func after_update_arg)
:sys_var_long_ptr_global(chain, name_arg, value_ptr_arg,
&LOCK_global_system_variables, after_update_arg)
{}
bool sys_var_long_ptr_global::check(THD *thd, set_var *var)
{
return get_unsigned(thd, var, 0, TRUE);
} }
bool sys_var_long_ptr_global::update(THD *thd, set_var *var) bool sys_var_long_ptr_global::update(THD *thd, set_var *var)
...@@ -1511,8 +1544,7 @@ bool sys_var_ulonglong_ptr::update(THD *thd, set_var *var) ...@@ -1511,8 +1544,7 @@ bool sys_var_ulonglong_ptr::update(THD *thd, set_var *var)
{ {
ulonglong tmp= var->save_result.ulonglong_value; ulonglong tmp= var->save_result.ulonglong_value;
pthread_mutex_lock(&LOCK_global_system_variables); pthread_mutex_lock(&LOCK_global_system_variables);
if (option_limits) bound_unsigned(thd, &tmp, option_limits);
fix_unsigned(thd, &tmp, FALSE, option_limits);
*value= (ulonglong) tmp; *value= (ulonglong) tmp;
pthread_mutex_unlock(&LOCK_global_system_variables); pthread_mutex_unlock(&LOCK_global_system_variables);
return 0; return 0;
...@@ -1563,38 +1595,8 @@ uchar *sys_var_enum_const::value_ptr(THD *thd, enum_var_type type, ...@@ -1563,38 +1595,8 @@ uchar *sys_var_enum_const::value_ptr(THD *thd, enum_var_type type,
bool sys_var_thd_ulong::check(THD *thd, set_var *var) bool sys_var_thd_ulong::check(THD *thd, set_var *var)
{ {
ulonglong tmp; if (get_unsigned(thd, var, max_system_variables.*offset, TRUE))
int got_warnings= get_unsigned(thd, var);
if (got_warnings == 2)
return TRUE; return TRUE;
tmp= var->save_result.ulonglong_value;
/* Don't use bigger value than given with --maximum-variable-name=.. */
if ((ulong) tmp > max_system_variables.*offset)
{
if (throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) tmp))
return TRUE;
tmp= max_system_variables.*offset;
}
if (option_limits)
{
if (fix_unsigned(thd, &tmp, (got_warnings == 0), option_limits))
return TRUE;
}
#if SIZEOF_LONG < SIZEOF_LONG_LONG
else if (tmp > ULONG_MAX)
{
if (throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) tmp))
return TRUE;
tmp= ULONG_MAX;
}
#endif
var->save_result.ulonglong_value= (ulong) tmp;
return ((check_func && (*check_func)(thd, var))); return ((check_func && (*check_func)(thd, var)));
} }
...@@ -1641,8 +1643,7 @@ bool sys_var_thd_ha_rows::update(THD *thd, set_var *var) ...@@ -1641,8 +1643,7 @@ bool sys_var_thd_ha_rows::update(THD *thd, set_var *var)
if ((ha_rows) tmp > max_system_variables.*offset) if ((ha_rows) tmp > max_system_variables.*offset)
tmp= max_system_variables.*offset; tmp= max_system_variables.*offset;
if (option_limits) bound_unsigned(thd, &tmp, option_limits);
fix_unsigned(thd, &tmp, FALSE, option_limits);
if (var->type == OPT_GLOBAL) if (var->type == OPT_GLOBAL)
{ {
...@@ -1684,30 +1685,7 @@ uchar *sys_var_thd_ha_rows::value_ptr(THD *thd, enum_var_type type, ...@@ -1684,30 +1685,7 @@ uchar *sys_var_thd_ha_rows::value_ptr(THD *thd, enum_var_type type,
bool sys_var_thd_ulonglong::check(THD *thd, set_var *var) bool sys_var_thd_ulonglong::check(THD *thd, set_var *var)
{ {
ulonglong tmp; return get_unsigned(thd, var, max_system_variables.*offset, FALSE);
int got_warnings= get_unsigned(thd, var);
if (got_warnings == 2)
return TRUE;
tmp= var->save_result.ulonglong_value;
if (tmp > max_system_variables.*offset)
{
if (throw_bounds_warning(thd, TRUE, TRUE, name, (longlong) tmp))
return TRUE;
tmp= max_system_variables.*offset;
}
if (option_limits)
{
if (fix_unsigned(thd, &tmp, (got_warnings == 0), option_limits))
return TRUE;
}
var->save_result.ulonglong_value= tmp;
return FALSE;
} }
bool sys_var_thd_ulonglong::update(THD *thd, set_var *var) bool sys_var_thd_ulonglong::update(THD *thd, set_var *var)
...@@ -2353,7 +2331,7 @@ bool sys_var_key_buffer_size::update(THD *thd, set_var *var) ...@@ -2353,7 +2331,7 @@ bool sys_var_key_buffer_size::update(THD *thd, set_var *var)
goto end; goto end;
} }
fix_unsigned(thd, &tmp, FALSE, option_limits); bound_unsigned(thd, &tmp, option_limits);
key_cache->param_buff_size= (ulonglong) tmp; key_cache->param_buff_size= (ulonglong) tmp;
/* If key cache didn't existed initialize it, else resize it */ /* If key cache didn't existed initialize it, else resize it */
...@@ -2407,7 +2385,7 @@ bool sys_var_key_cache_long::update(THD *thd, set_var *var) ...@@ -2407,7 +2385,7 @@ bool sys_var_key_cache_long::update(THD *thd, set_var *var)
if (key_cache->in_init) if (key_cache->in_init)
goto end; goto end;
fix_unsigned(thd, &tmp, FALSE, option_limits); bound_unsigned(thd, &tmp, option_limits);
*((ulong*) (((char*) key_cache) + offset))= (ulong) tmp; *((ulong*) (((char*) key_cache) + offset))= (ulong) tmp;
/* /*
......
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