Commit 634d3ff2 authored by unknown's avatar unknown

Fixes a number of problems with time/datetime <-> string conversion functions:

- bug #11655 "Wrong time is returning from nested selects - maximum time exists
- input and output TIME values were not validated properly in several conversion functions
- bug #20927 "sec_to_time treats big unsigned as signed"
- integer overflows were not checked in several functions. As a result, input values like 2^32 or 3600*2^32 were treated as 0
- BIGINT UNSIGNED values were treated as SIGNED in several functions
- in cases where both input string truncation and out-of-range TIME value occur, only 'truncated incorrect time value' warning was produced


include/my_time.h:
  Added defines for the TIME limits
  Added defines for the warning flags set by str_to_time() and check_time_range()
  Added check_time_range() declaration
mysql-test/r/func_sapdb.result:
  Fixed testcases which relied on incorrect TIMEDIFF() behaviour
mysql-test/r/func_time.result:
  Fixed testcase which relied on incorrect behaviour
  Added testcases for out-of-range values in SEC_TO_TIME(), TIME_TO_SEC(), ADDTIME(), SUBTIME() and EXTRACT()
mysql-test/t/func_time.test:
  Added testcases for out-of-range values in SEC_TO_TIME(), TIME_TO_SEC(), ADDTIME(), SUBTIME() and EXTRACT()
sql-common/my_time.c:
  Added check_time_range() to be used from str_to_time() and item_timefunc.cc
  Added new out-of-range flag to str_to_time() warnings
  Use '%u' instead of '%d' in my_*_to_str() because the arguments are unsigned
sql/field.cc:
  Replaced out-of-range checks with checks for flags returned by str_to_time()
sql/item_timefunc.cc:
  Added wrappers over make_datetime() and make_time() which perform out-of-range checks on input values
  Moved common code in Item_func_sec_to_time::val_str() and Item_func_sec_to_time::val_int() into a separate function sec_to_time()
  Replaced calls to make_datetime() with make_datetime_with_warn() in Item_func_add_time and Item_func_timediff
  Checks for 'unsigned int' overflows in Item_func_maketime
  Use make_time_with_warn() instead of make_time() in Item_func_maketime
  Fixed incorrect sizeof() in Item_func_str_to_date::get_time()
sql/time.cc:
  Check for return value of str_to_time() along with warning flags
parent 7c8931d8
......@@ -44,12 +44,24 @@ typedef long my_time_t;
#define TIME_FUZZY_DATE 1
#define TIME_DATETIME_ONLY 2
#define MYSQL_TIME_WARN_TRUNCATED 1
#define MYSQL_TIME_WARN_OUT_OF_RANGE 2
/* Limits for the TIME data type */
#define TIME_MAX_HOUR 838
#define TIME_MAX_MINUTE 59
#define TIME_MAX_SECOND 59
#define TIME_MAX_VALUE (TIME_MAX_HOUR*10000 + TIME_MAX_MINUTE*100 + \
TIME_MAX_SECOND)
enum enum_mysql_timestamp_type
str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
uint flags, int *was_cut);
bool str_to_time(const char *str,uint length, MYSQL_TIME *l_time,
int *was_cut);
int *warning);
int check_time_range(struct st_mysql_time *time, int *warning);
long calc_daynr(uint year,uint month,uint day);
......
......@@ -97,7 +97,9 @@ subtime("02:01:01.999999", "01:01:01.999999")
01:00:00.000000
select timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002");
timediff("1997-01-01 23:59:59.000001","1995-12-31 23:59:59.000002")
8807:59:59.999999
838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '8807:59:59.999999'
select timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002");
timediff("1997-12-31 23:59:59.000001","1997-12-30 01:01:01.000002")
46:58:57.999999
......@@ -208,13 +210,16 @@ NULL NULL
SELECT TIMEDIFF(t1,t4) As ttt, TIMEDIFF(t2, t3) As qqq from test;
ttt qqq
-744:00:00 NULL
26305:01:02 22:58:58
-26305:01:02 -22:58:58
838:59:59 22:58:58
-838:59:59 -22:58:58
NULL 26:02:02
00:00:00 -26:02:02
NULL NULL
NULL NULL
00:00:00 -24:00:00
Warnings:
Warning 1292 Truncated incorrect time value: '26305:01:02'
Warning 1292 Truncated incorrect time value: '-26305:01:02'
drop table t1, test;
select addtime("-01:01:01.01", "-23:59:59.1") as a;
a
......@@ -224,7 +229,9 @@ a
10000
select microsecond(19971231235959.01) as a;
a
10000
0
Warnings:
Warning 1292 Truncated incorrect time value: '19971231235959.01'
select date_add("1997-12-31",INTERVAL "10.09" SECOND_MICROSECOND) as a;
a
1997-12-31 00:00:10.090000
......
......@@ -330,7 +330,9 @@ extract(DAY_MINUTE FROM "02 10:11:12")
21011
select extract(DAY_SECOND FROM "225 10:11:12");
extract(DAY_SECOND FROM "225 10:11:12")
225101112
8385959
Warnings:
Warning 1292 Truncated incorrect time value: '225 10:11:12'
select extract(HOUR FROM "1999-01-02 10:11:12");
extract(HOUR FROM "1999-01-02 10:11:12")
10
......@@ -688,3 +690,91 @@ t1 CREATE TABLE `t1` (
`from_unixtime(1) + 0` double(23,6) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=latin1
drop table t1;
SELECT SEC_TO_TIME(3300000);
SEC_TO_TIME(3300000)
838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '3300000'
SELECT SEC_TO_TIME(3300000)+0;
SEC_TO_TIME(3300000)+0
8385959.000000
Warnings:
Warning 1292 Truncated incorrect time value: '3300000'
SELECT SEC_TO_TIME(3600 * 4294967296);
SEC_TO_TIME(3600 * 4294967296)
838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '15461882265600'
SELECT TIME_TO_SEC('916:40:00');
TIME_TO_SEC('916:40:00')
3020399
Warnings:
Warning 1292 Truncated incorrect time value: '916:40:00'
SELECT ADDTIME('500:00:00', '416:40:00');
ADDTIME('500:00:00', '416:40:00')
838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '916:40:00'
SELECT ADDTIME('916:40:00', '416:40:00');
ADDTIME('916:40:00', '416:40:00')
838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '916:40:00'
Warning 1292 Truncated incorrect time value: '1255:39:59'
SELECT SUBTIME('916:40:00', '416:40:00');
SUBTIME('916:40:00', '416:40:00')
422:19:59
Warnings:
Warning 1292 Truncated incorrect time value: '916:40:00'
SELECT SUBTIME('-916:40:00', '416:40:00');
SUBTIME('-916:40:00', '416:40:00')
-838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '-916:40:00'
Warning 1292 Truncated incorrect time value: '-1255:39:59'
SELECT MAKETIME(916,0,0);
MAKETIME(916,0,0)
838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '916:00:00'
SELECT MAKETIME(4294967296, 0, 0);
MAKETIME(4294967296, 0, 0)
838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '4294967296:00:00'
SELECT MAKETIME(-4294967296, 0, 0);
MAKETIME(-4294967296, 0, 0)
-838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '-4294967296:00:00'
SELECT MAKETIME(0, 4294967296, 0);
MAKETIME(0, 4294967296, 0)
NULL
SELECT MAKETIME(0, 0, 4294967296);
MAKETIME(0, 0, 4294967296)
NULL
SELECT MAKETIME(CAST(-1 AS UNSIGNED), 0, 0);
MAKETIME(CAST(-1 AS UNSIGNED), 0, 0)
838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '18446744073709551615:00:00'
SELECT EXTRACT(HOUR FROM '100000:02:03');
EXTRACT(HOUR FROM '100000:02:03')
838
Warnings:
Warning 1292 Truncated incorrect time value: '100000:02:03'
CREATE TABLE t1(f1 TIME);
INSERT INTO t1 VALUES('916:00:00 a');
Warnings:
Warning 1265 Data truncated for column 'f1' at row 1
Warning 1264 Data truncated; out of range for column 'f1' at row 1
SELECT * FROM t1;
f1
838:59:59
DROP TABLE t1;
SELECT SEC_TO_TIME(CAST(-1 AS UNSIGNED));
SEC_TO_TIME(CAST(-1 AS UNSIGNED))
838:59:59
Warnings:
Warning 1292 Truncated incorrect time value: '18446744073709551615'
End of 4.1 tests
......@@ -358,4 +358,47 @@ create table t1 select now() - now(), curtime() - curtime(),
show create table t1;
drop table t1;
# End of 4.1 tests
#
# Bug #11655: Wrong time is returning from nested selects - maximum time exists
#
# check if SEC_TO_TIME() handles out-of-range values correctly
SELECT SEC_TO_TIME(3300000);
SELECT SEC_TO_TIME(3300000)+0;
SELECT SEC_TO_TIME(3600 * 4294967296);
# check if TIME_TO_SEC() handles out-of-range values correctly
SELECT TIME_TO_SEC('916:40:00');
# check if ADDTIME() handles out-of-range values correctly
SELECT ADDTIME('500:00:00', '416:40:00');
SELECT ADDTIME('916:40:00', '416:40:00');
# check if SUBTIME() handles out-of-range values correctly
SELECT SUBTIME('916:40:00', '416:40:00');
SELECT SUBTIME('-916:40:00', '416:40:00');
# check if MAKETIME() handles out-of-range values correctly
SELECT MAKETIME(916,0,0);
SELECT MAKETIME(4294967296, 0, 0);
SELECT MAKETIME(-4294967296, 0, 0);
SELECT MAKETIME(0, 4294967296, 0);
SELECT MAKETIME(0, 0, 4294967296);
SELECT MAKETIME(CAST(-1 AS UNSIGNED), 0, 0);
# check if EXTRACT() handles out-of-range values correctly
SELECT EXTRACT(HOUR FROM '100000:02:03');
# check if we get proper warnings if both input string truncation
# and out-of-range value occur
CREATE TABLE t1(f1 TIME);
INSERT INTO t1 VALUES('916:00:00 a');
SELECT * FROM t1;
DROP TABLE t1;
#
# Bug #20927: sec_to_time treats big unsigned as signed
#
# check if SEC_TO_TIME() handles BIGINT UNSIGNED values correctly
SELECT SEC_TO_TIME(CAST(-1 AS UNSIGNED));
--echo End of 4.1 tests
......@@ -402,8 +402,10 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
There may be an optional [.second_part] after seconds
length Length of str
l_time Store result here
was_cut Set to 1 if value was cut during conversion or to 0
otherwise.
warning Set MYSQL_TIME_WARN_TRUNCATED flag if the input string
was cut during conversion, and/or
MYSQL_TIME_WARN_OUT_OF_RANGE flag, if the value is
out of range.
NOTES
Because of the extra days argument, this function can only
......@@ -414,16 +416,16 @@ str_to_datetime(const char *str, uint length, MYSQL_TIME *l_time,
1 error
*/
bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
int *was_cut)
bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time, int *warning)
{
long date[5],value;
ulong date[5];
ulonglong value;
const char *end=str+length, *end_of_days;
bool found_days,found_hours;
uint state;
l_time->neg=0;
*was_cut= 0;
*warning= 0;
for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
length--;
if (str != end && *str == '-')
......@@ -438,13 +440,16 @@ bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
/* Check first if this is a full TIMESTAMP */
if (length >= 12)
{ /* Probably full timestamp */
int was_cut;
enum enum_mysql_timestamp_type
res= str_to_datetime(str, length, l_time,
(TIME_FUZZY_DATE | TIME_DATETIME_ONLY), was_cut);
(TIME_FUZZY_DATE | TIME_DATETIME_ONLY), &was_cut);
if ((int) res >= (int) MYSQL_TIMESTAMP_ERROR)
{
if (was_cut)
*warning|= MYSQL_TIME_WARN_TRUNCATED;
return res == MYSQL_TIMESTAMP_ERROR;
/* We need to restore was_cut flag since str_to_datetime can modify it */
*was_cut= 0;
}
}
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
......@@ -524,7 +529,7 @@ bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
if (field_length > 0)
value*= (long) log_10_int[field_length];
else if (field_length < 0)
*was_cut= 1;
*warning|= MYSQL_TIME_WARN_TRUNCATED;
date[4]=value;
}
else
......@@ -538,10 +543,7 @@ bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
((str[1] == '-' || str[1] == '+') &&
(end - str) > 2 &&
my_isdigit(&my_charset_latin1, str[2]))))
{
*was_cut= 1;
return 1;
}
if (internal_format_positions[7] != 255)
{
......@@ -560,12 +562,12 @@ bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
}
}
/* Some simple checks */
if (date[2] >= 60 || date[3] >= 60)
{
*was_cut= 1;
/* Integer overflow checks */
if (date[0] > UINT_MAX || date[1] > UINT_MAX ||
date[2] > UINT_MAX || date[3] > UINT_MAX ||
date[4] > UINT_MAX)
return 1;
}
l_time->year= 0; /* For protocol::store_time */
l_time->month= 0;
l_time->day= date[0];
......@@ -575,6 +577,10 @@ bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
l_time->second_part= date[4];
l_time->time_type= MYSQL_TIMESTAMP_TIME;
/* Check if the value is valid and fits into TIME range */
if (check_time_range(l_time, warning))
return 1;
/* Check if there is garbage at end of the TIME specification */
if (str != end)
{
......@@ -582,7 +588,7 @@ bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
{
if (!my_isspace(&my_charset_latin1,*str))
{
*was_cut= 1;
*warning|= MYSQL_TIME_WARN_TRUNCATED;
break;
}
} while (++str != end);
......@@ -591,6 +597,47 @@ bool str_to_time(const char *str, uint length, MYSQL_TIME *l_time,
}
/*
Check 'time' value to lie in the TIME range
SYNOPSIS:
check_time_range()
time pointer to TIME value
warning set MYSQL_TIME_WARN_OUT_OF_RANGE flag if the value is out of range
DESCRIPTION
If the time value lies outside of the range [-838:59:59, 838:59:59],
set it to the closest endpoint of the range and set
MYSQL_TIME_WARN_OUT_OF_RANGE flag in the 'warning' variable.
RETURN
0 time value is valid, but was possibly truncated
1 time value is invalid
*/
int check_time_range(struct st_mysql_time *time, int *warning)
{
longlong hour;
if (time->minute >= 60 || time->second >= 60)
return 1;
hour= time->hour + (24*time->day);
if (hour <= TIME_MAX_HOUR &&
(hour != TIME_MAX_HOUR || time->minute != TIME_MAX_MINUTE ||
time->second != TIME_MAX_SECOND || !time->second_part))
return 0;
time->day= 0;
time->hour= TIME_MAX_HOUR;
time->minute= TIME_MAX_MINUTE;
time->second= TIME_MAX_SECOND;
time->second_part= 0;
*warning|= MYSQL_TIME_WARN_OUT_OF_RANGE;
return 0;
}
/*
Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
......@@ -776,7 +823,7 @@ void set_zero_time(MYSQL_TIME *tm, enum enum_mysql_timestamp_type time_type)
int my_time_to_str(const MYSQL_TIME *l_time, char *to)
{
uint extra_hours= 0;
return my_sprintf(to, (to, "%s%02d:%02d:%02d",
return my_sprintf(to, (to, "%s%02u:%02u:%02u",
(l_time->neg ? "-" : ""),
extra_hours+ l_time->hour,
l_time->minute,
......@@ -785,7 +832,7 @@ int my_time_to_str(const MYSQL_TIME *l_time, char *to)
int my_date_to_str(const MYSQL_TIME *l_time, char *to)
{
return my_sprintf(to, (to, "%04d-%02d-%02d",
return my_sprintf(to, (to, "%04u-%02u-%02u",
l_time->year,
l_time->month,
l_time->day));
......@@ -793,7 +840,7 @@ int my_date_to_str(const MYSQL_TIME *l_time, char *to)
int my_datetime_to_str(const MYSQL_TIME *l_time, char *to)
{
return my_sprintf(to, (to, "%04d-%02d-%02d %02d:%02d:%02d",
return my_sprintf(to, (to, "%04u-%02u-%02u %02u:%02u:%02u",
l_time->year,
l_time->month,
l_time->day,
......
......@@ -3961,9 +3961,10 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
{
TIME ltime;
long tmp;
int error;
int error= 0;
int warning;
if (str_to_time(from, len, &ltime, &error))
if (str_to_time(from, len, &ltime, &warning))
{
tmp=0L;
error= 2;
......@@ -3972,29 +3973,27 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
}
else
{
if (error)
if (warning & MYSQL_TIME_WARN_TRUNCATED)
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_TRUNCATED,
from, len, MYSQL_TIMESTAMP_TIME, 1);
if (ltime.month)
ltime.day=0;
tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second);
if (tmp > 8385959)
if (warning & MYSQL_TIME_WARN_OUT_OF_RANGE)
{
tmp=8385959;
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE,
from, len, MYSQL_TIMESTAMP_TIME, !error);
error= 1;
}
if (ltime.month)
ltime.day=0;
tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second);
if (error > 1)
error= 2;
}
if (ltime.neg)
tmp= -tmp;
error |= Field_time::store((longlong) tmp);
int3store(ptr,tmp);
return error;
}
......@@ -4003,16 +4002,16 @@ int Field_time::store(double nr)
{
long tmp;
int error= 0;
if (nr > 8385959.0)
if (nr > (double)TIME_MAX_VALUE)
{
tmp=8385959L;
tmp= TIME_MAX_VALUE;
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME);
error= 1;
}
else if (nr < -8385959.0)
else if (nr < (double)-TIME_MAX_VALUE)
{
tmp= -8385959L;
tmp= -TIME_MAX_VALUE;
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE, nr, MYSQL_TIMESTAMP_TIME);
error= 1;
......@@ -4040,17 +4039,17 @@ int Field_time::store(longlong nr)
{
long tmp;
int error= 0;
if (nr > (longlong) 8385959L)
if (nr > (longlong) TIME_MAX_VALUE)
{
tmp=8385959L;
tmp= TIME_MAX_VALUE;
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE, nr,
MYSQL_TIMESTAMP_TIME, 1);
error= 1;
}
else if (nr < (longlong) -8385959L)
else if (nr < (longlong) -TIME_MAX_VALUE)
{
tmp= -8385959L;
tmp= -TIME_MAX_VALUE;
set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
ER_WARN_DATA_OUT_OF_RANGE, nr,
MYSQL_TIMESTAMP_TIME, 1);
......
......@@ -95,6 +95,124 @@ static bool make_datetime(date_time_format_types format, TIME *ltime,
}
/*
Wrapper over make_datetime() with validation of the input TIME value
NOTE
see make_datetime() for more information
RETURN
1 if there was an error during converion
0 otherwise
*/
static bool make_datetime_with_warn(date_time_format_types format, TIME *ltime,
String *str)
{
int warning= 0;
bool rc;
if (make_datetime(format, ltime, str))
return 1;
if (check_time_range(ltime, &warning))
return 1;
if (!warning)
return 0;
make_truncated_value_warning(current_thd, str->ptr(), str->length(),
MYSQL_TIMESTAMP_TIME);
return make_datetime(format, ltime, str);
}
/*
Wrapper over make_time() with validation of the input TIME value
NOTE
see make_time() for more info
RETURN
1 if there was an error during conversion
0 otherwise
*/
static bool make_time_with_warn(const DATE_TIME_FORMAT *format,
TIME *l_time, String *str)
{
int warning= 0;
make_time(format, l_time, str);
if (check_time_range(l_time, &warning))
return 1;
if (warning)
{
make_truncated_value_warning(current_thd, str->ptr(), str->length(),
MYSQL_TIMESTAMP_TIME);
make_time(format, l_time, str);
}
return 0;
}
/*
Convert seconds to TIME value with overflow checking
SYNOPSIS:
sec_to_time()
seconds number of seconds
unsigned_flag 1, if 'seconds' is unsigned, 0, otherwise
ltime output TIME value
DESCRIPTION
If the 'seconds' argument is inside TIME data range, convert it to a
corresponding value.
Otherwise, truncate the resulting value to the nearest endpoint, and
produce a warning message.
RETURN
1 if the value was truncated during conversion
0 otherwise
*/
static bool sec_to_time(longlong seconds, bool unsigned_flag, TIME *ltime)
{
uint sec;
bzero((char *)ltime, sizeof(*ltime));
if (seconds < 0)
{
if (unsigned_flag)
goto overflow;
ltime->neg= 1;
if (seconds < -3020399)
goto overflow;
seconds= -seconds;
}
else if (seconds > 3020399)
goto overflow;
sec= (uint) ((ulonglong) seconds % 3600);
ltime->hour= (uint) (seconds/3600);
ltime->minute= sec/60;
ltime->second= sec % 60;
return 0;
overflow:
ltime->hour= TIME_MAX_HOUR;
ltime->minute= TIME_MAX_MINUTE;
ltime->second= TIME_MAX_SECOND;
char buf[22];
int len= (int)(longlong10_to_str(seconds, buf, unsigned_flag ? 10 : -10)
- buf);
make_truncated_value_warning(current_thd, buf, len, MYSQL_TIMESTAMP_TIME);
return 1;
}
/*
Date formats corresponding to compound %r and %T conversion specifiers
......@@ -1487,8 +1605,6 @@ int Item_func_now::save_in_field(Field *to, bool no_conversions)
String *Item_func_sec_to_time::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
longlong seconds=(longlong) args[0]->val_int();
uint sec;
TIME ltime;
if ((null_value=args[0]->null_value) || str->alloc(19))
......@@ -1497,18 +1613,7 @@ String *Item_func_sec_to_time::val_str(String *str)
return (String*) 0;
}
ltime.neg= 0;
if (seconds < 0)
{
seconds= -seconds;
ltime.neg= 1;
}
sec= (uint) ((ulonglong) seconds % 3600);
ltime.day= 0;
ltime.hour= (uint) (seconds/3600);
ltime.minute= sec/60;
ltime.second= sec % 60;
sec_to_time(args[0]->val_int(), args[0]->unsigned_flag, &ltime);
make_time((DATE_TIME_FORMAT *) 0, &ltime, str);
return str;
......@@ -1518,16 +1623,15 @@ String *Item_func_sec_to_time::val_str(String *str)
longlong Item_func_sec_to_time::val_int()
{
DBUG_ASSERT(fixed == 1);
longlong seconds=args[0]->val_int();
longlong sign=1;
TIME ltime;
if ((null_value=args[0]->null_value))
return 0;
if (seconds < 0)
{
seconds= -seconds;
sign= -1;
}
return sign*((seconds / 3600)*10000+((seconds/60) % 60)*100+ (seconds % 60));
sec_to_time(args[0]->val_int(), args[0]->unsigned_flag, &ltime);
return (ltime.neg ? -1 : 1) *
((ltime.hour)*10000 + ltime.minute*100 + ltime.second);
}
......@@ -2532,6 +2636,8 @@ String *Item_func_add_time::val_str(String *str)
if (l_time1.neg != l_time2.neg)
l_sign= -l_sign;
bzero((char *)&l_time3, sizeof(l_time3));
l_time3.neg= calc_time_diff(&l_time1, &l_time2, -l_sign,
&seconds, &microseconds);
......@@ -2560,7 +2666,7 @@ String *Item_func_add_time::val_str(String *str)
}
l_time3.hour+= days*24;
if (!make_datetime(l_time1.second_part || l_time2.second_part ?
if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ?
TIME_MICROSECOND : TIME_ONLY,
&l_time3, str))
return str;
......@@ -2617,6 +2723,8 @@ String *Item_func_timediff::val_str(String *str)
if (l_time1.neg != l_time2.neg)
l_sign= -l_sign;
bzero((char *)&l_time3, sizeof(l_time3));
l_time3.neg= calc_time_diff(&l_time1, &l_time2, l_sign,
&seconds, &microseconds);
......@@ -2630,7 +2738,7 @@ String *Item_func_timediff::val_str(String *str)
calc_time_from_sec(&l_time3, (long) seconds, microseconds);
if (!make_datetime(l_time1.second_part || l_time2.second_part ?
if (!make_datetime_with_warn(l_time1.second_part || l_time2.second_part ?
TIME_MICROSECOND : TIME_ONLY,
&l_time3, str))
return str;
......@@ -2650,29 +2758,57 @@ String *Item_func_maketime::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
TIME ltime;
bool overflow= 0;
long hour= (long) args[0]->val_int();
long minute= (long) args[1]->val_int();
long second= (long) args[2]->val_int();
longlong hour= args[0]->val_int();
longlong minute= args[1]->val_int();
longlong second= args[2]->val_int();
if ((null_value=(args[0]->null_value ||
args[1]->null_value ||
args[2]->null_value ||
minute > 59 || minute < 0 ||
second > 59 || second < 0 ||
minute < 0 || minute > 59 ||
second < 0 || second > 59 ||
str->alloc(19))))
return 0;
bzero((char *)&ltime, sizeof(ltime));
ltime.neg= 0;
/* Check for integer overflows */
if (hour < 0)
{
if (args[0]->unsigned_flag)
overflow= 1;
else
ltime.neg= 1;
hour= -hour;
}
ltime.hour= (ulong) hour;
ltime.minute= (ulong) minute;
ltime.second= (ulong) second;
make_time((DATE_TIME_FORMAT *) 0, &ltime, str);
if (-hour > UINT_MAX || hour > UINT_MAX)
overflow= 1;
if (!overflow)
{
ltime.hour= (uint) ((hour < 0 ? -hour : hour));
ltime.minute= (uint) minute;
ltime.second= (uint) second;
}
else
{
ltime.hour= TIME_MAX_HOUR;
ltime.minute= TIME_MAX_MINUTE;
ltime.second= TIME_MAX_SECOND;
char buf[28];
char *ptr= longlong10_to_str(hour, buf, args[0]->unsigned_flag ? 10 : -10);
int len = (int)(ptr - buf) +
my_sprintf(ptr, (ptr, ":%02u:%02u", (uint)minute, (uint)second));
make_truncated_value_warning(current_thd, buf, len, MYSQL_TIMESTAMP_TIME);
}
if (make_time_with_warn((DATE_TIME_FORMAT *) 0, &ltime, str))
{
null_value= 1;
return 0;
}
return str;
}
......@@ -2878,7 +3014,7 @@ bool Item_func_str_to_date::get_date(TIME *ltime, uint fuzzy_date)
goto null_date;
null_value= 0;
bzero((char*) ltime, sizeof(ltime));
bzero((char*) ltime, sizeof(*ltime));
date_time_format.format.str= (char*) format->ptr();
date_time_format.format.length= format->length();
if (extract_date_time(&date_time_format, val->ptr(), val->length(),
......
......@@ -255,9 +255,9 @@ my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *in_dst_time_gap)
bool
str_to_time_with_warn(const char *str, uint length, TIME *l_time)
{
int was_cut;
bool ret_val= str_to_time(str, length, l_time, &was_cut);
if (was_cut)
int warning;
bool ret_val= str_to_time(str, length, l_time, &warning);
if (ret_val || warning)
make_truncated_value_warning(current_thd, str, length, MYSQL_TIMESTAMP_TIME);
return ret_val;
}
......
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