mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
MDEV-14032 SEC_TO_TIME executes side effect two times
- Adding a helper class Sec6 to store (neg,seconds,microseconds) - Adding a helper class VSec6 (Sec6 with a flag for "IS NULL") - Wrapping related functions as methods of Sec6; * number_to_datetime() * number_to_time() * my_decimal2seconds() * Item::get_seconds() * A big piece of code in Item_func_sec_to_time::get_date() - Using the new classes in places where second-to-temporal conversion takes place: * Field_timestamp::store(double) * Field_timestamp::store(longlong) * Field_timestamp_with_dec::store_decimal(my_decimal) * Field_temporal_with_date::store(double) * Field_temporal_with_date::store(longlong) * Field_time::store(double) * Field_time::store(longlong) * Field_time::store_decimal(my_decimal) * Field_temporal_with_date::store_decimal(my_decimal) * get_interval_value() * Item_func_sec_to_time::get_date() * Item_func_from_unixtime::get_date() * Item_func_maketime::get_date() This change simplifies these methods and functions a lot. - Warnings are now sent at VSec6 initialization time, when the source data is available in its original data type representation. If Sec6::to_time() or Sec6::to_datetime() truncate data again during conversion to MYSQL_TIME, they send warnings, but only if no warnings were sent during VSec6 initialization. This helps prevents double warnings. The call for val_str() in Item_func_sec_to_time::get_date() is not needed any more, so it's removed. This change actually fixes the problem. As a good effect, FROM_UNIXTIME() and MAKETIME() now also send warnings in case if the seconds arguments is out of range. Previously these functions returned NULL silently. - Splitting the code in the global function make_truncated_value_warning() into a number of methods THD::raise_warning_xxxx(). This was needed to reuse the logic that chooses between: * ER_TRUNCATED_WRONG_VALUE * ER_WRONG_VALUE * ER_TRUNCATED_WRONG_VALUE_FOR_FIELD for non-temporal data types (Sec6). - Removing: * Item::get_seconds() * number_to_time_with_warn() as this code now resides inside methods of Sec6. - Cleanup (changes that are not directly related to the fix): * Removing calls for field_name_or_null() and passing NULL instead in Item_func_hybrid_field_type::get_date_from_{int|real}_op, because Item_func_hybrid_field_type::field_name_or_null() always returns NULL * Replacing a number of calls for make_truncated_value_warning() to calls for THD::raise_warning_xxx(). In these places we know that the execution went through a certain branch of make_truncated_value_warning(), (e.g. the exact error code is known, or field name is always NULL, or field name is always not-NULL). So calls for the entire make_truncated_value_warning() after splitting are not necessary.
This commit is contained in:
111
sql/sql_time.cc
111
sql/sql_time.cc
@ -313,8 +313,7 @@ adjust_time_range_with_warn(MYSQL_TIME *ltime, uint dec)
|
||||
if (check_time_range(ltime, dec, &warnings))
|
||||
return true;
|
||||
if (warnings)
|
||||
make_truncated_value_warning(current_thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
&str, MYSQL_TIMESTAMP_TIME, NullS);
|
||||
current_thd->push_warning_truncated_wrong_value("time", str.ptr());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -399,11 +398,14 @@ str_to_datetime_with_warn(CHARSET_INFO *cs,
|
||||
THD *thd= current_thd;
|
||||
bool ret_val= str_to_datetime(cs, str, length, l_time, flags, &status);
|
||||
if (ret_val || status.warnings)
|
||||
{
|
||||
const ErrConvString err(str, length, &my_charset_bin);
|
||||
make_truncated_value_warning(thd,
|
||||
ret_val ? Sql_condition::WARN_LEVEL_WARN :
|
||||
Sql_condition::time_warn_level(status.warnings),
|
||||
str, length, flags & TIME_TIME_ONLY ?
|
||||
&err, flags & TIME_TIME_ONLY ?
|
||||
MYSQL_TIMESTAMP_TIME : l_time->time_type, NullS);
|
||||
}
|
||||
DBUG_EXECUTE_IF("str_to_datetime_warn",
|
||||
push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
|
||||
ER_YES, str););
|
||||
@ -411,77 +413,11 @@ str_to_datetime_with_warn(CHARSET_INFO *cs,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
converts a pair of numbers (integer part, microseconds) to MYSQL_TIME
|
||||
|
||||
@param neg sign of the time value
|
||||
@param nr integer part of the number to convert
|
||||
@param sec_part microsecond part of the number
|
||||
@param ltime converted value will be written here
|
||||
@param fuzzydate conversion flags (TIME_INVALID_DATE, etc)
|
||||
@param str original number, as an ErrConv. For the warning
|
||||
@param field_name field name or NULL if not a field. For the warning
|
||||
|
||||
@returns 0 for success, 1 for a failure
|
||||
*/
|
||||
static bool number_to_time_with_warn(bool neg, ulonglong nr, ulong sec_part,
|
||||
MYSQL_TIME *ltime, ulonglong fuzzydate,
|
||||
const ErrConv *str,
|
||||
const char *field_name)
|
||||
{
|
||||
int was_cut;
|
||||
longlong res;
|
||||
enum_mysql_timestamp_type ts_type;
|
||||
bool have_warnings;
|
||||
|
||||
if (fuzzydate & TIME_TIME_ONLY)
|
||||
{
|
||||
fuzzydate= TIME_TIME_ONLY; // clear other flags
|
||||
ts_type= MYSQL_TIMESTAMP_TIME;
|
||||
res= number_to_time(neg, nr, sec_part, ltime, &was_cut);
|
||||
have_warnings= MYSQL_TIME_WARN_HAVE_WARNINGS(was_cut);
|
||||
}
|
||||
else
|
||||
{
|
||||
ts_type= MYSQL_TIMESTAMP_DATETIME;
|
||||
if (neg)
|
||||
{
|
||||
res= -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
res= number_to_datetime(nr, sec_part, ltime, fuzzydate, &was_cut);
|
||||
have_warnings= was_cut && (fuzzydate & TIME_NO_ZERO_IN_DATE);
|
||||
}
|
||||
}
|
||||
|
||||
if (res < 0 || have_warnings)
|
||||
{
|
||||
make_truncated_value_warning(current_thd,
|
||||
Sql_condition::WARN_LEVEL_WARN, str,
|
||||
res < 0 ? MYSQL_TIMESTAMP_ERROR : ts_type,
|
||||
field_name);
|
||||
}
|
||||
return res < 0;
|
||||
}
|
||||
|
||||
|
||||
bool double_to_datetime_with_warn(double value, MYSQL_TIME *ltime,
|
||||
ulonglong fuzzydate, const char *field_name)
|
||||
{
|
||||
const ErrConvDouble str(value);
|
||||
bool neg= value < 0;
|
||||
|
||||
if (neg)
|
||||
value= -value;
|
||||
|
||||
if (value > LONGLONG_MAX)
|
||||
value= static_cast<double>(LONGLONG_MAX);
|
||||
|
||||
longlong nr= static_cast<ulonglong>(floor(value));
|
||||
uint sec_part= static_cast<ulong>((value - floor(value))*TIME_SECOND_PART_FACTOR);
|
||||
return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str,
|
||||
field_name);
|
||||
return Sec6(value).convert_to_mysql_time(ltime, fuzzydate, &str, field_name);
|
||||
}
|
||||
|
||||
|
||||
@ -489,11 +425,7 @@ bool decimal_to_datetime_with_warn(const my_decimal *value, MYSQL_TIME *ltime,
|
||||
ulonglong fuzzydate, const char *field_name)
|
||||
{
|
||||
const ErrConvDecimal str(value);
|
||||
ulonglong nr;
|
||||
ulong sec_part;
|
||||
bool neg= my_decimal2seconds(value, &nr, &sec_part);
|
||||
return number_to_time_with_warn(neg, nr, sec_part, ltime, fuzzydate, &str,
|
||||
field_name);
|
||||
return Sec6(value).convert_to_mysql_time(ltime, fuzzydate, &str, field_name);
|
||||
}
|
||||
|
||||
|
||||
@ -501,8 +433,8 @@ bool int_to_datetime_with_warn(bool neg, ulonglong value, MYSQL_TIME *ltime,
|
||||
ulonglong fuzzydate, const char *field_name)
|
||||
{
|
||||
const ErrConvInteger str(neg ? - (longlong) value : (longlong) value, !neg);
|
||||
return number_to_time_with_warn(neg, value, 0, ltime,
|
||||
fuzzydate, &str, field_name);
|
||||
Sec6 sec(neg, value, 0);
|
||||
return sec.convert_to_mysql_time(ltime, fuzzydate, &str, field_name);
|
||||
}
|
||||
|
||||
|
||||
@ -932,9 +864,7 @@ void make_truncated_value_warning(THD *thd,
|
||||
timestamp_type time_type,
|
||||
const char *field_name)
|
||||
{
|
||||
char warn_buff[MYSQL_ERRMSG_SIZE];
|
||||
const char *type_str;
|
||||
CHARSET_INFO *cs= &my_charset_latin1;
|
||||
|
||||
switch (time_type) {
|
||||
case MYSQL_TIMESTAMP_DATE:
|
||||
@ -948,23 +878,9 @@ void make_truncated_value_warning(THD *thd,
|
||||
type_str= "datetime";
|
||||
break;
|
||||
}
|
||||
if (field_name)
|
||||
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
|
||||
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD),
|
||||
type_str, sval->ptr(), field_name,
|
||||
(ulong) thd->get_stmt_da()->current_row_for_warning());
|
||||
else
|
||||
{
|
||||
if (time_type > MYSQL_TIMESTAMP_ERROR)
|
||||
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
|
||||
ER_THD(thd, ER_TRUNCATED_WRONG_VALUE),
|
||||
type_str, sval->ptr());
|
||||
else
|
||||
cs->cset->snprintf(cs, warn_buff, sizeof(warn_buff),
|
||||
ER_THD(thd, ER_WRONG_VALUE), type_str, sval->ptr());
|
||||
}
|
||||
push_warning(thd, level,
|
||||
ER_TRUNCATED_WRONG_VALUE, warn_buff);
|
||||
return thd->push_warning_wrong_or_truncated_value(level,
|
||||
time_type <= MYSQL_TIMESTAMP_ERROR,
|
||||
type_str, sval->ptr(), field_name);
|
||||
}
|
||||
|
||||
|
||||
@ -1442,8 +1358,7 @@ time_to_datetime_with_warn(THD *thd,
|
||||
check_date(to, fuzzydate, &warn)))
|
||||
{
|
||||
ErrConvTime str(from);
|
||||
make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN,
|
||||
&str, MYSQL_TIMESTAMP_DATETIME, 0);
|
||||
thd->push_warning_truncated_wrong_value("datetime", str.ptr());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
Reference in New Issue
Block a user