Commit a95ede05 authored by dlenev@mysql.com's avatar dlenev@mysql.com

Manual merge of patch for bug #8068 "TIMEDIFF with first negative

argument gives wrong result" (and similar bug in ADDTIME/SUBTIME)
into 5.0 tree.
parents e66cda03 91c482e4
...@@ -195,6 +195,7 @@ insert into test values ...@@ -195,6 +195,7 @@ insert into test values
('2001-01-01 01:01:01', '-01:01:01', '-23:59:59', "1997-12-31 23:59:59.000001"), ('2001-01-01 01:01:01', '-01:01:01', '-23:59:59', "1997-12-31 23:59:59.000001"),
('1997-12-31 23:59:59.000001', '-23:59:59', '-01:01:01', '2001-01-01 01:01:01'), ('1997-12-31 23:59:59.000001', '-23:59:59', '-01:01:01', '2001-01-01 01:01:01'),
('2001-01-01 01:01:01', '01:01:01', '-1 01:01:01', null), ('2001-01-01 01:01:01', '01:01:01', '-1 01:01:01', null),
('2001-01-01 01:01:01', '-01:01:01', '1 01:01:01', '2001-01-01 01:01:01'),
('2001-01-01 01:01:01', null, '-1 01:01:01', null), ('2001-01-01 01:01:01', null, '-1 01:01:01', null),
(null, null, null, null), (null, null, null, null),
('2001-01-01 01:01:01', '01:01:01', '1 01:01:01', '2001-01-01 01:01:01'); ('2001-01-01 01:01:01', '01:01:01', '1 01:01:01', '2001-01-01 01:01:01');
...@@ -204,6 +205,7 @@ ttt qqq ...@@ -204,6 +205,7 @@ ttt qqq
2001-01-01 00:00:00 -25:01:00 2001-01-01 00:00:00 -25:01:00
1997-12-31 00:00:00 -25:01:00 1997-12-31 00:00:00 -25:01:00
2001-01-01 02:02:02 -24:00:00 2001-01-01 02:02:02 -24:00:00
2001-01-01 00:00:00 24:00:00
NULL NULL NULL NULL
NULL NULL NULL NULL
2001-01-01 02:02:02 26:02:02 2001-01-01 02:02:02 26:02:02
......
...@@ -106,6 +106,7 @@ insert into test values ...@@ -106,6 +106,7 @@ insert into test values
('2001-01-01 01:01:01', '-01:01:01', '-23:59:59', "1997-12-31 23:59:59.000001"), ('2001-01-01 01:01:01', '-01:01:01', '-23:59:59', "1997-12-31 23:59:59.000001"),
('1997-12-31 23:59:59.000001', '-23:59:59', '-01:01:01', '2001-01-01 01:01:01'), ('1997-12-31 23:59:59.000001', '-23:59:59', '-01:01:01', '2001-01-01 01:01:01'),
('2001-01-01 01:01:01', '01:01:01', '-1 01:01:01', null), ('2001-01-01 01:01:01', '01:01:01', '-1 01:01:01', null),
('2001-01-01 01:01:01', '-01:01:01', '1 01:01:01', '2001-01-01 01:01:01'),
('2001-01-01 01:01:01', null, '-1 01:01:01', null), ('2001-01-01 01:01:01', null, '-1 01:01:01', null),
(null, null, null, null), (null, null, null, null),
('2001-01-01 01:01:01', '01:01:01', '1 01:01:01', '2001-01-01 01:01:01'); ('2001-01-01 01:01:01', '01:01:01', '1 01:01:01', '2001-01-01 01:01:01');
......
...@@ -775,6 +775,81 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs, ...@@ -775,6 +775,81 @@ static bool get_interval_info(const char *str,uint length,CHARSET_INFO *cs,
} }
/*
Calculate difference between two datetime values as seconds + microseconds.
SYNOPSIS
calc_time_diff()
l_time1 - TIME/DATE/DATETIME value
l_time2 - TIME/DATE/DATETIME value
l_sign - 1 absolute values are substracted,
-1 absolute values are added.
seconds_out - Out parameter where difference between
l_time1 and l_time2 in seconds is stored.
microseconds_out- Out parameter where microsecond part of difference
between l_time1 and l_time2 is stored.
NOTE
This function calculates difference between l_time1 and l_time2 absolute
values. So one should set l_sign and correct result if he want to take
signs into account (i.e. for TIME values).
RETURN VALUES
Returns sign of difference.
1 means negative result
0 means positive result
*/
static bool calc_time_diff(TIME *l_time1, TIME *l_time2, int l_sign,
longlong *seconds_out, long *microseconds_out)
{
long days;
bool neg;
longlong microseconds;
/*
We suppose that if first argument is MYSQL_TIMESTAMP_TIME
the second argument should be TIMESTAMP_TIME also.
We should check it before calc_time_diff call.
*/
if (l_time1->time_type == MYSQL_TIMESTAMP_TIME) // Time value
days= l_time1->day - l_sign*l_time2->day;
else
{
days= calc_daynr((uint) l_time1->year,
(uint) l_time1->month,
(uint) l_time1->day);
if (l_time2->time_type == MYSQL_TIMESTAMP_TIME)
days-= l_sign*l_time2->day;
else
days-= l_sign*calc_daynr((uint) l_time2->year,
(uint) l_time2->month,
(uint) l_time2->day);
}
microseconds= ((longlong)days*LL(86400) +
(longlong)(l_time1->hour*3600L +
l_time1->minute*60L +
l_time1->second) -
l_sign*(longlong)(l_time2->hour*3600L +
l_time2->minute*60L +
l_time2->second)) * LL(1000000) +
(longlong)l_time1->second_part -
l_sign*(longlong)l_time2->second_part;
neg= 0;
if (microseconds < 0)
{
microseconds= -microseconds;
neg= 1;
}
*seconds_out= microseconds/1000000L;
*microseconds_out= (long) (microseconds%1000000L);
return neg;
}
longlong Item_func_period_add::val_int() longlong Item_func_period_add::val_int()
{ {
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
...@@ -2373,11 +2448,11 @@ String *Item_func_add_time::val_str(String *str) ...@@ -2373,11 +2448,11 @@ String *Item_func_add_time::val_str(String *str)
DBUG_ASSERT(fixed == 1); DBUG_ASSERT(fixed == 1);
TIME l_time1, l_time2, l_time3; TIME l_time1, l_time2, l_time3;
bool is_time= 0; bool is_time= 0;
long microseconds, seconds, days= 0; long days, microseconds;
longlong seconds;
int l_sign= sign; int l_sign= sign;
null_value=0; null_value=0;
l_time3.neg= 0;
if (is_date) // TIMESTAMP function if (is_date) // TIMESTAMP function
{ {
if (get_arg0_date(&l_time1, TIME_FUZZY_DATE) || if (get_arg0_date(&l_time1, TIME_FUZZY_DATE) ||
...@@ -2393,51 +2468,26 @@ String *Item_func_add_time::val_str(String *str) ...@@ -2393,51 +2468,26 @@ String *Item_func_add_time::val_str(String *str)
l_time2.time_type == MYSQL_TIMESTAMP_DATETIME) l_time2.time_type == MYSQL_TIMESTAMP_DATETIME)
goto null_date; goto null_date;
is_time= (l_time1.time_type == MYSQL_TIMESTAMP_TIME); is_time= (l_time1.time_type == MYSQL_TIMESTAMP_TIME);
if (is_time && (l_time2.neg == l_time1.neg && l_time1.neg))
l_time3.neg= 1;
} }
if (l_time1.neg != l_time2.neg) if (l_time1.neg != l_time2.neg)
l_sign= -l_sign; l_sign= -l_sign;
microseconds= l_time1.second_part + l_sign*l_time2.second_part; l_time3.neg= calc_time_diff(&l_time1, &l_time2, -l_sign,
seconds= (l_time1.hour*3600L + l_time1.minute*60L + l_time1.second + &seconds, &microseconds);
(l_time2.day*86400L + l_time2.hour*3600L +
l_time2.minute*60L + l_time2.second)*l_sign);
if (is_time)
seconds+= l_time1.day*86400L;
else
days+= calc_daynr((uint) l_time1.year,(uint) l_time1.month,
(uint) l_time1.day);
seconds= seconds + microseconds/1000000L;
microseconds= microseconds%1000000L;
days+= seconds/86400L;
seconds= seconds%86400L;
if (microseconds < 0) /*
{ If first argument was negative and diff between arguments
microseconds+= 1000000L; is non-zero we need to swap sign to get proper result.
seconds--; */
} if (l_time1.neg && (seconds || microseconds))
if (seconds < 0) l_time3.neg= 1-l_time3.neg; // Swap sign of result
{
days+= seconds/86400L - 1; if (!is_time && l_time3.neg)
seconds+= 86400L;
}
if (days < 0)
{
if (!is_time)
goto null_date; goto null_date;
if (microseconds)
{
microseconds= 1000000L - microseconds;
seconds++;
}
seconds= 86400L - seconds;
days= -(++days);
l_time3.neg= 1;
}
calc_time_from_sec(&l_time3, seconds, microseconds); days= (long)(seconds/86400L);
calc_time_from_sec(&l_time3, (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,&l_time3.year,&l_time3.month,&l_time3.day);
...@@ -2482,71 +2532,6 @@ void Item_func_add_time::print(String *str) ...@@ -2482,71 +2532,6 @@ void Item_func_add_time::print(String *str)
} }
/*
Calculate difference between two datetime values as seconds + microseconds.
SYNOPSIS
calc_time_diff()
l_time1 - TIME/DATE/DATETIME value
l_time2 - TIME/DATE/DATETIME value
l_sign - 1 absolute values are substracted,
-1 absolute values are added.
seconds_out - Out parameter where difference between
l_time1 and l_time2 in seconds is stored.
microseconds_out- Out parameter where microsecond part of difference
between l_time1 and l_time2 is stored.
NOTE
This function calculates difference between l_time1 and l_time2 absolute
values. So one should set l_sign and correct result if he want to take
signs into account (i.e. for TIME values).
RETURN VALUES
Returns sign of difference.
1 means negative result
0 means positive result
*/
static bool calc_time_diff(TIME *l_time1, TIME *l_time2, int l_sign,
longlong *seconds_out, long *microseconds_out)
{
long days;
bool neg;
longlong microseconds;
/*
We suppose that if first argument is MYSQL_TIMESTAMP_TIME
the second argument should be TIMESTAMP_TIME also.
We should check it before calc_time_diff call.
*/
if (l_time1->time_type == MYSQL_TIMESTAMP_TIME) // Time value
days= l_time1->day - l_sign*l_time2->day;
else // DateTime value
days= (calc_daynr((uint) l_time1->year,
(uint) l_time1->month,
(uint) l_time1->day) -
l_sign*calc_daynr((uint) l_time2->year,
(uint) l_time2->month,
(uint) l_time2->day));
microseconds= ((longlong)days*86400L +
l_time1->hour*3600L + l_time1->minute*60L + l_time1->second -
(longlong)l_sign*(l_time2->hour*3600L + l_time2->minute*60L +
l_time2->second))*1000000L +
l_time1->second_part - l_sign*l_time2->second_part;
neg= 0;
if (microseconds < 0)
{
microseconds= -microseconds;
neg= 1;
}
*seconds_out= microseconds/1000000L;
*microseconds_out= (long) (microseconds%1000000L);
return neg;
}
/* /*
TIMEDIFF(t,s) is a time function that calculates the TIMEDIFF(t,s) is a time function that calculates the
time value between a start and end time. time value between a start and end time.
...@@ -2572,16 +2557,15 @@ String *Item_func_timediff::val_str(String *str) ...@@ -2572,16 +2557,15 @@ String *Item_func_timediff::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;
l_time3.neg= calc_time_diff(&l_time1,&l_time2, l_sign, l_time3.neg= calc_time_diff(&l_time1, &l_time2, l_sign,
&seconds, &microseconds); &seconds, &microseconds);
/* /*
For MYSQL_TIMESTAMP_TIME only: For MYSQL_TIMESTAMP_TIME only:
If both argumets are negative values and diff between them 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_time2.neg == l_time1.neg) && l_time1.neg && if (l_time1.neg && (seconds || microseconds))
(seconds || microseconds))
l_time3.neg= 1-l_time3.neg; // Swap sign of result l_time3.neg= 1-l_time3.neg; // Swap sign of result
calc_time_from_sec(&l_time3, (long) seconds, microseconds); calc_time_from_sec(&l_time3, (long) seconds, microseconds);
......
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