mirror of
https://github.com/MariaDB/server.git
synced 2025-07-29 05:21:33 +03:00
MDEV-17563 Different results using table or view when comparing values of time type
MDEV-17625 Different warnings when comparing a garbage to DATETIME vs TIME - Splitting processes of data type conversion (to TIME/DATE,DATETIME) and warning generation. Warning are now only get collected during conversion (in an "int" variable), and are pushed in the very end of conversion (not in parallel). Warnings generated by the low level routines str_to_xxx() and number_to_xxx() can now be changed at the end, when TIME_FUZZY_DATES is applied, from "Invalid value" to "Truncated invalid value". Now "Illegal value" is issued only when the low level routine returned an error and TIME_FUZZY_DATES was not set. Otherwise, if the low level routine returned "false" (success), or if NULL was converted to a zero datetime by TIME_FUZZY_DATES, then "Truncated illegal value" is issued. This gives better warnings. - Methods Type_handler::Item_get_date() and Type_handler::Item_func_hybrid_field_type_get_date() now only convert and collect warning information, but do not push warnings. - Changing the return data type for Type_handler::Item_get_date() and Type_handler::Item_func_hybrid_field_type_get_date() from "bool" to "void". The conversion result (success vs error) can be checked by testing ltime->time_type. MYSQL_TIME_{NONE|ERROR} mean mean error, other values mean success. - Adding new wrapper methods Type_handler::Item_get_date_with_warn() and Type_handler::Item_func_hybrid_field_type_get_date_with_warn() to do conversion followed by raising warnings, and changing the code to call new Type_handler::***_with_warn() methods. - Adding a helper class Temporal::Status, a wrapper for MYSQL_TIME_STATUS with automatic initialization. - Adding a helper class Temporal::Warn, to collect warnings but without actually raising them. Moving a part of ErrConv into a separate class ErrBuff, and deriving both Temporal::Warn and ErrConv from ErrBuff. The ErrBuff part of Temporal::Warn is used to collect textual representation of the input data. - Adding a helper class Temporal::Warn_push. It's used to collect warning information during conversion, and automatically pushes warnings to the diagnostics area on its destructor time (in case of non-zero warning). - Moving more code from various functions inside class Temporal. - Adding more Temporal_hybrid constructors and protected Temporal methods make_from_xxx(), which convert and only collect warning information, but do not actually raise warnings. - Now the low level functions str_to_datetime() and str_to_time() always set status->warning if the return value is "true" (error). - Now the low level functions number_to_time() and number_to_datetime() set the "*was_cut" argument if the return value is "true" (error). - Adding a few DBUG_ASSERTs to make sure that str_to_xxx() and number_to_xxx() always set warnings on error. - Adding new warning flags MYSQL_TIME_WARN_EDOM and MYSQL_TIME_WARN_ZERO_DATE for the code symmetry. Before this change there was a special code path for (rc==true && was_cut==0) which was treated by Field_temporal::store_invalid_with_warning as "zero date violation". Now was_cut==0 always means that there are no any error/warnings/notes to be raised, not matter what rc is. - Using new Temporal_hybrid constructors in combination with Temporal::Warn_push inside str_to_datetime_with_warn(), double_to_datetime_with_warn(), int_to_datetime_with_warn(), Field::get_date(), Item::get_date_from_string(), and a few other places. - Removing methods Dec_ptr::to_datetime_with_warn(), Year::to_time_with_warn(), my_decimal::to_datetime_with_warn(), Dec_ptr::to_datetime_with_warn(). Fixing Sec6::to_time() and Sec6::to_datetime() to convert and only collect warnings, without raising warnings. Now warning raising functionality resides in Temporal::Warn_push. - Adding classes Longlong_hybrid_null and Double_null, to return both value and the "IS NULL" flag. Adding methods Item::to_double_null(), to_longlong_hybrid_null(), Item_func_hybrid_field_type::to_longlong_hybrid_null_op(), Item_func_hybrid_field_type::to_double_null_op(). Removing separate classes VInt and VInt_op, as they have been replaced by a single class Longlong_hybrid_null. - Adding a helper method Temporal::type_name_by_timestamp_type(), moving a part of make_truncated_value_warning() into it, and reusing in Temporal::Warn::push_conversion_warnings(). - Removing Item::make_zero_date() and Item_func_hybrid_field_type::make_zero_mysql_time(). They provided duplicate functionality. Now this code resides in Temporal::make_fuzzy_date(). The latter is now called for all Item types when data type conversion (to DATE/TIME/DATETIME) is involved, including Item_field and Item_direct_view_ref. This fixes MDEV-17563: Item_direct_view_ref now correctly converts NULL to a zero date when TIME_FUZZY_DATES says so.
This commit is contained in:
203
sql/sql_type.cc
203
sql/sql_type.cc
@ -160,15 +160,6 @@ date_mode_t Temporal::sql_mode_for_dates(THD *thd)
|
||||
}
|
||||
|
||||
|
||||
bool Dec_ptr::to_datetime_with_warn(THD *thd, MYSQL_TIME *to,
|
||||
date_mode_t fuzzydate, Item *item)
|
||||
{
|
||||
if (to_datetime_with_warn(thd, to, fuzzydate, item->field_name_or_null()))
|
||||
return item->null_value|= item->make_zero_date(to, fuzzydate);
|
||||
return item->null_value= false;
|
||||
}
|
||||
|
||||
|
||||
my_decimal *Temporal::to_decimal(my_decimal *to) const
|
||||
{
|
||||
return date2my_decimal(this, to);
|
||||
@ -182,6 +173,20 @@ my_decimal *Temporal::bad_to_decimal(my_decimal *to) const
|
||||
}
|
||||
|
||||
|
||||
void Temporal::make_from_str(THD *thd, Warn *warn,
|
||||
const char *str, size_t length,
|
||||
CHARSET_INFO *cs, date_mode_t fuzzydate)
|
||||
{
|
||||
DBUG_EXECUTE_IF("str_to_datetime_warn",
|
||||
push_warning(thd, Sql_condition::WARN_LEVEL_NOTE,
|
||||
ER_YES, ErrConvString(str, length,cs).ptr()););
|
||||
if (str_to_datetime(warn, str, length, cs, fuzzydate))
|
||||
make_fuzzy_date(&warn->warnings, fuzzydate);
|
||||
if (warn->warnings)
|
||||
warn->set_str(str, length, &my_charset_bin);
|
||||
}
|
||||
|
||||
|
||||
Temporal_hybrid::Temporal_hybrid(THD *thd, Item *item, date_mode_t fuzzydate)
|
||||
{
|
||||
if (item->get_date(thd, this, fuzzydate))
|
||||
@ -221,31 +226,30 @@ void Sec6::make_truncated_warning(THD *thd, const char *type_str) const
|
||||
}
|
||||
|
||||
|
||||
bool Sec6::convert_to_mysql_time(THD *thd, MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate, const ErrConv *str,
|
||||
const char *field_name) const
|
||||
bool Sec6::convert_to_mysql_time(THD *thd, int *warn, MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
int warn;
|
||||
bool is_time= bool(fuzzydate & TIME_TIME_ONLY);
|
||||
const char *typestr= is_time ? "time" : "datetime";
|
||||
bool rc= is_time ? to_time(ltime, &warn) :
|
||||
to_datetime(ltime, fuzzydate, &warn);
|
||||
bool rc= is_time ? to_time(ltime, warn) : to_datetime(ltime, fuzzydate, warn);
|
||||
DBUG_ASSERT(*warn || !rc);
|
||||
if (truncated())
|
||||
{
|
||||
// The value was already truncated at the constructor call time
|
||||
*warn|= MYSQL_TIME_WARN_TRUNCATED;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
void Temporal::push_conversion_warnings(THD *thd, bool totally_useless_value, int warn,
|
||||
const char *typestr,
|
||||
const char *field_name,
|
||||
const char *value)
|
||||
{
|
||||
if (MYSQL_TIME_WARN_HAVE_WARNINGS(warn))
|
||||
thd->push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN,
|
||||
!is_time, typestr,
|
||||
str->ptr(), field_name);
|
||||
}
|
||||
else if (rc || MYSQL_TIME_WARN_HAVE_WARNINGS(warn))
|
||||
thd->push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_WARN,
|
||||
rc, typestr, str->ptr(),
|
||||
field_name);
|
||||
totally_useless_value,
|
||||
typestr, value, field_name);
|
||||
else if (MYSQL_TIME_WARN_HAVE_NOTES(warn))
|
||||
thd->push_warning_wrong_or_truncated_value(Sql_condition::WARN_LEVEL_NOTE,
|
||||
rc, typestr, str->ptr(),
|
||||
field_name);
|
||||
return rc;
|
||||
false, typestr, value, field_name);
|
||||
}
|
||||
|
||||
|
||||
@ -340,11 +344,10 @@ const LEX_CSTRING Interval_DDhhmmssff::m_type_name=
|
||||
{STRING_WITH_LEN("INTERVAL DAY TO SECOND")};
|
||||
|
||||
|
||||
Interval_DDhhmmssff::Interval_DDhhmmssff(THD *thd, MYSQL_TIME_STATUS *st,
|
||||
Interval_DDhhmmssff::Interval_DDhhmmssff(THD *thd, Status *st,
|
||||
bool push_warnings,
|
||||
Item *item, ulong max_hour)
|
||||
{
|
||||
my_time_status_init(st);
|
||||
switch (item->cmp_type()) {
|
||||
case ROW_RESULT:
|
||||
DBUG_ASSERT(0);
|
||||
@ -425,7 +428,6 @@ Interval_DDhhmmssff::push_warning_wrong_or_truncated_value(THD *thd,
|
||||
|
||||
uint Interval_DDhhmmssff::fsp(THD *thd, Item *item)
|
||||
{
|
||||
MYSQL_TIME_STATUS st;
|
||||
switch (item->cmp_type()) {
|
||||
case INT_RESULT:
|
||||
case TIME_RESULT:
|
||||
@ -441,6 +443,7 @@ uint Interval_DDhhmmssff::fsp(THD *thd, Item *item)
|
||||
}
|
||||
if (!item->const_item() || item->is_expensive())
|
||||
return TIME_SECOND_PART_DIGITS;
|
||||
Status st;
|
||||
Interval_DDhhmmssff it(thd, &st, false/*no warnings*/, item, UINT_MAX32);
|
||||
return it.is_valid_interval_DDhhmmssff() ? st.precision :
|
||||
TIME_SECOND_PART_DIGITS;
|
||||
@ -3995,47 +3998,87 @@ bool Type_handler_string_result::Item_val_bool(Item *item) const
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
bool Type_handler_int_result::Item_get_date(THD *thd, Item *item,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
|
||||
bool Type_handler::Item_get_date_with_warn(THD *thd, Item *item,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
return item->get_date_from_int(thd, ltime, fuzzydate);
|
||||
Temporal::Warn_push warn(thd, item->field_name_or_null(), ltime, fuzzydate);
|
||||
Item_get_date(thd, item, &warn, ltime, fuzzydate);
|
||||
return ltime->time_type < 0;
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler_year::Item_get_date(THD *thd, Item *item, MYSQL_TIME *ltime,
|
||||
bool Type_handler::Item_func_hybrid_field_type_get_date_with_warn(THD *thd,
|
||||
Item_func_hybrid_field_type *item,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t mode) const
|
||||
{
|
||||
Temporal::Warn_push warn(thd, item->field_name_or_null(), ltime, mode);
|
||||
Item_func_hybrid_field_type_get_date(thd, item, &warn, ltime, mode);
|
||||
return ltime->time_type < 0;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************/
|
||||
void Type_handler_decimal_result::Item_get_date(THD *thd, Item *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
new(ltime) Temporal_hybrid(thd, warn, VDec(item).ptr(), fuzzydate);
|
||||
}
|
||||
|
||||
|
||||
void Type_handler_int_result::Item_get_date(THD *thd, Item *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *to,
|
||||
date_mode_t mode) const
|
||||
{
|
||||
new(to) Temporal_hybrid(thd, warn, item->to_longlong_hybrid_null(), mode);
|
||||
}
|
||||
|
||||
|
||||
void Type_handler_year::Item_get_date(THD *thd, Item *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
return item->null_value=
|
||||
VYear(item).to_mysql_time_with_warn(thd, ltime, fuzzydate,
|
||||
item->field_name_or_null());
|
||||
VYear year(item);
|
||||
DBUG_ASSERT(!year.truncated());
|
||||
Longlong_hybrid_null nr(Longlong_null(year.to_YYYYMMDD(), year.is_null()),
|
||||
item->unsigned_flag);
|
||||
new(ltime) Temporal_hybrid(thd, warn, nr, fuzzydate);
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler_real_result::Item_get_date(THD *thd, Item *item,
|
||||
void Type_handler_real_result::Item_get_date(THD *thd, Item *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
return item->get_date_from_real(thd, ltime, fuzzydate);
|
||||
new(ltime) Temporal_hybrid(thd, warn, item->to_double_null(), fuzzydate);
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler_string_result::Item_get_date(THD *thd, Item *item,
|
||||
void Type_handler_string_result::Item_get_date(THD *thd, Item *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
date_mode_t mode) const
|
||||
{
|
||||
return item->get_date_from_string(thd, ltime, fuzzydate);
|
||||
StringBuffer<40> tmp;
|
||||
new(ltime) Temporal_hybrid(thd, warn, item->val_str(&tmp), mode);
|
||||
}
|
||||
|
||||
|
||||
bool Type_handler_temporal_result::Item_get_date(THD *thd, Item *item,
|
||||
void Type_handler_temporal_result::Item_get_date(THD *thd, Item *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
DBUG_ASSERT(0); // Temporal type items must implement native get_date()
|
||||
item->null_value= true;
|
||||
set_zero_time(ltime, mysql_timestamp_type());
|
||||
return true;
|
||||
set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
|
||||
}
|
||||
|
||||
|
||||
@ -4176,26 +4219,31 @@ Type_handler_decimal_result::Item_func_hybrid_field_type_val_decimal(
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
void
|
||||
Type_handler_decimal_result::Item_func_hybrid_field_type_get_date(
|
||||
THD *thd,
|
||||
Item_func_hybrid_field_type *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
return VDec_op(item).to_datetime_with_warn(thd, ltime, fuzzydate, item);
|
||||
new (ltime) Temporal_hybrid(thd, warn, VDec_op(item).ptr(), fuzzydate);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
void
|
||||
Type_handler_year::Item_func_hybrid_field_type_get_date(
|
||||
THD *thd,
|
||||
Item_func_hybrid_field_type *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
return item->null_value=
|
||||
VYear_op(item).to_mysql_time_with_warn(thd, ltime, fuzzydate, NULL);
|
||||
VYear_op year(item);
|
||||
DBUG_ASSERT(!year.truncated());
|
||||
Longlong_hybrid_null nr(Longlong_null(year.to_YYYYMMDD(), year.is_null()),
|
||||
item->unsigned_flag);
|
||||
new(ltime) Temporal_hybrid(thd, warn, nr, fuzzydate);
|
||||
}
|
||||
|
||||
|
||||
@ -4238,18 +4286,18 @@ Type_handler_int_result::Item_func_hybrid_field_type_val_decimal(
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
void
|
||||
Type_handler_int_result::Item_func_hybrid_field_type_get_date(
|
||||
THD *thd,
|
||||
Item_func_hybrid_field_type *item,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *to,
|
||||
date_mode_t mode) const
|
||||
{
|
||||
return item->get_date_from_int_op(thd, ltime, fuzzydate);
|
||||
new(to) Temporal_hybrid(thd, warn, item->to_longlong_hybrid_null_op(), mode);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
String *
|
||||
@ -4288,14 +4336,15 @@ Type_handler_real_result::Item_func_hybrid_field_type_val_decimal(
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
void
|
||||
Type_handler_real_result::Item_func_hybrid_field_type_get_date(
|
||||
THD *thd,
|
||||
Item_func_hybrid_field_type *item,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *to,
|
||||
date_mode_t mode) const
|
||||
{
|
||||
return item->get_date_from_real_op(thd, ltime, fuzzydate);
|
||||
new(to) Temporal_hybrid(thd, warn, item->to_double_null_op(), mode);
|
||||
}
|
||||
|
||||
|
||||
@ -4337,14 +4386,16 @@ Type_handler_temporal_result::Item_func_hybrid_field_type_val_decimal(
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
void
|
||||
Type_handler_temporal_result::Item_func_hybrid_field_type_get_date(
|
||||
THD *thd,
|
||||
Item_func_hybrid_field_type *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
return item->date_op(thd, ltime, fuzzydate);
|
||||
if (item->date_op(thd, ltime, fuzzydate))
|
||||
set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
|
||||
}
|
||||
|
||||
|
||||
@ -4386,14 +4437,16 @@ Type_handler_time_common::Item_func_hybrid_field_type_val_decimal(
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
void
|
||||
Type_handler_time_common::Item_func_hybrid_field_type_get_date(
|
||||
THD *thd,
|
||||
Item_func_hybrid_field_type *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
{
|
||||
return item->time_op(thd, ltime);
|
||||
if (item->time_op(thd, ltime))
|
||||
set_zero_time(ltime, MYSQL_TIMESTAMP_NONE);
|
||||
}
|
||||
|
||||
|
||||
@ -4435,14 +4488,18 @@ Type_handler_string_result::Item_func_hybrid_field_type_val_decimal(
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
void
|
||||
Type_handler_string_result::Item_func_hybrid_field_type_get_date(
|
||||
THD *thd,
|
||||
Item_func_hybrid_field_type *item,
|
||||
Temporal::Warn *warn,
|
||||
MYSQL_TIME *ltime,
|
||||
date_mode_t fuzzydate) const
|
||||
date_mode_t mode) const
|
||||
{
|
||||
return item->get_date_from_str_op(thd, ltime, fuzzydate);
|
||||
StringBuffer<40> tmp;
|
||||
String *res= item->str_op(&tmp);
|
||||
DBUG_ASSERT((res == NULL) == item->null_value);
|
||||
new(ltime) Temporal_hybrid(thd, warn, res, mode);
|
||||
}
|
||||
|
||||
/***************************************************************************/
|
||||
@ -4907,7 +4964,7 @@ bool Type_handler_numeric::
|
||||
Item_func_min_max_get_date(THD *thd, Item_func_min_max *func,
|
||||
MYSQL_TIME *ltime, date_mode_t fuzzydate) const
|
||||
{
|
||||
return Item_get_date(thd, func, ltime, fuzzydate);
|
||||
return Item_get_date_with_warn(thd, func, ltime, fuzzydate);
|
||||
}
|
||||
|
||||
|
||||
@ -7508,9 +7565,9 @@ Type_handler_date_common::create_literal_item(THD *thd,
|
||||
CHARSET_INFO *cs,
|
||||
bool send_error) const
|
||||
{
|
||||
MYSQL_TIME_STATUS st;
|
||||
Temporal::Warn st;
|
||||
Item_literal *item= NULL;
|
||||
Temporal_hybrid tmp(&st, str, length, cs, sql_mode_for_dates(thd));
|
||||
Temporal_hybrid tmp(thd, &st, str, length, cs, sql_mode_for_dates(thd));
|
||||
if (tmp.is_valid_temporal() &&
|
||||
tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATE &&
|
||||
!have_important_literal_warnings(&st))
|
||||
@ -7528,9 +7585,9 @@ Type_handler_temporal_with_date::create_literal_item(THD *thd,
|
||||
CHARSET_INFO *cs,
|
||||
bool send_error) const
|
||||
{
|
||||
MYSQL_TIME_STATUS st;
|
||||
Temporal::Warn st;
|
||||
Item_literal *item= NULL;
|
||||
Temporal_hybrid tmp(&st, str, length, cs, sql_mode_for_dates(thd));
|
||||
Temporal_hybrid tmp(thd, &st, str, length, cs, sql_mode_for_dates(thd));
|
||||
if (tmp.is_valid_temporal() &&
|
||||
tmp.get_mysql_time()->time_type == MYSQL_TIMESTAMP_DATETIME &&
|
||||
!have_important_literal_warnings(&st))
|
||||
|
Reference in New Issue
Block a user