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, ...@@ -341,9 +341,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
{ {
uint days; uint days;
days= calc_daynr(l_time->year,1,1) + yearday - 1; 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; goto err;
get_date_from_daynr(days,&l_time->year,&l_time->month,&l_time->day);
} }
if (week_number >= 0 && weekday) if (week_number >= 0 && weekday)
...@@ -388,9 +387,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format, ...@@ -388,9 +387,8 @@ static bool extract_date_time(DATE_TIME_FORMAT *format,
(weekday - 1); (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; 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 || 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) ...@@ -1385,13 +1383,16 @@ bool Item_func_from_days::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
if ((null_value=args[0]->null_value)) if ((null_value=args[0]->null_value))
return 1; return 1;
bzero(ltime, sizeof(MYSQL_TIME)); 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))) if (get_date_from_daynr((long) value, &ltime->year, &ltime->month,
return TRUE; &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; 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) ...@@ -2388,14 +2389,12 @@ bool Item_func_makedate::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
year= year_2000_handling(year); year= year_2000_handling(year);
days= calc_daynr(year,1,1) + daynr - 1; days= calc_daynr(year,1,1) + daynr - 1;
/* Day number from year 0 to 9999-12-31 */ if (get_date_from_daynr(days, &ltime->year, &ltime->month, &ltime->day))
if (days >= 0 && days <= MAX_DAY_NUMBER) goto err;
{ ltime->time_type= MYSQL_TIMESTAMP_DATE;
bzero(ltime, sizeof(*ltime)); ltime->neg= 0;
ltime->time_type= MYSQL_TIMESTAMP_DATE; ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0;
get_date_from_daynr(days, &ltime->year, &ltime->month, &ltime->day); return (null_value= 0);
return (null_value= 0);
}
err: err:
return (null_value= 1); return (null_value= 1);
...@@ -2489,8 +2488,8 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date) ...@@ -2489,8 +2488,8 @@ bool Item_func_add_time::get_date(MYSQL_TIME *ltime, uint fuzzy_date)
if (!is_time) if (!is_time)
{ {
get_date_from_daynr(days,&ltime->year,&ltime->month,&ltime->day); if (get_date_from_daynr(days,&ltime->year,&ltime->month,&ltime->day) ||
if (!ltime->day) !ltime->day)
return (null_value= 1); return (null_value= 1);
return (null_value= 0); return (null_value= 0);
} }
......
...@@ -2445,7 +2445,7 @@ void free_field_buffers_larger_than(TABLE *table, uint32 size); ...@@ -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); int set_zone(int nr,int min_zone,int max_zone);
ulong convert_period_to_month(ulong period); ulong convert_period_to_month(ulong period);
ulong convert_month_to_period(ulong month); 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); uint *day);
my_time_t TIME_to_timestamp(THD *thd, const MYSQL_TIME *t, uint *error); 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, bool str_to_time_with_warn(const char *str,uint length,MYSQL_TIME *l_time,
......
...@@ -24,9 +24,9 @@ ...@@ -24,9 +24,9 @@
#include <m_ctype.h> #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. Name description of interval names used in statements.
...@@ -146,46 +146,42 @@ uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year) ...@@ -146,46 +146,42 @@ uint calc_week(MYSQL_TIME *l_time, uint week_behaviour, uint *year)
/* Change a daynr to year, month and day */ /* Change a daynr to year, month and day */
/* Daynr 0 is returned as date 00.00.00 */ /* 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 *ret_day)
{ {
uint year,temp,leap_day,day_of_year,days_in_year; uint year,temp,leap_day,day_of_year,days_in_year;
uchar *month_pos; uchar *month_pos;
DBUG_ENTER("get_date_from_daynr"); DBUG_ENTER("get_date_from_daynr");
if (daynr <= 365L || daynr >= 3652500) if (daynr < 365 || daynr > MAX_DAY_NUMBER)
{ /* Fix if wrong daynr */ DBUG_RETURN(1);
*ret_year= *ret_month = *ret_day =0;
year= (uint) (daynr*100 / 36525L);
temp=(((year-1)/100+1)*3)/4;
day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
while (day_of_year > (days_in_year= calc_days_in_year(year)))
{
day_of_year-=days_in_year;
(year)++;
} }
else leap_day=0;
if (days_in_year == 366)
{ {
year= (uint) (daynr*100 / 36525L); if (day_of_year > 31+28)
temp=(((year-1)/100+1)*3)/4;
day_of_year=(uint) (daynr - (long) year * 365L) - (year-1)/4 +temp;
while (day_of_year > (days_in_year= calc_days_in_year(year)))
{ {
day_of_year-=days_in_year; day_of_year--;
(year)++; if (day_of_year == 31+28)
leap_day=1; /* Handle leapyears leapday */
} }
leap_day=0;
if (days_in_year == 366)
{
if (day_of_year > 31+28)
{
day_of_year--;
if (day_of_year == 31+28)
leap_day=1; /* Handle leapyears leapday */
}
}
*ret_month=1;
for (month_pos= days_in_month ;
day_of_year > (uint) *month_pos ;
day_of_year-= *(month_pos++), (*ret_month)++)
;
*ret_year=year;
*ret_day=day_of_year+leap_day;
} }
DBUG_VOID_RETURN; *ret_month=1;
for (month_pos= days_in_month ;
day_of_year > (uint) *month_pos ;
day_of_year-= *(month_pos++), (*ret_month)++)
;
*ret_year=year;
*ret_day=day_of_year+leap_day;
DBUG_RETURN(0);
} }
/* Functions to handle periods */ /* Functions to handle periods */
...@@ -805,7 +801,6 @@ void make_truncated_value_warning(THD *thd, ...@@ -805,7 +801,6 @@ void make_truncated_value_warning(THD *thd,
/* Daynumber from year 0 to 9999-12-31 */ /* Daynumber from year 0 to 9999-12-31 */
#define MAX_DAY_NUMBER 3652424L
#define COMBINE(X) \ #define COMBINE(X) \
(((((X)->day * 24LL + (X)->hour) * 60LL + \ (((((X)->day * 24LL + (X)->hour) * 60LL + \
(X)->minute) * 60LL + (X)->second)*1000000LL + \ (X)->minute) * 60LL + (X)->second)*1000000LL + \
...@@ -872,19 +867,18 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type, ...@@ -872,19 +867,18 @@ bool date_add_interval(MYSQL_TIME *ltime, interval_type int_type,
daynr= usec; daynr= usec;
/* Day number from year 0 to 9999-12-31 */ /* 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; goto invalid_date;
get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
&ltime->day);
break; break;
} }
case INTERVAL_WEEK: case INTERVAL_WEEK:
period= (calc_daynr(ltime->year,ltime->month,ltime->day) + period= (calc_daynr(ltime->year,ltime->month,ltime->day) +
sign * (long) interval.day); sign * (long) interval.day);
/* Daynumber from year 0 to 9999-12-31 */ /* 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; goto invalid_date;
get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
break; break;
case INTERVAL_YEAR: case INTERVAL_YEAR:
ltime->year+= sign * (long) interval.year; ltime->year+= sign * (long) interval.year;
...@@ -1034,4 +1028,3 @@ int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b) ...@@ -1034,4 +1028,3 @@ int my_time_compare(MYSQL_TIME *a, MYSQL_TIME *b)
return 0; 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