mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
A cleanup for MDEV-15340 + fix MDEV-15363 Wrong result for CAST(LAST_DAY(TIME'00:00:00') AS TIME)
The change N7 in MDEV-15340 (see the commit message) introduced a regression in how CAST(AS TIME), HOUR(), TIME_TO_SEC() treat datetimes '0000-00-DD mm:hh:ss' (i.e. with zero YYYYMM part and a non-zero day). These functions historically do not mix days to hours on datetime-to-time conversion. Implementations of the underlying methods used get_arg0_time() to fetch MYSQL_TIME. After MDEV-15340, get_arg0_time() went through the Time() constructor, which always adds '0000-00-DD' to hours automatically (as in all other places in the code we do mix days to hours). Changes: 1. Extending Time() to make it possible to choose a desired way of treating '0000-00-DD' (ignore or mix to hours) on datetime-to-time conversion. Adding a helper class Time::Options for this, which now describes two aspects of Time() creation: 1. Flags for get_date() 2. Days/hours mixing behavior. 2. Removing Item_func::get_arg0_time(). Using Time() directly in all affected classes. Forcing Time() to ignore (rather than mix) '0000-00-DD' in these affected classes by passing a suitable Options value. 3. Adding Time::to_seconds(), to reuse the code between Item_func_time_to_sec::decimal_op() and Item_func_time_to_sec::int_op(). 4. Item_func::get_arg0_date() now returns only a datetime value, with automatic time-to-datetime conversion if needed. An assert was added to catch attempts to pass TIME_TIME_ONLY to get_arg0_date(). All callers were checked not to pass TIME_TIME_ONLY, this revealed a bug MDEV-15363. 5. Changing Item_func_last_day::get_date() to remove the TIME_TIME_ONLY flag before calling get_arg0_date(). This fixes MDEV-15363.
This commit is contained in:
@ -94,6 +94,47 @@ struct SORT_FIELD_ATTR;
|
||||
*/
|
||||
class Time: private MYSQL_TIME
|
||||
{
|
||||
public:
|
||||
enum datetime_to_time_mode_t
|
||||
{
|
||||
DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS,
|
||||
DATETIME_TO_TIME_YYYYMMDD_TRUNCATE
|
||||
};
|
||||
class Options
|
||||
{
|
||||
sql_mode_t m_get_date_flags;
|
||||
datetime_to_time_mode_t m_datetime_to_time_mode;
|
||||
public:
|
||||
Options()
|
||||
:m_get_date_flags(flags_for_get_date()),
|
||||
m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
|
||||
{ }
|
||||
Options(sql_mode_t flags)
|
||||
:m_get_date_flags(flags),
|
||||
m_datetime_to_time_mode(DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
|
||||
{ }
|
||||
Options(sql_mode_t flags, datetime_to_time_mode_t dtmode)
|
||||
:m_get_date_flags(flags),
|
||||
m_datetime_to_time_mode(dtmode)
|
||||
{ }
|
||||
sql_mode_t get_date_flags() const
|
||||
{ return m_get_date_flags; }
|
||||
datetime_to_time_mode_t datetime_to_time_mode() const
|
||||
{ return m_datetime_to_time_mode; }
|
||||
};
|
||||
/*
|
||||
CAST(AS TIME) historically does not mix days to hours.
|
||||
This is different comparing to how implicit conversion
|
||||
in Field::store_time_dec() works (e.g. on INSERT).
|
||||
*/
|
||||
class Options_for_cast: public Options
|
||||
{
|
||||
public:
|
||||
Options_for_cast()
|
||||
:Options(flags_for_get_date(), DATETIME_TO_TIME_YYYYMMDD_TRUNCATE)
|
||||
{ }
|
||||
};
|
||||
private:
|
||||
bool is_valid_value_slow() const
|
||||
{
|
||||
return time_type == MYSQL_TIMESTAMP_NONE || is_valid_time_slow();
|
||||
@ -113,7 +154,7 @@ class Time: private MYSQL_TIME
|
||||
e.g. returned from Item::get_date().
|
||||
After this call, "this" is a valid TIME value.
|
||||
*/
|
||||
void valid_datetime_to_valid_time()
|
||||
void valid_datetime_to_valid_time(const Options opt)
|
||||
{
|
||||
DBUG_ASSERT(time_type == MYSQL_TIMESTAMP_DATE ||
|
||||
time_type == MYSQL_TIMESTAMP_DATETIME);
|
||||
@ -123,7 +164,9 @@ class Time: private MYSQL_TIME
|
||||
*/
|
||||
DBUG_ASSERT(day < 32);
|
||||
DBUG_ASSERT(hour < 24);
|
||||
if (year == 0 && month == 0)
|
||||
if (year == 0 && month == 0 &&
|
||||
opt.datetime_to_time_mode() ==
|
||||
DATETIME_TO_TIME_YYYYMMDD_000000DD_MIX_TO_HOURS)
|
||||
{
|
||||
/*
|
||||
The maximum hour value after mixing days will be 31*24+23=767,
|
||||
@ -148,12 +191,12 @@ class Time: private MYSQL_TIME
|
||||
- either a valid TIME (within the supported TIME range),
|
||||
- or MYSQL_TIMESTAMP_NONE
|
||||
*/
|
||||
void valid_MYSQL_TIME_to_valid_value()
|
||||
void valid_MYSQL_TIME_to_valid_value(const Options opt)
|
||||
{
|
||||
switch (time_type) {
|
||||
case MYSQL_TIMESTAMP_DATE:
|
||||
case MYSQL_TIMESTAMP_DATETIME:
|
||||
valid_datetime_to_valid_time();
|
||||
valid_datetime_to_valid_time(opt);
|
||||
break;
|
||||
case MYSQL_TIMESTAMP_NONE:
|
||||
break;
|
||||
@ -165,11 +208,11 @@ class Time: private MYSQL_TIME
|
||||
break;
|
||||
}
|
||||
}
|
||||
void make_from_item(class Item *item, sql_mode_t flags);
|
||||
void make_from_item(class Item *item, const Options opt);
|
||||
public:
|
||||
Time() { time_type= MYSQL_TIMESTAMP_NONE; }
|
||||
Time(Item *item) { make_from_item(item, flags_for_get_date()); }
|
||||
Time(Item *item, sql_mode_t flags) { make_from_item(item, flags); }
|
||||
Time(Item *item) { make_from_item(item, Options()); }
|
||||
Time(Item *item, const Options opt) { make_from_item(item, opt); }
|
||||
static sql_mode_t flags_for_get_date()
|
||||
{ return TIME_TIME_ONLY | TIME_INVALID_DATES; }
|
||||
static sql_mode_t comparison_flags_for_get_date()
|
||||
@ -207,6 +250,15 @@ public:
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
longlong to_seconds_abs() const
|
||||
{
|
||||
DBUG_ASSERT(is_valid_time_slow());
|
||||
return hour * 3600L + minute * 60 + second;
|
||||
}
|
||||
longlong to_seconds() const
|
||||
{
|
||||
return neg ? -to_seconds_abs() : to_seconds_abs();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user