Commit 8a7bc052 authored by unknown's avatar unknown
Browse files

Fix Bug #9191 "TIMESTAMP/from_unixtime() no longer accepts 2^31-1"

(4.1 version, with post-review fixes)
  
  The fix for another Bug (6439) limited FROM_UNIXTIME() to
  TIMESTAMP_MAX_VALUE which is 2145916799 or 2037-12-01 23:59:59 GMT,
  however unix timestamp in general is not considered to be limited 
  by this value. All dates up to power(2,31)-1 are valid.
  
  This patch extends allowed TIMESTAMP range so, that max
  TIMESTAMP value is power(2,31)-1. It also corrects
  FROM_UNIXTIME() and UNIX_TIMESTAMP() functions, so that
  max allowed UNIX_TIMESTAMP() is power(2,31)-1. FROM_UNIXTIME()
  is fixed accordingly to allow conversion of dates up to
  2038-01-19 03:14:07 UTC. The patch also fixes CONVERT_TZ()
  function to allow extended range of dates.
  
  The main problem solved in the patch is possible overflows
  of variables, used in broken-time representation to time_t
  conversion (required for UNIX_TIMESTAMP).


acinclude.m4:
  Add new macro to check time_t range
configure.in:
  Call the macro to check time_t range
include/my_time.h:
  Move time-related defines to proper place.
  Add a function to perform a rough check if
  a TIMESTAMP value fits into the boundaries.
  Note: it is defined as "static inline", as
  otherwise libmysql won't compile (due to the
  way how gcc handles "inline" directive).
mysql-test/r/func_time.result:
  Update test result
mysql-test/r/timezone.result:
  Update test result
mysql-test/r/timezone2.result:
  Update test result
mysql-test/t/func_time.test:
  Add test for Bug#9191 and update test to be consistent
  with new TIMESTAMP boundaries
mysql-test/t/timezone.test:
  Update old tests to be consistent
  with new TIMESTAMP boundaries
mysql-test/t/timezone2.test:
  Update tests for convert_tz to be consistent with new
  TIMESTAMP boundaries
sql/item_timefunc.cc:
  Fix convert_tz to allow dates from the new (extended)
  TIMESTAMP range
sql/mysql_priv.h:
  Move time handling defaults to my_time.h
sql-common/my_time.c:
  Because of increased TIMESTAMP_MAX_VALUE overflows in my_system_gmt_sec()
  became possible. Here we make it safe against the overflows by stepping
  back from the boundary dates which are likely to trigger them.
sql/time.cc:
  Update TIME_to_timestamp to allow conversion of
  extended date range
sql/tztime.cc:
  Fix new (4.1) implementation of broken-down time representation
  to time_t conversion routine to avoid overflows during conversion
  of boundary dates
mysql-test/r/timezone4.result:
  New BitKeeper file ``mysql-test/r/timezone4.result''
mysql-test/t/timezone4-master.opt:
  New BitKeeper file ``mysql-test/t/timezone4-master.opt''
mysql-test/t/timezone4.test:
  New BitKeeper file ``mysql-test/t/timezone4.test''
parent 5d46e299
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -1835,6 +1835,30 @@ dnl END OF MYSQL_CHECK_NDBCLUSTER SECTION
dnl ---------------------------------------------------------------------------


dnl
dnl  Macro to check time_t range: according to C standard
dnl  array index myst be greater then 0 => if time_t is signed
dnl  the code in the macros below won't compile.
dnl

AC_DEFUN([MYSQL_CHECK_TIME_T],[
    AC_MSG_CHECKING(if time_t is unsigned)
    AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
        [[
#include <time.h>
        ]],
        [[
        int array[(((time_t)-1) > 0) ? 1 : -1];
        ]] )
    ], [
    AC_DEFINE([TIME_T_UNSIGNED], 1, [Define to 1 if time_t is unsigned])
    AC_MSG_RESULT(yes)
    ],
    [AC_MSG_RESULT(no)]
    )
])


dnl By default, many hosts won't let programs access large files;
dnl one must use special compiler options to get large-file access to work.
dnl For more details about this brain damage please see:
+7 −0
Original line number Diff line number Diff line
@@ -1820,6 +1820,13 @@ then
  AC_MSG_ERROR("MySQL needs a off_t type.")
fi

dnl
dnl check if time_t is unsigned
dnl

MYSQL_CHECK_TIME_T


# do we need #pragma interface/#pragma implementation ?
# yes if it's gcc 2.x, and not icc pretending to be gcc, and not cygwin
AC_MSG_CHECKING(the need for @%:@pragma interface/implementation)
+32 −0
Original line number Diff line number Diff line
@@ -38,6 +38,14 @@ typedef long my_time_t;
#define MY_TIME_T_MAX LONG_MAX
#define MY_TIME_T_MIN LONG_MIN


/* Time handling defaults */
#define TIMESTAMP_MAX_YEAR 2038
#define YY_PART_YEAR	   70
#define TIMESTAMP_MIN_YEAR (1900 + YY_PART_YEAR - 1)
#define TIMESTAMP_MAX_VALUE INT_MAX32
#define TIMESTAMP_MIN_VALUE 1

#define YY_PART_YEAR	   70

/* Flags to str_to_datetime */
@@ -55,6 +63,30 @@ long calc_daynr(uint year,uint month,uint day);

void init_time(void);


/*
  Function to check sanity of a TIMESTAMP value

  DESCRIPTION
    Check if a given MYSQL_TIME value fits in TIMESTAMP range.
    This function doesn't make precise check, but rather a rough
    estimate.

  RETURN VALUES
    FALSE   The value seems sane
    TRUE    The MYSQL_TIME value is definitely out of range
*/

static inline bool validate_timestamp_range(const MYSQL_TIME *t)
{
  if ((t->year > TIMESTAMP_MAX_YEAR || t->year < TIMESTAMP_MIN_YEAR) ||
      (t->year == TIMESTAMP_MAX_YEAR && (t->month > 1 || t->day > 19)) ||
      (t->year == TIMESTAMP_MIN_YEAR && (t->month < 12 || t->day < 31)))
    return FALSE;

  return TRUE;
}

my_time_t 
my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap);

+38 −2
Original line number Diff line number Diff line
@@ -483,12 +483,48 @@ unix_timestamp('1969-12-01 19:00:01')
select from_unixtime(-1);
from_unixtime(-1)
NULL
select from_unixtime(2145916800);
from_unixtime(2145916800)
select from_unixtime(2147483647);
from_unixtime(2147483647)
2038-01-19 06:14:07
select from_unixtime(2147483648);
from_unixtime(2147483648)
NULL
select from_unixtime(0);
from_unixtime(0)
1970-01-01 03:00:00
select unix_timestamp(from_unixtime(2147483647));
unix_timestamp(from_unixtime(2147483647))
2147483647
select unix_timestamp(from_unixtime(2147483648));
unix_timestamp(from_unixtime(2147483648))
NULL
select unix_timestamp('2039-01-20 01:00:00');
unix_timestamp('2039-01-20 01:00:00')
0
select unix_timestamp('1968-01-20 01:00:00');
unix_timestamp('1968-01-20 01:00:00')
0
select unix_timestamp('2038-02-10 01:00:00');
unix_timestamp('2038-02-10 01:00:00')
0
select unix_timestamp('1969-11-20 01:00:00');
unix_timestamp('1969-11-20 01:00:00')
0
select unix_timestamp('2038-01-20 01:00:00');
unix_timestamp('2038-01-20 01:00:00')
0
select unix_timestamp('1969-12-30 01:00:00');
unix_timestamp('1969-12-30 01:00:00')
0
select unix_timestamp('2038-01-17 12:00:00');
unix_timestamp('2038-01-17 12:00:00')
2147331600
select unix_timestamp('1970-01-01 03:00:01');
unix_timestamp('1970-01-01 03:00:01')
1
select unix_timestamp('2038-01-19 07:14:07');
unix_timestamp('2038-01-19 07:14:07')
0
CREATE TABLE t1 (datetime datetime, timestamp timestamp, date date, time time);
INSERT INTO t1 values ("2001-01-02 03:04:05", "2002-01-02 03:04:05", "2003-01-02", "06:07:08");
SELECT * from t1;
+4 −4
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ Warning 1299 Invalid TIMESTAMP value in column 'ts' at row 2
DROP TABLE t1;
select unix_timestamp('1970-01-01 01:00:00'), 
unix_timestamp('1970-01-01 01:00:01'),
unix_timestamp('2038-01-01 00:59:59'),
unix_timestamp('2038-01-01 01:00:00');
unix_timestamp('1970-01-01 01:00:00')	unix_timestamp('1970-01-01 01:00:01')	unix_timestamp('2038-01-01 00:59:59')	unix_timestamp('2038-01-01 01:00:00')
0	1	2145916799	0
unix_timestamp('2038-01-19 04:14:07'),
unix_timestamp('2038-01-19 04:14:08');
unix_timestamp('1970-01-01 01:00:00')	unix_timestamp('1970-01-01 01:00:01')	unix_timestamp('2038-01-19 04:14:07')	unix_timestamp('2038-01-19 04:14:08')
0	1	2147483647	0
Loading