Commit a44331ab authored by Sergei Golubchik's avatar Sergei Golubchik

MDEV-456 An out-of-range datetime value (with a 5-digit year) can be created and cause troubles

fix Item_func_add_time::get_date() to generate valid dates.
Move the validity check inside get_date_from_daynr()
instead of relying on callers
(5 that had it, and 2 that did not, but should've)
parent 95ee3fbf
create table t1 (d datetime);
insert t1 values (addtime('9999-12-31 23:59:59', '00:00:01')),
(from_days(3652499));
select * from t1;
d
NULL
NULL
drop table t1;
#
# MDEV-456 An out-of-range datetime value (with a 5-digit year) can be created and cause troubles
#
create table t1 (d datetime);
insert t1 values (addtime('9999-12-31 23:59:59', '00:00:01')),
(from_days(3652499));
select * from t1;
drop table t1;
......@@ -341,9 +341,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
{
uint days;
days= calc_daynr(l_time->year,1,1) + yearday - 1;
if (days <= 0 || days > MAX_DAY_NUMBER)
if (get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day))
goto err;
get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day);
}
if (week_number >= 0 && weekday)
......@@ -388,9 +387,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
(weekday - 1);
}
if (days <= 0 || days > MAX_DAY_NUMBER)
if (get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day))
goto err;
get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day);
}
if (l_time->month > 12 || l_time->day > 31 || l_time->hour > 23 ||
......@@ -1385,13 +1383,16 @@ bool Item_func_from_days::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
if ((null_value=args[0]->null_value))
return 1;
bzero(ltime, sizeof(MYSQL_TIME));
get_date_from_daynr((long) value, &ltime->year, &ltime->month, &ltime->day);
if ((null_value= ((fuzzy_date & TIME_NO_ZERO_DATE) && ltime->year == 0)))
return TRUE;
if (get_date_from_daynr((long) value, &ltime->year, &ltime->month,
&ltime->day))
return (null_value= 1);
if ((fuzzy_date & TIME_NO_ZERO_DATE) && ltime->year == 0)
return (null_value= 1);
ltime->time_type= MYSQL_TIMESTAMP_DATE;
return 0;
return (null_value= 0);
}
......@@ -2388,14 +2389,12 @@ bool Item_func_makedate::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
year= year_2000_handling(year);
days= calc_daynr(year,1,1) + daynr - 1;
/* Day number from year 0 to 9999-12-31 */
if (days >= 0 && days <= MAX_DAY_NUMBER)
{
bzero(ltime, sizeof(*ltime));
if (get_date_from_daynr(days, &ltime->year, &ltime->month, &ltime->day))
goto err;
ltime->time_type= MYSQL_TIMESTAMP_DATE;
get_date_from_daynr(days, &ltime->year, &ltime->month, &ltime->day);
ltime->neg= 0;
ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
return (null_value= 0);
}
err:
return (null_value= 1);
......@@ -2489,8 +2488,8 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
if (!is_time)
{
get_date_from_daynr(days,&ltime->year,&ltime->month,&ltime->day);
if (!ltime->day)
if (get_date_from_daynr(days,&ltime->year,&ltime->month,&ltime->day) ||
!ltime->day)
return (null_value= 1);
return (null_value= 0);
}
......
......@@ -2445,7 +2445,7 @@ void free_field_buffers_larger_than(TABLE *table, uint32 size);
int set_zone(int nr,int min_zone,int max_zone);
ulong convert_period_to_month(ulong period);
ulong convert_month_to_period(ulong month);
void get_date_from_daynr(long daynr,uint *year, uint *month,
bool get_date_from_daynr(long daynr,uint *year, uint *month,
uint *day);
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error);
bool str_to_time_with_warn(const char *str,uint length,MYSQL_TIME *l_time,
......
......@@ -24,9 +24,9 @@
#include <m_ctype.h>
/* Some functions to calculate dates */
#define MAX_DAY_NUMBER 3652424L
#ifndef TESTTIME
/* Some functions to calculate dates */
/*
Name description of interval names used in statements.
......@@ -146,19 +146,16 @@ uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year)
/* Change a daynr to year, month and day */
/* Daynr 0 is returned as date 00.00.00 */
void get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
bool get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
uint *ret_day)
{
uint year,temp,leap_day,day_of_year,days_in_year;
uchar *month_pos;
DBUG_ENTER("get_date_from_daynr");
if (daynr <= 365L || daynr >= 3652500)
{ /* Fix if wrong daynr */
*ret_year= *ret_month = *ret_day =0;
}
else
{
if (daynr < 365 || daynr > MAX_DAY_NUMBER)
DBUG_RETURN(1);
year= (uint) (daynr*100 / 36525L);
temp=(((year-1)/100+1)*3)/4;
day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
......@@ -184,8 +181,7 @@ void get_date_from_daynr(long daynr,uint *ret_year,uint *ret_month,
;
*ret_year=year;
*ret_day=day_of_year+leap_day;
}
DBUG_VOID_RETURN;
DBUG_RETURN(0);
}
/* Functions to handle periods */
......@@ -805,7 +801,6 @@ void make_truncated_value_warning(THD *thd,
/* Daynumber from year 0 to 9999-12-31 */
#define MAX_DAY_NUMBER 3652424L
#define COMBINE(X) \
(((((X)->day * 24LL + (X)->hour) * 60LL + \
(X)->minute) * 60LL + (X)->second)*1000000LL + \
......@@ -872,19 +867,18 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
daynr= usec;
/* Day number from year 0 to 9999-12-31 */
if ((ulonglong) daynr > MAX_DAY_NUMBER)
if (get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
&ltime->day))
goto invalid_date;
get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
&ltime->day);
break;
}
case INTERVAL_WEEK:
period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
sign * (long) interval.day);
/* Daynumber from year 0 to 9999-12-31 */
if ((ulong) period > MAX_DAY_NUMBER)
if (get_date_from_daynr((long) period,&ltime->year,&ltime->month,
&ltime->day))
goto invalid_date;
get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
break;
case INTERVAL_YEAR:
ltime->year+= sign * (long) interval.year;
......@@ -1034,4 +1028,3 @@ int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
return 0;
}
#endif
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