Commit d096079d authored by Tatiana A. Nurnberg's avatar Tatiana A. Nurnberg

Bug#37553: MySql Error Compare TimeDiff & Time

We pretended that TIMEDIFF() would always return positive results;
this gave strange results in comparisons of the TIMEDIFF(low,hi)<TIME(0)
type that rendered a negative result, but still gave false in comparison.
We also inadvertantly dropped the sign when converting times to
decimal.

CAST(time AS DECIMAL) handles signs of the times correctly.
TIMEDIFF() marked up as signed. Time/date comparison code switched to
signed for clarity.

mysql-test/r/func_sapdb.result:
  show that time-related comparisons work with negative
  time values now.
  show that converting time to DECIMAL no longer drops sign.
mysql-test/t/func_sapdb.test:
  show that time-related comparisons work with negative
  time values now.
  show that converting time to DECIMAL no longer drops sign.
sql/item_cmpfunc.cc:
  signed returns
sql/item_cmpfunc.h:
  signed now (time/date < > =)
sql/item_func.cc:
  signed now
sql/item_timefunc.h:
  Functions such as TIMEDIFF() return signed results!
  The file-comments pretended we were doing that all along, anyway...
sql/my_decimal.cc:
  heed sign when converting time to my_decimal;
  times may actually be negative!
  Needed for SELECT CAST(time('-73:42:12') AS DECIMAL);
sql/mysql_priv.h:
  using signed for dates and times now
parent df8a5474
...@@ -256,3 +256,15 @@ a ...@@ -256,3 +256,15 @@ a
select str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f"); select str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f");
str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f") str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f")
2003-01-02 10:11:12.001200 2003-01-02 10:11:12.001200
select timediff('2008-09-29 20:10:10','2008-09-30 20:10:10'),time('00:00:00');
timediff('2008-09-29 20:10:10','2008-09-30 20:10:10') time('00:00:00')
-24:00:00 00:00:00
select timediff('2008-09-29 20:10:10','2008-09-30 20:10:10')>time('00:00:00');
timediff('2008-09-29 20:10:10','2008-09-30 20:10:10')>time('00:00:00')
0
select timediff('2008-09-29 20:10:10','2008-09-30 20:10:10')<time('00:00:00');
timediff('2008-09-29 20:10:10','2008-09-30 20:10:10')<time('00:00:00')
1
SELECT CAST(time('-73:42:12') AS DECIMAL);
CAST(time('-73:42:12') AS DECIMAL)
-734212
...@@ -135,3 +135,20 @@ select str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f"); ...@@ -135,3 +135,20 @@ select str_to_date("2003-01-02 10:11:12.0012", "%Y-%m-%d %H:%i:%S.%f");
--enable_ps_protocol --enable_ps_protocol
# End of 4.1 tests # End of 4.1 tests
#
# Bug#37553: MySql Error Compare TimeDiff & Time
#
# calculations involving negative time values ignored sign
select timediff('2008-09-29 20:10:10','2008-09-30 20:10:10'),time('00:00:00');
select timediff('2008-09-29 20:10:10','2008-09-30 20:10:10')>time('00:00:00');
select timediff('2008-09-29 20:10:10','2008-09-30 20:10:10')<time('00:00:00');
# show that conversion to DECIMAL no longer drops sign
SELECT CAST(time('-73:42:12') AS DECIMAL);
# End of 5.0 tests
...@@ -745,11 +745,11 @@ Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value) ...@@ -745,11 +745,11 @@ Arg_comparator::can_compare_as_dates(Item *a, Item *b, ulonglong *const_value)
obtained value obtained value
*/ */
ulonglong longlong
get_time_value(THD *thd, Item ***item_arg, Item **cache_arg, get_time_value(THD *thd, Item ***item_arg, Item **cache_arg,
Item *warn_item, bool *is_null) Item *warn_item, bool *is_null)
{ {
ulonglong value; longlong value;
Item *item= **item_arg; Item *item= **item_arg;
MYSQL_TIME ltime; MYSQL_TIME ltime;
...@@ -761,7 +761,7 @@ get_time_value(THD *thd, Item ***item_arg, Item **cache_arg, ...@@ -761,7 +761,7 @@ get_time_value(THD *thd, Item ***item_arg, Item **cache_arg,
else else
{ {
*is_null= item->get_time(&ltime); *is_null= item->get_time(&ltime);
value= !*is_null ? TIME_to_ulonglong_datetime(&ltime) : 0; value= !*is_null ? (longlong) TIME_to_ulonglong_datetime(&ltime) : 0;
} }
/* /*
Do not cache GET_USER_VAR() function as its const_item() may return TRUE Do not cache GET_USER_VAR() function as its const_item() may return TRUE
...@@ -886,11 +886,11 @@ void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1) ...@@ -886,11 +886,11 @@ void Arg_comparator::set_datetime_cmp_func(Item **a1, Item **b1)
obtained value obtained value
*/ */
ulonglong longlong
get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
Item *warn_item, bool *is_null) Item *warn_item, bool *is_null)
{ {
ulonglong value= 0; longlong value= 0;
String buf, *str= 0; String buf, *str= 0;
Item *item= **item_arg; Item *item= **item_arg;
...@@ -925,7 +925,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, ...@@ -925,7 +925,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
enum_field_types f_type= warn_item->field_type(); enum_field_types f_type= warn_item->field_type();
timestamp_type t_type= f_type == timestamp_type t_type= f_type ==
MYSQL_TYPE_DATE ? MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME; MYSQL_TYPE_DATE ? MYSQL_TIMESTAMP_DATE : MYSQL_TIMESTAMP_DATETIME;
value= get_date_from_str(thd, str, t_type, warn_item->name, &error); value= (longlong) get_date_from_str(thd, str, t_type, warn_item->name, &error);
/* /*
If str did not contain a valid date according to the current If str did not contain a valid date according to the current
SQL_MODE, get_date_from_str() has already thrown a warning, SQL_MODE, get_date_from_str() has already thrown a warning,
...@@ -979,7 +979,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, ...@@ -979,7 +979,7 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
int Arg_comparator::compare_datetime() int Arg_comparator::compare_datetime()
{ {
bool a_is_null, b_is_null; bool a_is_null, b_is_null;
ulonglong a_value, b_value; longlong a_value, b_value;
/* Get DATE/DATETIME/TIME value of the 'a' item. */ /* Get DATE/DATETIME/TIME value of the 'a' item. */
a_value= (*get_value_func)(thd, &a, &a_cache, *b, &a_is_null); a_value= (*get_value_func)(thd, &a, &a_cache, *b, &a_is_null);
......
...@@ -42,7 +42,7 @@ class Arg_comparator: public Sql_alloc ...@@ -42,7 +42,7 @@ class Arg_comparator: public Sql_alloc
bool is_nulls_eq; // TRUE <=> compare for the EQUAL_FUNC bool is_nulls_eq; // TRUE <=> compare for the EQUAL_FUNC
enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE, enum enum_date_cmp_type { CMP_DATE_DFLT= 0, CMP_DATE_WITH_DATE,
CMP_DATE_WITH_STR, CMP_STR_WITH_DATE }; CMP_DATE_WITH_STR, CMP_STR_WITH_DATE };
ulonglong (*get_value_func)(THD *thd, Item ***item_arg, Item **cache_arg, longlong (*get_value_func)(THD *thd, Item ***item_arg, Item **cache_arg,
Item *warn_item, bool *is_null); Item *warn_item, bool *is_null);
public: public:
DTCollation cmp_collation; DTCollation cmp_collation;
...@@ -1028,7 +1028,7 @@ public: ...@@ -1028,7 +1028,7 @@ public:
*/ */
class cmp_item_datetime :public cmp_item class cmp_item_datetime :public cmp_item
{ {
ulonglong value; longlong value;
public: public:
THD *thd; THD *thd;
/* Item used for issuing warnings. */ /* Item used for issuing warnings. */
......
...@@ -2283,7 +2283,7 @@ uint Item_func_min_max::cmp_datetimes(ulonglong *value) ...@@ -2283,7 +2283,7 @@ uint Item_func_min_max::cmp_datetimes(ulonglong *value)
{ {
Item **arg= args + i; Item **arg= args + i;
bool is_null; bool is_null;
ulonglong res= get_datetime_value(thd, &arg, 0, datetime_item, &is_null); longlong res= get_datetime_value(thd, &arg, 0, datetime_item, &is_null);
if ((null_value= args[i]->null_value)) if ((null_value= args[i]->null_value))
return 0; return 0;
if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0) if (i == 0 || (res < min_max ? cmp_sign : -cmp_sign) > 0)
......
...@@ -408,6 +408,7 @@ public: ...@@ -408,6 +408,7 @@ public:
{ {
return save_time_in_field(field); return save_time_in_field(field);
} }
bool result_as_longlong() { return TRUE; }
}; };
......
...@@ -216,7 +216,7 @@ my_decimal *date2my_decimal(MYSQL_TIME *ltime, my_decimal *dec) ...@@ -216,7 +216,7 @@ my_decimal *date2my_decimal(MYSQL_TIME *ltime, my_decimal *dec)
date = (ltime->year*100L + ltime->month)*100L + ltime->day; date = (ltime->year*100L + ltime->month)*100L + ltime->day;
if (ltime->time_type > MYSQL_TIMESTAMP_DATE) if (ltime->time_type > MYSQL_TIMESTAMP_DATE)
date= ((date*100L + ltime->hour)*100L+ ltime->minute)*100L + ltime->second; date= ((date*100L + ltime->hour)*100L+ ltime->minute)*100L + ltime->second;
if (int2my_decimal(E_DEC_FATAL_ERROR, date, FALSE, dec)) if (int2my_decimal(E_DEC_FATAL_ERROR, ltime->neg ? -date : date, FALSE, dec))
return dec; return dec;
if (ltime->second_part) if (ltime->second_part)
{ {
......
...@@ -1556,7 +1556,7 @@ void make_date(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time, ...@@ -1556,7 +1556,7 @@ void make_date(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time,
String *str); String *str);
void make_time(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time, void make_time(const DATE_TIME_FORMAT *format, const MYSQL_TIME *l_time,
String *str); String *str);
ulonglong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, longlong get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg,
Item *warn_item, bool *is_null); Item *warn_item, bool *is_null);
int test_if_number(char *str,int *res,bool allow_wildcards); int test_if_number(char *str,int *res,bool allow_wildcards);
......
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