Commit b41b0fd6 authored by unknown's avatar unknown

Bug#18997: DATE_ADD and DATE_SUB perform year2K autoconversion magic on 4-digit year value

if input year for date_add() / date_sub() with INTERVAL is low enough for
calc_daynr() to possibly return incorrect results (calc_daynr() has no information
on whether the year is low because it was a two-digit year ('77) or because it
was a really low four-digit year (0077) and will indiscriminately try to turn the
value into a four-digit year by adding 1900 or 2000 respectively), the functions
will now throw NULL.


include/my_time.h:
  new define YY_MAGIC_BELOW: if year is below this threshold, magic kicks in in
  calc_daynr(). the idea is to convert two-digit years to four-digit ones, adding
  1900 to values >= YY_PART_YEAR or adding 2000 otherwise.
  current value of YY_MAGIC_BELOW derived from original code in calc_daynr()
mysql-test/r/func_time.result:
  test where 2 digit -> 4 digit year magic kicks in, and whether we throw NULL when
  it happens
mysql-test/t/func_time.test:
  test where 2 digit -> 4 digit year magic kicks in, and whether we throw NULL when
  it happens
sql-common/my_time.c:
  use new const YY_MAGIC_BELOW, apply 2-digit -> 4-digit magic only to years
  below this threshold.
sql/item_timefunc.cc:
  throw NULL when year in date_add() / date_sub() would be affected by
  2 digit -> 4 digit magic.
parent e8c981c7
...@@ -38,7 +38,10 @@ typedef long my_time_t; ...@@ -38,7 +38,10 @@ typedef long my_time_t;
#define MY_TIME_T_MAX LONG_MAX #define MY_TIME_T_MAX LONG_MAX
#define MY_TIME_T_MIN LONG_MIN #define MY_TIME_T_MIN LONG_MIN
/* two-digit years < this are 20..; >= this are 19.. */
#define YY_PART_YEAR 70 #define YY_PART_YEAR 70
/* apply above magic to years < this */
#define YY_MAGIC_BELOW 200
/* Flags to str_to_datetime */ /* Flags to str_to_datetime */
#define TIME_FUZZY_DATE 1 #define TIME_FUZZY_DATE 1
......
...@@ -736,42 +736,28 @@ select last_day('2005-00-00'); ...@@ -736,42 +736,28 @@ select last_day('2005-00-00');
last_day('2005-00-00') last_day('2005-00-00')
NULL NULL
Warnings: Warnings:
Warning 1292 Truncated incorrect datetime value: '2005-00-00' Warning 1292 Incorrect datetime value: '2005-00-00'
select last_day('2005-00-01'); select last_day('2005-00-01');
last_day('2005-00-01') last_day('2005-00-01')
NULL NULL
Warnings: Warnings:
Warning 1292 Truncated incorrect datetime value: '2005-00-01' Warning 1292 Incorrect datetime value: '2005-00-01'
select last_day('2005-01-00'); select last_day('2005-01-00');
last_day('2005-01-00') last_day('2005-01-00')
NULL NULL
Warnings: Warnings:
Warning 1292 Truncated incorrect datetime value: '2005-01-00' Warning 1292 Incorrect datetime value: '2005-01-00'
select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')), select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')),
monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m')); monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m'));
monthname(str_to_date(null, '%m')) monthname(str_to_date(null, '%m')) monthname(str_to_date(1, '%m')) monthname(str_to_date(0, '%m')) monthname(str_to_date(null, '%m')) monthname(str_to_date(null, '%m')) monthname(str_to_date(1, '%m')) monthname(str_to_date(0, '%m'))
NULL NULL January NULL NULL NULL January NULL
End of 4.1 tests
explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1, explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1,
timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2; timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2;
id select_type table type possible_keys key key_len ref rows Extra id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used 1 SIMPLE NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings: Warnings:
Note 1003 select timestampdiff(WEEK,_latin1'2001-02-01',_latin1'2001-05-01') AS `a1`,timestampdiff(SECOND_FRAC,_latin1'2001-02-01 12:59:59.120000',_latin1'2001-05-01 12:58:58.119999') AS `a2` Note 1003 select timestampdiff(WEEK,_latin1'2001-02-01',_latin1'2001-05-01') AS `a1`,timestampdiff(SECOND_FRAC,_latin1'2001-02-01 12:59:59.120000',_latin1'2001-05-01 12:58:58.119999') AS `a2`
select last_day('2005-00-00');
last_day('2005-00-00')
NULL
Warnings:
Warning 1292 Incorrect datetime value: '2005-00-00'
select last_day('2005-00-01');
last_day('2005-00-01')
NULL
Warnings:
Warning 1292 Incorrect datetime value: '2005-00-01'
select last_day('2005-01-00');
last_day('2005-01-00')
NULL
Warnings:
Warning 1292 Incorrect datetime value: '2005-01-00'
select time_format('100:00:00', '%H %k %h %I %l'); select time_format('100:00:00', '%H %k %h %I %l');
time_format('100:00:00', '%H %k %h %I %l') time_format('100:00:00', '%H %k %h %I %l')
100 100 04 04 4 100 100 04 04 4
...@@ -891,3 +877,38 @@ id day id day ...@@ -891,3 +877,38 @@ id day id day
1 2005-06-01 3 2005-07-15 1 2005-06-01 3 2005-07-15
3 2005-07-01 3 2005-07-15 3 2005-07-01 3 2005-07-15
DROP TABLE t1,t2; DROP TABLE t1,t2;
End of 5.0 tests
select date_sub("0050-01-01 00:00:01",INTERVAL 2 SECOND);
date_sub("0050-01-01 00:00:01",INTERVAL 2 SECOND)
NULL
select date_sub("0199-01-01 00:00:01",INTERVAL 2 SECOND);
date_sub("0199-01-01 00:00:01",INTERVAL 2 SECOND)
NULL
select date_add("0199-12-31 23:59:59",INTERVAL 2 SECOND);
date_add("0199-12-31 23:59:59",INTERVAL 2 SECOND)
NULL
select date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND);
date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND)
0199-12-31 23:59:59
select date_sub("0200-01-01 00:00:01",INTERVAL 1 SECOND);
date_sub("0200-01-01 00:00:01",INTERVAL 1 SECOND)
0200-01-01 00:00:00
select date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND);
date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND)
0199-12-31 23:59:59
select date_add("2001-01-01 23:59:59",INTERVAL -2000 YEAR);
date_add("2001-01-01 23:59:59",INTERVAL -2000 YEAR)
0001-01-01 23:59:59
select date_sub("50-01-01 00:00:01",INTERVAL 2 SECOND);
date_sub("50-01-01 00:00:01",INTERVAL 2 SECOND)
2049-12-31 23:59:59
select date_sub("90-01-01 00:00:01",INTERVAL 2 SECOND);
date_sub("90-01-01 00:00:01",INTERVAL 2 SECOND)
1989-12-31 23:59:59
select date_sub("0069-01-01 00:00:01",INTERVAL 2 SECOND);
date_sub("0069-01-01 00:00:01",INTERVAL 2 SECOND)
NULL
select date_sub("0169-01-01 00:00:01",INTERVAL 2 SECOND);
date_sub("0169-01-01 00:00:01",INTERVAL 2 SECOND)
NULL
End of 5.1 tests
...@@ -367,19 +367,11 @@ select last_day('2005-01-00'); ...@@ -367,19 +367,11 @@ select last_day('2005-01-00');
select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')), select monthname(str_to_date(null, '%m')), monthname(str_to_date(null, '%m')),
monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m')); monthname(str_to_date(1, '%m')), monthname(str_to_date(0, '%m'));
# End of 4.1 tests --echo End of 4.1 tests
explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1, explain extended select timestampdiff(SQL_TSI_WEEK, '2001-02-01', '2001-05-01') as a1,
timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2; timestampdiff(SQL_TSI_FRAC_SECOND, '2001-02-01 12:59:59.120000', '2001-05-01 12:58:58.119999') as a2;
#
# Bug #10568
#
select last_day('2005-00-00');
select last_day('2005-00-01');
select last_day('2005-01-00');
# #
# Bug #10590: %h, %I, and %l format specifies should all return results in # Bug #10590: %h, %I, and %l format specifies should all return results in
# the 0-11 range # the 0-11 range
...@@ -482,4 +474,23 @@ SELECT * FROM t1, t2 ...@@ -482,4 +474,23 @@ SELECT * FROM t1, t2
DROP TABLE t1,t2; DROP TABLE t1,t2;
# End of 5.0 tests --echo End of 5.0 tests
#
# Bug #18997
#
select date_sub("0050-01-01 00:00:01",INTERVAL 2 SECOND);
select date_sub("0199-01-01 00:00:01",INTERVAL 2 SECOND);
select date_add("0199-12-31 23:59:59",INTERVAL 2 SECOND);
select date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND);
select date_sub("0200-01-01 00:00:01",INTERVAL 1 SECOND);
select date_sub("0200-01-01 00:00:01",INTERVAL 2 SECOND);
select date_add("2001-01-01 23:59:59",INTERVAL -2000 YEAR);
select date_sub("50-01-01 00:00:01",INTERVAL 2 SECOND);
select date_sub("90-01-01 00:00:01",INTERVAL 2 SECOND);
select date_sub("0069-01-01 00:00:01",INTERVAL 2 SECOND);
select date_sub("0169-01-01 00:00:01",INTERVAL 2 SECOND);
--echo End of 5.1 tests
...@@ -689,7 +689,7 @@ long calc_daynr(uint year,uint month,uint day) ...@@ -689,7 +689,7 @@ long calc_daynr(uint year,uint month,uint day)
if (year == 0 && month == 0 && day == 0) if (year == 0 && month == 0 && day == 0)
DBUG_RETURN(0); /* Skip errors */ DBUG_RETURN(0); /* Skip errors */
if (year < 200) if (year < YY_MAGIC_BELOW)
{ {
if ((year=year+1900) < 1900+YY_PART_YEAR) if ((year=year+1900) < 1900+YY_PART_YEAR)
year+=100; year+=100;
......
...@@ -1963,6 +1963,9 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date) ...@@ -1963,6 +1963,9 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
if (date_sub_interval) if (date_sub_interval)
interval.neg = !interval.neg; interval.neg = !interval.neg;
if (ltime->year < YY_MAGIC_BELOW)
return (null_value=1);
return (null_value= date_add_interval(ltime, int_type, interval)); return (null_value= date_add_interval(ltime, int_type, interval));
} }
......
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