Merge pchardin@bk-internal.mysql.com:/home/bk/mysql-4.1

into  outpost.site:/home/cps/mysql/trees/4.1-runtime-bug9191


configure.in:
  Auto merged
include/my_time.h:
  Auto merged
mysql-test/r/func_time.result:
  Auto merged
mysql-test/r/rename.result:
  Auto merged
mysql-test/t/func_time.test:
  Auto merged
sql-common/my_time.c:
  Auto merged
sql/item_timefunc.cc:
  Auto merged
sql/time.cc:
  Auto merged
mysql-test/t/rename.test:
  choose one of the race problem solutions. It was solved
  differently in -runtime and mainstream
This commit is contained in:
unknown
2006-11-10 15:05:38 +03:00
19 changed files with 389 additions and 72 deletions

View File

@ -714,14 +714,27 @@ long calc_daynr(uint year,uint month,uint day)
RETURN VALUE
Time in UTC seconds since Unix Epoch representation.
*/
my_time_t
my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap)
my_time_t
my_system_gmt_sec(const MYSQL_TIME *t_src, long *my_timezone,
bool *in_dst_time_gap)
{
uint loop;
time_t tmp;
time_t tmp= 0;
int shift= 0;
MYSQL_TIME tmp_time;
MYSQL_TIME *t= &tmp_time;
struct tm *l_time,tm_tmp;
long diff, current_timezone;
/*
Use temp variable to avoid trashing input data, which could happen in
case of shift required for boundary dates processing.
*/
memcpy(&tmp_time, t_src, sizeof(MYSQL_TIME));
if (!validate_timestamp_range(t))
return 0;
/*
Calculate the gmt time based on current time and timezone
The -1 on the end is to ensure that if have a date that exists twice
@ -735,13 +748,89 @@ my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap)
Note: this code assumes that our time_t estimation is not too far away
from real value (we assume that localtime_r(tmp) will return something
within 24 hrs from t) which is probably true for all current time zones.
*/
tmp=(time_t) (((calc_daynr((uint) t->year,(uint) t->month,(uint) t->day) -
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
3600);
current_timezone= my_time_zone;
Note2: For the dates, which have time_t representation close to
MAX_INT32 (efficient time_t limit for supported platforms), we should
do a small trick to avoid overflow. That is, convert the date, which is
two days earlier, and then add these days to the final value.
The same trick is done for the values close to 0 in time_t
representation for platfroms with unsigned time_t (QNX).
To be more verbose, here is a sample (extracted from the code below):
(calc_daynr(2038, 1, 19) - (long) days_at_timestart)*86400L + 4*3600L
would return -2147480896 because of the long type overflow. In result
we would get 1901 year in localtime_r(), which is an obvious error.
Alike problem raises with the dates close to Epoch. E.g.
(calc_daynr(1969, 12, 31) - (long) days_at_timestart)*86400L + 23*3600L
will give -3600.
On some platforms, (E.g. on QNX) time_t is unsigned and localtime(-3600)
wil give us a date around 2106 year. Which is no good.
Theoreticaly, there could be problems with the latter conversion:
there are at least two timezones, which had time switches near 1 Jan
of 1970 (because of political reasons). These are America/Hermosillo and
America/Mazatlan time zones. They changed their offset on
1970-01-01 08:00:00 UTC from UTC-8 to UTC-7. For these zones
the code below will give incorrect results for dates close to
1970-01-01, in the case OS takes into account these historical switches.
Luckily, it seems that we support only one platform with unsigned
time_t. It's QNX. And QNX does not support historical timezone data at all.
E.g. there are no /usr/share/zoneinfo/ files or any other mean to supply
historical information for localtime_r() etc. That is, the problem is not
relevant to QNX.
We are safe with shifts close to MAX_INT32, as there are no known
time switches on Jan 2038 yet :)
*/
if ((t->year == TIMESTAMP_MAX_YEAR) && (t->month == 1) && (t->day > 4))
{
/*
Below we will pass (uint) (t->day - shift) to calc_daynr.
As we don't want to get an overflow here, we will shift
only safe dates. That's why we have (t->day > 4) above.
*/
t->day-= 2;
shift= 2;
}
#ifdef TIME_T_UNSIGNED
else
{
/*
We can get 0 in time_t representaion only on 1969, 31 of Dec or on
1970, 1 of Jan. For both dates we use shift, which is added
to t->day in order to step out a bit from the border.
This is required for platforms, where time_t is unsigned.
As far as I know, among the platforms we support it's only QNX.
Note: the order of below if-statements is significant.
*/
if ((t->year == TIMESTAMP_MIN_YEAR + 1) && (t->month == 1)
&& (t->day <= 10))
{
t->day+= 2;
shift= -2;
}
if ((t->year == TIMESTAMP_MIN_YEAR) && (t->month == 12)
&& (t->day == 31))
{
t->year++;
t->month= 1;
t->day= 2;
shift= -2;
}
}
#endif
tmp= (time_t) (((calc_daynr((uint) t->year, (uint) t->month, (uint) t->day) -
(long) days_at_timestart)*86400L + (long) t->hour*3600L +
(long) (t->minute*60 + t->second)) + (time_t) my_time_zone -
3600);
current_timezone= my_time_zone;
localtime_r(&tmp,&tm_tmp);
l_time=&tm_tmp;
for (loop=0;
@ -793,7 +882,24 @@ my_system_gmt_sec(const MYSQL_TIME *t, long *my_timezone, bool *in_dst_time_gap)
*in_dst_time_gap= 1;
}
*my_timezone= current_timezone;
/* shift back, if we were dealing with boundary dates */
tmp+= shift*86400L;
/*
This is possible for dates, which slightly exceed boundaries.
Conversion will pass ok for them, but we don't allow them.
First check will pass for platforms with signed time_t.
instruction above (tmp+= shift*86400L) could exceed
MAX_INT32 (== TIMESTAMP_MAX_VALUE) and overflow will happen.
So, tmp < TIMESTAMP_MIN_VALUE will be triggered. On platfroms
with unsigned time_t tmp+= shift*86400L might result in a number,
larger then TIMESTAMP_MAX_VALUE, so another check will work.
*/
if ((tmp < TIMESTAMP_MIN_VALUE) || (tmp > TIMESTAMP_MAX_VALUE))
tmp= 0;
end:
return (my_time_t) tmp;
} /* my_system_gmt_sec */