summaryrefslogtreecommitdiff
path: root/sql/item_timefunc.cc
diff options
context:
space:
mode:
authorAlexander Barkov <bar@mariadb.com>2018-11-23 19:04:42 +0400
committerAlexander Barkov <bar@mariadb.com>2018-11-26 08:10:47 +0400
commit4447a02cf13a49876001a40ca7db8fdedb731fd5 (patch)
tree1ccf39024e26a1efa68237e5d44a2296a990441d /sql/item_timefunc.cc
parent27f3329ff6cb755b600d536347669bef1a7d98b5 (diff)
downloadmariadb-git-4447a02cf13a49876001a40ca7db8fdedb731fd5.tar.gz
MDEV-16991 Rounding vs truncation for TIME, DATETIME, TIMESTAMPbb-10.4-mdev16991
Diffstat (limited to 'sql/item_timefunc.cc')
-rw-r--r--sql/item_timefunc.cc151
1 files changed, 93 insertions, 58 deletions
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index df4dc8cb96d..1b728f561b7 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -131,7 +131,7 @@ static bool extract_date_time(THD *thd, DATE_TIME_FORMAT *format,
timestamp_type cached_timestamp_type,
const char **sub_pattern_end,
const char *date_time_type,
- date_mode_t fuzzydate)
+ date_conv_mode_t fuzzydate)
{
int weekday= 0, yearday= 0, daypart= 0;
int week_number= -1;
@@ -808,8 +808,9 @@ longlong Item_func_period_diff::val_int()
longlong Item_func_to_days::val_int()
{
DBUG_ASSERT(fixed == 1);
- Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
- return (null_value= !d.is_valid_date()) ? 0 : d.daynr();
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.daynr();
}
@@ -817,14 +818,15 @@ longlong Item_func_to_seconds::val_int_endpoint(bool left_endp,
bool *incl_endp)
{
DBUG_ASSERT(fixed == 1);
- Datetime dt(current_thd, args[0], TIME_FUZZY_DATES);
+ // val_int_endpoint() is called only if args[0] is a temporal Item_field
+ Datetime_from_temporal dt(current_thd, args[0], TIME_FUZZY_DATES);
if ((null_value= !dt.is_valid_datetime()))
{
/* got NULL, leave the incl_endp intact */
return LONGLONG_MIN;
}
/* Set to NULL if invalid date, but keep the value */
- null_value= dt.check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE);
+ null_value= dt.check_date(TIME_NO_ZEROS);
/*
Even if the evaluation return NULL, seconds is useful for pruning
*/
@@ -835,7 +837,11 @@ longlong Item_func_to_seconds::val_int()
{
DBUG_ASSERT(fixed == 1);
THD *thd= current_thd;
- Datetime dt(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
+ /*
+ Unlike val_int_endpoint(), we cannot use Datetime_from_temporal here.
+ The argument can be of a non-temporal data type.
+ */
+ Datetime dt(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
return (null_value= !dt.is_valid_datetime()) ? 0 : dt.to_seconds();
}
@@ -880,7 +886,8 @@ enum_monotonicity_info Item_func_to_seconds::get_monotonicity_info() const
longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
{
DBUG_ASSERT(fixed == 1);
- Datetime dt(current_thd, args[0], date_mode_t(0));
+ // val_int_endpoint() is only called if args[0] is a temporal Item_field
+ Datetime_from_temporal dt(current_thd, args[0], TIME_CONV_NONE);
longlong res;
if ((null_value= !dt.is_valid_datetime()))
{
@@ -889,7 +896,7 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
}
res= (longlong) dt.daynr();
/* Set to NULL if invalid date, but keep the value */
- null_value= dt.check_date(TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE);
+ null_value= dt.check_date(TIME_NO_ZEROS);
if (null_value)
{
/*
@@ -933,22 +940,25 @@ longlong Item_func_to_days::val_int_endpoint(bool left_endp, bool *incl_endp)
longlong Item_func_dayofyear::val_int()
{
DBUG_ASSERT(fixed == 1);
- Date d(current_thd, args[0], TIME_NO_ZERO_IN_DATE | TIME_NO_ZERO_DATE);
- return (null_value= !d.is_valid_date()) ? 0 : d.dayofyear();
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.dayofyear();
}
longlong Item_func_dayofmonth::val_int()
{
DBUG_ASSERT(fixed == 1);
- Date d(current_thd, args[0], date_mode_t(0));
- return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->day;
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.get_mysql_time()->day;
}
longlong Item_func_month::val_int()
{
DBUG_ASSERT(fixed == 1);
- Date d(current_thd, args[0], date_mode_t(0));
- return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->month;
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.get_mysql_time()->month;
}
@@ -970,9 +980,9 @@ String* Item_func_monthname::val_str(String* str)
DBUG_ASSERT(fixed == 1);
const char *month_name;
uint err;
- Date d(current_thd, args[0], date_mode_t(0));
-
- if ((null_value= (!d.is_valid_date() || !d.get_mysql_time()->month)))
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ if ((null_value= (!d.is_valid_datetime() || !d.get_mysql_time()->month)))
return (String *) 0;
month_name= locale->month_names->type_names[d.get_mysql_time()->month - 1];
@@ -989,21 +999,24 @@ String* Item_func_monthname::val_str(String* str)
longlong Item_func_quarter::val_int()
{
DBUG_ASSERT(fixed == 1);
- Date d(current_thd, args[0], date_mode_t(0));
- return (null_value= !d.is_valid_date()) ? 0 : d.quarter();
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.quarter();
}
longlong Item_func_hour::val_int()
{
DBUG_ASSERT(fixed == 1);
- Time tm(current_thd, args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->hour;
}
longlong Item_func_minute::val_int()
{
DBUG_ASSERT(fixed == 1);
- Time tm(current_thd, args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->minute;
}
@@ -1013,7 +1026,8 @@ longlong Item_func_minute::val_int()
longlong Item_func_second::val_int()
{
DBUG_ASSERT(fixed == 1);
- Time tm(current_thd, args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
return (null_value= !tm.is_valid_time()) ? 0 : tm.get_mysql_time()->second;
}
@@ -1062,8 +1076,8 @@ longlong Item_func_week::val_int()
DBUG_ASSERT(fixed == 1);
uint week_format;
THD *thd= current_thd;
- Date d(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
- if ((null_value= !d.is_valid_date()))
+ Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
+ if ((null_value= !d.is_valid_datetime()))
return 0;
if (arg_count > 1)
week_format= (uint)args[1]->val_int();
@@ -1076,8 +1090,9 @@ longlong Item_func_week::val_int()
longlong Item_func_yearweek::val_int()
{
DBUG_ASSERT(fixed == 1);
- Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
- return (null_value= !d.is_valid_date()) ? 0 :
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 :
d.yearweek((week_mode((uint) args[1]->val_int()) | WEEK_YEAR));
}
@@ -1085,8 +1100,9 @@ longlong Item_func_yearweek::val_int()
longlong Item_func_weekday::val_int()
{
DBUG_ASSERT(fixed == 1);
- Date d(current_thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
- return ((null_value= !d.is_valid_date())) ? 0 :
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_NO_ZEROS, thd));
+ return ((null_value= !d.is_valid_datetime())) ? 0 :
calc_weekday(d.daynr(), odbc_type) + MY_TEST(odbc_type);
}
@@ -1123,8 +1139,9 @@ String* Item_func_dayname::val_str(String* str)
longlong Item_func_year::val_int()
{
DBUG_ASSERT(fixed == 1);
- Date d(current_thd, args[0], date_mode_t(0));
- return (null_value= !d.is_valid_date()) ? 0 : d.get_mysql_time()->year;
+ THD *thd= current_thd;
+ Datetime d(thd, args[0], Datetime::Options(TIME_CONV_NONE, thd));
+ return (null_value= !d.is_valid_datetime()) ? 0 : d.get_mysql_time()->year;
}
@@ -1155,7 +1172,8 @@ enum_monotonicity_info Item_func_year::get_monotonicity_info() const
longlong Item_func_year::val_int_endpoint(bool left_endp, bool *incl_endp)
{
DBUG_ASSERT(fixed == 1);
- Datetime dt(current_thd, args[0], date_mode_t(0));
+ // val_int_endpoint() is cally only if args[0] is a temporal Item_field
+ Datetime_from_temporal dt(current_thd, args[0], TIME_CONV_NONE);
if ((null_value= !dt.is_valid_datetime()))
{
/* got NULL, leave the incl_endp intact */
@@ -1200,7 +1218,7 @@ bool Item_func_unix_timestamp::get_timestamp_value(my_time_t *seconds,
}
THD *thd= current_thd;
- Datetime dt(thd, args[0], TIME_NO_ZERO_IN_DATE);
+ Datetime dt(thd, args[0], Datetime::Options(TIME_NO_ZERO_IN_DATE, thd));
if ((null_value= !dt.is_valid_datetime()))
return true;
@@ -1264,7 +1282,8 @@ longlong Item_func_unix_timestamp::val_int_endpoint(bool left_endp, bool *incl_e
longlong Item_func_time_to_sec::int_op()
{
DBUG_ASSERT(fixed == 1);
- Time tm(current_thd, args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
return ((null_value= !tm.is_valid_time())) ? 0 : tm.to_seconds();
}
@@ -1272,7 +1291,8 @@ longlong Item_func_time_to_sec::int_op()
my_decimal *Item_func_time_to_sec::decimal_op(my_decimal* buf)
{
DBUG_ASSERT(fixed == 1);
- Time tm(current_thd, args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
if ((null_value= !tm.is_valid_time()))
return 0;
const MYSQL_TIME *ltime= tm.get_mysql_time();
@@ -1621,9 +1641,8 @@ int Item_func_now_local::save_in_field(Field *field, bool no_conversions)
{
THD *thd= field->get_thd();
my_time_t ts= thd->query_start();
- uint dec= MY_MIN(decimals, field->decimals());
- ulong sec_part= dec ? thd->query_start_sec_part() : 0;
- sec_part-= my_time_fraction_remainder(sec_part, dec);
+ ulong sec_part= decimals ? thd->query_start_sec_part() : 0;
+ sec_part-= my_time_fraction_remainder(sec_part, decimals);
field->set_notnull();
((Field_timestamp*)field)->store_TIME(ts, sec_part);
return 0;
@@ -1698,9 +1717,10 @@ bool Item_func_sysdate_local::get_date(THD *thd, MYSQL_TIME *res,
bool Item_func_sec_to_time::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
DBUG_ASSERT(fixed == 1);
- VSec6 sec(thd, args[0], "seconds", LONGLONG_MAX);
+ VSec9 sec(thd, args[0], "seconds", LONGLONG_MAX);
if ((null_value= sec.is_null()))
return true;
+ sec.round(decimals, thd->temporal_round_mode());
if (sec.sec_to_time(ltime, decimals) && !sec.truncated())
sec.make_truncated_warning(thd, "seconds");
return false;
@@ -1858,10 +1878,10 @@ String *Item_func_date_format::val_str(String *str)
uint size;
const MY_LOCALE *lc= 0;
DBUG_ASSERT(fixed == 1);
-
- if ((null_value= args[0]->get_date(current_thd, &l_time,
- is_time_format ? TIME_TIME_ONLY :
- date_mode_t(0))))
+ date_conv_mode_t mode= is_time_format ? TIME_TIME_ONLY : TIME_CONV_NONE;
+ THD *thd= current_thd;
+ if ((null_value= args[0]->get_date(thd, &l_time,
+ Temporal::Options(mode, thd))))
return 0;
if (!(format = args[1]->val_str(str)) || !format->length())
@@ -1918,12 +1938,16 @@ bool Item_func_from_unixtime::get_date(THD *thd, MYSQL_TIME *ltime,
bzero((char *)ltime, sizeof(*ltime));
ltime->time_type= MYSQL_TIMESTAMP_TIME;
- VSec6 sec(thd, args[0], "unixtime", TIMESTAMP_MAX_VALUE);
+ VSec9 sec(thd, args[0], "unixtime", TIMESTAMP_MAX_VALUE);
DBUG_ASSERT(sec.sec() <= TIMESTAMP_MAX_VALUE);
if (sec.is_null() || sec.truncated() || sec.neg())
return (null_value= 1);
+ sec.round(MY_MIN(decimals, TIME_SECOND_PART_DIGITS), thd->temporal_round_mode());
+ if (sec.sec() > TIMESTAMP_MAX_VALUE)
+ return (null_value= true); // Went out of range after rounding
+
tz->gmt_sec_to_TIME(ltime, (my_time_t) sec.sec());
ltime->second_part= sec.usec();
@@ -1952,7 +1976,8 @@ bool Item_func_convert_tz::get_date(THD *thd, MYSQL_TIME *ltime,
if ((null_value= (from_tz == 0 || to_tz == 0)))
return true;
- Datetime *dt= new(ltime) Datetime(thd, args[0], TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE);
+ Datetime::Options opt(TIME_NO_ZEROS, thd);
+ Datetime *dt= new(ltime) Datetime(thd, args[0], opt);
if ((null_value= !dt->is_valid_datetime()))
return true;
@@ -2133,7 +2158,8 @@ uint Extract_source::week(THD *thd) const
longlong Item_extract::val_int()
{
DBUG_ASSERT(fixed == 1);
- Extract_source dt(current_thd, args[0], m_date_mode);
+ THD *thd= current_thd;
+ Extract_source dt(thd, args[0], m_date_mode);
if ((null_value= !dt.is_valid_extract_source()))
return 0;
switch (int_type) {
@@ -2141,7 +2167,7 @@ longlong Item_extract::val_int()
case INTERVAL_YEAR_MONTH: return dt.year_month();
case INTERVAL_QUARTER: return dt.quarter();
case INTERVAL_MONTH: return dt.month();
- case INTERVAL_WEEK: return dt.week(current_thd);
+ case INTERVAL_WEEK: return dt.week(thd);
case INTERVAL_DAY: return dt.day();
case INTERVAL_DAY_HOUR: return dt.day_hour();
case INTERVAL_DAY_MINUTE: return dt.day_minute();
@@ -2420,7 +2446,7 @@ void Item_char_typecast::fix_length_and_dec_internal(CHARSET_INFO *from_cs)
bool Item_time_typecast::get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode)
{
- Time *tm= new(to) Time(thd, args[0], Time::Options_for_cast(mode),
+ Time *tm= new(to) Time(thd, args[0], Time::Options_for_cast(mode, thd),
MY_MIN(decimals, TIME_SECOND_PART_DIGITS));
return (null_value= !tm->is_valid_time());
}
@@ -2429,7 +2455,8 @@ bool Item_time_typecast::get_date(THD *thd, MYSQL_TIME *to, date_mode_t mode)
bool Item_date_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
date_mode_t tmp= (fuzzydate | sql_mode_for_dates(thd)) & ~TIME_TIME_ONLY;
- Date *d= new(ltime) Date(thd, args[0], tmp);
+ // Force truncation
+ Date *d= new(ltime) Date(thd, args[0], Date::Options(date_conv_mode_t(tmp)));
return (null_value= !d->is_valid_date());
}
@@ -2437,7 +2464,9 @@ bool Item_date_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzy
bool Item_datetime_typecast::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
date_mode_t tmp= (fuzzydate | sql_mode_for_dates(thd)) & ~TIME_TIME_ONLY;
- Datetime *dt= new(ltime) Datetime(thd, args[0], tmp,
+ // Force rounding if the current sql_mode says so
+ Datetime::Options opt(date_conv_mode_t(tmp), thd);
+ Datetime *dt= new(ltime) Datetime(thd, args[0], opt,
MY_MIN(decimals, TIME_SECOND_PART_DIGITS));
return (null_value= !dt->is_valid_datetime());
}
@@ -2558,6 +2587,7 @@ bool Item_func_timediff::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzy
return (null_value= adjust_time_range_with_warn(thd, ltime, decimals));
}
+
/**
MAKETIME(h,m,s) is a time function that calculates a time value
from the total number of hours, minutes, and seconds.
@@ -2569,7 +2599,7 @@ bool Item_func_maketime::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzy
DBUG_ASSERT(fixed == 1);
Longlong_hybrid hour(args[0]->val_int(), args[0]->unsigned_flag);
longlong minute= args[1]->val_int();
- VSec6 sec(thd, args[2], "seconds", 59);
+ VSec9 sec(thd, args[2], "seconds", 59);
DBUG_ASSERT(sec.sec() <= 59);
if (args[0]->null_value || args[1]->null_value || sec.is_null() ||
@@ -2577,7 +2607,8 @@ bool Item_func_maketime::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzy
return (null_value= 1);
int warn;
- new(ltime) Time(&warn, hour.neg(), hour.abs(), (uint) minute, sec);
+ new(ltime) Time(&warn, hour.neg(), hour.abs(), (uint) minute, sec,
+ thd->temporal_round_mode(), decimals);
if (warn)
{
// use check_time_range() to set ltime to the max value depending on dec
@@ -2607,7 +2638,8 @@ bool Item_func_maketime::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzy
longlong Item_func_microsecond::val_int()
{
DBUG_ASSERT(fixed == 1);
- Time tm(current_thd, args[0], Time::Options_for_cast());
+ THD *thd= current_thd;
+ Time tm(thd, args[0], Time::Options_for_cast(thd));
return ((null_value= !tm.is_valid_time())) ?
0 : tm.get_mysql_time()->second_part;
}
@@ -2621,12 +2653,12 @@ longlong Item_func_timestamp_diff::val_int()
long months= 0;
int neg= 1;
THD *thd= current_thd;
- date_mode_t fuzzydate= TIME_NO_ZERO_DATE | TIME_NO_ZERO_IN_DATE;
+ Datetime::Options opt(TIME_NO_ZEROS, thd);
null_value= 0;
- if (Datetime(thd, args[0], fuzzydate).copy_to_mysql_time(&ltime1) ||
- Datetime(thd, args[1], fuzzydate).copy_to_mysql_time(&ltime2))
+ if (Datetime(thd, args[0], opt).copy_to_mysql_time(&ltime1) ||
+ Datetime(thd, args[1], opt).copy_to_mysql_time(&ltime2))
goto null_date;
if (calc_time_diff(&ltime2,&ltime1, 1,
@@ -2935,7 +2967,8 @@ bool Item_func_str_to_date::get_date_common(THD *thd, MYSQL_TIME *ltime,
date_time_format.format.length= format->length();
if (extract_date_time(thd, &date_time_format, val->ptr(), val->length(),
ltime, tstype, 0, "datetime",
- fuzzydate | sql_mode_for_dates(thd)))
+ date_conv_mode_t(fuzzydate) |
+ sql_mode_for_dates(thd)))
return (null_value=1);
return (null_value= 0);
}
@@ -2943,8 +2976,10 @@ bool Item_func_str_to_date::get_date_common(THD *thd, MYSQL_TIME *ltime,
bool Item_func_last_day::get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate)
{
- Date *d= new(ltime) Date(thd, args[0], fuzzydate & ~TIME_TIME_ONLY);
- if ((null_value= (!d->is_valid_date() || ltime->month == 0)))
+ Datetime::Options opt(date_conv_mode_t(fuzzydate & ~TIME_TIME_ONLY),
+ time_round_mode_t(fuzzydate));
+ Datetime *d= new(ltime) Datetime(thd, args[0], opt);
+ if ((null_value= (!d->is_valid_datetime() || ltime->month == 0)))
return true;
uint month_idx= ltime->month-1;
ltime->day= days_in_month[month_idx];