Commit 4bfec573 authored by Evgeny Potemkin's avatar Evgeny Potemkin

Bug#57039: constant subtime expression returns incorrect result.

The subtime function wasn't able to produce correct int representation of
its result. For constant expressions the Item_datetime_cache is used to
speedup evaluation and Item_datetime_cache expects underlying item to return
correct int representation of DATETIME value. These two factors combined led
to a wrong query result.

Now the Item_func_add_time has function val_datetime which performs the
calculation and saves result into given MYSQL_TIME struct, it also sets
null_value to appropriate value. val_int and val_str member functions
convert the result obtained from val_datetime to int or string respectively
and returns it.
parent c70a6f5c
...@@ -1316,3 +1316,14 @@ SELECT 1 FROM t1 ORDER BY @x:=makedate(a,a); ...@@ -1316,3 +1316,14 @@ SELECT 1 FROM t1 ORDER BY @x:=makedate(a,a);
1 1
DROP TABLE t1; DROP TABLE t1;
End of 5.1 tests End of 5.1 tests
#
# Bug#57039: constant subtime expression returns incorrect result.
#
CREATE TABLE t1 (`date_date` datetime NOT NULL);
INSERT INTO t1 VALUES ('2008-01-03 00:00:00'), ('2008-01-03 00:00:00');
SELECT * FROM t1 WHERE date_date >= subtime(now(), "00:30:00");
date_date
SELECT * FROM t1 WHERE date_date <= addtime(date_add("2000-1-1", INTERVAL "1:1:1" HOUR_SECOND), "00:20:00");
date_date
DROP TABLE t1;
#
...@@ -833,3 +833,14 @@ SELECT 1 FROM t1 ORDER BY @x:=makedate(a,a); ...@@ -833,3 +833,14 @@ SELECT 1 FROM t1 ORDER BY @x:=makedate(a,a);
DROP TABLE t1; DROP TABLE t1;
--echo End of 5.1 tests --echo End of 5.1 tests
--echo #
--echo # Bug#57039: constant subtime expression returns incorrect result.
--echo #
CREATE TABLE t1 (`date_date` datetime NOT NULL);
INSERT INTO t1 VALUES ('2008-01-03 00:00:00'), ('2008-01-03 00:00:00');
SELECT * FROM t1 WHERE date_date >= subtime(now(), "00:30:00");
SELECT * FROM t1 WHERE date_date <= addtime(date_add("2000-1-1", INTERVAL "1:1:1" HOUR_SECOND), "00:20:00");
DROP TABLE t1;
--echo #
...@@ -2857,10 +2857,11 @@ void Item_func_add_time::fix_length_and_dec() ...@@ -2857,10 +2857,11 @@ void Item_func_add_time::fix_length_and_dec()
Result: Time value or datetime value Result: Time value or datetime value
*/ */
String *Item_func_add_time::val_str(String *str) MYSQL_TIME *Item_func_add_time::val_datetime(MYSQL_TIME *time,
date_time_format_types *format)
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
MYSQL_TIME l_time1, l_time2, l_time3; MYSQL_TIME l_time1, l_time2;
bool is_time= 0; bool is_time= 0;
long days, microseconds; long days, microseconds;
longlong seconds; longlong seconds;
...@@ -2886,41 +2887,38 @@ String *Item_func_add_time::val_str(String *str) ...@@ -2886,41 +2887,38 @@ String *Item_func_add_time::val_str(String *str)
if (l_time1.neg != l_time2.neg) if (l_time1.neg != l_time2.neg)
l_sign= -l_sign; l_sign= -l_sign;
bzero((char *)&l_time3, sizeof(l_time3)); bzero((char *)time, sizeof(MYSQL_TIME));
l_time3.neg= calc_time_diff(&l_time1, &l_time2, -l_sign, time->neg= calc_time_diff(&l_time1, &l_time2, -l_sign,
&seconds, &microseconds); &seconds, &microseconds);
/* /*
If first argument was negative and diff between arguments If first argument was negative and diff between arguments
is non-zero we need to swap sign to get proper result. is non-zero we need to swap sign to get proper result.
*/ */
if (l_time1.neg && (seconds || microseconds)) if (l_time1.neg && (seconds || microseconds))
l_time3.neg= 1-l_time3.neg; // Swap sign of result time->neg= 1 - time->neg; // Swap sign of result
if (!is_time && l_time3.neg) if (!is_time && time->neg)
goto null_date; goto null_date;
days= (long)(seconds/86400L); days= (long)(seconds/86400L);
calc_time_from_sec(&l_time3, (long)(seconds%86400L), microseconds); calc_time_from_sec(time, (long)(seconds%86400L), microseconds);
if (!is_time) if (!is_time)
{ {
get_date_from_daynr(days,&l_time3.year,&l_time3.month,&l_time3.day); get_date_from_daynr(days, &time->year, &time->month, &time->day);
if (l_time3.day && *format= l_time1.second_part || l_time2.second_part ?
!make_datetime(l_time1.second_part || l_time2.second_part ? DATE_TIME_MICROSECOND : DATE_TIME;
DATE_TIME_MICROSECOND : DATE_TIME, if (time->day)
&l_time3, str)) return time;
return str;
goto null_date; goto null_date;
} }
*format= l_time1.second_part || l_time2.second_part ?
l_time3.hour+= days*24; TIME_MICROSECOND : TIME_ONLY;
if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ? time->hour+= days*24;
TIME_MICROSECOND : TIME_ONLY, return time;
&l_time3, str))
return str;
null_date: null_date:
null_value=1; null_value=1;
...@@ -2928,6 +2926,38 @@ null_date: ...@@ -2928,6 +2926,38 @@ null_date:
} }
String *Item_func_add_time::val_str(String *str)
{
MYSQL_TIME ltime;
date_time_format_types format;
val_datetime(&ltime, &format);
if (null_value)
return 0;
if (!make_datetime_with_warn(format, &ltime, str))
return str;
null_value= 1;
return 0;
}
longlong Item_func_add_time::val_int()
{
MYSQL_TIME ltime;
date_time_format_types format;
val_datetime(&ltime, &format);
if (null_value)
return 0;
return TIME_to_ulonglong_datetime(&ltime);
}
void Item_func_add_time::print(String *str, enum_query_type query_type) void Item_func_add_time::print(String *str, enum_query_type query_type)
{ {
if (is_date) if (is_date)
......
...@@ -949,6 +949,8 @@ public: ...@@ -949,6 +949,8 @@ public:
return save_date_in_field(field); return save_date_in_field(field);
return Item_str_func::save_in_field(field, no_conversions); return Item_str_func::save_in_field(field, no_conversions);
} }
longlong val_int();
MYSQL_TIME *val_datetime(MYSQL_TIME *time, date_time_format_types *format);
}; };
class Item_func_timediff :public Item_str_timefunc class Item_func_timediff :public Item_str_timefunc
......
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