summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/Makefile.am17
-rw-r--r--sql/field.cc685
-rw-r--r--sql/field.h15
-rw-r--r--sql/field_conv.cc12
-rw-r--r--sql/ha_berkeley.cc8
-rw-r--r--sql/item.cc4
-rw-r--r--sql/item_create.cc5
-rw-r--r--sql/item_create.h1
-rw-r--r--sql/item_timefunc.cc292
-rw-r--r--sql/item_timefunc.h56
-rw-r--r--sql/lex.h1
-rw-r--r--sql/log.cc18
-rw-r--r--sql/mysql_priv.h27
-rw-r--r--sql/mysqld.cc23
-rw-r--r--sql/set_var.cc76
-rw-r--r--sql/set_var.h24
-rw-r--r--sql/share/czech/errmsg.txt2
-rw-r--r--sql/share/danish/errmsg.txt2
-rw-r--r--sql/share/dutch/errmsg.txt2
-rw-r--r--sql/share/english/errmsg.txt2
-rw-r--r--sql/share/estonian/errmsg.txt2
-rw-r--r--sql/share/french/errmsg.txt2
-rw-r--r--sql/share/german/errmsg.txt2
-rw-r--r--sql/share/greek/errmsg.txt2
-rw-r--r--sql/share/hungarian/errmsg.txt2
-rw-r--r--sql/share/italian/errmsg.txt2
-rw-r--r--sql/share/japanese/errmsg.txt2
-rw-r--r--sql/share/korean/errmsg.txt2
-rw-r--r--sql/share/norwegian-ny/errmsg.txt2
-rw-r--r--sql/share/norwegian/errmsg.txt2
-rw-r--r--sql/share/polish/errmsg.txt2
-rw-r--r--sql/share/portuguese/errmsg.txt2
-rw-r--r--sql/share/romanian/errmsg.txt2
-rw-r--r--sql/share/russian/errmsg.txt2
-rw-r--r--sql/share/serbian/errmsg.txt2
-rw-r--r--sql/share/slovak/errmsg.txt2
-rw-r--r--sql/share/spanish/errmsg.txt2
-rw-r--r--sql/share/swedish/errmsg.txt2
-rw-r--r--sql/share/ukrainian/errmsg.txt2
-rw-r--r--sql/slave.cc25
-rw-r--r--sql/sql_base.cc1
-rw-r--r--sql/sql_cache.cc7
-rw-r--r--sql/sql_class.cc4
-rw-r--r--sql/sql_class.h3
-rw-r--r--sql/sql_insert.cc4
-rw-r--r--sql/sql_load.cc2
-rw-r--r--sql/sql_parse.cc5
-rw-r--r--sql/sql_select.cc1
-rw-r--r--sql/sql_show.cc10
-rw-r--r--sql/time.cc299
-rw-r--r--sql/tzfile.h137
-rw-r--r--sql/tztime.cc2559
-rw-r--r--sql/tztime.h71
-rw-r--r--sql/unireg.cc1
54 files changed, 3935 insertions, 504 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am
index cdbf78bed0e..66ebed4658f 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -26,7 +26,8 @@ INCLUDES = @MT_INCLUDES@ \
WRAPLIBS= @WRAPLIBS@
SUBDIRS = share
libexec_PROGRAMS = mysqld
-noinst_PROGRAMS = gen_lex_hash
+bin_PROGRAMS = mysql_tzinfo_to_sql
+noinst_PROGRAMS = gen_lex_hash test_time
gen_lex_hash_LDFLAGS = @NOINST_LDFLAGS@
LDADD = @isam_libs@ \
../myisam/libmyisam.a \
@@ -57,8 +58,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
lex.h lex_symbol.h sql_acl.h sql_crypt.h \
log_event.h sql_repl.h slave.h \
stacktrace.h sql_sort.h sql_cache.h set_var.h \
- spatial.h gstream.h client_settings.h \
- examples/ha_example.h examples/ha_archive.h
+ spatial.h gstream.h client_settings.h tzfile.h \
+ tztime.h examples/ha_example.h examples/ha_archive.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \
@@ -88,10 +89,18 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
client.c sql_client.cc mini_client_errors.c pack.c\
stacktrace.c repl_failsafe.h repl_failsafe.cc \
gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \
- examples/ha_example.cc examples/ha_archive.cc
+ tztime.cc examples/ha_example.cc examples/ha_archive.cc
gen_lex_hash_SOURCES = gen_lex_hash.cc
gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS)
+mysql_tzinfo_to_sql_SOURCES = tztime.cc tzfile.h
+mysql_tzinfo_to_sql_CPPFLAGS = -DTZINFO2SQL $(AM_CPPFLAGS)
+mysql_tzinfo_to_sql_LDADD = $(LDADD) $(CXXLDFLAGS)
+
+test_time_SOURCES = tztime.cc time.cc tzfile.h
+test_time_CPPFLAGS = -DTESTTIME $(AM_CPPFLAGS)
+test_time_LDADD = $(LDADD) $(CXXLDFLAGS)
+
DEFS = -DMYSQL_SERVER \
-DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \
-DDATADIR="\"$(MYSQLDATAdir)\"" \
diff --git a/sql/field.cc b/sql/field.cc
index b76e6781247..21256b2bbcd 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -398,7 +398,7 @@ bool Field::get_date(TIME *ltime,uint fuzzydate)
char buff[40];
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
if (!(res=val_str(&tmp)) ||
- str_to_TIME(res->ptr(),res->length(),ltime,fuzzydate) <=
+ str_to_TIME_with_warn(res->ptr(), res->length(), ltime, fuzzydate) <=
TIMESTAMP_DATETIME_ERROR)
return 1;
return 0;
@@ -409,7 +409,7 @@ bool Field::get_time(TIME *ltime)
char buff[40];
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
if (!(res=val_str(&tmp)) ||
- str_to_time(res->ptr(),res->length(),ltime))
+ str_to_time_with_warn(res->ptr(), res->length(), ltime))
return 1;
return 0;
}
@@ -462,7 +462,7 @@ void Field_decimal::overflow(bool negative)
uint len=field_length;
char *to=ptr, filler= '9';
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
if (negative)
{
if (!unsigned_flag)
@@ -546,7 +546,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
char *left_wall,*right_wall;
char tmp_char;
/*
- To remember if current_thd->cuted_fields has already been incremented,
+ To remember if table->in_use->cuted_fields has already been incremented,
to do that only once
*/
bool is_cuted_fields_incr=0;
@@ -572,7 +572,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
from++;
if (from == end)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
is_cuted_fields_incr=1;
}
else if (*from == '+' || *from == '-') // Found some sign ?
@@ -642,13 +642,13 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
it makes the code easer to read.
*/
- if (current_thd->count_cuted_fields)
+ if (table->in_use->count_cuted_fields)
{
// Skip end spaces
for (;from != end && my_isspace(&my_charset_bin, *from); from++) ;
if (from != end) // If still something left, warn
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
is_cuted_fields_incr=1;
}
}
@@ -794,7 +794,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
/*
Write digits of the frac_% parts ;
- Depending on current_thd->count_cutted_fields, we may also want
+ Depending on table->in_use->count_cutted_fields, we may also want
to know if some non-zero tail of these parts will
be truncated (for example, 0.002->0.00 will generate a warning,
while 0.000->0.00 will not)
@@ -812,7 +812,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
{
if (pos == right_wall)
{
- if (current_thd->count_cuted_fields && !is_cuted_fields_incr)
+ if (table->in_use->count_cuted_fields && !is_cuted_fields_incr)
break; // Go on below to see if we lose non zero digits
return 0;
}
@@ -826,7 +826,8 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
if (tmp_char != '0') // Losing a non zero digit ?
{
if (!is_cuted_fields_incr)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_TRUNCATED, 1);
return 0;
}
continue;
@@ -843,7 +844,7 @@ int Field_decimal::store(const char *from, uint len, CHARSET_INFO *cs)
if (tmp_char != '0') // Losing a non zero digit ?
{
if (!is_cuted_fields_incr)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
return 0;
}
continue;
@@ -1061,18 +1062,18 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
if (tmp < 0)
{
tmp=0; /* purecov: inspected */
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (tmp > 255)
{
tmp= 255;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs))
+ else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error= 1;
}
}
@@ -1081,18 +1082,18 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs)
if (tmp < -128)
{
tmp= -128;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (tmp >= 128)
{
tmp= 127;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs))
+ else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error= 1;
}
}
@@ -1110,13 +1111,13 @@ int Field_tiny::store(double nr)
if (nr < 0.0)
{
*ptr=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > 255.0)
{
*ptr=(char) 255;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1127,13 +1128,13 @@ int Field_tiny::store(double nr)
if (nr < -128.0)
{
*ptr= (char) -128;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > 127.0)
{
*ptr=127;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1150,13 +1151,13 @@ int Field_tiny::store(longlong nr)
if (nr < 0L)
{
*ptr=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > 255L)
{
*ptr= (char) 255;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1167,13 +1168,13 @@ int Field_tiny::store(longlong nr)
if (nr < -128L)
{
*ptr= (char) -128;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > 127L)
{
*ptr=127;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1265,18 +1266,18 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs)
if (tmp < 0)
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (tmp > (uint16) ~0)
{
tmp=(uint16) ~0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs))
+ else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error= 1;
}
}
@@ -1285,18 +1286,18 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs)
if (tmp < INT_MIN16)
{
tmp= INT_MIN16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (tmp > INT_MAX16)
{
tmp=INT_MAX16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs))
+ else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error= 1;
}
}
@@ -1322,13 +1323,13 @@ int Field_short::store(double nr)
if (nr < 0)
{
res=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) (uint16) ~0)
{
res=(int16) (uint16) ~0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1339,13 +1340,13 @@ int Field_short::store(double nr)
if (nr < (double) INT_MIN16)
{
res=INT_MIN16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) INT_MAX16)
{
res=INT_MAX16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1371,13 +1372,13 @@ int Field_short::store(longlong nr)
if (nr < 0L)
{
res=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (longlong) (uint16) ~0)
{
res=(int16) (uint16) ~0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1388,13 +1389,13 @@ int Field_short::store(longlong nr)
if (nr < INT_MIN16)
{
res=INT_MIN16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > INT_MAX16)
{
res=INT_MAX16;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1540,18 +1541,18 @@ int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs)
if (tmp < 0)
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (tmp >= (long) (1L << 24))
{
tmp=(long) (1L << 24)-1L;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs))
+ else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error= 1;
}
}
@@ -1560,18 +1561,18 @@ int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs)
if (tmp < INT_MIN24)
{
tmp= INT_MIN24;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (tmp > INT_MAX24)
{
tmp=INT_MAX24;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
- else if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs))
+ else if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error= 1;
}
}
@@ -1590,14 +1591,14 @@ int Field_medium::store(double nr)
if (nr < 0)
{
int3store(ptr,0);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr >= (double) (long) (1L << 24))
{
uint32 tmp=(uint32) (1L << 24)-1L;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1609,14 +1610,14 @@ int Field_medium::store(double nr)
{
long tmp=(long) INT_MIN24;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) INT_MAX24)
{
long tmp=(long) INT_MAX24;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1633,14 +1634,14 @@ int Field_medium::store(longlong nr)
if (nr < 0L)
{
int3store(ptr,0);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr >= (longlong) (long) (1L << 24))
{
long tmp=(long) (1L << 24)-1L;;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1652,14 +1653,14 @@ int Field_medium::store(longlong nr)
{
long tmp=(long) INT_MIN24;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (longlong) INT_MAX24)
{
long tmp=(long) INT_MAX24;
int3store(ptr,tmp);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1771,10 +1772,10 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs)
else
tmp=my_strntol(cs,from,len,10,&end,&error);
if (error ||
- (from+len != end && current_thd->count_cuted_fields &&
+ (from+len != end && table->in_use->count_cuted_fields &&
!test_if_int(from,len,end,cs)))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error= 1;
}
#ifdef WORDS_BIGENDIAN
@@ -1799,13 +1800,13 @@ int Field_long::store(double nr)
if (nr < 0)
{
res=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) (ulong) ~0L)
{
res=(int32) (uint32) ~0L;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1816,13 +1817,13 @@ int Field_long::store(double nr)
if (nr < (double) INT_MIN32)
{
res=(int32) INT_MIN32;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (double) INT_MAX32)
{
res=(int32) INT_MAX32;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1844,18 +1845,26 @@ int Field_long::store(longlong nr)
{
int error= 0;
int32 res;
+
+ /*
+ This assert has nothing to do with this method per se, it was put here
+ only because it is one of the best places for catching places there its
+ condition is broken.
+ */
+ DBUG_ASSERT(table->in_use == current_thd);
+
if (unsigned_flag)
{
if (nr < 0)
{
res=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr >= (LL(1) << 32))
{
res=(int32) (uint32) ~0L;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1866,13 +1875,13 @@ int Field_long::store(longlong nr)
if (nr < (longlong) INT_MIN32)
{
res=(int32) INT_MIN32;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > (longlong) INT_MAX32)
{
res=(int32) INT_MAX32;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -1905,6 +1914,8 @@ double Field_long::val_real(void)
longlong Field_long::val_int(void)
{
int32 j;
+ /* See the comment in Field_long::store(long long) */
+ DBUG_ASSERT(table->in_use == current_thd);
#ifdef WORDS_BIGENDIAN
if (table->db_low_byte_first)
j=sint4korr(ptr);
@@ -2029,10 +2040,10 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs)
else
tmp=my_strntoll(cs,from,len,10,&end,&error);
if (error ||
- (from+len != end && current_thd->count_cuted_fields &&
+ (from+len != end && table->in_use->count_cuted_fields &&
!test_if_int(from,len,end,cs)))
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error= 1;
}
#ifdef WORDS_BIGENDIAN
@@ -2057,13 +2068,13 @@ int Field_longlong::store(double nr)
if (nr < 0)
{
res=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr >= (double) ~ (ulonglong) 0)
{
res= ~(longlong) 0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -2074,13 +2085,13 @@ int Field_longlong::store(double nr)
if (nr <= (double) LONGLONG_MIN)
{
res=(longlong) LONGLONG_MIN;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr >= (double) LONGLONG_MAX)
{
res=(longlong) LONGLONG_MAX;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -2251,10 +2262,10 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs)
int error;
char *end;
double nr= my_strntod(cs,(char*) from,len,&end,&error);
- if (error || ((uint) (end-from) != len && current_thd->count_cuted_fields))
+ if (error || ((uint) (end-from) != len && table->in_use->count_cuted_fields))
{
error= 1;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
}
Field_float::store(nr);
return error;
@@ -2270,13 +2281,13 @@ int Field_float::store(double nr)
{
j= 0;
set_null();
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (unsigned_flag && nr < 0)
{
j= 0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -2300,13 +2311,13 @@ int Field_float::store(double nr)
if (nr < -max_value)
{
j= (float)-max_value;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > max_value)
{
j= (float)max_value;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -2331,7 +2342,7 @@ int Field_float::store(longlong nr)
float j= (float) nr;
if (unsigned_flag && j < 0)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
j=0;
error= 1;
}
@@ -2553,10 +2564,10 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs)
int error;
char *end;
double nr= my_strntod(cs,(char*) from, len, &end, &error);
- if (error || ((uint) (end-from) != len && current_thd->count_cuted_fields))
+ if (error || ((uint) (end-from) != len && table->in_use->count_cuted_fields))
{
error= 1;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
}
Field_double::store(nr);
return error;
@@ -2571,13 +2582,13 @@ int Field_double::store(double nr)
{
nr= 0;
set_null();
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (unsigned_flag && nr < 0)
{
nr= 0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else
@@ -2597,13 +2608,13 @@ int Field_double::store(double nr)
if (nr < -max_value)
{
nr= -max_value;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
else if (nr > max_value)
{
nr= max_value;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
}
}
@@ -2626,7 +2637,7 @@ int Field_double::store(longlong nr)
int error= 0;
if (unsigned_flag && j < 0)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
error= 1;
j=0;
}
@@ -2900,7 +2911,39 @@ void Field_timestamp::set_timestamp_offsets()
int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs)
{
- long tmp=(long) str_to_timestamp(from,len);
+ TIME l_time;
+ my_time_t tmp= 0;
+ int error;
+ bool have_smth_to_conv;
+ bool in_dst_time_gap;
+ THD *thd= table->in_use;
+
+ have_smth_to_conv= (str_to_TIME(from, len, &l_time, 0, &error) >
+ TIMESTAMP_DATETIME_ERROR);
+
+ if (error)
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED,
+ from, len, TIMESTAMP_DATETIME, 1);
+
+ if (have_smth_to_conv)
+ {
+ if (!(tmp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
+ {
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE,
+ from, len, TIMESTAMP_DATETIME, !error);
+
+ error= 1;
+ }
+ else if (in_dst_time_gap)
+ {
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_INVALID_TIMESTAMP,
+ from, len, TIMESTAMP_DATETIME, !error);
+ error= 1;
+ }
+ }
+
#ifdef WORDS_BIGENDIAN
if (table->db_low_byte_first)
{
@@ -2909,7 +2952,7 @@ int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs)
else
#endif
longstore(ptr,tmp);
- return 0;
+ return error;
}
int Field_timestamp::store(double nr)
@@ -2917,8 +2960,10 @@ int Field_timestamp::store(double nr)
int error= 0;
if (nr < 0 || nr > 99991231235959.0)
{
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE,
+ nr, TIMESTAMP_DATETIME);
nr= 0; // Avoid overflow on buff
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
error= 1;
}
error|= Field_timestamp::store((longlong) rint(nr));
@@ -2926,96 +2971,37 @@ int Field_timestamp::store(double nr)
}
-/*
- Convert a datetime of formats YYMMDD, YYYYMMDD or YYMMDDHHMSS to
- YYYYMMDDHHMMSS. The high date '99991231235959' is checked before this
- function.
-*/
-
-static longlong fix_datetime(longlong nr, TIME *time_res,
- const char *field_name, bool *error)
-{
- long part1,part2;
-
- *error= 0;
- if (nr == LL(0) || nr >= LL(10000101000000))
- goto ok;
- if (nr < 101)
- goto err;
- if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
- {
- nr= (nr+20000000L)*1000000L; // YYMMDD, year: 2000-2069
- goto ok;
- }
- if (nr < (YY_PART_YEAR)*10000L+101L)
- goto err;
- if (nr <= 991231L)
- {
- nr= (nr+19000000L)*1000000L; // YYMMDD, year: 1970-1999
- goto ok;
- }
- if (nr < 10000101L)
- goto err;
- if (nr <= 99991231L)
- {
- nr= nr*1000000L;
- goto ok;
- }
- if (nr < 101000000L)
- goto err;
- if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959))
- {
- nr= nr+LL(20000000000000); // YYMMDDHHMMSS, 2000-2069
- goto ok;
- }
- if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000))
- goto err;
- if (nr <= LL(991231235959))
- nr= nr+LL(19000000000000); // YYMMDDHHMMSS, 1970-1999
-
- ok:
- part1=(long) (nr/LL(1000000));
- part2=(long) (nr - (longlong) part1*LL(1000000));
- time_res->year= (int) (part1/10000L); part1%=10000L;
- time_res->month= (int) part1 / 100;
- time_res->day= (int) part1 % 100;
- time_res->hour= (int) (part2/10000L); part2%=10000L;
- time_res->minute=(int) part2 / 100;
- time_res->second=(int) part2 % 100;
-
- if (time_res->year <= 9999 && time_res->month <= 12 &&
- time_res->day <= 31 && time_res->hour <= 23 &&
- time_res->minute <= 59 && time_res->second <= 59)
- return nr;
-
- err:
- THD *thd= current_thd;
- if (thd->count_cuted_fields)
- {
- thd->cuted_fields++;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_DATA_TRUNCATED, ER(ER_WARN_DATA_TRUNCATED),
- field_name, thd->row_count);
- }
- *error= 1;
- return LL(0);
-}
-
-
int Field_timestamp::store(longlong nr)
{
TIME l_time;
- time_t timestamp= 0;
- bool error;
+ my_time_t timestamp= 0;
+ int error;
+ bool in_dst_time_gap;
+ THD *thd= table->in_use;
- if ((nr= fix_datetime(nr, &l_time, field_name, &error)))
+ if (number_to_TIME(nr, &l_time, 0, &error))
{
- long not_used;
-
- if (!(timestamp= my_gmt_sec(&l_time, &not_used)))
- goto err;
- }
+ if (!(timestamp= TIME_to_timestamp(thd, &l_time, &in_dst_time_gap)))
+ {
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE,
+ nr, TIMESTAMP_DATETIME, 1);
+ error= 1;
+ }
+ if (in_dst_time_gap)
+ {
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_INVALID_TIMESTAMP,
+ nr, TIMESTAMP_DATETIME, !error);
+ error= 1;
+ }
+ }
+ else if (error)
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_TRUNCATED,
+ nr, TIMESTAMP_DATETIME, 1);
+
#ifdef WORDS_BIGENDIAN
if (table->db_low_byte_first)
{
@@ -3024,12 +3010,8 @@ int Field_timestamp::store(longlong nr)
else
#endif
longstore(ptr,(uint32) timestamp);
+
return error;
-
-err:
- longstore(ptr,(uint32) 0);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
- return 1;
}
@@ -3040,12 +3022,9 @@ double Field_timestamp::val_real(void)
longlong Field_timestamp::val_int(void)
{
- int part_time;
uint32 temp;
- time_t time_arg;
- struct tm *l_time;
- longlong res;
- struct tm tm_tmp;
+ TIME time_tmp;
+ THD *thd= table->in_use;
#ifdef WORDS_BIGENDIAN
if (table->db_low_byte_first)
@@ -3056,32 +3035,21 @@ longlong Field_timestamp::val_int(void)
if (temp == 0L) // No time
return(0); /* purecov: inspected */
- time_arg=(time_t) temp;
- localtime_r(&time_arg,&tm_tmp);
- l_time=&tm_tmp;
-
- part_time= l_time->tm_year % 100;
- res= ((longlong) (part_time+ ((part_time < YY_PART_YEAR) ? 2000 : 1900))*
- LL(10000000000));
- part_time= l_time->tm_mon+1;
- res+= (longlong) part_time * LL(100000000);
- part_time=l_time->tm_mday;
- res+= (longlong) ((long) part_time * 1000000L);
- part_time=l_time->tm_hour;
- res+= (longlong) (part_time * 10000L);
- part_time=l_time->tm_min;
- res+= (longlong) (part_time * 100);
- part_time=l_time->tm_sec;
- return res+part_time;
+
+ thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, (my_time_t)temp);
+ thd->time_zone_used= 1;
+
+ return time_tmp.year * LL(10000000000) + time_tmp.month * LL(100000000) +
+ time_tmp.day * 1000000L + time_tmp.hour * 10000L +
+ time_tmp.minute * 100 + time_tmp.second;
}
String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
{
uint32 temp, temp2;
- time_t time_arg;
- struct tm *l_time;
- struct tm tm_tmp;
+ TIME time_tmp;
+ THD *thd= table->in_use;
char *to;
val_buffer->alloc(field_length+1);
@@ -3101,11 +3069,11 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
return val_ptr;
}
val_buffer->set_charset(&my_charset_bin); // Safety
- time_arg=(time_t) temp;
- localtime_r(&time_arg,&tm_tmp);
- l_time=&tm_tmp;
+
+ thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp,(my_time_t)temp);
+ thd->time_zone_used= 1;
- temp= l_time->tm_year % 100;
+ temp= time_tmp.year % 100;
if (temp < YY_PART_YEAR)
{
*to++= '2';
@@ -3120,27 +3088,27 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to++= '-';
- temp=l_time->tm_mon+1;
+ temp=time_tmp.month;
temp2=temp/10; temp=temp-temp2*10;
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to++= '-';
- temp=l_time->tm_mday;
+ temp=time_tmp.day;
temp2=temp/10; temp=temp-temp2*10;
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to++= ' ';
- temp=l_time->tm_hour;
+ temp=time_tmp.hour;
temp2=temp/10; temp=temp-temp2*10;
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to++= ':';
- temp=l_time->tm_min;
+ temp=time_tmp.minute;
temp2=temp/10; temp=temp-temp2*10;
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
*to++= ':';
- temp=l_time->tm_sec;
+ temp=time_tmp.second;
temp2=temp/10; temp=temp-temp2*10;
*to++= (char) ('0'+(char) (temp2));
*to++= (char) ('0'+(char) (temp));
@@ -3152,6 +3120,7 @@ String *Field_timestamp::val_str(String *val_buffer, String *val_ptr)
bool Field_timestamp::get_date(TIME *ltime, uint fuzzydate)
{
long temp;
+ THD *thd= table->in_use;
#ifdef WORDS_BIGENDIAN
if (table->db_low_byte_first)
temp=uint4korr(ptr);
@@ -3166,19 +3135,8 @@ bool Field_timestamp::get_date(TIME *ltime, uint fuzzydate)
}
else
{
- struct tm tm_tmp;
- time_t time_arg= (time_t) temp;
- localtime_r(&time_arg,&tm_tmp);
- struct tm *start= &tm_tmp;
- ltime->year= start->tm_year+1900;
- ltime->month= start->tm_mon+1;
- ltime->day= start->tm_mday;
- ltime->hour= start->tm_hour;
- ltime->minute= start->tm_min;
- ltime->second= start->tm_sec;
- ltime->second_part= 0;
- ltime->neg= 0;
- ltime->time_type=TIMESTAMP_DATETIME;
+ thd->variables.time_zone->gmt_sec_to_TIME(ltime, (my_time_t)temp);
+ thd->time_zone_used= 1;
}
return 0;
}
@@ -3245,7 +3203,7 @@ void Field_timestamp::sql_type(String &res) const
void Field_timestamp::set_time()
{
- long tmp= (long) current_thd->query_start();
+ long tmp= (long) table->in_use->query_start();
#ifdef WORDS_BIGENDIAN
if (table->db_low_byte_first)
{
@@ -3267,25 +3225,35 @@ int Field_time::store(const char *from,uint len,CHARSET_INFO *cs)
{
TIME ltime;
long tmp;
- int error= 0;
- if (str_to_time(from,len,&ltime))
+ int error;
+
+ if (str_to_time(from, len, &ltime, &error))
{
tmp=0L;
error= 1;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED,
+ from, len, TIMESTAMP_TIME, 1);
}
else
{
+ if (error)
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_TRUNCATED,
+ from, len, TIMESTAMP_TIME, 1);
+
if (ltime.month)
ltime.day=0;
tmp=(ltime.day*24L+ltime.hour)*10000L+(ltime.minute*100+ltime.second);
if (tmp > 8385959)
{
tmp=8385959;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE,
+ from, len, TIMESTAMP_TIME, !error);
error= 1;
}
}
+
if (ltime.neg)
tmp= -tmp;
error |= Field_time::store((longlong) tmp);
@@ -3300,13 +3268,15 @@ int Field_time::store(double nr)
if (nr > 8385959.0)
{
tmp=8385959L;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME);
error= 1;
}
else if (nr < -8385959.0)
{
tmp= -8385959L;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME);
error= 1;
}
else
@@ -3317,7 +3287,8 @@ int Field_time::store(double nr)
if (tmp % 100 > 59 || tmp/100 % 100 > 59)
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME);
error= 1;
}
}
@@ -3333,13 +3304,15 @@ int Field_time::store(longlong nr)
if (nr > (longlong) 8385959L)
{
tmp=8385959L;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME, 1);
error= 1;
}
else if (nr < (longlong) -8385959L)
{
tmp= -8385959L;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME, 1);
error= 1;
}
else
@@ -3348,7 +3321,8 @@ int Field_time::store(longlong nr)
if (tmp % 100 > 59 || tmp/100 % 100 > 59)
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_TIME, 1);
error= 1;
}
}
@@ -3460,11 +3434,11 @@ int Field_year::store(const char *from, uint len,CHARSET_INFO *cs)
if (nr < 0 || nr >= 100 && nr <= 1900 || nr > 2155)
{
*ptr=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
- if (current_thd->count_cuted_fields && !test_if_int(from,len,end,cs))
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ if (table->in_use->count_cuted_fields && !test_if_int(from,len,end,cs))
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
if (nr != 0 || len != 4)
{
if (nr < YY_PART_YEAR)
@@ -3493,7 +3467,7 @@ int Field_year::store(longlong nr)
if (nr < 0 || nr >= 100 && nr <= 1900 || nr > 2155)
{
*ptr=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, 1);
return 1;
}
if (nr != 0 || field_length != 4) // 0000 -> 0; 00 -> 2000
@@ -3557,15 +3531,21 @@ int Field_date::store(const char *from, uint len,CHARSET_INFO *cs)
{
TIME l_time;
uint32 tmp;
- int error= 0;
- if (str_to_TIME(from,len,&l_time,1) <= TIMESTAMP_DATETIME_ERROR)
+ int error;
+
+ if (str_to_TIME(from, len, &l_time, 1, &error) <=
+ TIMESTAMP_DATETIME_ERROR)
{
tmp=0;
error= 1;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
}
else
tmp=(uint32) l_time.year*10000L + (uint32) (l_time.month*100+l_time.day);
+
+ if (error)
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED,
+ from, len, TIMESTAMP_DATE, 1);
+
#ifdef WORDS_BIGENDIAN
if (table->db_low_byte_first)
{
@@ -3587,7 +3567,9 @@ int Field_date::store(double nr)
if (nr < 0.0 || nr > 99991231.0)
{
tmp=0L;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE,
+ nr, TIMESTAMP_DATE);
error= 1;
}
else
@@ -3613,7 +3595,9 @@ int Field_date::store(longlong nr)
if (nr < 0 || nr > LL(99991231))
{
tmp=0L;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE,
+ nr, TIMESTAMP_DATE, 0);
error= 1;
}
else
@@ -3740,15 +3724,20 @@ int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs)
{
TIME l_time;
long tmp;
- int error= 0;
- if (str_to_TIME(from,len,&l_time,1) <= TIMESTAMP_DATETIME_ERROR)
+ int error;
+ if (str_to_TIME(from, len, &l_time, 1, &error) <=
+ TIMESTAMP_DATETIME_ERROR)
{
tmp=0L;
error= 1;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
}
else
tmp= l_time.day + l_time.month*32 + l_time.year*16*32;
+
+ if (error)
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED,
+ from, len, TIMESTAMP_DATE, 1);
+
int3store(ptr,tmp);
return error;
}
@@ -3758,7 +3747,8 @@ int Field_newdate::store(double nr)
if (nr < 0.0 || nr > 99991231235959.0)
{
(void) Field_newdate::store((longlong) -1);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_TRUNCATED, nr, TIMESTAMP_DATE);
return 1;
}
else
@@ -3775,7 +3765,8 @@ int Field_newdate::store(longlong nr)
if (nr < 0L || nr > 99991231L)
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_DATE, 1);
error= 1;
}
else
@@ -3793,7 +3784,8 @@ int Field_newdate::store(longlong nr)
if (month > 12 || day > 31)
{
tmp=0L; // Don't allow date to change
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE, nr, TIMESTAMP_DATE, 1);
error= 1;
}
else
@@ -3811,7 +3803,7 @@ void Field_newdate::store_time(TIME *ltime,timestamp_type type)
else
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
}
int3store(ptr,tmp);
}
@@ -3910,12 +3902,19 @@ void Field_newdate::sql_type(String &res) const
int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs)
{
- longlong tmp=str_to_datetime(from,len,1);
- if (tmp < 0)
- {
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
- tmp= 0;
- }
+ TIME time_tmp;
+ int error;
+ ulonglong tmp= 0;
+
+ if (str_to_TIME(from, len, &time_tmp, 1, &error) >
+ TIMESTAMP_DATETIME_ERROR)
+ tmp= TIME_to_ulonglong_datetime(&time_tmp);
+
+ if (error)
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE,
+ from, len, TIMESTAMP_DATETIME, 1);
+
#ifdef WORDS_BIGENDIAN
if (table->db_low_byte_first)
{
@@ -3924,7 +3923,7 @@ int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs)
else
#endif
longlongstore(ptr,tmp);
- return 0;
+ return error;
}
@@ -3933,8 +3932,10 @@ int Field_datetime::store(double nr)
int error= 0;
if (nr < 0.0 || nr > 99991231235959.0)
{
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_OUT_OF_RANGE,
+ nr, TIMESTAMP_DATETIME);
nr=0.0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE);
error= 1;
}
error |= Field_datetime::store((longlong) rint(nr));
@@ -3945,9 +3946,16 @@ int Field_datetime::store(double nr)
int Field_datetime::store(longlong nr)
{
TIME not_used;
- bool error;
+ int error;
+ longlong initial_nr= nr;
- nr= fix_datetime(nr, &not_used, field_name, &error);
+ nr= number_to_TIME(nr, &not_used, 1, &error);
+
+ if (error)
+ set_datetime_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_TRUNCATED, initial_nr,
+ TIMESTAMP_DATETIME, 1);
+
#ifdef WORDS_BIGENDIAN
if (table->db_low_byte_first)
{
@@ -3969,7 +3977,7 @@ void Field_datetime::store_time(TIME *ltime,timestamp_type type)
else
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
}
#ifdef WORDS_BIGENDIAN
if (table->db_low_byte_first)
@@ -4146,7 +4154,10 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
char buff[80];
String tmpstr(buff,sizeof(buff), &my_charset_bin);
uint copy_length;
-
+
+ /* See the comment for Field_long::store(long long) */
+ DBUG_ASSERT(table->in_use == current_thd);
+
/* Convert character set if nesessary */
if (String::needs_conversion(length, cs, field_charset, &not_used))
{
@@ -4168,7 +4179,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
field_charset->cset->fill(field_charset,ptr+copy_length,
field_length-copy_length,' ');
- if ((copy_length < length) && current_thd->count_cuted_fields)
+ if ((copy_length < length) && table->in_use->count_cuted_fields)
{ // Check if we loosed some info
const char *end=from+length;
from+= copy_length;
@@ -4176,7 +4187,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs)
MY_SEQ_SPACES);
if (from != end)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error=1;
}
}
@@ -4224,6 +4235,8 @@ String *Field_string::val_str(String *val_buffer __attribute__((unused)),
String *val_ptr)
{
uint length= field_charset->cset->lengthsp(field_charset, ptr, field_length);
+ /* See the comment for Field_long::store(long long) */
+ DBUG_ASSERT(table->in_use == current_thd);
val_ptr->set((const char*) ptr, length, field_charset);
return val_ptr;
}
@@ -4351,7 +4364,7 @@ int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs)
if (length > field_length)
{
length=field_length;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error= 1;
}
memcpy(ptr+HA_KEY_BLOB_LENGTH,from,length);
@@ -4669,7 +4682,7 @@ int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs)
min(length, copy_length),
copy_length);
if (copy_length < length)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
Field_blob::store_length(copy_length);
if (was_conversion || table->copy_blobs || copy_length <= MAX_FIELD_WIDTH)
@@ -5228,11 +5241,11 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs)
if (err || end != from+length || tmp > typelib->count)
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
}
}
else
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
}
store_type((ulonglong) tmp);
return err;
@@ -5250,7 +5263,7 @@ int Field_enum::store(longlong nr)
int error= 0;
if ((uint) nr > typelib->count || nr == 0)
{
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
nr=0;
error=1;
}
@@ -5406,11 +5419,11 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs)
tmp > (ulonglong) (((longlong) 1 << typelib->count) - (longlong) 1))
{
tmp=0;
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
}
}
else if (got_warning)
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
store_type(tmp);
return err;
}
@@ -5423,7 +5436,7 @@ int Field_set::store(longlong nr)
(longlong) 1))
{
nr&= (longlong) (((longlong) 1 << typelib->count) - (longlong) 1);
- set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED);
+ set_warning(MYSQL_ERROR::WARN_LEVEL_WARN, ER_WARN_DATA_TRUNCATED, 1);
error=1;
}
store_type((ulonglong) nr);
@@ -5802,14 +5815,122 @@ create_field::create_field(Field *old_field,Field *orig_field)
/* Warning handling */
-void Field::set_warning(const uint level, const uint code)
+
+/*
+ Produce warning or note about data saved into field
+
+ SYNOPSYS
+ set_warning()
+ level - level of message (Note/Warning/Error)
+ code - error code of message to be produced
+ cuted_increment - whenever we should increase cut fields count or not
+
+ NOTE
+ This function won't produce warning and increase cut fields counter
+ if count_cuted_fields == FIELD_CHECK_IGNORE for current thread.
+
+ RETURN VALUE
+ true - if count_cuted_fields == FIELD_CHECK_IGNORE
+ false - otherwise
+*/
+bool
+Field::set_warning(const uint level, const uint code, int cuted_increment)
{
- THD *thd= current_thd;
+ THD *thd= table->in_use;
if (thd->count_cuted_fields)
{
- thd->cuted_fields++;
- push_warning_printf(thd, (MYSQL_ERROR::enum_warning_level) level,
- code, ER(code), field_name, thd->row_count);
+ thd->cuted_fields+= cuted_increment;
+ push_warning_printf(thd, (MYSQL_ERROR::enum_warning_level) level,
+ code, ER(code), field_name, thd->row_count);
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ Produce warning or note about datetime string data saved into field
+
+ SYNOPSYS
+ set_warning()
+ level - level of message (Note/Warning/Error)
+ code - error code of message to be produced
+ str - string value which we tried to save
+ str_len - length of string which we tried to save
+ ts_type - type of datetime value (datetime/date/time)
+ cuted_increment - whenever we should increase cut fields count or not
+
+ NOTE
+ This function will always produce some warning but won't increase cut
+ fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current
+ thread.
+*/
+void
+Field::set_datetime_warning(const uint level, const uint code,
+ const char *str, uint str_length,
+ timestamp_type ts_type, int cuted_increment)
+{
+ if (set_warning(level, code, cuted_increment))
+ make_truncated_value_warning(table->in_use, str, str_length, ts_type);
+}
+
+
+/*
+ Produce warning or note about integer datetime value saved into field
+
+ SYNOPSYS
+ set_warning()
+ level - level of message (Note/Warning/Error)
+ code - error code of message to be produced
+ nr - numeric value which we tried to save
+ ts_type - type of datetime value (datetime/date/time)
+ cuted_increment - whenever we should increase cut fields count or not
+
+ NOTE
+ This function will always produce some warning but won't increase cut
+ fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current
+ thread.
+*/
+void
+Field::set_datetime_warning(const uint level, const uint code,
+ longlong nr, timestamp_type ts_type,
+ int cuted_increment)
+{
+ if (set_warning(level, code, cuted_increment))
+ {
+ char str_nr[22];
+ char *str_end= longlong10_to_str(nr, str_nr, -10);
+ make_truncated_value_warning(table->in_use, str_nr, str_end - str_nr,
+ ts_type);
+ }
+}
+
+
+/*
+ Produce warning or note about double datetime data saved into field
+
+ SYNOPSYS
+ set_warning()
+ level - level of message (Note/Warning/Error)
+ code - error code of message to be produced
+ nr - double value which we tried to save
+ ts_type - type of datetime value (datetime/date/time)
+
+ NOTE
+ This function will always produce some warning but won't increase cut
+ fields counter if count_cuted_fields == FIELD_CHECK_IGNORE for current
+ thread.
+*/
+void
+Field::set_datetime_warning(const uint level, const uint code,
+ double nr, timestamp_type ts_type)
+{
+ if (set_warning(level, code, 1))
+ {
+ /* DBL_DIG is enough to print '-[digits].E+###' */
+ char str_nr[DBL_DIG + 8];
+ uint str_len= my_sprintf(str_nr, (str_nr, "%g", nr));
+ make_truncated_value_warning(table->in_use, str_nr, str_len, ts_type);
}
}
diff --git a/sql/field.h b/sql/field.h
index 2b6ef28c184..f2a166d29c3 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -45,6 +45,10 @@ public:
char *ptr; // Position to field in record
uchar *null_ptr; // Byte where null_bit is
+ /*
+ Note that you can use table->in_use as replacement for current_thd member
+ only inside of val_*() and store() members (e.g. you can't use it in cons)
+ */
struct st_table *table; // Pointer for table
struct st_table *orig_table; // Pointer to original table
const char *table_name,*field_name;
@@ -264,7 +268,16 @@ public:
virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; }
virtual bool has_charset(void) const { return FALSE; }
virtual void set_charset(CHARSET_INFO *charset) { }
- void set_warning(const unsigned int level, const unsigned int code);
+ bool set_warning(const unsigned int level, const unsigned int code,
+ int cuted_increment);
+ void set_datetime_warning(const uint level, const uint code,
+ const char *str, uint str_len,
+ timestamp_type ts_type, int cuted_increment);
+ void set_datetime_warning(const uint level, const uint code,
+ longlong nr, timestamp_type ts_type,
+ int cuted_increment);
+ void set_datetime_warning(const uint level, const uint code,
+ double nr, timestamp_type ts_type);
virtual field_cast_enum field_cast_type()= 0;
bool field_cast_compatible(field_cast_enum type);
/* maximum possible display length */
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 0974c552364..e98068ef974 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -121,7 +121,8 @@ set_field_to_null(Field *field)
field->reset();
if (current_thd->count_cuted_fields == CHECK_FIELD_WARN)
{
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,ER_WARN_DATA_TRUNCATED);
+ field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_DATA_TRUNCATED, 1);
return 0;
}
if (!current_thd->no_errors)
@@ -178,7 +179,8 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions)
}
if (current_thd->count_cuted_fields == CHECK_FIELD_WARN)
{
- field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,ER_WARN_NULL_TO_NOTNULL);
+ field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_NULL_TO_NOTNULL, 1);
return 0;
}
if (!current_thd->no_errors)
@@ -229,7 +231,7 @@ static void do_copy_not_null(Copy_field *copy)
if (*copy->from_null_ptr & copy->from_bit)
{
copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_DATA_TRUNCATED);
+ ER_WARN_DATA_TRUNCATED, 1);
copy->to_field->reset();
}
else
@@ -329,7 +331,7 @@ static void do_cut_string(Copy_field *copy)
if (!my_isspace(system_charset_info, *ptr)) // QQ: ucs incompatible
{
copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_DATA_TRUNCATED);
+ ER_WARN_DATA_TRUNCATED, 1);
break;
}
}
@@ -350,7 +352,7 @@ static void do_varstring(Copy_field *copy)
length=copy->to_length-2;
if (current_thd->count_cuted_fields)
copy->to_field->set_warning(MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_DATA_TRUNCATED);
+ ER_WARN_DATA_TRUNCATED, 1);
}
int2store(copy->to_ptr,length);
memcpy(copy->to_ptr+2, copy->from_ptr,length);
diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc
index 4dde893116f..a13b6147468 100644
--- a/sql/ha_berkeley.cc
+++ b/sql/ha_berkeley.cc
@@ -843,8 +843,8 @@ int ha_berkeley::write_row(byte * record)
else
{
DB_TXN *sub_trans = transaction;
- /* Don't use sub transactions in temporary tables (in_use == 0) */
- ulong thd_options = table->in_use ? table->in_use->options : 0;
+ /* Don't use sub transactions in temporary tables */
+ ulong thd_options = table->tmp_table == NO_TMP_TABLE ? table->in_use->options : 0;
for (uint retry=0 ; retry < berkeley_trans_retry ; retry++)
{
key_map changed_keys(0);
@@ -1067,7 +1067,7 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row)
DBT prim_key, key, old_prim_key;
int error;
DB_TXN *sub_trans;
- ulong thd_options = table->in_use ? table->in_use->options : 0;
+ ulong thd_options = table->tmp_table == NO_TMP_TABLE ? table->in_use->options : 0;
bool primary_key_changed;
DBUG_ENTER("update_row");
LINT_INIT(error);
@@ -1260,7 +1260,7 @@ int ha_berkeley::delete_row(const byte * record)
int error;
DBT row, prim_key;
key_map keys=table->keys_in_use;
- ulong thd_options = table->in_use ? table->in_use->options : 0;
+ ulong thd_options = table->tmp_table == NO_TMP_TABLE ? table->in_use->options : 0;
DBUG_ENTER("delete_row");
statistic_increment(ha_delete_count,&LOCK_status);
diff --git a/sql/item.cc b/sql/item.cc
index 853f5bc8ecc..d1a911aacaf 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -228,7 +228,7 @@ bool Item::get_date(TIME *ltime,uint fuzzydate)
char buff[40];
String tmp(buff,sizeof(buff), &my_charset_bin),*res;
if (!(res=val_str(&tmp)) ||
- str_to_TIME(res->ptr(),res->length(),ltime,fuzzydate) <=
+ str_to_TIME_with_warn(res->ptr(),res->length(),ltime,fuzzydate) <=
TIMESTAMP_DATETIME_ERROR)
{
bzero((char*) ltime,sizeof(*ltime));
@@ -247,7 +247,7 @@ bool Item::get_time(TIME *ltime)
char buff[40];
String tmp(buff,sizeof(buff),&my_charset_bin),*res;
if (!(res=val_str(&tmp)) ||
- str_to_time(res->ptr(),res->length(),ltime))
+ str_to_time_with_warn(res->ptr(), res->length(), ltime))
{
bzero((char*) ltime,sizeof(*ltime));
return 1;
diff --git a/sql/item_create.cc b/sql/item_create.cc
index 53d4f14d1ee..4977ba2c5d3 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -89,6 +89,11 @@ Item *create_func_conv(Item* a, Item *b, Item *c)
return new Item_func_conv(a,b,c);
}
+Item *create_func_convert_tz(Item* a, Item *b, Item *c)
+{
+ return new Item_func_convert_tz(a,b,c);
+}
+
Item *create_func_cos(Item* a)
{
return new Item_func_cos(a);
diff --git a/sql/item_create.h b/sql/item_create.h
index 7577627ef04..19f0c9133f2 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -31,6 +31,7 @@ Item *create_func_char_length(Item* a);
Item *create_func_cast(Item *a, Cast_target cast_type, int len, CHARSET_INFO *cs);
Item *create_func_connection_id(void);
Item *create_func_conv(Item* a, Item *b, Item *c);
+Item *create_func_convert_tz(Item* a, Item *b, Item *c);
Item *create_func_cos(Item* a);
Item *create_func_cot(Item* a);
Item *create_func_crc32(Item* a);
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index 45d66addc9f..a5ea72374c1 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -894,6 +894,9 @@ longlong Item_func_year::val_int()
longlong Item_func_unix_timestamp::val_int()
{
+ TIME ltime;
+ bool not_used;
+
DBUG_ASSERT(fixed == 1);
if (arg_count == 0)
return (longlong) current_thd->query_start();
@@ -903,12 +906,19 @@ longlong Item_func_unix_timestamp::val_int()
if (field->type() == FIELD_TYPE_TIMESTAMP)
return ((Field_timestamp*) field)->get_timestamp();
}
- String *str=args[0]->val_str(&value);
- if ((null_value=args[0]->null_value))
+
+ if (get_arg0_date(&ltime, 0))
{
- return 0; /* purecov: inspected */
+ /*
+ We have to set null_value again because get_arg0_date will also set it
+ to true if we have wrong datetime parameter (and we should return 0 in
+ this case).
+ */
+ null_value= args[0]->null_value;
+ return 0;
}
- return (longlong) str_to_timestamp(str->ptr(),str->length());
+
+ return (longlong) TIME_to_timestamp(current_thd, &ltime, &not_used);
}
@@ -1126,23 +1136,14 @@ bool Item_func_from_days::get_date(TIME *ltime, uint fuzzy_date)
void Item_func_curdate::fix_length_and_dec()
{
- struct tm start;
-
collation.set(&my_charset_bin);
decimals=0;
max_length=MAX_DATE_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
- store_now_in_tm(current_thd->query_start(),&start);
+ store_now_in_TIME(&ltime);
- /* For getdate */
- ltime.year= start.tm_year+1900;
- ltime.month= start.tm_mon+1;
- ltime.day= start.tm_mday;
- ltime.hour= 0;
- ltime.minute= 0;
- ltime.second= 0;
- ltime.second_part=0;
- ltime.neg=0;
+ /* We don't need to set second_part and neg because they already 0 */
+ ltime.hour= ltime.minute= ltime.second= 0;
ltime.time_type=TIMESTAMP_DATE;
value= (longlong) TIME_to_ulonglong_date(&ltime);
}
@@ -1159,31 +1160,39 @@ String *Item_func_curdate::val_str(String *str)
return str;
}
-bool Item_func_curdate::get_date(TIME *res,
- uint fuzzy_date __attribute__((unused)))
+/*
+ Converts current time in my_time_t to TIME represenatation for local
+ time zone. Defines time zone (local) used for whole CURDATE function.
+*/
+void Item_func_curdate_local::store_now_in_TIME(TIME *now_time)
{
- *res=ltime;
- return 0;
+ THD *thd= current_thd;
+ thd->variables.time_zone->gmt_sec_to_TIME(now_time,
+ (my_time_t)thd->query_start());
+ thd->time_zone_used= 1;
}
/*
- Converts time in time_t to struct tm represenatation for local timezone.
- Defines timezone (local) used for whole CURDATE function
+ Converts current time in my_time_t to TIME represenatation for UTC
+ time zone. Defines time zone (UTC) used for whole UTC_DATE function.
*/
-void Item_func_curdate_local::store_now_in_tm(time_t now, struct tm *now_tm)
+void Item_func_curdate_utc::store_now_in_TIME(TIME *now_time)
{
- localtime_r(&now,now_tm);
+ my_tz_UTC->gmt_sec_to_TIME(now_time,
+ (my_time_t)(current_thd->query_start()));
+ /*
+ We are not flagging this query as using time zone, since it uses fixed
+ UTC-SYSTEM time-zone.
+ */
}
-/*
- Converts time in time_t to struct tm represenatation for UTC
- Defines timezone (UTC) used for whole UTC_DATE function
-*/
-void Item_func_curdate_utc::store_now_in_tm(time_t now, struct tm *now_tm)
+bool Item_func_curdate::get_date(TIME *res,
+ uint fuzzy_date __attribute__((unused)))
{
- gmtime_r(&now,now_tm);
+ *res=ltime;
+ return 0;
}
@@ -1197,17 +1206,12 @@ String *Item_func_curtime::val_str(String *str)
void Item_func_curtime::fix_length_and_dec()
{
- struct tm start;
- String tmp((char*) buff,sizeof(buff), &my_charset_bin);
TIME ltime;
+ String tmp((char*) buff,sizeof(buff), &my_charset_bin);
- decimals=0;
- store_now_in_tm(current_thd->query_start(),&start);
- ltime.hour= start.tm_hour;
- ltime.minute= start.tm_min;
- ltime.second= start.tm_sec;
- ltime.second_part= 0;
- ltime.neg= 0;
+ decimals=0;
+ collation.set(&my_charset_bin);
+ store_now_in_TIME(&ltime);
value= TIME_to_ulonglong_time(&ltime);
make_time((DATE_TIME_FORMAT *) 0, &ltime, &tmp);
max_length= buff_length= tmp.length();
@@ -1215,22 +1219,30 @@ void Item_func_curtime::fix_length_and_dec()
/*
- Converts time in time_t to struct tm represenatation for local timezone.
- Defines timezone (local) used for whole CURTIME function
+ Converts current time in my_time_t to TIME represenatation for local
+ time zone. Defines time zone (local) used for whole CURTIME function.
*/
-void Item_func_curtime_local::store_now_in_tm(time_t now, struct tm *now_tm)
+void Item_func_curtime_local::store_now_in_TIME(TIME *now_time)
{
- localtime_r(&now,now_tm);
+ THD *thd= current_thd;
+ thd->variables.time_zone->gmt_sec_to_TIME(now_time,
+ (my_time_t)thd->query_start());
+ thd->time_zone_used= 1;
}
/*
- Converts time in time_t to struct tm represenatation for UTC.
- Defines timezone (UTC) used for whole UTC_TIME function
+ Converts current time in my_time_t to TIME represenatation for UTC
+ time zone. Defines time zone (UTC) used for whole UTC_TIME function.
*/
-void Item_func_curtime_utc::store_now_in_tm(time_t now, struct tm *now_tm)
+void Item_func_curtime_utc::store_now_in_TIME(TIME *now_time)
{
- gmtime_r(&now,now_tm);
+ my_tz_UTC->gmt_sec_to_TIME(now_time,
+ (my_time_t)(current_thd->query_start()));
+ /*
+ We are not flagging this query as using time zone, since it uses fixed
+ UTC-SYSTEM time-zone.
+ */
}
@@ -1244,18 +1256,12 @@ String *Item_func_now::val_str(String *str)
void Item_func_now::fix_length_and_dec()
{
- struct tm start;
String tmp((char*) buff,sizeof(buff),&my_charset_bin);
decimals=0;
collation.set(&my_charset_bin);
- store_now_in_tm(current_thd->query_start(),&start);
-
- /* For getdate */
- localtime_to_TIME(&ltime, &start);
- ltime.time_type= TIMESTAMP_DATETIME;
-
+ store_now_in_TIME(&ltime);
value= (longlong) TIME_to_ulonglong_datetime(&ltime);
make_datetime((DATE_TIME_FORMAT *) 0, &ltime, &tmp);
@@ -1263,39 +1269,47 @@ void Item_func_now::fix_length_and_dec()
}
-bool Item_func_now::get_date(TIME *res,
- uint fuzzy_date __attribute__((unused)))
+/*
+ Converts current time in my_time_t to TIME represenatation for local
+ time zone. Defines time zone (local) used for whole NOW function.
+*/
+void Item_func_now_local::store_now_in_TIME(TIME *now_time)
{
- *res=ltime;
- return 0;
+ THD *thd= current_thd;
+ thd->variables.time_zone->gmt_sec_to_TIME(now_time,
+ (my_time_t)thd->query_start());
+ thd->time_zone_used= 1;
}
-int Item_func_now::save_in_field(Field *to, bool no_conversions)
+/*
+ Converts current time in my_time_t to TIME represenatation for UTC
+ time zone. Defines time zone (UTC) used for whole UTC_TIMESTAMP function.
+*/
+void Item_func_now_utc::store_now_in_TIME(TIME *now_time)
{
- to->set_notnull();
- to->store_time(&ltime,TIMESTAMP_DATETIME);
- return 0;
+ my_tz_UTC->gmt_sec_to_TIME(now_time,
+ (my_time_t)(current_thd->query_start()));
+ /*
+ We are not flagging this query as using time zone, since it uses fixed
+ UTC-SYSTEM time-zone.
+ */
}
-/*
- Converts time in time_t to struct tm represenatation for local timezone.
- Defines timezone (local) used for whole CURRENT_TIMESTAMP function
-*/
-void Item_func_now_local::store_now_in_tm(time_t now, struct tm *now_tm)
+bool Item_func_now::get_date(TIME *res,
+ uint fuzzy_date __attribute__((unused)))
{
- localtime_r(&now,now_tm);
+ *res=ltime;
+ return 0;
}
-/*
- Converts time in time_t to struct tm represenatation for UTC.
- Defines timezone (UTC) used for whole UTC_TIMESTAMP function
-*/
-void Item_func_now_utc::store_now_in_tm(time_t now, struct tm *now_tm)
+int Item_func_now::save_in_field(Field *to, bool no_conversions)
{
- gmtime_r(&now,now_tm);
+ to->set_notnull();
+ to->store_time(&ltime,TIMESTAMP_DATETIME);
+ return 0;
}
@@ -1455,7 +1469,7 @@ String *Item_func_date_format::val_str(String *str)
{
String *res;
if (!(res=args[0]->val_str(str)) ||
- (str_to_time(res->ptr(),res->length(),&l_time)))
+ (str_to_time_with_warn(res->ptr(), res->length(), &l_time)))
goto null_date;
l_time.year=l_time.month=l_time.day=0;
@@ -1489,24 +1503,31 @@ null_date:
}
+void Item_func_from_unixtime::fix_length_and_dec()
+{
+ thd= current_thd;
+ collation.set(&my_charset_bin);
+ decimals=0;
+ max_length=MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
+ thd->time_zone_used= 1;
+}
+
+
String *Item_func_from_unixtime::val_str(String *str)
{
- struct tm tm_tmp;
- time_t tmp;
- TIME ltime;
+ TIME time_tmp;
+ my_time_t tmp;
DBUG_ASSERT(fixed == 1);
tmp= (time_t) args[0]->val_int();
if ((null_value=args[0]->null_value))
goto null_date;
-
- localtime_r(&tmp,&tm_tmp);
-
- localtime_to_TIME(&ltime, &tm_tmp);
-
+
+ thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, tmp);
+
if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN))
goto null_date;
- make_datetime((DATE_TIME_FORMAT *) 0, &ltime, str);
+ make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str);
return str;
null_date:
@@ -1517,28 +1538,109 @@ null_date:
longlong Item_func_from_unixtime::val_int()
{
- TIME ltime;
- struct tm tm_tmp;
- time_t tmp;
+ TIME time_tmp;
+ my_time_t tmp;
+
DBUG_ASSERT(fixed == 1);
tmp= (time_t) (ulong) args[0]->val_int();
if ((null_value=args[0]->null_value))
return 0;
- localtime_r(&tmp,&tm_tmp);
- localtime_to_TIME(&ltime, &tm_tmp);
- return (longlong) TIME_to_ulonglong_datetime(&ltime);
+
+ current_thd->variables.time_zone->gmt_sec_to_TIME(&time_tmp, tmp);
+
+ return (longlong) TIME_to_ulonglong_datetime(&time_tmp);
}
bool Item_func_from_unixtime::get_date(TIME *ltime,
uint fuzzy_date __attribute__((unused)))
{
- time_t tmp=(time_t) (ulong) args[0]->val_int();
+ my_time_t tmp=(my_time_t) args[0]->val_int();
if ((null_value=args[0]->null_value))
return 1;
- struct tm tm_tmp;
- localtime_r(&tmp,&tm_tmp);
- localtime_to_TIME(ltime, &tm_tmp);
+
+ current_thd->variables.time_zone->gmt_sec_to_TIME(ltime, tmp);
+
+ return 0;
+}
+
+
+void Item_func_convert_tz::fix_length_and_dec()
+{
+ String str;
+
+ thd= current_thd;
+ collation.set(&my_charset_bin);
+ decimals= 0;
+ max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
+
+ if (args[1]->const_item())
+ from_tz= my_tz_find(thd, args[1]->val_str(&str));
+
+ if (args[2]->const_item())
+ to_tz= my_tz_find(thd, args[2]->val_str(&str));
+}
+
+
+String *Item_func_convert_tz::val_str(String *str)
+{
+ TIME time_tmp;
+
+ if (get_date(&time_tmp, 0))
+ return 0;
+
+ if (str->alloc(20*MY_CHARSET_BIN_MB_MAXLEN))
+ {
+ null_value= 1;
+ return 0;
+ }
+
+ make_datetime((DATE_TIME_FORMAT *) 0, &time_tmp, str);
+ return str;
+}
+
+
+longlong Item_func_convert_tz::val_int()
+{
+ TIME time_tmp;
+
+ if (get_date(&time_tmp, 0))
+ return 0;
+
+ return (longlong)TIME_to_ulonglong_datetime(&time_tmp);
+}
+
+
+bool Item_func_convert_tz::get_date(TIME *ltime,
+ uint fuzzy_date __attribute__((unused)))
+{
+ my_time_t my_time_tmp;
+ bool not_used;
+ String str;
+
+ if (!args[1]->const_item())
+ from_tz= my_tz_find(thd, args[1]->val_str(&str));
+
+ if (!args[2]->const_item())
+ to_tz= my_tz_find(thd, args[2]->val_str(&str));
+
+ if (from_tz==0 || to_tz==0 || get_arg0_date(ltime, 0))
+ {
+ null_value= 1;
+ return 1;
+ }
+
+ /* Check if we in range where we treat datetime values as non-UTC */
+ if (ltime->year < TIMESTAMP_MAX_YEAR && ltime->year > TIMESTAMP_MIN_YEAR ||
+ ltime->year==TIMESTAMP_MAX_YEAR && ltime->month==1 && ltime->day==1 ||
+ ltime->year==TIMESTAMP_MIN_YEAR && ltime->month==12 && ltime->day==31)
+ {
+ my_time_tmp= from_tz->TIME_to_gmt_sec(ltime, &not_used);
+ if (my_time_tmp >= TIMESTAMP_MIN_VALUE && my_time_tmp <= TIMESTAMP_MAX_VALUE)
+ to_tz->gmt_sec_to_TIME(ltime, my_time_tmp);
+ }
+
+ null_value= 0;
return 0;
}
@@ -1795,7 +1897,7 @@ longlong Item_extract::val_int()
else
{
String *res= args[0]->val_str(&value);
- if (!res || str_to_time(res->ptr(),res->length(),&ltime))
+ if (!res || str_to_time_with_warn(res->ptr(), res->length(), &ltime))
{
null_value=1;
return 0;
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index f00eb93e0e5..a7ff2924786 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -348,6 +348,7 @@ public:
Item_date_func() :Item_str_func() {}
Item_date_func(Item *a) :Item_str_func(a) {}
Item_date_func(Item *a,Item *b) :Item_str_func(a,b) {}
+ Item_date_func(Item *a,Item *b, Item *c) :Item_str_func(a,b,c) {}
enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; }
Field *tmp_table_field(TABLE *t_arg)
{
@@ -356,7 +357,7 @@ public:
};
-/* Abstract CURTIME function. Children should define what timezone is used */
+/* Abstract CURTIME function. Children should define what time zone is used */
class Item_func_curtime :public Item_func
{
@@ -378,10 +379,10 @@ public:
}
/*
Abstract method that defines which time zone is used for conversion.
- Converts time from time_t representation to broken down representation
- in struct tm using gmtime_r or localtime_r functions.
+ Converts time current time in my_time_t representation to broken-down
+ TIME representation using UTC-SYSTEM or per-thread time zone.
*/
- virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0;
+ virtual void store_now_in_TIME(TIME *now_time)=0;
};
@@ -391,7 +392,7 @@ public:
Item_func_curtime_local() :Item_func_curtime() {}
Item_func_curtime_local(Item *a) :Item_func_curtime(a) {}
const char *func_name() const { return "curtime"; }
- void store_now_in_tm(time_t now, struct tm *now_tm);
+ virtual void store_now_in_TIME(TIME *now_time);
};
@@ -401,7 +402,7 @@ public:
Item_func_curtime_utc() :Item_func_curtime() {}
Item_func_curtime_utc(Item *a) :Item_func_curtime(a) {}
const char *func_name() const { return "utc_time"; }
- void store_now_in_tm(time_t now, struct tm *now_tm);
+ virtual void store_now_in_TIME(TIME *now_time);
};
@@ -413,12 +414,11 @@ class Item_func_curdate :public Item_date
TIME ltime;
public:
Item_func_curdate() :Item_date() {}
- void set_result_from_tm(struct tm *now);
longlong val_int() { DBUG_ASSERT(fixed == 1); return (value) ; }
String *val_str(String *str);
void fix_length_and_dec();
bool get_date(TIME *res, uint fuzzy_date);
- virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0;
+ virtual void store_now_in_TIME(TIME *now_time)=0;
};
@@ -427,7 +427,7 @@ class Item_func_curdate_local :public Item_func_curdate
public:
Item_func_curdate_local() :Item_func_curdate() {}
const char *func_name() const { return "curdate"; }
- void store_now_in_tm(time_t now, struct tm *now_tm);
+ void store_now_in_TIME(TIME *now_time);
};
@@ -436,7 +436,7 @@ class Item_func_curdate_utc :public Item_func_curdate
public:
Item_func_curdate_utc() :Item_func_curdate() {}
const char *func_name() const { return "utc_date"; }
- void store_now_in_tm(time_t now, struct tm *now_tm);
+ void store_now_in_TIME(TIME *now_time);
};
@@ -458,7 +458,7 @@ public:
String *val_str(String *str);
void fix_length_and_dec();
bool get_date(TIME *res, uint fuzzy_date);
- virtual void store_now_in_tm(time_t now, struct tm *now_tm)=0;
+ virtual void store_now_in_TIME(TIME *now_time)=0;
};
@@ -468,7 +468,7 @@ public:
Item_func_now_local() :Item_func_now() {}
Item_func_now_local(Item *a) :Item_func_now(a) {}
const char *func_name() const { return "now"; }
- void store_now_in_tm(time_t now, struct tm *now_tm);
+ virtual void store_now_in_TIME(TIME *now_time);
virtual enum Functype functype() const { return NOW_FUNC; }
};
@@ -479,7 +479,7 @@ public:
Item_func_now_utc() :Item_func_now() {}
Item_func_now_utc(Item *a) :Item_func_now(a) {}
const char *func_name() const { return "utc_timestamp"; }
- void store_now_in_tm(time_t now, struct tm *now_tm);
+ virtual void store_now_in_TIME(TIME *now_time);
};
@@ -509,6 +509,7 @@ public:
class Item_func_from_unixtime :public Item_date_func
{
+ THD *thd;
public:
Item_func_from_unixtime(Item *a) :Item_date_func(a) {}
double val()
@@ -519,12 +520,29 @@ class Item_func_from_unixtime :public Item_date_func
longlong val_int();
String *val_str(String *str);
const char *func_name() const { return "from_unixtime"; }
- void fix_length_and_dec()
- {
- collation.set(&my_charset_bin);
- decimals=0;
- max_length=MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
- }
+ void fix_length_and_dec();
+ bool get_date(TIME *res, uint fuzzy_date);
+};
+
+
+/*
+ We need Time_zone class declaration for storing pointers in
+ Item_func_convert_tz.
+*/
+class Time_zone;
+
+class Item_func_convert_tz :public Item_date_func
+{
+ THD *thd;
+ Time_zone *from_tz, *to_tz;
+ public:
+ Item_func_convert_tz(Item *a, Item *b, Item *c):
+ Item_date_func(a, b, c) {}
+ longlong val_int();
+ double val() { return (double) val_int(); }
+ String *val_str(String *str);
+ const char *func_name() const { return "convert_tz"; }
+ void fix_length_and_dec();
bool get_date(TIME *res, uint fuzzy_date);
};
diff --git a/sql/lex.h b/sql/lex.h
index fde5076a25e..b1626c75c28 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -499,6 +499,7 @@ static SYMBOL sql_functions[] = {
{ "CONNECTION_ID", F_SYM(FUNC_ARG0),0,CREATE_FUNC(create_func_connection_id)},
{ "CONTAINS", F_SYM(FUNC_ARG2),0,CREATE_FUNC_GEOM(create_func_contains)},
{ "CONV", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_conv)},
+ { "CONVERT_TZ", F_SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_convert_tz)},
{ "COUNT", SYM(COUNT_SYM)},
{ "COS", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cos)},
{ "COT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_cot)},
diff --git a/sql/log.cc b/sql/log.cc
index 47a6a4a9b4c..09e83392dac 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -1269,10 +1269,24 @@ COLLATION_CONNECTION=%lu,COLLATION_DATABASE=%lu,COLLATION_SERVER=%lu",
if (e.write(file))
goto err;
}
+ /*
+ We use the same ONE_SHOT trick for making replication of time zones
+ working in 4.1. Again in 5.0 we have better means for doing this.
+ */
+ if (thd->time_zone_used &&
+ thd->variables.time_zone != global_system_variables.time_zone)
+ {
+ char buf[MAX_TIME_ZONE_NAME_LENGTH + 26];
+ char *buf_end= strxmov(buf, "SET ONE_SHOT TIME_ZONE='",
+ thd->variables.time_zone->get_name()->ptr(),
+ "'", NullS);
+ Query_log_event e(thd, buf, buf_end - buf, 0);
+ e.set_log_pos(this);
+ if (e.write(file))
+ goto err;
+ }
#endif
- /* Add logging of timezones here */
-
if (thd->last_insert_id_used)
{
Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT,
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index b2d21c3fb55..db8d534064d 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -39,6 +39,14 @@ extern const key_map key_map_empty;
extern const key_map key_map_full;
extern const char *primary_key_name;
+/*
+ Portable time_t replacement.
+ Should be signed and hold seconds for 1902-2038 range.
+*/
+typedef long my_time_t;
+#define MY_TIME_T_MAX LONG_MAX
+#define MY_TIME_T_MIN LONG_MIN
+
#include "mysql_com.h"
#include <violite.h>
#include "unireg.h"
@@ -349,6 +357,7 @@ inline THD *_current_thd(void)
#include "sql_udf.h"
class user_var_entry;
#include "item.h"
+#include "tztime.h"
typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
/* sql_parse.cc */
void free_items(Item *item);
@@ -378,6 +387,7 @@ struct Query_cache_query_flags
uint character_set_results_num;
uint collation_connection_num;
ha_rows limit;
+ Time_zone *time_zone;
};
#define QUERY_CACHE_FLAGS_SIZE sizeof(Query_cache_query_flags)
#include "sql_cache.h"
@@ -822,7 +832,7 @@ extern Le_creator le_creator;
extern uchar *days_in_month;
extern char language[LIBLEN],reg_ext[FN_EXTLEN];
extern char glob_hostname[FN_REFLEN], mysql_home[FN_REFLEN];
-extern char pidfile_name[FN_REFLEN], time_zone[30], *opt_init_file;
+extern char pidfile_name[FN_REFLEN], system_time_zone[30], *opt_init_file;
extern char log_error_file[FN_REFLEN];
extern double log_10[32];
extern ulonglong log_10_int[20];
@@ -878,6 +888,7 @@ extern my_bool opt_enable_named_pipe, opt_sync_frm;
extern my_bool opt_secure_auth;
extern char *shared_memory_base_name, *mysqld_unix_port;
extern bool opt_enable_shared_memory;
+extern char *default_tz_name;
extern MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log;
extern FILE *bootstrap_file;
@@ -988,12 +999,16 @@ uint calc_days_in_year(uint year);
void get_date_from_daynr(long daynr,uint *year, uint *month,
uint *day);
void init_time(void);
-long my_gmt_sec(TIME *, long *current_timezone);
-time_t str_to_timestamp(const char *str,uint length);
-bool str_to_time(const char *str,uint length,TIME *l_time);
-longlong str_to_datetime(const char *str,uint length, uint fuzzy_date);
+my_time_t my_system_gmt_sec(const TIME *, long *current_timezone, bool *not_exist);
+my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *not_exist);
+bool str_to_time(const char *str,uint length,TIME *l_time, int *was_cut);
+bool str_to_time_with_warn(const char *str,uint length,TIME *l_time);
timestamp_type str_to_TIME(const char *str, uint length, TIME *l_time,
- uint flags);
+ uint flags, int *was_cut);
+timestamp_type str_to_TIME_with_warn(const char *str, uint length,
+ TIME *l_time, uint flags);
+longlong number_to_TIME(longlong nr, TIME *time_res, bool fuzzy_date,
+ int *was_cut);
void localtime_to_TIME(TIME *to, struct tm *from);
void calc_time_from_sec(TIME *to, long seconds, long microseconds);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 841898ac505..782f4021ea9 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -324,7 +324,8 @@ ulonglong log_10_int[20]=
time_t start_time;
-char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], time_zone[30];
+char mysql_home[FN_REFLEN], pidfile_name[FN_REFLEN], system_time_zone[30];
+char *default_tz_name;
char log_error_file[FN_REFLEN], glob_hostname[FN_REFLEN];
char* log_error_file_ptr= log_error_file;
char mysql_real_data_home[FN_REFLEN],
@@ -911,6 +912,7 @@ void clean_up(bool print_message)
if (use_slave_mask)
bitmap_free(&slave_error_mask);
#endif
+ my_tz_free();
#ifndef NO_EMBEDDED_ACCESS_CHECKS
acl_free(1);
grant_free();
@@ -2270,9 +2272,17 @@ static int init_common_variables(const char *conf_file_name, int argc,
{
struct tm tm_tmp;
localtime_r(&start_time,&tm_tmp);
- strmov(time_zone,tzname[tm_tmp.tm_isdst != 0 ? 1 : 0]);
+ strmov(system_time_zone, tzname[tm_tmp.tm_isdst != 0 ? 1 : 0]);
}
#endif
+ /*
+ We set SYSTEM time zone as reasonable default and
+ also for failure of my_tz_init() and bootstrap mode.
+ If user explicitly set time zone with --default-time-zone
+ option we will change this value in my_tz_init().
+ */
+ global_system_variables.time_zone= my_tz_SYSTEM;
+
/*
Init mutexes for the global MYSQL_LOG objects.
@@ -2810,7 +2820,8 @@ we force server id to 2, but this MySQL server will not act as a slave.");
*/
error_handler_hook = my_message_sql;
start_signal_handler(); // Creates pidfile
- if (acl_init((THD *)0, opt_noacl))
+ if (acl_init((THD *)0, opt_noacl) ||
+ my_tz_init((THD *)0, default_tz_name, opt_bootstrap))
{
abort_loop=1;
select_thread_in_use=0;
@@ -3897,7 +3908,8 @@ enum options_mysqld
OPT_DATE_FORMAT,
OPT_TIME_FORMAT,
OPT_DATETIME_FORMAT,
- OPT_LOG_QUERIES_NOT_USING_INDEXES
+ OPT_LOG_QUERIES_NOT_USING_INDEXES,
+ OPT_DEFAULT_TIME_ZONE
};
@@ -4010,6 +4022,9 @@ Disable with --skip-bdb (will save memory).",
{"default-table-type", OPT_STORAGE_ENGINE,
"(deprecated) Use default-storage-engine.", 0, 0,
0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"default-time-zone", OPT_DEFAULT_TIME_ZONE, "Set the default time zone.",
+ (gptr*) &default_tz_name, (gptr*) &default_tz_name,
+ 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0 },
{"delay-key-write", OPT_DELAY_KEY_WRITE, "Type of DELAY_KEY_WRITE.",
0,0,0, GET_STR, OPT_ARG, 0, 0, 0, 0, 0, 0},
{"delay-key-write-for-all-tables", OPT_DELAY_KEY_WRITE_ALL,
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 590b550ac3a..aa45afc3c30 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -441,6 +441,7 @@ static sys_var_thd_ulong sys_default_week_format("default_week_format",
sys_var_thd_ulong sys_group_concat_max_len("group_concat_max_len",
&SV::group_concat_max_len);
+sys_var_thd_time_zone sys_time_zone("time_zone");
/* Read only variables */
@@ -586,6 +587,7 @@ sys_var *sys_variables[]=
&sys_thread_cache_size,
&sys_time_format,
&sys_timestamp,
+ &sys_time_zone,
&sys_tmp_table_size,
&sys_trans_alloc_block_size,
&sys_trans_prealloc_size,
@@ -809,8 +811,9 @@ struct show_var_st init_vars[]= {
{"thread_stack", (char*) &thread_stack, SHOW_LONG},
{sys_time_format.name, (char*) &sys_time_format, SHOW_SYS},
#ifdef HAVE_TZNAME
- {"timezone", time_zone, SHOW_CHAR},
+ {"system_time_zone", system_time_zone, SHOW_CHAR},
#endif
+ {"time_zone", (char*) &sys_time_zone, SHOW_SYS},
{sys_tmp_table_size.name, (char*) &sys_tmp_table_size, SHOW_SYS},
{"tmpdir", (char*) &opt_mysql_tmpdir, SHOW_CHAR_PTR},
{sys_trans_alloc_block_size.name, (char*) &sys_trans_alloc_block_size,
@@ -2352,6 +2355,77 @@ bool sys_var_rand_seed2::update(THD *thd, set_var *var)
}
+bool sys_var_thd_time_zone::check(THD *thd, set_var *var)
+{
+ char buff[MAX_TIME_ZONE_NAME_LENGTH];
+ String str(buff, sizeof(buff), &my_charset_latin1);
+ String *res= var->value->val_str(&str);
+
+#if defined(HAVE_REPLICATION) && (MYSQL_VERSION_ID < 50000)
+ if ((var->type == OPT_GLOBAL) &&
+ (mysql_bin_log.is_open() ||
+ active_mi->slave_running || active_mi->rli.slave_running))
+ {
+ my_printf_error(0, "Binary logging and replication forbid changing "
+ "of the global server time zone", MYF(0));
+ return 1;
+ }
+#endif
+
+ if (!(var->save_result.time_zone= my_tz_find(thd, res)))
+ {
+ my_error(ER_UNKNOWN_TIME_ZONE, MYF(0), res ? res->c_ptr() : "NULL");
+ return 1;
+ }
+ return 0;
+}
+
+
+bool sys_var_thd_time_zone::update(THD *thd, set_var *var)
+{
+ /* We are using Time_zone object found during check() phase */
+ *get_tz_ptr(thd,var->type)= var->save_result.time_zone;
+ return 0;
+}
+
+
+byte *sys_var_thd_time_zone::value_ptr(THD *thd, enum_var_type type,
+ LEX_STRING *base)
+{
+ /*
+ We can use ptr() instead of c_ptr() here because String contaning
+ time zone name is guaranteed to be zero ended.
+ */
+ return (byte *)((*get_tz_ptr(thd,type))->get_name()->ptr());
+}
+
+
+Time_zone** sys_var_thd_time_zone::get_tz_ptr(THD *thd,
+ enum_var_type type)
+{
+ if (type == OPT_GLOBAL)
+ return &global_system_variables.time_zone;
+ else
+ return &thd->variables.time_zone;
+}
+
+
+void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type)
+{
+ if (type == OPT_GLOBAL)
+ {
+ if (default_tz_name)
+ {
+ String str(default_tz_name, &my_charset_latin1);
+ global_system_variables.time_zone= my_tz_find(thd, &str);
+ }
+ else
+ global_system_variables.time_zone= my_tz_SYSTEM;
+ }
+ else
+ thd->variables.time_zone= global_system_variables.time_zone;
+}
+
/*
Functions to update thd->options bits
*/
diff --git a/sql/set_var.h b/sql/set_var.h
index 9bed6f01dcc..c2b4ca34b2d 100644
--- a/sql/set_var.h
+++ b/sql/set_var.h
@@ -716,6 +716,29 @@ public:
SHOW_TYPE type() { return show_type; }
};
+class sys_var_thd_time_zone :public sys_var_thd
+{
+public:
+ sys_var_thd_time_zone(const char *name_arg):
+ sys_var_thd(name_arg)
+ {
+#if MYSQL_VERSION_ID < 50000
+ no_support_one_shot= 0;
+#endif
+ }
+ bool check(THD *thd, set_var *var);
+ SHOW_TYPE type() { return SHOW_CHAR; }
+ bool check_update_type(Item_result type)
+ {
+ return type != STRING_RESULT; /* Only accept strings */
+ }
+ bool check_default(enum_var_type type) { return 0; }
+ bool update(THD *thd, set_var *var);
+ byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
+ virtual void set_default(THD *thd, enum_var_type type);
+ Time_zone **get_tz_ptr(THD *thd, enum_var_type type);
+};
+
/****************************************************************************
Classes for parsing of the SET command
****************************************************************************/
@@ -749,6 +772,7 @@ public:
ulong ulong_value;
ulonglong ulonglong_value;
DATE_TIME_FORMAT *date_time_format;
+ Time_zone *time_zone;
} save_result;
LEX_STRING base; /* for structs */
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
index 6b9b81ec87d..2d377929229 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -310,3 +310,5 @@ character-set=latin2
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
index 767b1ee5f7a..af7b8263e6b 100644
--- a/sql/share/danish/errmsg.txt
+++ b/sql/share/danish/errmsg.txt
@@ -304,3 +304,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Modtog fejl %d '%-.100s' fra %s",
"Modtog temporary fejl %d '%-.100s' fra %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 14aa6ea7922..aa20996680e 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -312,3 +312,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index 8e3af2afe2a..b5a7f7962cf 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -301,3 +301,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index c6fc4987fb8..0cc6e06ab26 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -306,3 +306,5 @@ character-set=latin7
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index e6bda1b35a4..2e23db62ddb 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -301,3 +301,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index e51ff9d1554..c63162c84f6 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -313,3 +313,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index 42381c4d198..fa94b0f5107 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -301,3 +301,5 @@ character-set=greek
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index e0f0b3aa1ba..56fae82c438 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -303,3 +303,5 @@ character-set=latin2
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index 36204da1120..31768f172b4 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -301,3 +301,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index dbf05026da0..4385f25c991 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -303,3 +303,5 @@ character-set=ujis
"This command is not supported in the prepared statement protocol yet",
"Got NDB error %d '%-.100s'",
"Got temporary NDB error %d '%-.100s'",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index 4f66ac1c1f3..a6e84fad01e 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -301,3 +301,5 @@ character-set=euckr
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index 64385ce17ab..eaf7b3482ee 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -303,3 +303,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Mottok feil %d '%-.100s' fra %s",
"Mottok temporary feil %d '%-.100s' fra %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index c841c0e6458..692c10db58f 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -303,3 +303,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Mottok feil %d '%-.100s' fa %s",
"Mottok temporary feil %d '%-.100s' fra %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index da2af606f97..19f2c1c6983 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -305,3 +305,5 @@ character-set=latin2
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index a999b765c91..c77d10d83de 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -302,3 +302,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index ca82b19215a..5ee4efd0063 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -305,3 +305,5 @@ character-set=latin2
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index 32da15409d0..20188723f6d 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -303,3 +303,5 @@ character-set=koi8r
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt
index c143e65461b..cc822431464 100644
--- a/sql/share/serbian/errmsg.txt
+++ b/sql/share/serbian/errmsg.txt
@@ -307,3 +307,5 @@ character-set=cp1250
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index 87fbeb5b6ab..ee6aac5081b 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -309,3 +309,5 @@ character-set=latin2
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index 3c4e2bacb9c..483ec7068a2 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -303,3 +303,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index 7ee4eabd6c3..d9f3adf92d4 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -301,3 +301,5 @@ character-set=latin1
"This command is not supported in the prepared statement protocol yet",
"Fick felkod %d '%-.100s' från %s",
"Fick tilfällig felkod %d '%-.100s' från %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt
index 958b6bf8674..acf6f5121e8 100644
--- a/sql/share/ukrainian/errmsg.txt
+++ b/sql/share/ukrainian/errmsg.txt
@@ -306,3 +306,5 @@ character-set=koi8u
"This command is not supported in the prepared statement protocol yet",
"Got error %d '%-.100s' from %s",
"Got temporary error %d '%-.100s' from %s",
+"Unknown or incorrect time zone: '%-.64s'",
+"Invalid TIMESTAMP value in column '%s' at row %ld",
diff --git a/sql/slave.cc b/sql/slave.cc
index 1a59e5b2b5b..a1bbebca743 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -1248,7 +1248,30 @@ be equal for replication to work";
mysql_free_result(master_res);
}
- /* Add a timezones check here */
+ /*
+ Perform analogous check for time zone. Theoretically we also should
+ perform check here to verify that SYSTEM time zones are the same on
+ slave and master, but we can't rely on value of @@system_time_zone
+ variable (it is time zone abbreviation) since it determined at start
+ time and so could differ for slave and master even if they are really
+ in the same system time zone. So we are omiting this check and just
+ relying on documentation. Also according to Monty there are many users
+ who are using replication between servers in various time zones. Hence
+ such check will broke everything for them. (And now everything will
+ work for them because by default both their master and slave will have
+ 'SYSTEM' time zone).
+ */
+ if (!mysql_real_query(mysql, "SELECT @@GLOBAL.TIME_ZONE", 25) &&
+ (master_res= mysql_store_result(mysql)))
+ {
+ if ((master_row= mysql_fetch_row(master_res)) &&
+ strcmp(master_row[0],
+ global_system_variables.time_zone->get_name()->ptr()))
+ errmsg= "The slave I/O thread stops because master and slave have \
+different values for the TIME_ZONE global variable. The values must \
+be equal for replication to work";
+ mysql_free_result(master_res);
+ }
if (errmsg)
{
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 889c95125c5..f705b592e5a 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -1760,6 +1760,7 @@ TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
}
tmp_table->reginfo.lock_type=TL_WRITE; // Simulate locked
+ tmp_table->in_use= thd;
tmp_table->tmp_table = (tmp_table->file->has_transactions() ?
TRANSACTIONAL_TMP_TABLE : TMP_TABLE);
tmp_table->table_cache_key=(char*) (tmp_table+1);
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index b9fe61ac48a..5c6215e6fb9 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -786,6 +786,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
flags.collation_connection_num=
thd->variables.collation_connection->number;
flags.limit= thd->variables.select_limit;
+ flags.time_zone= thd->variables.time_zone;
STRUCT_LOCK(&structure_guard_mutex);
if (query_cache_size == 0)
@@ -972,6 +973,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
UINT_MAX);
flags.collation_connection_num= thd->variables.collation_connection->number;
flags.limit= thd->variables.select_limit;
+ flags.time_zone= thd->variables.time_zone;
memcpy((void *)(sql + (tot_length - QUERY_CACHE_FLAGS_SIZE)),
&flags, QUERY_CACHE_FLAGS_SIZE);
query_block = (Query_cache_block *) hash_search(&queries, (byte*) sql,
@@ -3231,9 +3233,10 @@ void Query_cache::queries_dump()
Query_cache_query_flags flags;
memcpy(&flags, str+len, QUERY_CACHE_FLAGS_SIZE);
str[len]= 0; // make zero ending DB name
- DBUG_PRINT("qcache", ("F:%u C:%u L:%lu (%u) '%s' '%s'",
+ DBUG_PRINT("qcache", ("F:%u C:%u L:%lu T:'%s' (%u) '%s' '%s'",
flags.client_long_flag,
- flags.character_set_client_num, (ulong)flags.limit,
+ flags.character_set_client_num,
+ (ulong)flags.limit, flags.time_zone->get_name(),
len, str, strend(str)+1));
DBUG_PRINT("qcache", ("-b- 0x%lx 0x%lx 0x%lx 0x%lx 0x%lx", (ulong) block,
(ulong) block->next, (ulong) block->prev,
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 704662fa4bf..a04823c6c43 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -157,8 +157,8 @@ bool foreign_key_prefix(Key *a, Key *b)
THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
last_insert_id_used(0),
- insert_id_used(0), rand_used(0), in_lock_tables(0),
- global_read_lock(0), bootstrap(0)
+ insert_id_used(0), rand_used(0), time_zone_used(0),
+ in_lock_tables(0), global_read_lock(0), bootstrap(0)
{
host= user= priv_user= db= ip=0;
host_or_ip= "connecting host";
diff --git a/sql/sql_class.h b/sql/sql_class.h
index e9ad659a2cc..cd4849d13ae 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -405,6 +405,8 @@ struct system_variables
CHARSET_INFO *collation_database;
CHARSET_INFO *collation_connection;
+ Time_zone *time_zone;
+
/* DATE, DATETIME and TIME formats */
DATE_TIME_FORMAT *date_format;
DATE_TIME_FORMAT *datetime_format;
@@ -826,6 +828,7 @@ public:
bool last_cuted_field;
bool no_errors, password, is_fatal_error;
bool query_start_used,last_insert_id_used,insert_id_used,rand_used;
+ bool time_zone_used;
bool in_lock_tables,global_read_lock;
bool query_error, bootstrap, cleanup_done;
bool tmp_table_used;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 037dd99d3b6..f7f30b079b8 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -945,6 +945,10 @@ TABLE *delayed_insert::get_local_table(THD* client_thd)
/* _rowid is not used with delayed insert */
copy->rowid_field=0;
+
+ /* Adjust in_use for pointing to client thread */
+ copy->in_use= client_thd;
+
return copy;
/* Got fatal error */
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index f72fab9ea3a..167fb2daf8b 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -530,7 +530,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table,
((Field_timestamp*) field)->set_time();
else if (field != table->next_number_field)
field->set_warning((uint) MYSQL_ERROR::WARN_LEVEL_WARN,
- ER_WARN_NULL_TO_NOTNULL);
+ ER_WARN_NULL_TO_NOTNULL, 1);
}
continue;
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 28e833b8421..10d780bca48 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -3489,7 +3489,8 @@ purposes internal to the MySQL server", MYF(0));
thd->variables.collation_server=
global_system_variables.collation_server;
thd->update_charset();
- /* Add timezone stuff here */
+ thd->variables.time_zone=
+ global_system_variables.time_zone;
thd->one_shot_set= 0;
}
}
@@ -3847,7 +3848,7 @@ mysql_init_query(THD *thd)
thd->total_warn_count=0; // Warnings for this query
thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
thd->sent_row_count= thd->examined_row_count= 0;
- thd->is_fatal_error= thd->rand_used= 0;
+ thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0;
thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
SERVER_QUERY_NO_INDEX_USED |
SERVER_QUERY_NO_GOOD_INDEX_USED);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index f7a0d5259a6..b32cb228c72 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -4975,6 +4975,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
table->db_low_byte_first=1; // True for HEAP and MyISAM
table->temp_pool_slot = temp_pool_slot;
table->copy_blobs= 1;
+ table->in_use= thd;
table->keys_for_keyread.init();
table->keys_in_use.init();
table->read_only_keys.init();
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 0b565968b08..4642abfcfc4 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -529,7 +529,6 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
}
else
{
- struct tm tm_tmp;
const char *str;
handler *file=table->file;
file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK);
@@ -562,24 +561,21 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
protocol->store_null();
else
{
- localtime_r(&file->create_time,&tm_tmp);
- localtime_to_TIME(&time, &tm_tmp);
+ thd->variables.time_zone->gmt_sec_to_TIME(&time, file->create_time);
protocol->store(&time);
}
if (!file->update_time)
protocol->store_null();
else
{
- localtime_r(&file->update_time,&tm_tmp);
- localtime_to_TIME(&time, &tm_tmp);
+ thd->variables.time_zone->gmt_sec_to_TIME(&time, file->update_time);
protocol->store(&time);
}
if (!file->check_time)
protocol->store_null();
else
{
- localtime_r(&file->check_time,&tm_tmp);
- localtime_to_TIME(&time, &tm_tmp);
+ thd->variables.time_zone->gmt_sec_to_TIME(&time, file->check_time);
protocol->store(&time);
}
str= (table->table_charset ? table->table_charset->name : "default");
diff --git a/sql/time.cc b/sql/time.cc
index 992f1afc4af..6a57dd1038c 100644
--- a/sql/time.cc
+++ b/sql/time.cc
@@ -23,16 +23,26 @@
static ulong const days_at_timestart=719528; /* daynr at 1970.01.01 */
uchar *days_in_month= (uchar*) "\037\034\037\036\037\036\037\037\036\037\036\037";
- /* Init some variabels needed when using my_local_time */
- /* Currently only my_time_zone is inited */
+/*
+ Offset of system time zone from UTC in seconds used to speed up
+ work of my_system_gmt_sec() function.
+*/
static long my_time_zone=0;
+
+/*
+ Prepare offset of system time zone from UTC for my_system_gmt_sec() func.
+
+ SYNOPSIS
+ init_time()
+*/
void init_time(void)
{
time_t seconds;
struct tm *l_time,tm_tmp;;
TIME my_time;
+ bool not_used;
seconds= (time_t) time((time_t*) 0);
localtime_r(&seconds,&tm_tmp);
@@ -44,33 +54,40 @@ void init_time(void)
my_time.hour= (uint) l_time->tm_hour;
my_time.minute= (uint) l_time->tm_min;
my_time.second= (uint) l_time->tm_sec;
- my_gmt_sec(&my_time, &my_time_zone); /* Init my_time_zone */
+ my_system_gmt_sec(&my_time, &my_time_zone, &not_used); /* Init my_time_zone */
}
+
/*
- Convert current time to sec. since 1970.01.01
- This code handles also day light saving time.
- The idea is to cache the time zone (including daylight saving time)
- for the next call to make things faster.
+ Convert time in TIME representation in system time zone to its
+ my_time_t form (number of seconds in UTC since begginning of Unix Epoch).
-*/
+ SYNOPSIS
+ my_system_gmt_sec()
+ t - time value to be converted
+ my_timezone - pointer to long where offset of system time zone
+ from UTC will be stored for caching
+ in_dst_time_gap - set to true if time falls into spring time-gap
-long my_gmt_sec(TIME *t, long *my_timezone)
+ NOTES
+ The idea is to cache the time zone offset from UTC (including daylight
+ saving time) for the next call to make things faster. But currently we
+ just calculate this offset during startup (by calling init_time()
+ function) and use it all the time.
+ Time value provided should be legal time value (e.g. '2003-01-01 25:00:00'
+ is not allowed).
+
+ RETURN VALUE
+ Time in UTC seconds since Unix Epoch representation.
+*/
+my_time_t
+my_system_gmt_sec(const TIME *t, long *my_timezone, bool *in_dst_time_gap)
{
uint loop;
time_t tmp;
struct tm *l_time,tm_tmp;
long diff, current_timezone;
- if (t->year > TIMESTAMP_MAX_YEAR || t->year < TIMESTAMP_MIN_YEAR)
- return 0;
-
- if (t->hour >= 24)
- { /* Fix for time-loop */
- t->day+=t->hour/24;
- t->hour%=24;
- }
-
/*
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
@@ -125,14 +142,13 @@ long my_gmt_sec(TIME *t, long *my_timezone)
tmp+=3600 - t->minute*60 - t->second; // Move to next hour
else if (diff == -3600)
tmp-=t->minute*60 + t->second; // Move to previous hour
+
+ *in_dst_time_gap= 1;
}
*my_timezone= current_timezone;
- if (tmp < TIMESTAMP_MIN_VALUE || tmp > TIMESTAMP_MAX_VALUE)
- tmp= 0;
-
- return (long) tmp;
-} /* my_gmt_sec */
+ return (my_time_t) tmp;
+} /* my_system_gmt_sec */
/* Some functions to calculate dates */
@@ -164,6 +180,7 @@ long calc_daynr(uint year,uint month,uint day)
} /* calc_daynr */
+#ifndef TESTTIME
/* Calc weekday from daynr */
/* Returns 0 for monday, 1 for tuesday .... */
@@ -346,6 +363,8 @@ static char time_separator=':';
flags Bitmap of following items
TIME_FUZZY_DATE Set if we should allow partial dates
TIME_DATETIME_ONLY Set if we only allow full datetimes.
+ was_cut Set to 1 if value was cut during conversion or to 0
+ otherwise.
DESCRIPTION
At least the following formats are recogniced (based on number of digits)
@@ -383,7 +402,8 @@ static char time_separator=':';
#define MAX_DATE_PARTS 8
timestamp_type
-str_to_TIME(const char *str, uint length, TIME *l_time, uint flags)
+str_to_TIME(const char *str, uint length, TIME *l_time, uint flags,
+ int *was_cut)
{
uint field_length, year_length, digits, i, number_of_fields;
uint date[MAX_DATE_PARTS], date_len[MAX_DATE_PARTS];
@@ -403,11 +423,16 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags)
LINT_INIT(year_length);
LINT_INIT(last_field_pos);
+ *was_cut= 0;
+
// Skip space at start
for (; str != end && my_isspace(&my_charset_latin1, *str) ; str++)
;
if (str == end || ! my_isdigit(&my_charset_latin1, *str))
+ {
+ *was_cut= 1;
DBUG_RETURN(TIMESTAMP_NONE);
+ }
is_internal_format= 0;
/* This has to be changed if want to activate different timestamp formats */
@@ -449,7 +474,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags)
if (pos == end)
{
if (flags & TIME_DATETIME_ONLY)
+ {
+ *was_cut= 1;
DBUG_RETURN(TIMESTAMP_NONE); // Can't be a full datetime
+ }
/* Date field. Set hour, minutes and seconds to 0 */
date[0]= date[1]= date[2]= date[3]= date[4]= 0;
start_loop= 5; // Start with first date part
@@ -486,7 +514,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags)
}
date_len[i]= (uint) (str - start);
if (tmp_value > 999999) // Impossible date part
+ {
+ *was_cut= 1;
DBUG_RETURN(TIMESTAMP_NONE);
+ }
date[i]=tmp_value;
not_zero_date|= tmp_value;
@@ -523,7 +554,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags)
if (my_isspace(&my_charset_latin1,*str))
{
if (!(allow_space & (1 << i)))
+ {
+ *was_cut= 1;
DBUG_RETURN(TIMESTAMP_NONE);
+ }
found_space= 1;
}
str++;
@@ -551,7 +585,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags)
last_field_pos= str;
}
if (found_delimitier && !found_space && (flags & TIME_DATETIME_ONLY))
+ {
+ *was_cut= 1;
DBUG_RETURN(TIMESTAMP_NONE); // Can't be a datetime
+ }
str= last_field_pos;
@@ -566,7 +603,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags)
{
year_length= date_len[(uint) format_position[0]];
if (!year_length) // Year must be specified
+ {
+ *was_cut= 1;
DBUG_RETURN(TIMESTAMP_NONE);
+ }
l_time->year= date[(uint) format_position[0]];
l_time->month= date[(uint) format_position[1]];
@@ -584,7 +624,10 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags)
if (format_position[7] != (uchar) 255)
{
if (l_time->hour > 12)
+ {
+ *was_cut= 1;
goto err;
+ }
l_time->hour= l_time->hour%12 + add_hours;
}
}
@@ -624,7 +667,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags)
}
}
if (not_zero_date)
- current_thd->cuted_fields++;
+ *was_cut= 1;
goto err;
}
@@ -635,8 +678,7 @@ str_to_TIME(const char *str, uint length, TIME *l_time, uint flags)
{
if (!my_isspace(&my_charset_latin1,*str))
{
- make_truncated_value_warning(current_thd, str_begin, length,
- l_time->time_type);
+ *was_cut= 1;
break;
}
}
@@ -650,43 +692,59 @@ err:
}
-time_t str_to_timestamp(const char *str,uint length)
+/*
+ Convert a timestamp string to a TIME value and produce a warning
+ if string was truncated during conversion.
+
+ NOTE
+ See description of str_to_TIME() for more information.
+*/
+timestamp_type
+str_to_TIME_with_warn(const char *str, uint length, TIME *l_time, uint flags)
{
- TIME l_time;
- long not_used;
- time_t timestamp= 0;
-
- if (str_to_TIME(str,length,&l_time,0) > TIMESTAMP_DATETIME_ERROR &&
- !(timestamp= my_gmt_sec(&l_time, &not_used)))
- current_thd->cuted_fields++;
- return timestamp;
+ int was_cut;
+ timestamp_type ts_type= str_to_TIME(str, length, l_time, flags, &was_cut);
+ if (was_cut)
+ make_truncated_value_warning(current_thd, str, length, ts_type);
+ return ts_type;
}
/*
- Convert a string to datetime.
+ Convert a datetime from broken-down TIME representation to corresponding
+ TIMESTAMP value.
SYNOPSIS
- str_to_datetime()
- str String to parse (see str_to_TIME() synopsis)
- length Length of str
- fuzzy_date Flags (see str_to_TIME() synopsis)
-
+ TIME_to_timestamp()
+ thd - current thread
+ t - datetime in broken-down representation,
+ in_dst_time_gap - pointer to bool which is set to true if t represents
+ value which doesn't exists (falls into the spring
+ time-gap) or to false otherwise.
+
RETURN
- -1 if error
- datetime value otherwise
+ Number seconds in UTC since start of Unix Epoch corresponding to t.
+ 0 - t contains datetime value which is out of TIMESTAMP range.
+
*/
-
-longlong str_to_datetime(const char *str,uint length, uint fuzzy_date)
+my_time_t TIME_to_timestamp(THD *thd, const TIME *t, bool *in_dst_time_gap)
{
- TIME l_time;
- if (str_to_TIME(str,length,&l_time,fuzzy_date) <= TIMESTAMP_DATETIME_ERROR)
- return -1;
- return (longlong) (l_time.year*LL(10000000000) +
- l_time.month*LL(100000000)+
- l_time.day*LL(1000000)+
- l_time.hour*LL(10000)+
- (longlong) (l_time.minute*100+l_time.second));
+ my_time_t timestamp;
+
+ *in_dst_time_gap= 0;
+
+ if (t->year < TIMESTAMP_MAX_YEAR && t->year > TIMESTAMP_MIN_YEAR ||
+ t->year == TIMESTAMP_MAX_YEAR && t->month == 1 && t->day == 1 ||
+ t->year == TIMESTAMP_MIN_YEAR && t->month == 12 && t->day == 31)
+ {
+ thd->time_zone_used= 1;
+ timestamp= thd->variables.time_zone->TIME_to_gmt_sec(t, in_dst_time_gap);
+ if (timestamp >= TIMESTAMP_MIN_VALUE && timestamp <= TIMESTAMP_MAX_VALUE)
+ return timestamp;
+ }
+
+ /* If we are here we have range error. */
+ return(0);
}
@@ -701,6 +759,8 @@ longlong str_to_datetime(const char *str,uint length, uint fuzzy_date)
There may be an optional [.second_part] after seconds
length Length of str
l_time Store result here
+ was_cut Set to 1 if value was cut during conversion or to 0
+ otherwise.
NOTES
Because of the extra days argument, this function can only
@@ -711,7 +771,7 @@ longlong str_to_datetime(const char *str,uint length, uint fuzzy_date)
1 error
*/
-bool str_to_time(const char *str,uint length,TIME *l_time)
+bool str_to_time(const char *str, uint length, TIME *l_time, int *was_cut)
{
long date[5],value;
const char *end=str+length, *end_of_days;
@@ -720,6 +780,7 @@ bool str_to_time(const char *str,uint length,TIME *l_time)
uint state;
l_time->neg=0;
+ *was_cut= 0;
for (; str != end && my_isspace(&my_charset_latin1,*str) ; str++)
length--;
if (str != end && *str == '-')
@@ -736,9 +797,12 @@ bool str_to_time(const char *str,uint length,TIME *l_time)
{ // Probably full timestamp
enum timestamp_type res= str_to_TIME(str,length,l_time,
(TIME_FUZZY_DATE |
- TIME_DATETIME_ONLY));
+ TIME_DATETIME_ONLY),
+ was_cut);
if ((int) res >= (int) TIMESTAMP_DATETIME_ERROR)
return res == TIMESTAMP_DATETIME_ERROR;
+ /* We need to restore was_cut flag since str_to_TIME can modify it */
+ *was_cut= 0;
}
/* Not a timestamp. Try to get this as a DAYS_TO_SECOND string */
@@ -841,7 +905,7 @@ fractional:
/* Some simple checks */
if (date[2] >= 60 || date[3] >= 60)
{
- current_thd->cuted_fields++;
+ *was_cut= 1;
return 1;
}
l_time->year= 0; // For protocol::store_time
@@ -860,8 +924,7 @@ fractional:
{
if (!my_isspace(&my_charset_latin1,*str))
{
- make_truncated_value_warning(current_thd, str_begin, length,
- TIMESTAMP_TIME);
+ *was_cut= 1;
break;
}
} while (++str != end);
@@ -871,6 +934,113 @@ fractional:
/*
+ Convert a time string to a TIME struct and produce a warning
+ if string was cut during conversion.
+
+ NOTE
+ See str_to_time() for more info.
+*/
+bool
+str_to_time_with_warn(const char *str, uint length, TIME *l_time)
+{
+ int was_cut;
+ bool ret_val= str_to_time(str, length, l_time, &was_cut);
+ if (was_cut)
+ make_truncated_value_warning(current_thd, str, length, TIMESTAMP_TIME);
+ return ret_val;
+}
+
+
+/*
+ Convert datetime value specified as number to broken-down TIME
+ representation and form value of DATETIME type as side-effect.
+
+ SYNOPSIS
+ number_to_TIME()
+ nr - datetime value as number
+ time_res - pointer for structure for broken-down representation
+ fuzzy_date - indicates whenever we allow fuzzy dates
+ was_cut - set ot 1 if there was some kind of error during
+ conversion or to 0 if everything was OK.
+
+ DESCRIPTION
+ Convert a datetime value of formats YYMMDD, YYYYMMDD, YYMMDDHHMSS,
+ YYYYMMDDHHMMSS to broken-down TIME representation. Return value in
+ YYYYMMDDHHMMSS format as side-effect.
+
+ This function also checks if datetime value fits in DATETIME range.
+
+ RETURN VALUE
+ Datetime value in YYYYMMDDHHMMSS format.
+ If input value is not valid datetime value then 0 is returned.
+*/
+
+longlong number_to_TIME(longlong nr, TIME *time_res, bool fuzzy_date,
+ int *was_cut)
+{
+ long part1,part2;
+
+ *was_cut= 0;
+
+ if (nr == LL(0) || nr >= LL(10000101000000))
+ goto ok;
+ if (nr < 101)
+ goto err;
+ if (nr <= (YY_PART_YEAR-1)*10000L+1231L)
+ {
+ nr= (nr+20000000L)*1000000L; // YYMMDD, year: 2000-2069
+ goto ok;
+ }
+ if (nr < (YY_PART_YEAR)*10000L+101L)
+ goto err;
+ if (nr <= 991231L)
+ {
+ nr= (nr+19000000L)*1000000L; // YYMMDD, year: 1970-1999
+ goto ok;
+ }
+ if (nr < 10000101L)
+ goto err;
+ if (nr <= 99991231L)
+ {
+ nr= nr*1000000L;
+ goto ok;
+ }
+ if (nr < 101000000L)
+ goto err;
+ if (nr <= (YY_PART_YEAR-1)*LL(10000000000)+LL(1231235959))
+ {
+ nr= nr+LL(20000000000000); // YYMMDDHHMMSS, 2000-2069
+ goto ok;
+ }
+ if (nr < YY_PART_YEAR*LL(10000000000)+ LL(101000000))
+ goto err;
+ if (nr <= LL(991231235959))
+ nr= nr+LL(19000000000000); // YYMMDDHHMMSS, 1970-1999
+
+ ok:
+ part1=(long) (nr/LL(1000000));
+ part2=(long) (nr - (longlong) part1*LL(1000000));
+ time_res->year= (int) (part1/10000L); part1%=10000L;
+ time_res->month= (int) part1 / 100;
+ time_res->day= (int) part1 % 100;
+ time_res->hour= (int) (part2/10000L); part2%=10000L;
+ time_res->minute=(int) part2 / 100;
+ time_res->second=(int) part2 % 100;
+
+ if (time_res->year <= 9999 && time_res->month <= 12 &&
+ time_res->day <= 31 && time_res->hour <= 23 &&
+ time_res->minute <= 59 && time_res->second <= 59 &&
+ (fuzzy_date || (time_res->month != 0 && time_res->day != 0) || nr==0))
+ return nr;
+
+ err:
+
+ *was_cut= 1;
+ return LL(0);
+}
+
+
+/*
Convert a system time structure to TIME
*/
@@ -1307,6 +1477,7 @@ void make_datetime(const DATE_TIME_FORMAT *format __attribute__((unused)),
str->set_charset(&my_charset_bin);
}
+
void make_truncated_value_warning(THD *thd, const char *str_val,
uint str_length, timestamp_type time_type)
{
@@ -1323,19 +1494,17 @@ void make_truncated_value_warning(THD *thd, const char *str_val,
case TIMESTAMP_DATE:
type_str= "date";
break;
- case TIMESTAMP_DATETIME:
- type_str= "datetime";
- break;
case TIMESTAMP_TIME:
type_str= "time";
break;
+ case TIMESTAMP_DATETIME: // FALLTHROUGH
default:
- type_str= "string";
+ type_str= "datetime";
break;
}
sprintf(warn_buff, ER(ER_TRUNCATED_WRONG_VALUE),
type_str, str.ptr());
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_TRUNCATED_WRONG_VALUE, warn_buff);
}
@@ -1446,3 +1615,5 @@ void TIME_to_string(const TIME *time, String *str)
DBUG_ASSERT(0);
}
}
+
+#endif
diff --git a/sql/tzfile.h b/sql/tzfile.h
new file mode 100644
index 00000000000..623cddc1f12
--- /dev/null
+++ b/sql/tzfile.h
@@ -0,0 +1,137 @@
+/* Copyright (C) 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ This file is based on public domain code from ftp://elsie.ncih.nist.gov/
+ Initial source code is in the public domain, so clarified as of
+ 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
+/*
+ Information about time zone files.
+*/
+
+#ifndef TZDIR
+#define TZDIR "/usr/share/zoneinfo" /* Time zone object file directory */
+#endif /* !defined TZDIR */
+
+/*
+ Each file begins with. . .
+*/
+
+#define TZ_MAGIC "TZif"
+
+struct tzhead {
+ char tzh_magic[4]; /* TZ_MAGIC */
+ char tzh_reserved[16]; /* reserved for future use */
+ char tzh_ttisgmtcnt[4]; /* coded number of trans. time flags */
+ char tzh_ttisstdcnt[4]; /* coded number of trans. time flags */
+ char tzh_leapcnt[4]; /* coded number of leap seconds */
+ char tzh_timecnt[4]; /* coded number of transition times */
+ char tzh_typecnt[4]; /* coded number of local time types */
+ char tzh_charcnt[4]; /* coded number of abbr. chars */
+};
+
+/*
+ . . .followed by. . .
+
+ tzh_timecnt (char [4])s coded transition times a la time(2)
+ tzh_timecnt (unsigned char)s types of local time starting at above
+ tzh_typecnt repetitions of
+ one (char [4]) coded UTC offset in seconds
+ one (unsigned char) used to set tm_isdst
+ one (unsigned char) that's an abbreviation list index
+ tzh_charcnt (char)s '\0'-terminated zone abbreviations
+ tzh_leapcnt repetitions of
+ one (char [4]) coded leap second transition times
+ one (char [4]) total correction after above
+ tzh_ttisstdcnt (char)s indexed by type; if TRUE, transition
+ time is standard time, if FALSE,
+ transition time is wall clock time
+ if absent, transition times are
+ assumed to be wall clock time
+ tzh_ttisgmtcnt (char)s indexed by type; if TRUE, transition
+ time is UTC, if FALSE,
+ transition time is local time
+ if absent, transition times are
+ assumed to be local time
+*/
+
+/*
+ In the current implementation, we refuse to deal with files that
+ exceed any of the limits below.
+*/
+
+#ifndef TZ_MAX_TIMES
+/*
+ The TZ_MAX_TIMES value below is enough to handle a bit more than a
+ year's worth of solar time (corrected daily to the nearest second) or
+ 138 years of Pacific Presidential Election time
+ (where there are three time zone transitions every fourth year).
+*/
+#define TZ_MAX_TIMES 370
+#endif /* !defined TZ_MAX_TIMES */
+
+#ifndef TZ_MAX_TYPES
+#ifdef SOLAR
+#define TZ_MAX_TYPES 256 /* Limited by what (unsigned char)'s can hold */
+#else
+/*
+ Must be at least 14 for Europe/Riga as of Jan 12 1995,
+ as noted by Earl Chew <earl@hpato.aus.hp.com>.
+*/
+#define TZ_MAX_TYPES 20 /* Maximum number of local time types */
+#endif /* defined SOLAR */
+#endif /* !defined TZ_MAX_TYPES */
+
+#ifndef TZ_MAX_CHARS
+#define TZ_MAX_CHARS 50 /* Maximum number of abbreviation characters */
+ /* (limited by what unsigned chars can hold) */
+#endif /* !defined TZ_MAX_CHARS */
+
+#ifndef TZ_MAX_LEAPS
+#define TZ_MAX_LEAPS 50 /* Maximum number of leap second corrections */
+#endif /* !defined TZ_MAX_LEAPS */
+
+#ifndef TZ_MAX_REV_RANGES
+#ifdef SOLAR
+/* Solar (Asia/RiyadhXX) zones need significantly bigger TZ_MAX_REV_RANGES */
+#define TZ_MAX_REV_RANGES (TZ_MAX_TIMES*2+TZ_MAX_LEAPS*2+2)
+#else
+#define TZ_MAX_REV_RANGES (TZ_MAX_TIMES+TZ_MAX_LEAPS+2)
+#endif
+#endif
+
+#define SECS_PER_MIN 60
+#define MINS_PER_HOUR 60
+#define HOURS_PER_DAY 24
+#define DAYS_PER_WEEK 7
+#define DAYS_PER_NYEAR 365
+#define DAYS_PER_LYEAR 366
+#define SECS_PER_HOUR (SECS_PER_MIN * MINS_PER_HOUR)
+#define SECS_PER_DAY ((long) SECS_PER_HOUR * HOURS_PER_DAY)
+#define MONS_PER_YEAR 12
+
+#define TM_YEAR_BASE 1900
+
+#define EPOCH_YEAR 1970
+
+/*
+ Accurate only for the past couple of centuries,
+ that will probably do.
+*/
+
+#define isleap(y) (((y) % 4) == 0 && (((y) % 100) != 0 || ((y) % 400) == 0))
diff --git a/sql/tztime.cc b/sql/tztime.cc
new file mode 100644
index 00000000000..5a23c90a574
--- /dev/null
+++ b/sql/tztime.cc
@@ -0,0 +1,2559 @@
+/* Copyright (C) 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+/*
+ Most of the following code and structures were derived from
+ public domain code from ftp://elsie.nci.nih.gov/pub
+ (We will refer to this code as to elsie-code further.)
+*/
+
+#ifdef __GNUC__
+#pragma implementation // gcc: Class implementation
+#endif
+
+
+#include "mysql_priv.h"
+#include "tzfile.h"
+#include <m_string.h>
+#include <my_dir.h>
+
+
+/*
+ Now we don't use abbreviations in server but we will do this in future.
+*/
+#if defined(TZINFO2SQL) || defined(TESTTIME)
+#define ABBR_ARE_USED
+#endif
+
+/* For debug purposes only */
+#undef ABBR_ARE_USED
+#define ABBR_ARE_USED
+
+/* Structure describing local time type (e.g. Moscow summer time (MSD)) */
+typedef struct ttinfo
+{
+ long tt_gmtoff; // Offset from UTC in seconds
+ int tt_isdst; // Is daylight saving time or not. Used to set tm_isdst
+#ifdef ABBR_ARE_USED
+ int tt_abbrind; // Index of start of abbreviation for this time type.
+#endif
+ /*
+ We don't use tt_ttisstd and tt_ttisgmt members of original elsie-code struct
+ since we don't support POSIX-style TZ descriptions in variables.
+ */
+} TRAN_TYPE_INFO;
+
+/* Structure describing leap-second corrections. */
+typedef struct lsinfo
+{
+ my_time_t ls_trans; // Transition time
+ long ls_corr; // Correction to apply
+} LS_INFO;
+
+/*
+ Structure with information describing ranges of my_time_t shifted to local
+ time (my_time_t + offset). Used for local TIME -> my_time_t conversion.
+ See comments for TIME_to_gmt_sec() for more info.
+*/
+typedef struct revtinfo
+{
+ long rt_offset; // Offset of local time from UTC in seconds
+ int rt_type; // Type of period 0 - Normal period. 1 - Spring time-gap
+} REVT_INFO;
+
+#ifdef TZNAME_MAX
+#define MY_TZNAME_MAX TZNAME_MAX
+#endif
+#ifndef TZNAME_MAX
+#define MY_TZNAME_MAX 255
+#endif
+
+/*
+ Structure which fully describes time zone which is
+ described in our db or in zoneinfo files.
+*/
+typedef struct st_time_zone_info
+{
+ int leapcnt; // Number of leap-second corrections
+ int timecnt; // Number of transitions between time types
+ int typecnt; // Number of local time types
+ int charcnt; // Number of characters used for abbreviations
+ int revcnt; // Number of transition descr. for TIME->my_time_t conversion
+ /* The following are dynamical arrays are allocated in MEM_ROOT */
+ my_time_t *ats; // Times of transitions between time types
+ unsigned char *types; // Local time types for transitions
+ TRAN_TYPE_INFO *ttis; // Local time types descriptions
+#ifdef ABBR_ARE_USED
+ /* Storage for local time types abbreviations. They are stored as ASCIIZ */
+ char *chars;
+#endif
+ /*
+ Leap seconds corrections descriptions, this array is shared by
+ all time zones who use leap seconds.
+ */
+ LS_INFO *lsis;
+ /*
+ Starting points and descriptions of shifted my_time_t (my_time_t + offset)
+ ranges on which shifted my_time_t -> my_time_t mapping is linear or undefined.
+ Used for tm -> my_time_t conversion.
+ */
+ my_time_t *revts;
+ REVT_INFO *revtis;
+ /*
+ Time type which is used for times smaller than first transition or if
+ there are no transitions at all.
+ */
+ TRAN_TYPE_INFO *fallback_tti;
+
+} TIME_ZONE_INFO;
+
+
+static my_bool prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage);
+
+
+#if defined(TZINFO2SQL) || defined(TESTTIME)
+
+/*
+ Load time zone description from zoneinfo (TZinfo) file.
+
+ SYNOPSIS
+ tz_load()
+ name - path to zoneinfo file
+ sp - TIME_ZONE_INFO structure to fill
+
+ RETURN VALUES
+ 0 - Ok
+ 1 - Error
+*/
+static my_bool
+tz_load(const char *name, TIME_ZONE_INFO *sp, MEM_ROOT *storage)
+{
+ char *p;
+ int i;
+ FILE *file;
+
+ if (!(file= my_fopen(name, O_RDONLY|O_BINARY, MYF(MY_WME))))
+ return 1;
+ {
+ struct tzhead *tzhp;
+ union
+ {
+ struct tzhead tzhead;
+ char buf[sizeof(struct tzhead) + sizeof(my_time_t) * TZ_MAX_TIMES +
+ TZ_MAX_TIMES + sizeof(TRAN_TYPE_INFO) * TZ_MAX_TYPES +
+#ifdef ABBR_ARE_USED
+ max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1))) +
+#endif
+ sizeof(LS_INFO) * TZ_MAX_LEAPS];
+ } u;
+ int ttisstdcnt;
+ int ttisgmtcnt;
+ char *tzinfo_buf;
+
+ i= my_fread(file, u.buf, sizeof(u.buf), MYF(MY_WME));
+
+ if (my_fclose(file, MYF(MY_WME)) != 0)
+ return 1;
+
+ if (i < (int)sizeof(struct tzhead))
+ return 1;
+
+ ttisstdcnt= int4net(u.tzhead.tzh_ttisgmtcnt);
+ ttisgmtcnt= int4net(u.tzhead.tzh_ttisstdcnt);
+ sp->leapcnt= int4net(u.tzhead.tzh_leapcnt);
+ sp->timecnt= int4net(u.tzhead.tzh_timecnt);
+ sp->typecnt= int4net(u.tzhead.tzh_typecnt);
+ sp->charcnt= int4net(u.tzhead.tzh_charcnt);
+ p= u.tzhead.tzh_charcnt + sizeof u.tzhead.tzh_charcnt;
+ if (sp->leapcnt < 0 || sp->leapcnt > TZ_MAX_LEAPS ||
+ sp->typecnt <= 0 || sp->typecnt > TZ_MAX_TYPES ||
+ sp->timecnt < 0 || sp->timecnt > TZ_MAX_TIMES ||
+ sp->charcnt < 0 || sp->charcnt > TZ_MAX_CHARS ||
+ (ttisstdcnt != sp->typecnt && ttisstdcnt != 0) ||
+ (ttisgmtcnt != sp->typecnt && ttisgmtcnt != 0))
+ return 1;
+ if (i - (p - u.buf) < sp->timecnt * 4 + /* ats */
+ sp->timecnt + /* types */
+ sp->typecnt * (4 + 2) + /* ttinfos */
+ sp->charcnt + /* chars */
+ sp->leapcnt * (4 + 4) + /* lsinfos */
+ ttisstdcnt + /* ttisstds */
+ ttisgmtcnt) /* ttisgmts */
+ return 1;
+
+ if (!(tzinfo_buf= (char *)alloc_root(storage,
+ ALIGN_SIZE(sp->timecnt *
+ sizeof(my_time_t)) +
+ ALIGN_SIZE(sp->timecnt) +
+ ALIGN_SIZE(sp->typecnt *
+ sizeof(TRAN_TYPE_INFO)) +
+#ifdef ABBR_ARE_USED
+ ALIGN_SIZE(sp->charcnt) +
+#endif
+ sp->leapcnt * sizeof(LS_INFO))))
+ return 1;
+
+ sp->ats= (my_time_t *)tzinfo_buf;
+ tzinfo_buf+= ALIGN_SIZE(sp->timecnt * sizeof(my_time_t));
+ sp->types= (unsigned char *)tzinfo_buf;
+ tzinfo_buf+= ALIGN_SIZE(sp->timecnt);
+ sp->ttis= (TRAN_TYPE_INFO *)tzinfo_buf;
+ tzinfo_buf+= ALIGN_SIZE(sp->typecnt * sizeof(TRAN_TYPE_INFO));
+#ifdef ABBR_ARE_USED
+ sp->chars= tzinfo_buf;
+ tzinfo_buf+= ALIGN_SIZE(sp->charcnt);
+#endif
+ sp->lsis= (LS_INFO *)tzinfo_buf;
+
+ for (i= 0; i < sp->timecnt; i++, p+= 4)
+ sp->ats[i]= int4net(p);
+
+ for (i= 0; i < sp->timecnt; i++)
+ {
+ sp->types[i]= (unsigned char) *p++;
+ if (sp->types[i] >= sp->typecnt)
+ return 1;
+ }
+ for (i= 0; i < sp->typecnt; i++)
+ {
+ TRAN_TYPE_INFO * ttisp;
+
+ ttisp= &sp->ttis[i];
+ ttisp->tt_gmtoff= int4net(p);
+ p+= 4;
+ ttisp->tt_isdst= (unsigned char) *p++;
+ if (ttisp->tt_isdst != 0 && ttisp->tt_isdst != 1)
+ return 1;
+ ttisp->tt_abbrind= (unsigned char) *p++;
+ if (ttisp->tt_abbrind < 0 ||
+ ttisp->tt_abbrind > sp->charcnt)
+ return 1;
+ }
+ for (i= 0; i < sp->charcnt; i++)
+ sp->chars[i]= *p++;
+ sp->chars[i]= '\0'; /* ensure '\0' at end */
+ for (i= 0; i < sp->leapcnt; i++)
+ {
+ LS_INFO *lsisp;
+
+ lsisp= &sp->lsis[i];
+ lsisp->ls_trans= int4net(p);
+ p+= 4;
+ lsisp->ls_corr= int4net(p);
+ p+= 4;
+ }
+ /*
+ Since we don't support POSIX style TZ definitions in variables we
+ don't read further like glibc or elsie code.
+ */
+ }
+
+ return prepare_tz_info(sp, storage);
+}
+#endif /* defined(TZINFO2SQL) || defined(TESTTIME) */
+
+
+/*
+ Finish preparation of time zone description for use in TIME_to_gmt_sec()
+ and gmt_sec_to_TIME() functions.
+
+ SYNOPSIS
+ prepare_tz_info()
+ sp - pointer to time zone description
+ storage - pointer to MEM_ROOT where arrays for map allocated
+
+ DESCRIPTION
+ First task of this function is to find fallback time type which will
+ be used if there are no transitions or we have moment in time before
+ any transitions.
+ Second task is to build "shifted my_time_t" -> my_time_t map used in
+ TIME -> my_time_t conversion.
+ Note: See description of TIME_to_gmt_sec() function first.
+ In order to perform TIME -> my_time_t conversion we need to build table
+ which defines "shifted by tz offset and leap seconds my_time_t" ->
+ my_time_t function wich is almost the same (except ranges of ambiguity)
+ as reverse function to piecewise linear function used for my_time_t ->
+ "shifted my_time_t" conversion and which is also specified as table in
+ zoneinfo file or in our db (It is specified as start of time type ranges
+ and time type offsets). So basic idea is very simple - let us iterate
+ through my_time_t space from one point of discontinuity of my_time_t ->
+ "shifted my_time_t" function to another and build our approximation of
+ reverse function. (Actually we iterate through ranges on which
+ my_time_t -> "shifted my_time_t" is linear function).
+
+ RETURN VALUES
+ 0 Ok
+ 1 Error
+*/
+static my_bool
+prepare_tz_info(TIME_ZONE_INFO *sp, MEM_ROOT *storage)
+{
+ my_time_t cur_t= MY_TIME_T_MIN;
+ my_time_t cur_l, end_t, end_l;
+ my_time_t cur_max_seen_l= MY_TIME_T_MIN;
+ long cur_offset, cur_corr, cur_off_and_corr;
+ int next_trans_idx, next_leap_idx;
+ int i;
+ /*
+ Temporary arrays where we will store tables. Needed because
+ we don't know table sizes ahead. (Well we can estimate their
+ upper bound but this will take extra space.)
+ */
+ my_time_t revts[TZ_MAX_REV_RANGES];
+ REVT_INFO revtis[TZ_MAX_REV_RANGES];
+
+ LINT_INIT(end_l);
+
+ /*
+ Let us setup fallback time type which will be used if we have not any
+ transitions or if we have moment of time before first transition.
+ We will find first non-DST local time type and use it (or use first
+ local time type if all of them are DST types).
+ */
+ for (i= 0; i < sp->typecnt && sp->ttis[i].tt_isdst; i++)
+ /* no-op */ ;
+ if (i == sp->typecnt)
+ i= 0;
+ sp->fallback_tti= &(sp->ttis[i]);
+
+
+ /*
+ Let us build shifted my_time_t -> my_time_t map.
+ */
+ sp->revcnt= 0;
+
+ /* Let us find initial offset */
+ if (sp->timecnt == 0 || cur_t < sp->ats[0])
+ {
+ /*
+ If we have not any transitions or t is before first transition we are using
+ already found fallback time type which index is already in i.
+ */
+ next_trans_idx= 0;
+ }
+ else
+ {
+ /* cur_t == sp->ats[0] so we found transition */
+ i= sp->types[0];
+ next_trans_idx= 1;
+ }
+
+ cur_offset= sp->ttis[i].tt_gmtoff;
+
+
+ /* let us find leap correction... unprobable, but... */
+ for (next_leap_idx= 0; next_leap_idx < sp->leapcnt &&
+ cur_t >= sp->lsis[next_leap_idx].ls_trans;
+ ++next_leap_idx)
+ continue;
+
+ if (next_leap_idx > 0)
+ cur_corr= sp->lsis[next_leap_idx - 1].ls_corr;
+ else
+ cur_corr= 0;
+
+ /* Iterate trough t space */
+ while (sp->revcnt < TZ_MAX_REV_RANGES - 1)
+ {
+ cur_off_and_corr= cur_offset - cur_corr;
+
+ /*
+ We assuming that cur_t could be only overflowed downwards,
+ we also assume that end_t won't be overflowed in this case.
+ */
+ if (cur_off_and_corr < 0 &&
+ cur_t < MY_TIME_T_MIN - cur_off_and_corr)
+ cur_t= MY_TIME_T_MIN - cur_off_and_corr;
+
+ cur_l= cur_t + cur_off_and_corr;
+
+ /*
+ Let us choose end_t as point before next time type change or leap
+ second correction.
+ */
+ end_t= min((next_trans_idx < sp->timecnt) ? sp->ats[next_trans_idx] - 1:
+ MY_TIME_T_MAX,
+ (next_leap_idx < sp->leapcnt) ?
+ sp->lsis[next_leap_idx].ls_trans - 1: MY_TIME_T_MAX);
+ /*
+ again assuming that end_t can be overlowed only in positive side
+ we also assume that end_t won't be overflowed in this case.
+ */
+ if (cur_off_and_corr > 0 &&
+ end_t > MY_TIME_T_MAX - cur_off_and_corr)
+ end_t= MY_TIME_T_MAX - cur_off_and_corr;
+
+ end_l= end_t + cur_off_and_corr;
+
+
+ if (end_l > cur_max_seen_l)
+ {
+ /* We want special handling in the case of first range */
+ if (cur_max_seen_l == MY_TIME_T_MIN)
+ {
+ revts[sp->revcnt]= cur_l;
+ revtis[sp->revcnt].rt_offset= cur_off_and_corr;
+ revtis[sp->revcnt].rt_type= 0;
+ sp->revcnt++;
+ cur_max_seen_l= end_l;
+ }
+ else
+ {
+ if (cur_l > cur_max_seen_l + 1)
+ {
+ /* We have a spring time-gap and we are not at the first range */
+ revts[sp->revcnt]= cur_max_seen_l + 1;
+ revtis[sp->revcnt].rt_offset= revtis[sp->revcnt-1].rt_offset;
+ revtis[sp->revcnt].rt_type= 1;
+ sp->revcnt++;
+ if (sp->revcnt == TZ_MAX_TIMES + TZ_MAX_LEAPS + 1)
+ break; /* That was too much */
+ cur_max_seen_l= cur_l - 1;
+ }
+
+ /* Assume here end_l > cur_max_seen_l (because end_l>=cur_l) */
+
+ revts[sp->revcnt]= cur_max_seen_l + 1;
+ revtis[sp->revcnt].rt_offset= cur_off_and_corr;
+ revtis[sp->revcnt].rt_type= 0;
+ sp->revcnt++;
+ cur_max_seen_l= end_l;
+ }
+ }
+
+ if (end_t == MY_TIME_T_MAX ||
+ (cur_off_and_corr > 0) &&
+ (end_t >= MY_TIME_T_MAX - cur_off_and_corr))
+ /* end of t space */
+ break;
+
+ cur_t= end_t + 1;
+
+ /*
+ Let us find new offset and correction. Because of our choice of end_t
+ cur_t can only be point where new time type starts or/and leap
+ correction is performed.
+ */
+ if (sp->timecnt != 0 && cur_t >= sp->ats[0]) /* else reuse old offset */
+ if (next_trans_idx < sp->timecnt &&
+ cur_t == sp->ats[next_trans_idx])
+ {
+ /* We are at offset point */
+ cur_offset= sp->ttis[sp->types[next_trans_idx]].tt_gmtoff;
+ ++next_trans_idx;
+ }
+
+ if (next_leap_idx < sp->leapcnt &&
+ cur_t == sp->lsis[next_leap_idx].ls_trans)
+ {
+ /* we are at leap point */
+ cur_corr= sp->lsis[next_leap_idx].ls_corr;
+ ++next_leap_idx;
+ }
+ }
+
+ /* check if we have had enough space */
+ if (sp->revcnt == TZ_MAX_REV_RANGES - 1)
+ return 1;
+
+ /* set maximum end_l as finisher */
+ revts[sp->revcnt]= end_l;
+
+ /* Allocate arrays of proper size in sp and copy result there */
+ if (!(sp->revts= (my_time_t *)alloc_root(storage,
+ sizeof(my_time_t) * (sp->revcnt + 1))) ||
+ !(sp->revtis= (REVT_INFO *)alloc_root(storage,
+ sizeof(REVT_INFO) * sp->revcnt)))
+ return 1;
+
+ memcpy(sp->revts, revts, sizeof(my_time_t) * (sp->revcnt + 1));
+ memcpy(sp->revtis, revtis, sizeof(REVT_INFO) * sp->revcnt);
+
+ return 0;
+}
+
+
+static const uint mon_lengths[2][MONS_PER_YEAR]=
+{
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const uint mon_starts[2][MONS_PER_YEAR]=
+{
+ { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 },
+ { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335 }
+};
+
+static const uint year_lengths[2]=
+{
+ DAYS_PER_NYEAR, DAYS_PER_LYEAR
+};
+
+#define LEAPS_THRU_END_OF(y) ((y) / 4 - (y) / 100 + (y) / 400)
+
+
+/*
+ Converts time from my_time_t representation (seconds in UTC since Epoch)
+ to broken down representation using given local time zone offset.
+
+ SYNOPSIS
+ sec_to_TIME()
+ tmp - pointer to structure for broken down representation
+ t - my_time_t value to be converted
+ offset - local time zone offset
+
+ DESCRIPTION
+ Convert my_time_t with offset to TIME struct. Differs from timesub
+ (from elsie code) because doesn't contain any leap correction and
+ TM_GMTOFF and is_dst setting and contains some MySQL specific
+ initialization. Funny but with removing of these we almost have
+ glibc's offtime function.
+*/
+static void
+sec_to_TIME(TIME * tmp, my_time_t t, long offset)
+{
+ long days;
+ long rem;
+ int y;
+ int yleap;
+ const uint *ip;
+
+ days= t / SECS_PER_DAY;
+ rem= t % SECS_PER_DAY;
+
+ /*
+ We do this as separate step after dividing t, because this
+ allows us handle times near my_time_t bounds without overflows.
+ */
+ rem+= offset;
+ while (rem < 0)
+ {
+ rem+= SECS_PER_DAY;
+ days--;
+ }
+ while (rem >= SECS_PER_DAY)
+ {
+ rem -= SECS_PER_DAY;
+ days++;
+ }
+ tmp->hour= (uint)(rem / SECS_PER_HOUR);
+ rem= rem % SECS_PER_HOUR;
+ tmp->minute= (uint)(rem / SECS_PER_MIN);
+ /*
+ A positive leap second requires a special
+ representation. This uses "... ??:59:60" et seq.
+ */
+ tmp->second= (uint)(rem % SECS_PER_MIN);
+
+ y= EPOCH_YEAR;
+ while (days < 0 || days >= (long)year_lengths[yleap= isleap(y)])
+ {
+ int newy;
+
+ newy= y + days / DAYS_PER_NYEAR;
+ if (days < 0)
+ newy--;
+ days-= (newy - y) * DAYS_PER_NYEAR +
+ LEAPS_THRU_END_OF(newy - 1) -
+ LEAPS_THRU_END_OF(y - 1);
+ y= newy;
+ }
+ tmp->year= y;
+
+ ip= mon_lengths[yleap];
+ for (tmp->month= 0; days >= (long) ip[tmp->month]; tmp->month++)
+ days= days - (long) ip[tmp->month];
+ tmp->month++;
+ tmp->day= (uint)(days + 1);
+
+ /* filling MySQL specific TIME members */
+ tmp->neg= 0; tmp->second_part= 0;
+ tmp->time_type= TIMESTAMP_DATETIME;
+}
+
+
+/*
+ Find time range wich contains given my_time_t value
+
+ SYNOPSIS
+ find_time_range()
+ t - my_time_t value for which we looking for range
+ range_boundaries - sorted array of range starts.
+ higher_bound - number of ranges
+
+ DESCRIPTION
+ Performs binary search for range which contains given my_time_t value.
+ It has sense if number of ranges is greater than zero and my_time_t value
+ is greater or equal than beginning of first range. It also assumes that
+ t belongs to some range specified or end of last is MY_TIME_T_MAX.
+
+ With this localtime_r on real data may takes less time than with linear
+ search (I've seen 30% speed up).
+
+ RETURN VALUE
+ Index of range to which t belongs
+*/
+static uint
+find_time_range(my_time_t t, const my_time_t *range_boundaries,
+ uint higher_bound)
+{
+ uint i, lower_bound= 0;
+
+ /*
+ Function will work without this assertion but result would be meaningless.
+ */
+ DBUG_ASSERT(higher_bound > 0 && t >= range_boundaries[0]);
+
+ /*
+ Do binary search for minimal interval which contain t. We preserve:
+ range_boundaries[lower_bound] <= t < range_boundaries[higher_bound]
+ invariant and decrease this higher_bound - lower_bound gap twice
+ times on each step.
+ */
+
+ while (higher_bound - lower_bound > 1)
+ {
+ i= (lower_bound + higher_bound) >> 1;
+ if (range_boundaries[i] <= t)
+ lower_bound= i;
+ else
+ higher_bound= i;
+ }
+ return lower_bound;
+}
+
+/*
+ Find local time transition for given my_time_t.
+
+ SYNOPSIS
+ find_transition_type()
+ t - my_time_t value to be converted
+ sp - pointer to struct with time zone description
+
+ RETURN VALUE
+ Pointer to structure in time zone description describing
+ local time type for given my_time_t.
+*/
+static
+const TRAN_TYPE_INFO *
+find_transition_type(my_time_t t, const TIME_ZONE_INFO *sp)
+{
+ if (unlikely(sp->timecnt == 0 || t < sp->ats[0]))
+ {
+ /*
+ If we have not any transitions or t is before first transition let
+ us use fallback time type.
+ */
+ return sp->fallback_tti;
+ }
+
+ /*
+ Do binary search for minimal interval between transitions which
+ contain t. With this localtime_r on real data may takes less
+ time than with linear search (I've seen 30% speed up).
+ */
+ return &(sp->ttis[sp->types[find_time_range(t, sp->ats, sp->timecnt)]]);
+}
+
+
+/*
+ Converts time in my_time_t representation (seconds in UTC since Epoch) to
+ broken down TIME representation in local time zone.
+
+ SYNOPSIS
+ gmt_sec_to_TIME()
+ tmp - pointer to structure for broken down represenatation
+ sec_in_utc - my_time_t value to be converted
+ sp - pointer to struct with time zone description
+
+ TODO
+ We can improve this function by creating joined array of transitions and
+ leap corrections. This will require adding extra field to TRAN_TYPE_INFO
+ for storing number of "extra" seconds to minute occured due to correction
+ (60th and 61st second, look how we calculate them as "hit" in this
+ function).
+ Under realistic assumptions about frequency of transitions the same array
+ can be used fot TIME -> my_time_t conversion. For this we need to
+ implement tweaked binary search which will take into account that some
+ TIME has two matching my_time_t ranges and some of them have none.
+*/
+static void
+gmt_sec_to_TIME(TIME *tmp, my_time_t sec_in_utc, const TIME_ZONE_INFO *sp)
+{
+ const TRAN_TYPE_INFO *ttisp;
+ const LS_INFO *lp;
+ long corr= 0;
+ int hit= 0;
+ int i;
+
+ /*
+ Find proper transition (and its local time type) for our sec_in_utc value.
+ Funny but again by separating this step in function we receive code
+ which very close to glibc's code. No wonder since they obviously use
+ the same base and all steps are sensible.
+ */
+ ttisp= find_transition_type(sec_in_utc, sp);
+
+ /*
+ Let us find leap correction for our sec_in_utc value and number of extra
+ secs to add to this minute.
+ This loop is rarely used because most users will use time zones without
+ leap seconds, and even in case when we have such time zone there won't
+ be many iterations (we have about 22 corrections at this moment (2004)).
+ */
+ for ( i= sp->leapcnt; i-- > 0; )
+ {
+ lp= &sp->lsis[i];
+ if (sec_in_utc >= lp->ls_trans)
+ {
+ if (sec_in_utc == lp->ls_trans)
+ {
+ hit= ((i == 0 && lp->ls_corr > 0) ||
+ lp->ls_corr > sp->lsis[i - 1].ls_corr);
+ if (hit)
+ {
+ while (i > 0 &&
+ sp->lsis[i].ls_trans == sp->lsis[i - 1].ls_trans + 1 &&
+ sp->lsis[i].ls_corr == sp->lsis[i - 1].ls_corr + 1)
+ {
+ hit++;
+ i--;
+ }
+ }
+ }
+ corr= lp->ls_corr;
+ break;
+ }
+ }
+
+ sec_to_TIME(tmp, sec_in_utc, ttisp->tt_gmtoff - corr);
+
+ tmp->second+= hit;
+}
+
+
+/*
+ Converts local time in broken down representation to local
+ time zone analog of my_time_t represenation.
+
+ SYNOPSIS
+ sec_since_epoch()
+ year, mon, mday, hour, min, sec - broken down representation.
+
+ DESCRIPTION
+ Converts time in broken down representation to my_time_t representation
+ ignoring time zone. Note that we cannot convert back some valid _local_
+ times near ends of my_time_t range because of my_time_t overflow. But we
+ ignore this fact now since MySQL will never pass such argument.
+
+ RETURN VALUE
+ Seconds since epoch time representation.
+*/
+static my_time_t
+sec_since_epoch(int year, int mon, int mday, int hour, int min ,int sec)
+{
+#ifndef WE_WANT_TO_HANDLE_UNORMALIZED_DATES
+ /*
+ It turns out that only whenever month is normalized or unnormalized
+ plays role.
+ */
+ DBUG_ASSERT(mon > 0 && mon < 13);
+ long days= year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
+ LEAPS_THRU_END_OF(year - 1) -
+ LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
+ days+= mon_starts[isleap(year)][mon - 1];
+#else
+ long norm_month= (mon - 1) % MONS_PER_YEAR;
+ long a_year= year + (mon - 1)/MONS_PER_YEAR - (int)(norm_month < 0);
+ long days= a_year * DAYS_PER_NYEAR - EPOCH_YEAR * DAYS_PER_NYEAR +
+ LEAPS_THRU_END_OF(a_year - 1) -
+ LEAPS_THRU_END_OF(EPOCH_YEAR - 1);
+ days+= mon_starts[isleap(a_year)]
+ [norm_month + (norm_month < 0 ? MONS_PER_YEAR : 0)];
+#endif
+ days+= mday - 1;
+
+ return ((days * HOURS_PER_DAY + hour) * MINS_PER_HOUR + min) *
+ SECS_PER_MIN + sec;
+}
+
+
+/*
+ Converts local time in broken down TIME representation to my_time_t
+ representation.
+
+ SYNOPSIS
+ TIME_to_gmt_sec()
+ t - pointer to structure for broken down represenatation
+ sp - pointer to struct with time zone description
+ in_dst_time_gap - pointer to bool which is set to true if datetime
+ value passed doesn't really exist (i.e. falls into
+ spring time-gap) and is not touched otherwise.
+
+ DESCRIPTION
+ This is mktime analog for MySQL. It is essentially different
+ from mktime (or hypotetical my_mktime) because:
+ - It has no idea about tm_isdst member so if it
+ has two answers it will give the smaller one
+ - If we are in spring time gap then it will return
+ beginning of the gap
+ - It can give wrong results near the ends of my_time_t due to
+ overflows, but we are safe since in MySQL we will never
+ call this function for such dates (its restriction for year
+ between 1970 and 2038 gives us several days of reserve).
+ - By default it doesn't support un-normalized input. But if
+ sec_since_epoch() function supports un-normalized dates
+ then this function should handle un-normalized input right,
+ altough it won't normalize structure TIME.
+
+ Traditional approach to problem of conversion from broken down
+ representation to time_t is iterative. Both elsie's and glibc
+ implementation try to guess what time_t value should correspond to
+ this broken-down value. They perform localtime_r function on their
+ guessed value and then calculate the difference and try to improve
+ their guess. Elsie's code guesses time_t value in bit by bit manner,
+ Glibc's code tries to add difference between broken-down value
+ corresponding to guess and target broken-down value to current guess.
+ It also uses caching of last found correction... So Glibc's approach
+ is essentially faster but introduces some undetermenism (in case if
+ is_dst member of broken-down representation (tm struct) is not known
+ and we have two possible answers).
+
+ We use completely different approach. It is better since it is both
+ faster than iterative implementations and fully determenistic. If you
+ look at my_time_t to TIME conversion then you'll find that it consist
+ of two steps:
+ The first is calculating shifted my_time_t value and the second - TIME
+ calculation from shifted my_time_t value (well it is a bit simplified
+ picture). The part in which we are interested in is my_time_t -> shifted
+ my_time_t conversion. It is piecewise linear function which is defined
+ by combination of transition times as break points and times offset
+ as changing function parameter. The possible inverse function for this
+ converison would be ambiguos but with MySQL's restrictions we can use
+ some function which is the same as inverse function on unambigiuos
+ ranges and coincides with one of branches of inverse function in
+ other ranges. Thus we just need to build table which will determine
+ this shifted my_time_t -> my_time_t conversion similar to existing
+ (my_time_t -> shifted my_time_t table). We do this in
+ prepare_tz_info function.
+
+ TODO
+ If we can even more improve this function. For doing this we will need to
+ build joined map of transitions and leap corrections for gmt_sec_to_TIME()
+ function (similar to revts/revtis). Under realistic assumptions about
+ frequency of transitions we can use the same array for TIME_to_gmt_sec().
+ We need to implement special version of binary search for this. Such step
+ will be beneficial to CPU cache since we will decrease data-set used for
+ conversion twice.
+
+ RETURN VALUE
+ Seconds in UTC since Epoch.
+ 0 in case of error.
+*/
+static my_time_t
+TIME_to_gmt_sec(const TIME *t, const TIME_ZONE_INFO *sp, bool *in_dst_time_gap)
+{
+ my_time_t local_t;
+ uint saved_seconds;
+ uint i;
+
+ DBUG_ENTER("TIME_to_gmt_sec");
+
+ /* We need this for correct leap seconds handling */
+ if (t->second < SECS_PER_MIN)
+ saved_seconds= 0;
+ else
+ saved_seconds= t->second;
+
+ /*
+ NOTE If we want to convert full my_time_t range without MySQL
+ restrictions we should catch overflow here somehow.
+ */
+
+ local_t= sec_since_epoch(t->year, t->month, t->day,
+ t->hour, t->minute,
+ saved_seconds ? 0 : t->second);
+
+ /* We have at least one range */
+ DBUG_ASSERT(sp->revcnt >= 1);
+
+ if (local_t < sp->revts[0] || local_t > sp->revts[sp->revcnt])
+ {
+ /*
+ This means that source time can't be represented as my_time_t due to
+ limited my_time_t range.
+ */
+ DBUG_RETURN(0);
+ }
+
+ /* binary search for our range */
+ i= find_time_range(local_t, sp->revts, sp->revcnt);
+
+ if (sp->revtis[i].rt_type)
+ {
+ /*
+ Oops! We are in spring time gap.
+ May be we should return error here?
+ Now we are returning my_time_t value corresponding to the
+ beginning of the gap.
+ */
+ *in_dst_time_gap= 1;
+ DBUG_RETURN(sp->revts[i] - sp->revtis[i].rt_offset + saved_seconds);
+ }
+ else
+ DBUG_RETURN(local_t - sp->revtis[i].rt_offset + saved_seconds);
+}
+
+
+/*
+ End of elsie derived code.
+*/
+
+
+#if !defined(TESTTIME) && !defined(TZINFO2SQL)
+
+/*
+ String with names of SYSTEM time zone.
+*/
+static const String tz_SYSTEM_name("SYSTEM", 6, &my_charset_latin1);
+
+
+/*
+ Instance of this class represents local time zone used on this system
+ (specified by TZ environment variable or via any other system mechanism).
+ It uses system functions (localtime_r, my_system_gmt_sec) for conversion
+ and is always available. Because of this it is used by default - if there
+ were no explicit time zone specified. On the other hand because of this
+ conversion methods provided by this class is significantly slower and
+ possibly less multi-threaded-friendly than corresponding Time_zone_db
+ methods so the latter should be preffered there it is possible.
+*/
+class Time_zone_system : public Time_zone
+{
+public:
+ virtual my_time_t TIME_to_gmt_sec(const TIME *t,
+ bool *in_dst_time_gap) const;
+ virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const;
+ virtual const String * get_name() const;
+};
+
+
+/*
+ Converts local time in system time zone in TIME representation
+ to its my_time_t representation.
+
+ SYNOPSIS
+ TIME_to_gmt_sec()
+ t - pointer to TIME structure with local time in
+ broken-down representation.
+ in_dst_time_gap - pointer to bool which is set to true if datetime
+ value passed doesn't really exist (i.e. falls into
+ spring time-gap) and is not touched otherwise.
+
+ DESCRIPTION
+ This method uses system function (localtime_r()) for conversion
+ local time in system time zone in TIME structure to its my_time_t
+ representation. Unlike the same function for Time_zone_db class
+ it it won't handle unnormalized input properly. Still it will
+ return lowest possible my_time_t in case of ambiguity or if we
+ provide time corresponding to the time-gap.
+
+ You should call init_time() function before using this function.
+
+ RETURN VALUE
+ Corresponding my_time_t value or 0 in case of error
+*/
+my_time_t
+Time_zone_system::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const
+{
+ long not_used;
+ return my_system_gmt_sec(t, &not_used, in_dst_time_gap);
+}
+
+
+/*
+ Converts time from UTC seconds since Epoch (my_time_t) representation
+ to system local time zone broken-down representation.
+
+ SYNOPSIS
+ gmt_sec_to_TIME()
+ tmp - pointer to TIME structure to fill-in
+ t - my_time_t value to be converted
+
+ NOTE
+ We assume that value passed to this function will fit into time_t range
+ supported by localtime_r. This conversion is putting restriction on
+ TIMESTAMP range in MySQL. If we can get rid of SYSTEM time zone at least
+ for interaction with client then we can extend TIMESTAMP range down to
+ the 1902 easily.
+*/
+void
+Time_zone_system::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const
+{
+ struct tm tmp_tm;
+ time_t tmp_t= (time_t)t;
+
+ localtime_r(&tmp_t, &tmp_tm);
+ localtime_to_TIME(tmp, &tmp_tm);
+ tmp->time_type= TIMESTAMP_DATETIME;
+}
+
+
+/*
+ Get name of time zone
+
+ SYNOPSIS
+ get_name()
+
+ RETURN VALUE
+ Name of time zone as String
+*/
+const String *
+Time_zone_system::get_name() const
+{
+ return &tz_SYSTEM_name;
+}
+
+
+/*
+ Instance of this class represents UTC time zone. It uses system gmtime_r
+ function for conversions and is always available. It is used only for
+ my_time_t -> TIME conversions in various UTC_... functions, it is not
+ intended for TIME -> my_time_t conversions and shouldn't be exposed to user.
+*/
+class Time_zone_utc : public Time_zone
+{
+public:
+ virtual my_time_t TIME_to_gmt_sec(const TIME *t,
+ bool *in_dst_time_gap) const;
+ virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const;
+ virtual const String * get_name() const;
+};
+
+
+/*
+ Convert UTC time from TIME representation to its my_time_t representation.
+
+ SYNOPSIS
+ TIME_to_gmt_sec()
+ t - pointer to TIME structure with local time
+ in broken-down representation.
+ in_dst_time_gap - pointer to bool which is set to true if datetime
+ value passed doesn't really exist (i.e. falls into
+ spring time-gap) and is not touched otherwise.
+
+ DESCRIPTION
+ Since Time_zone_utc is used only internally for my_time_t -> TIME
+ conversions, this function of Time_zone interface is not implemented for
+ this class and should not be called.
+
+ RETURN VALUE
+ 0
+*/
+my_time_t
+Time_zone_utc::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const
+{
+ /* Should be never called */
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+/*
+ Converts time from UTC seconds since Epoch (my_time_t) representation
+ to broken-down representation (also in UTC).
+
+ SYNOPSIS
+ gmt_sec_to_TIME()
+ tmp - pointer to TIME structure to fill-in
+ t - my_time_t value to be converted
+
+ NOTE
+ See note for apropriate Time_zone_system method.
+*/
+void
+Time_zone_utc::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const
+{
+ struct tm tmp_tm;
+ time_t tmp_t= (time_t)t;
+ gmtime_r(&tmp_t, &tmp_tm);
+ localtime_to_TIME(tmp, &tmp_tm);
+ tmp->time_type= TIMESTAMP_DATETIME;
+}
+
+
+/*
+ Get name of time zone
+
+ SYNOPSIS
+ get_name()
+
+ DESCRIPTION
+ Since Time_zone_utc is used only internally by SQL's UTC_* functions it
+ is not accessible directly, and hence this function of Time_zone
+ interface is not implemented for this class and should not be called.
+
+ RETURN VALUE
+ 0
+*/
+const String *
+Time_zone_utc::get_name() const
+{
+ /* Should be never called */
+ DBUG_ASSERT(0);
+ return 0;
+}
+
+
+/*
+ Instance of this class represents some time zone which is
+ described in mysql.time_zone family of tables.
+*/
+class Time_zone_db : public Time_zone
+{
+public:
+ Time_zone_db(TIME_ZONE_INFO *tz_info_arg, const String * tz_name_arg);
+ virtual my_time_t TIME_to_gmt_sec(const TIME *t,
+ bool *in_dst_time_gap) const;
+ virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const;
+ virtual const String * get_name() const;
+private:
+ TIME_ZONE_INFO *tz_info;
+ const String *tz_name;
+};
+
+
+/*
+ Initializes object representing time zone described by mysql.time_zone
+ tables.
+
+ SYNOPSIS
+ Time_zone_db()
+ tz_info_arg - pointer to TIME_ZONE_INFO structure which is filled
+ according to db or other time zone description
+ (for example by my_tz_init()).
+ Several Time_zone_db instances can share one
+ TIME_ZONE_INFO structure.
+ tz_name_arg - name of time zone.
+*/
+Time_zone_db::Time_zone_db(TIME_ZONE_INFO *tz_info_arg,
+ const String *tz_name_arg):
+ tz_info(tz_info_arg), tz_name(tz_name_arg)
+{
+}
+
+
+/*
+ Converts local time in time zone described from TIME
+ representation to its my_time_t representation.
+
+ SYNOPSIS
+ TIME_to_gmt_sec()
+ t - pointer to TIME structure with local time
+ in broken-down representation.
+ in_dst_time_gap - pointer to bool which is set to true if datetime
+ value passed doesn't really exist (i.e. falls into
+ spring time-gap) and is not touched otherwise.
+
+ DESCRIPTION
+ Please see ::TIME_to_gmt_sec for function description and
+ parameter restrictions.
+
+ RETURN VALUE
+ Corresponding my_time_t value or 0 in case of error
+*/
+my_time_t
+Time_zone_db::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const
+{
+ return ::TIME_to_gmt_sec(t, tz_info, in_dst_time_gap);
+}
+
+
+/*
+ Converts time from UTC seconds since Epoch (my_time_t) representation
+ to local time zone described in broken-down representation.
+
+ SYNOPSIS
+ gmt_sec_to_TIME()
+ tmp - pointer to TIME structure to fill-in
+ t - my_time_t value to be converted
+*/
+void
+Time_zone_db::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const
+{
+ ::gmt_sec_to_TIME(tmp, t, tz_info);
+}
+
+
+/*
+ Get name of time zone
+
+ SYNOPSIS
+ get_name()
+
+ RETURN VALUE
+ Name of time zone as ASCIIZ-string
+*/
+const String *
+Time_zone_db::get_name() const
+{
+ return tz_name;
+}
+
+
+/*
+ Instance of this class represents time zone which
+ was specified as offset from UTC.
+*/
+class Time_zone_offset : public Time_zone
+{
+public:
+ Time_zone_offset(long tz_offset_arg);
+ virtual my_time_t TIME_to_gmt_sec(const TIME *t,
+ bool *in_dst_time_gap) const;
+ virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const;
+ virtual const String * get_name() const;
+ /*
+ This have to be public because we want to be able to access it from
+ my_offset_tzs_get_key() function
+ */
+ long offset;
+private:
+ /* Extra reserve because of snprintf */
+ char name_buff[7+16];
+ String name;
+};
+
+
+/*
+ Initializes object representing time zone described by its offset from UTC.
+
+ SYNOPSIS
+ Time_zone_offset()
+ tz_offset_arg - offset from UTC in seconds.
+ Positive for direction to east.
+*/
+Time_zone_offset::Time_zone_offset(long tz_offset_arg):
+ offset(tz_offset_arg)
+{
+ uint hours= abs(offset / SECS_PER_HOUR);
+ uint minutes= abs(offset % SECS_PER_HOUR / SECS_PER_MIN);
+ ulong length= my_snprintf(name_buff, sizeof(name_buff), "%s%02d:%02d",
+ (offset>=0) ? "+" : "-", hours, minutes);
+ name.set(name_buff, length, &my_charset_latin1);
+}
+
+
+/*
+ Converts local time in time zone described as offset from UTC
+ from TIME representation to its my_time_t representation.
+
+ SYNOPSIS
+ TIME_to_gmt_sec()
+ t - pointer to TIME structure with local time
+ in broken-down representation.
+ in_dst_time_gap - pointer to bool which should be set to true if
+ datetime value passed doesn't really exist
+ (i.e. falls into spring time-gap) and is not
+ touched otherwise.
+ It is not really used in this class.
+
+ RETURN VALUE
+ Corresponding my_time_t value or 0 in case of error
+*/
+my_time_t
+Time_zone_offset::TIME_to_gmt_sec(const TIME *t, bool *in_dst_time_gap) const
+{
+ return sec_since_epoch(t->year, t->month, t->day,
+ t->hour, t->minute, t->second) -
+ offset;
+}
+
+
+/*
+ Converts time from UTC seconds since Epoch (my_time_t) representation
+ to local time zone described as offset from UTC and in broken-down
+ representation.
+
+ SYNOPSIS
+ gmt_sec_to_TIME()
+ tmp - pointer to TIME structure to fill-in
+ t - my_time_t value to be converted
+*/
+void
+Time_zone_offset::gmt_sec_to_TIME(TIME *tmp, my_time_t t) const
+{
+ sec_to_TIME(tmp, t, offset);
+}
+
+
+/*
+ Get name of time zone
+
+ SYNOPSIS
+ get_name()
+
+ RETURN VALUE
+ Name of time zone as pointer to String object
+*/
+const String *
+Time_zone_offset::get_name() const
+{
+ return &name;
+}
+
+
+static Time_zone_utc tz_UTC;
+static Time_zone_system tz_SYSTEM;
+
+Time_zone *my_tz_UTC= &tz_UTC;
+Time_zone *my_tz_SYSTEM= &tz_SYSTEM;
+
+static HASH tz_names;
+static HASH offset_tzs;
+static MEM_ROOT tz_storage;
+
+/*
+ These mutex protects offset_tzs and tz_storage.
+ These protection needed only when we are trying to set
+ time zone which is specified as offset, and searching for existing
+ time zone in offset_tzs or creating if it didn't existed before in
+ tz_storage. So contention is low.
+*/
+static pthread_mutex_t tz_LOCK;
+
+/*
+ This two static variables are inteded for holding info about leap seconds
+ shared by all time zones.
+*/
+static uint tz_leapcnt= 0;
+static LS_INFO *tz_lsis= 0;
+
+
+typedef struct st_tz_names_entry: public Sql_alloc
+{
+ String name;
+ Time_zone *tz;
+} TZ_NAMES_ENTRY;
+
+
+/*
+ We are going to call both of these functions from C code so
+ they should obey C calling conventions.
+*/
+
+extern "C" byte* my_tz_names_get_key(TZ_NAMES_ENTRY *entry, uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= entry->name.length();
+ return (byte*) entry->name.ptr();
+}
+
+extern "C" byte* my_offset_tzs_get_key(Time_zone_offset *entry, uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length= sizeof(long);
+ return (byte*) &entry->offset;
+}
+
+
+/*
+ Initialize time zone support infrastructure.
+
+ SYNOPSIS
+ my_tz_init()
+ thd - current thread object
+ default_tzname - default time zone or 0 if none.
+ bootstrap - indicates whenever we are in bootstrap mode
+
+ DESCRIPTION
+ This function will init memory structures needed for time zone support,
+ it will register mandatory SYSTEM time zone in them. It will try to open
+ mysql.time_zone_leap_seconds table and and load information which further
+ will be shared among all time zones loaded. It will also try to load
+ information about default time zone. If system tables with time zone
+ descriptions don't exist it won't fail (unless default_tzname is time zone
+ from tables). If bootstrap parameter is true then this routine assumes that
+ we are in bootstrap mode and won't load time zone descriptions unless someone
+ specifies default time zone which is supposedly stored in those tables.
+ It'll also set default time zone if it is specified.
+
+ RETURN VALUES
+ 0 - ok
+ 1 - Error
+*/
+my_bool
+my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap)
+{
+ THD *thd;
+ TABLE_LIST tables;
+ TABLE *table;
+ TABLE *lock_ptr;
+ MYSQL_LOCK *lock;
+ TZ_NAMES_ENTRY *tmp_tzname;
+ my_bool return_val= 1;
+ int res;
+ uint not_used;
+
+ DBUG_ENTER("my_tz_init");
+
+ /*
+ To be able to run this from boot, we allocate a temporary THD
+ */
+ if (!(thd= new THD))
+ DBUG_RETURN(1);
+ thd->store_globals();
+
+ /* Init all memory structures that require explicit destruction */
+ if (hash_init(&tz_names, &my_charset_latin1, 20,
+ 0, 0, (hash_get_key)my_tz_names_get_key, 0, 0))
+ {
+ sql_print_error("Fatal error: OOM while initializing time zones");
+ goto end;
+ }
+ if (hash_init(&offset_tzs, &my_charset_latin1, 26, 0, 0,
+ (hash_get_key)my_offset_tzs_get_key, 0, 0))
+ {
+ sql_print_error("Fatal error: OOM while initializing time zones");
+ hash_free(&tz_names);
+ goto end;
+ }
+ init_alloc_root(&tz_storage, 32 * 1024, 0);
+ VOID(pthread_mutex_init(&tz_LOCK, MY_MUTEX_INIT_FAST));
+
+ /* Add 'SYSTEM' time zone to tz_names hash */
+ if (!(tmp_tzname= new (&tz_storage) TZ_NAMES_ENTRY()))
+ {
+ sql_print_error("Fatal error: OOM while initializing time zones");
+ goto end_with_cleanup;
+ }
+ tmp_tzname->name.set("SYSTEM", 6, &my_charset_latin1);
+ tmp_tzname->tz= my_tz_SYSTEM;
+ if (my_hash_insert(&tz_names, (const byte *)tmp_tzname))
+ {
+ sql_print_error("Fatal error: OOM while initializing time zones");
+ goto end_with_cleanup;
+ }
+
+ if (bootstrap)
+ {
+ /* If we are in bootstrap mode we should not load time zone tables */
+ return_val= 0;
+ goto end_with_setting_default_tz;
+ }
+
+ /*
+ After this point all memory structures are inited and we even can live
+ without time zone description tables. Now try to load information about
+ leap seconds shared by all time zones.
+ */
+
+ thd->db= my_strdup("mysql",MYF(0));
+ thd->db_length= 5; // Safety
+ bzero((char*) &tables,sizeof(tables));
+ tables.alias= tables.real_name= (char*)"time_zone_leap_second";
+ tables.lock_type= TL_READ;
+ tables.db= thd->db;
+
+ if (open_tables(thd, &tables, &not_used))
+ {
+ sql_print_error("Warning: Can't open time zone table: %s "
+ "trying to live without them", thd->net.last_error);
+ /* We will try emulate that everything is ok */
+ return_val= 0;
+ goto end_with_setting_default_tz;
+ }
+
+ lock_ptr= tables.table;
+ if (!(lock= mysql_lock_tables(thd, &lock_ptr, 1)))
+ {
+ sql_print_error("Fatal error: Can't lock time zone table: %s",
+ thd->net.last_error);
+ goto end_with_cleanup;
+ }
+
+
+ /*
+ Now we are going to load leap seconds descriptions that are shared
+ between all time zones that use them. We are using index for getting
+ records in proper order. Since we share the same MEM_ROOT between
+ all time zones we just allocate enough memory for it first.
+ */
+ if (!(tz_lsis= (LS_INFO*) alloc_root(&tz_storage,
+ sizeof(LS_INFO) * TZ_MAX_LEAPS)))
+ {
+ sql_print_error("Fatal error: Out of memory while loading "
+ "mysql.time_zone_leap_second table");
+ goto end_with_unlock;
+ }
+
+ table= tables.table;
+ table->file->index_init(0);
+ tz_leapcnt= 0;
+
+ res= table->file->index_first(table->record[0]);
+
+ while (!res)
+ {
+ if (tz_leapcnt + 1 > TZ_MAX_LEAPS)
+ {
+ sql_print_error("Fatal error: While loading mysql.time_zone_leap_second"
+ " table: too much leaps");
+ table->file->index_end();
+ goto end_with_unlock;
+ }
+
+ tz_lsis[tz_leapcnt].ls_trans= (my_time_t)table->field[0]->val_int();
+ tz_lsis[tz_leapcnt].ls_corr= (long)table->field[1]->val_int();
+
+ tz_leapcnt++;
+
+ DBUG_PRINT("info",
+ ("time_zone_leap_second table: tz_leapcnt=%u tt_time=%lld offset=%ld",
+ tz_leapcnt, (longlong)tz_lsis[tz_leapcnt-1].ls_trans,
+ tz_lsis[tz_leapcnt-1].ls_corr));
+
+ res= table->file->index_next(table->record[0]);
+ }
+
+ table->file->index_end();
+
+ if (res != HA_ERR_END_OF_FILE)
+ {
+ sql_print_error("Fatal error: Error while loading "
+ "mysql.time_zone_leap_second table");
+ goto end_with_unlock;
+ }
+
+ /*
+ Loading of info about leap seconds succeeded
+ */
+
+ return_val= 0;
+
+
+end_with_unlock:
+ mysql_unlock_tables(thd, lock);
+ thd->version--; /* Force close to free memory */
+
+end_with_setting_default_tz:
+ /* If not an error and have default time zone try to load it */
+ if (!return_val && default_tzname)
+ {
+ String tzname(default_tzname, &my_charset_latin1);
+ if (!(global_system_variables.time_zone= my_tz_find(thd, &tzname)))
+ {
+ sql_print_error("Fatal error: Illegal or unknown default time zone '%s'",
+ default_tzname);
+ return_val= 1;
+ }
+ }
+
+end_with_cleanup:
+
+ /* if there were error free time zone describing structs */
+ if (return_val)
+ my_tz_free();
+end:
+ close_thread_tables(thd);
+ delete thd;
+ if (org_thd)
+ org_thd->store_globals(); /* purecov: inspected */
+ else
+ {
+ /* Remember that we don't have a THD */
+ my_pthread_setspecific_ptr(THR_THD, 0);
+ my_pthread_setspecific_ptr(THR_MALLOC, 0);
+ }
+ DBUG_RETURN(return_val);
+}
+
+
+/*
+ Free resources used by time zone support infrastructure.
+
+ SYNOPSIS
+ my_tz_free()
+*/
+void my_tz_free()
+{
+ VOID(pthread_mutex_destroy(&tz_LOCK));
+ hash_free(&offset_tzs);
+ hash_free(&tz_names);
+ free_root(&tz_storage, MYF(0));
+}
+
+
+/*
+ Load time zone description from system tables.
+
+ SYNOPSIS
+ tz_load_from_db()
+ thd - current thread object
+ tz_name - name of time zone that should be loaded.
+
+ DESCRIPTION
+ This function will try to open system tables describing time zones
+ and to load information about time zone specified. It will also update
+ information in hash used for time zones lookup.
+
+ RETURN VALUES
+ Returns pointer to newly created Time_zone object or 0 in case of error.
+
+*/
+static Time_zone*
+tz_load_from_db(THD *thd, const String *tz_name)
+{
+ TABLE_LIST tables[4];
+ TABLE *table= 0;
+ TABLE *lock_ptr[4];
+ MYSQL_LOCK *lock;
+ char system_db_name[]= "mysql";
+ char *db_save;
+ uint db_length_save;
+ TIME_ZONE_INFO *tz_info;
+ TZ_NAMES_ENTRY *tmp_tzname;
+ Time_zone *return_val= 0;
+ int res;
+ uint tzid, ttid;
+ bool uses_leap_seconds;
+ my_time_t ttime;
+ char buff[MAX_FIELD_WIDTH];
+ String abbr(buff, sizeof(buff), &my_charset_latin1);
+ char *alloc_buff, *tz_name_buff;
+ /*
+ Temporary arrays that are used for loading of data for filling
+ TIME_ZONE_INFO structure
+ */
+ my_time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+ TRAN_TYPE_INFO ttis[TZ_MAX_TYPES];
+#ifdef ABBR_ARE_USED
+ char chars[max(TZ_MAX_CHARS + 1, (2 * (MY_TZNAME_MAX + 1)))];
+#endif
+ uint not_used;
+
+ DBUG_ENTER("tz_load_from_db");
+
+
+ /* Prepare tz_info for loading also let us make copy of time zone name */
+ if (!(alloc_buff= alloc_root(&tz_storage, sizeof(TIME_ZONE_INFO) +
+ tz_name->length() + 1)))
+ {
+ sql_print_error("Error: Out of memory while loading time zone "
+ "description");
+ return 0;
+ }
+ tz_info= (TIME_ZONE_INFO *)alloc_buff;
+ bzero(tz_info, sizeof(TIME_ZONE_INFO));
+ tz_name_buff= alloc_buff + sizeof(TIME_ZONE_INFO);
+ /*
+ By writing zero to the end we guarantee that we can call ptr()
+ instead of c_ptr() for time zone name.
+ */
+ strmake(tz_name_buff, tz_name->ptr(), tz_name->length());
+
+ /*
+ Open and lock time zone description tables
+ */
+ db_save= thd->db;
+ db_length_save= thd->db_length;
+ thd->db= system_db_name;
+ thd->db_length= 5;
+
+ bzero((char*) &tables,sizeof(tables));
+ tables[0].alias= tables[0].real_name= (char*)"time_zone_name";
+ tables[1].alias= tables[1].real_name= (char*)"time_zone";
+ tables[2].alias= tables[2].real_name= (char*)"time_zone_transition";
+ tables[3].alias= tables[3].real_name= (char*)"time_zone_transition_type";
+ tables[0].next= tables+1;
+ tables[1].next= tables+2;
+ tables[2].next= tables+3;
+ tables[0].lock_type= tables[1].lock_type= tables[2].lock_type=
+ tables[3].lock_type= TL_READ;
+ tables[0].db= tables[1].db= tables[2].db= tables[3].db= thd->db;
+ if (open_tables(thd, tables, &not_used))
+ {
+ sql_print_error("Error: Can't open time zone tables: %s",
+ thd->net.last_error);
+ goto end;
+ }
+
+ lock_ptr[0]= tables[0].table;
+ lock_ptr[1]= tables[1].table;
+ lock_ptr[2]= tables[2].table;
+ lock_ptr[3]= tables[3].table;
+ if (!(lock= mysql_lock_tables(thd, lock_ptr, 4)))
+ {
+ sql_print_error("Error: Can't lock time zone tables: %s",
+ thd->net.last_error);
+ goto end_with_close;
+ }
+
+ /*
+ Let us find out time zone id by its name (there is only one index
+ and it is specifically for this purpose).
+ */
+ table= tables[0].table;
+
+ table->field[0]->store(tz_name->ptr(), tz_name->length(), &my_charset_latin1);
+ table->file->index_init(0);
+
+ if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
+ 0, HA_READ_KEY_EXACT))
+ {
+ sql_print_error("Error: Can't find description of time zone.");
+ goto end_with_unlock;
+ }
+
+ tzid= table->field[1]->val_int();
+
+ table->file->index_end();
+
+ /*
+ Now we need to lookup record in mysql.time_zone table in order to
+ understand whenever this timezone uses leap seconds (again we are
+ using the only index in this table).
+ */
+ table= tables[1].table;
+ table->field[0]->store((longlong)tzid);
+ table->file->index_init(0);
+
+ if (table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
+ 0, HA_READ_KEY_EXACT))
+ {
+ sql_print_error("Error: Can't find description of time zone.");
+ goto end_with_unlock;
+ }
+
+ /* If Uses_leap_seconds == 'Y' */
+ if (table->field[1]->val_int() == 1)
+ {
+ tz_info->leapcnt= tz_leapcnt;
+ tz_info->lsis= tz_lsis;
+ }
+
+ table->file->index_end();
+
+ /*
+ Now we will iterate through records for out time zone in
+ mysql.time_zone_transition_type table. Because we want records
+ only for our time zone guess what are we doing?
+ Right - using special index.
+ */
+ table= tables[3].table;
+ table->field[0]->store((longlong)tzid);
+ table->file->index_init(0);
+
+ // FIXME Is there any better approach than explicitly specifying 4 ???
+ res= table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
+ 4, HA_READ_KEY_EXACT);
+ while (!res)
+ {
+ ttid= table->field[1]->val_int();
+
+ if (ttid > TZ_MAX_TYPES)
+ {
+ sql_print_error("Error while loading time zone description from "
+ "mysql.time_zone_transition_type table: too big "
+ "transition type id");
+ goto end_with_unlock;
+ }
+
+ ttis[ttid].tt_gmtoff= table->field[2]->val_int();
+ ttis[ttid].tt_isdst= (table->field[3]->val_int() > 0);
+
+#ifdef ABBR_ARE_USED
+ // FIXME should we do something with duplicates here ?
+ table->field[4]->val_str(&abbr, &abbr);
+ if (tz_info->charcnt + abbr.length() + 1 > sizeof(chars))
+ {
+ sql_print_error("Error while loading time zone description from "
+ "mysql.time_zone_transition_type table: not enough "
+ "room for abbreviations");
+ goto end_with_unlock;
+ }
+ ttis[ttid].tt_abbrind= tz_info->charcnt;
+ memcpy(chars + tz_info->charcnt, abbr.ptr(), abbr.length());
+ tz_info->charcnt+= abbr.length();
+ chars[tz_info->charcnt]= 0;
+ tz_info->charcnt++;
+
+ DBUG_PRINT("info",
+ ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
+ "abbr='%s' tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff,
+ chars + ttis[ttid].tt_abbrind, ttis[ttid].tt_isdst));
+#else
+ DBUG_PRINT("info",
+ ("time_zone_transition_type table: tz_id=%u tt_id=%u tt_gmtoff=%ld "
+ "tt_isdst=%u", tzid, ttid, ttis[ttid].tt_gmtoff, ttis[ttid].tt_isdst));
+#endif
+
+ /* ttid is increasing because we are reading using index */
+ DBUG_ASSERT(ttid >= tz_info->typecnt);
+
+ tz_info->typecnt= ttid + 1;
+
+ res= table->file->index_next_same(table->record[0],
+ (byte*)table->field[0]->ptr, 4);
+ }
+
+ if (res != HA_ERR_END_OF_FILE)
+ {
+ sql_print_error("Error while loading time zone description from "
+ "mysql.time_zone_transition_type table");
+ goto end_with_unlock;
+ }
+
+ table->file->index_end();
+
+
+ /*
+ At last we are doing the same thing for records in
+ mysql.time_zone_transition table. Here we additionaly need records
+ in ascending order by index scan also satisfies us.
+ */
+ table= tables[2].table;
+ table->field[0]->store((longlong)tzid);
+ table->file->index_init(0);
+
+ // FIXME Is there any better approach than explicitly specifying 4 ???
+ res= table->file->index_read(table->record[0], (byte*)table->field[0]->ptr,
+ 4, HA_READ_KEY_EXACT);
+ while (!res)
+ {
+ ttime= (my_time_t)table->field[1]->val_int();
+ ttid= (uint)table->field[2]->val_int();
+
+ if (tz_info->timecnt + 1 > TZ_MAX_TIMES)
+ {
+ sql_print_error("Error while loading time zone description from "
+ "mysql.time_zone_transition table: "
+ "too much transitions");
+ goto end_with_unlock;
+ }
+ if (ttid + 1 > tz_info->typecnt)
+ {
+ sql_print_error("Error while loading time zone description from "
+ "mysql.time_zone_transition table: "
+ "bad transition type id");
+ goto end_with_unlock;
+ }
+
+ ats[tz_info->timecnt]= ttime;
+ types[tz_info->timecnt]= ttid;
+ tz_info->timecnt++;
+
+ DBUG_PRINT("info",
+ ("time_zone_transition table: tz_id=%u tt_time=%lld tt_id=%u",
+ tzid, (longlong)ttime, ttid));
+
+ res= table->file->index_next_same(table->record[0],
+ (byte*)table->field[0]->ptr, 4);
+ }
+
+ /*
+ We have to allow HA_ERR_KEY_NOT_FOUND because some time zones
+ for example UTC have no transitons.
+ */
+ if (res != HA_ERR_END_OF_FILE && res != HA_ERR_KEY_NOT_FOUND)
+ {
+ sql_print_error("Error while loading time zone description from "
+ "mysql.time_zone_transition table");
+ goto end_with_unlock;
+ }
+
+ table->file->index_end();
+ table= 0;
+
+ /*
+ Now we will allocate memory and init TIME_ZONE_INFO structure.
+ */
+ if (!(alloc_buff= alloc_root(&tz_storage,
+ ALIGN_SIZE(sizeof(my_time_t) *
+ tz_info->timecnt) +
+ ALIGN_SIZE(tz_info->timecnt) +
+#ifdef ABBR_ARE_USED
+ ALIGN_SIZE(tz_info->charcnt) +
+#endif
+ sizeof(TRAN_TYPE_INFO) * tz_info->typecnt)))
+ {
+ sql_print_error("Error: Out of memory while loading time zone "
+ "description");
+ goto end_with_unlock;
+ }
+
+
+ tz_info->ats= (my_time_t *)alloc_buff;
+ memcpy(tz_info->ats, ats, tz_info->timecnt * sizeof(my_time_t));
+ alloc_buff+= ALIGN_SIZE(sizeof(my_time_t) * tz_info->timecnt);
+ tz_info->types= (unsigned char *)alloc_buff;
+ memcpy(tz_info->types, types, tz_info->timecnt);
+ alloc_buff+= ALIGN_SIZE(tz_info->timecnt);
+#ifdef ABBR_ARE_USED
+ tz_info->chars= alloc_buff;
+ memcpy(tz_info->chars, chars, tz_info->charcnt);
+ alloc_buff+= ALIGN_SIZE(tz_info->charcnt);
+#endif
+ tz_info->ttis= (TRAN_TYPE_INFO *)alloc_buff;
+ memcpy(tz_info->ttis, ttis, tz_info->typecnt * sizeof(TRAN_TYPE_INFO));
+
+ /*
+ Let us check how correct our time zone description and build
+ reversed map. We don't check for tz->timecnt < 1 since it ok for GMT.
+ */
+ if (tz_info->typecnt < 1)
+ {
+ sql_print_error("Error: loading time zone without transition types");
+ goto end_with_unlock;
+ }
+ if (prepare_tz_info(tz_info, &tz_storage))
+ {
+ sql_print_error("Error: Unable to build mktime map for time zone");
+ goto end_with_unlock;
+ }
+
+
+ if (!(tmp_tzname= new (&tz_storage) TZ_NAMES_ENTRY()) ||
+ !(tmp_tzname->tz= new (&tz_storage) Time_zone_db(tz_info,
+ &(tmp_tzname->name))) ||
+ (tmp_tzname->name.set(tz_name_buff, tz_name->length(),
+ &my_charset_latin1),
+ my_hash_insert(&tz_names, (const byte *)tmp_tzname)))
+ {
+ sql_print_error("Error: Out of memory while loading time zone");
+ goto end_with_unlock;
+ }
+
+ /*
+ Loading of time zone succeeded
+ */
+ return_val= tmp_tzname->tz;
+
+end_with_unlock:
+
+ if (table)
+ table->file->index_end();
+
+ mysql_unlock_tables(thd, lock);
+
+end_with_close:
+ close_thread_tables(thd);
+
+end:
+ thd->db= db_save;
+ thd->db_length= db_length_save;
+ DBUG_RETURN(return_val);
+}
+
+
+/*
+ Parse string that specifies time zone as offset from UTC.
+
+ SYNOPSIS
+ str_to_offset()
+ str - pointer to string which contains offset
+ length - length of string
+ offset - out parameter for storing found offset in seconds.
+
+ DESCRIPTION
+ This function parses string which contains time zone offset
+ in form similar to '+10:00' and converts found value to
+ seconds from UTC form (east is positive).
+
+ RETURN VALUE
+ 0 - Ok
+ 1 - String doesn't contain valid time zone offset
+*/
+my_bool
+str_to_offset(const char *str, uint length, long *offset)
+{
+ const char *end= str + length;
+ my_bool negative;
+ ulong number_tmp;
+ long offset_tmp;
+
+ if (length < 4)
+ return 1;
+
+ if (*str == '+')
+ negative= 0;
+ else if (*str == '-')
+ negative= 1;
+ else
+ return 1;
+ str++;
+
+ number_tmp= 0;
+
+ while (str < end && my_isdigit(&my_charset_latin1, *str))
+ {
+ number_tmp= number_tmp*10 + *str - '0';
+ str++;
+ }
+
+ if (str + 1 >= end || *str != ':')
+ return 1;
+ str++;
+
+ offset_tmp = number_tmp * MINS_PER_HOUR; number_tmp= 0;
+
+ while (str < end && my_isdigit(&my_charset_latin1, *str))
+ {
+ number_tmp= number_tmp * 10 + *str - '0';
+ str++;
+ }
+
+ if (str != end)
+ return 1;
+
+ offset_tmp= (offset_tmp + number_tmp) * SECS_PER_MIN;
+
+ if (negative)
+ offset_tmp= -offset_tmp;
+
+ /*
+ Check if offset is in range prescribed by standard
+ (from -12:59 to 13:00).
+ */
+
+ if (number_tmp > 59 || offset_tmp < -13 * SECS_PER_HOUR + 1 ||
+ offset_tmp > 13 * SECS_PER_HOUR)
+ return 1;
+
+ *offset= offset_tmp;
+
+ return 0;
+}
+
+
+/*
+ Get Time_zone object for specified time zone.
+
+ SYNOPSIS
+ my_tz_find()
+ thd - current thread
+ name - time zone specification
+
+ DESCRIPTION
+ This function checks if name is one of time zones described in db,
+ predefined SYSTEM time zone or valid time zone specification as
+ offset from UTC (In last case it will create proper Time_zone_offset
+ object if there were not any.). If name is ok it returns corresponding
+ Time_zone object.
+
+ Clients of this function are not responsible for releasing resources
+ occupied by returned Time_zone object so they can just forget pointers
+ to Time_zone object if they are not needed longer.
+
+ Other important property of this function: if some Time_zone found once
+ it will be for sure found later, so this function can also be used for
+ checking if proper Time_zone object exists (and if there will be error
+ it will be reported during first call).
+
+ If name pointer is 0 then this function returns 0 (this allows to pass 0
+ values as parameter without additional external check and this property
+ is used by @@time_zone variable handling code).
+
+ It will perform lookup in system tables (mysql.time_zone*) if needed.
+
+ RETURN VALUE
+ Pointer to corresponding Time_zone object. 0 - in case of bad time zone
+ specification or other error.
+
+*/
+Time_zone *
+my_tz_find(THD *thd, const String * name)
+{
+ TZ_NAMES_ENTRY *tmp_tzname;
+ Time_zone *result_tz= 0;
+ long offset;
+
+ DBUG_ENTER("my_tz_find");
+ DBUG_PRINT("enter", ("time zone name='%s'",
+ name ? ((String *)name)->c_ptr() : "NULL"));
+
+ if (!name)
+ DBUG_RETURN(0);
+
+ VOID(pthread_mutex_lock(&tz_LOCK));
+
+ if (!str_to_offset(name->ptr(), name->length(), &offset))
+ {
+
+ if (!(result_tz= (Time_zone_offset *)hash_search(&offset_tzs,
+ (const byte *)&offset,
+ sizeof(long))))
+ {
+ DBUG_PRINT("info", ("Creating new Time_zone_offset object"));
+
+ if (!(result_tz= new (&tz_storage) Time_zone_offset(offset)) ||
+ my_hash_insert(&offset_tzs, (const byte *) result_tz))
+ {
+ sql_print_error("Fatal error: Out of memory "
+ "while setting new time zone");
+ result_tz= 0;
+ }
+ }
+ } else {
+ if ((tmp_tzname= (TZ_NAMES_ENTRY *)hash_search(&tz_names, name->ptr(),
+ name->length())))
+ result_tz= tmp_tzname->tz;
+ else
+ result_tz= tz_load_from_db(current_thd, name);
+ }
+
+ VOID(pthread_mutex_unlock(&tz_LOCK));
+
+ DBUG_RETURN(result_tz);
+}
+
+#endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */
+
+
+#ifdef TZINFO2SQL
+/*
+ This code belongs to mysql_tzinfo_to_sql converter command line utility.
+ This utility should be used by db admin for populating mysql.time_zone
+ tables.
+*/
+
+
+/*
+ Print info about time zone described by TIME_ZONE_INFO struct as
+ SQL statements populating mysql.time_zone* tables.
+
+ SYNOPSIS
+ print_tz_as_sql()
+ tz_name - name of time zone
+ sp - structure describing time zone
+*/
+void
+print_tz_as_sql(const char* tz_name, const TIME_ZONE_INFO *sp)
+{
+ uint i;
+
+ /* Here we assume that all time zones have same leap correction tables */
+ printf("INSERT INTO time_zone (Use_leap_seconds) VALUES ('%s');\n",
+ sp->leapcnt ? "Y" : "N");
+ printf("SET @time_zone_id= LAST_INSERT_ID();\n");
+ printf("INSERT INTO time_zone_name (Name, Time_zone_id) VALUES \
+('%s', @time_zone_id);\n", tz_name);
+
+ if (sp->timecnt)
+ {
+ printf("INSERT INTO time_zone_transition \
+(Time_zone_id, Transition_time, Transition_type_id) VALUES\n");
+ for (i= 0; i < sp->timecnt; i++)
+ printf("%s(@time_zone_id, %ld, %u)\n", (i == 0 ? " " : ","), sp->ats[i],
+ (uint)sp->types[i]);
+ printf(";\n");
+ }
+
+ printf("INSERT INTO time_zone_transition_type \
+(Time_zone_id, Transition_type_id, Offset, Is_DST, Abbreviation) VALUES\n");
+
+ for (i= 0; i < sp->typecnt; i++)
+ printf("%s(@time_zone_id, %u, %ld, %d, '%s')\n", (i == 0 ? " " : ","), i,
+ sp->ttis[i].tt_gmtoff, sp->ttis[i].tt_isdst,
+ sp->chars + sp->ttis[i].tt_abbrind);
+ printf(";\n");
+}
+
+
+/*
+ Print info about leap seconds in time zone as SQL statements
+ populating mysql.time_zone_leap_second table.
+
+ SYNOPSIS
+ print_tz_leaps_as_sql()
+ sp - structure describing time zone
+*/
+void
+print_tz_leaps_as_sql(const TIME_ZONE_INFO *sp)
+{
+ uint i;
+
+ /*
+ We are assuming that there are only one list of leap seconds
+ For all timezones.
+ */
+ printf("TRUNCATE TABLE time_zone_leap_second;\n");
+
+ if (sp->leapcnt)
+ {
+ printf("INSERT INTO time_zone_leap_second \
+(Transition_time, Correction) VALUES\n");
+ for (i= 0; i < sp->leapcnt; i++)
+ printf("%s(%ld, %ld)\n", (i == 0 ? " " : ","),
+ sp->lsis[i].ls_trans, sp->lsis[i].ls_corr);
+ printf(";\n");
+ }
+
+ printf("ALTER TABLE time_zone_leap_second ORDER BY Transition_time;\n");
+}
+
+
+/*
+ Some variables used as temporary or as parameters
+ in recursive scan_tz_dir() code.
+*/
+TIME_ZONE_INFO tz_info;
+MEM_ROOT tz_storage;
+char fullname[FN_REFLEN + 1];
+char *root_name_end;
+
+
+/*
+ Recursively scan zoneinfo directory and print all found time zone
+ descriptions as SQL.
+
+ SYNOPSIS
+ scan_tz_dir()
+ name_end - pointer to end of path to directory to be searched.
+
+ DESCRIPTION
+ This auxiliary recursive function also uses several global
+ variables as in parameters and for storing temporary values.
+
+ fullname - path to directory that should be scanned.
+ root_name_end - pointer to place in fullname where part with
+ path to initial directory ends.
+ current_tz_id - last used time zone id
+
+ RETURN VALUE
+ 0 - Ok, 1 - Fatal error
+
+*/
+my_bool
+scan_tz_dir(char * name_end)
+{
+ MY_DIR *cur_dir;
+ char *name_end_tmp;
+ uint i;
+
+ if (!(cur_dir= my_dir(fullname, MYF(MY_WANT_STAT))))
+ return 1;
+
+ name_end= strmake(name_end, "/", FN_REFLEN - (name_end - fullname));
+
+ for (i= 0; i < cur_dir->number_off_files; i++)
+ {
+ if (cur_dir->dir_entry[i].name[0] != '.')
+ {
+ name_end_tmp= strmake(name_end, cur_dir->dir_entry[i].name,
+ FN_REFLEN - (name_end - fullname));
+
+ if (MY_S_ISDIR(cur_dir->dir_entry[i].mystat->st_mode))
+ {
+ if (scan_tz_dir(name_end_tmp))
+ {
+ my_dirend(cur_dir);
+ return 1;
+ }
+ }
+ else if (MY_S_ISREG(cur_dir->dir_entry[i].mystat->st_mode))
+ {
+ init_alloc_root(&tz_storage, 32768, 0);
+ if (!tz_load(fullname, &tz_info, &tz_storage))
+ print_tz_as_sql(root_name_end + 1, &tz_info);
+ else
+ fprintf(stderr,
+ "Warning: Unable to load '%s' as time zone. Skipping it.\n",
+ fullname);
+ free_root(&tz_storage, MYF(0));
+ }
+ else
+ fprintf(stderr, "Warning: '%s' is not regular file or directory\n",
+ fullname);
+ }
+ }
+
+ my_dirend(cur_dir);
+
+ return 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ MY_INIT(argv[0]);
+
+ if (argc != 2 && argc != 3)
+ {
+ fprintf(stderr, "Usage:\n");
+ fprintf(stderr, " %s timezonedir\n", argv[0]);
+ fprintf(stderr, " %s timezonefile timezonename\n", argv[0]);
+ fprintf(stderr, " %s --leap timezonefile\n", argv[0]);
+ return 1;
+ }
+
+ if (argc == 2)
+ {
+ root_name_end= strmake(fullname, argv[1], FN_REFLEN);
+
+ printf("TRUNCATE TABLE time_zone;\n");
+ printf("TRUNCATE TABLE time_zone_name;\n");
+ printf("TRUNCATE TABLE time_zone_transition;\n");
+ printf("TRUNCATE TABLE time_zone_transition_type;\n");
+
+ if (scan_tz_dir(root_name_end))
+ {
+ fprintf(stderr, "There were fatal errors during processing "
+ "of zoneinfo directory\n");
+ return 1;
+ }
+
+ printf("ALTER TABLE time_zone_transition "
+ "ORDER BY Time_zone_id, Transition_time;\n");
+ printf("ALTER TABLE time_zone_transition_type "
+ "ORDER BY Time_zone_id, Transition_type_id;\n");
+ }
+ else
+ {
+ init_alloc_root(&tz_storage, 32768, 0);
+
+ if (strcmp(argv[1], "--leap") == 0)
+ {
+ if (tz_load(argv[2], &tz_info, &tz_storage))
+ {
+ fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
+ return 1;
+ }
+ print_tz_leaps_as_sql(&tz_info);
+ }
+ else
+ {
+ if (tz_load(argv[1], &tz_info, &tz_storage))
+ {
+ fprintf(stderr, "Problems with zoneinfo file '%s'\n", argv[2]);
+ return 1;
+ }
+ print_tz_as_sql(argv[2], &tz_info);
+ }
+
+ free_root(&tz_storage, MYF(0));
+ }
+
+ return 0;
+}
+
+#endif /* defined(TZINFO2SQL) */
+
+
+#ifdef TESTTIME
+
+/*
+ Some simple brute-force test wich allowed to catch a pair of bugs.
+ Also can provide interesting facts about system's time zone support
+ implementation.
+*/
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#endif
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif
+
+my_bool
+is_equal_TIME_tm(const TIME* time_arg, const struct tm * tm_arg)
+{
+ return (time_arg->year == (uint)tm_arg->tm_year+TM_YEAR_BASE) &&
+ (time_arg->month == (uint)tm_arg->tm_mon+1) &&
+ (time_arg->day == (uint)tm_arg->tm_mday) &&
+ (time_arg->hour == (uint)tm_arg->tm_hour) &&
+ (time_arg->minute == (uint)tm_arg->tm_min) &&
+ (time_arg->second == (uint)tm_arg->tm_sec) &&
+ time_arg->second_part == 0;
+}
+
+
+int
+main(int argc, char **argv)
+{
+ my_bool localtime_negative;
+ TIME_ZONE_INFO tz_info;
+ struct tm tmp;
+ TIME time_tmp;
+ my_time_t t, t1, t2;
+ char fullname[FN_REFLEN+1];
+ char *str_end;
+ long not_used;
+ bool not_used_2;
+ MEM_ROOT tz_storage;
+
+ MY_INIT(argv[0]);
+
+ init_alloc_root(&tz_storage, 32768, 0);
+
+ /* let us set some well known timezone */
+ setenv("TZ", "MET", 1);
+ tzset();
+
+ /* Some initial time zone related system info */
+ printf("time_t: %s %u bit\n", TYPE_SIGNED(time_t) ? "signed" : "unsigned",
+ (uint)TYPE_BIT(time_t));
+ if (TYPE_SIGNED(time_t))
+ {
+ t= -100;
+ localtime_negative= test(localtime_r(&t, &tmp) != 0);
+ printf("localtime_r %s negative params \
+ (time_t=%d is %d-%d-%d %d:%d:%d)\n",
+ (localtime_negative ? "supports" : "doesn't support"), (int)t,
+ TM_YEAR_BASE + tmp.tm_year, tmp.tm_mon + 1, tmp.tm_mday,
+ tmp.tm_hour, tmp.tm_min, tmp.tm_sec);
+
+ printf("mktime %s negative results (%d)\n",
+ (t == mktime(&tmp) ? "doesn't support" : "supports"),
+ (int)mktime(&tmp));
+ }
+
+ tmp.tm_year= 103; tmp.tm_mon= 2; tmp.tm_mday= 30;
+ tmp.tm_hour= 2; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= -1;
+ t= mktime(&tmp);
+ printf("mktime returns %s for spring time gap (%d)\n",
+ (t != (time_t)-1 ? "something" : "error"), (int)t);
+
+ tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
+ tmp.tm_hour= 0; tmp.tm_min= 0; tmp.tm_sec= 0; tmp.tm_isdst= 0;
+ t= mktime(&tmp);
+ printf("mktime returns %s for non existing date (%d)\n",
+ (t != (time_t)-1 ? "something" : "error"), (int)t);
+
+ tmp.tm_year= 103; tmp.tm_mon= 8; tmp.tm_mday= 1;
+ tmp.tm_hour= 25; tmp.tm_min=0; tmp.tm_sec=0; tmp.tm_isdst=1;
+ t= mktime(&tmp);
+ printf("mktime %s unnormalized input (%d)\n",
+ (t != (time_t)-1 ? "handles" : "doesn't handle"), (int)t);
+
+ tmp.tm_year= 103; tmp.tm_mon= 9; tmp.tm_mday= 26;
+ tmp.tm_hour= 0; tmp.tm_min= 30; tmp.tm_sec= 0; tmp.tm_isdst= 1;
+ mktime(&tmp);
+ tmp.tm_hour= 2; tmp.tm_isdst= -1;
+ t=mktime(&tmp);
+ tmp.tm_hour= 4; tmp.tm_isdst= 0;
+ mktime(&tmp);
+ tmp.tm_hour= 2; tmp.tm_isdst= -1;
+ t1=mktime(&tmp);
+ printf("mktime is %s (%d %d)\n",
+ (t == t1 ? "determenistic" : "is non-determenistic"),
+ (int)t, (int)t1);
+
+ /* Let us load time zone description */
+ str_end= strmake(fullname, TZDIR, FN_REFLEN);
+ strmake(str_end, "/MET", FN_REFLEN - (str_end - fullname));
+
+ if (tz_load(fullname, &tz_info, &tz_storage))
+ {
+ printf("Unable to load time zone info from '%s'\n", fullname);
+ free_root(&tz_storage, MYF(0));
+ return 1;
+ }
+
+ printf("Testing our implementation\n");
+
+ if (TYPE_SIGNED(time_t) && localtime_negative)
+ {
+ for (t= -40000; t < 20000; t++)
+ {
+ localtime_r(&t,&tmp);
+ gmt_sec_to_TIME(&time_tmp, t, &tz_info);
+ if (!is_equal_TIME_tm(&time_tmp, &tmp))
+ {
+ printf("Problem with negative time_t = %d\n", (int)t);
+ free_root(&tz_storage, MYF(0));
+ return 1;
+ }
+ }
+ printf("gmt_sec_to_TIME = localtime for time_t in [-40000,20000) range\n");
+ }
+
+ for (t= 1000000000; t < 1100000000; t+= 13)
+ {
+ localtime_r(&t,&tmp);
+ gmt_sec_to_TIME(&time_tmp, t, &tz_info);
+
+ if (!is_equal_TIME_tm(&time_tmp, &tmp))
+ {
+ printf("Problem with time_t = %d\n", (int)t);
+ free_root(&tz_storage, MYF(0));
+ return 1;
+ }
+ }
+ printf("gmt_sec_to_TIME = localtime for time_t in [1000000000,1100000000) range\n");
+
+ init_time();
+
+ /*
+ Be careful here! my_system_gmt_sec doesn't fully handle unnormalized
+ dates.
+ */
+ for (time_tmp.year= 1980; time_tmp.year < 2010; time_tmp.year++)
+ for (time_tmp.month= 1; time_tmp.month < 13; time_tmp.month++)
+ for (time_tmp.day= 1;
+ time_tmp.day < mon_lengths[isleap(time_tmp.year)][time_tmp.month-1];
+ time_tmp.day++)
+ for (time_tmp.hour= 0; time_tmp.hour < 24; time_tmp.hour++)
+ for (time_tmp.minute= 0; time_tmp.minute < 60; time_tmp.minute+= 5)
+ for (time_tmp.second=0; time_tmp.second<60; time_tmp.second+=25)
+ {
+ t= my_system_gmt_sec(&time_tmp, &not_used, &not_used_2);
+ t1= TIME_to_gmt_sec(&time_tmp, &tz_info, &not_used_2);
+ if (t != t1)
+ {
+ /*
+ We need special handling during autumn since my_system_gmt_sec
+ prefers greater time_t values (in MET) for ambiguity.
+ And BTW that is a bug which should be fixed !!!
+ */
+ tmp.tm_year= time_tmp.year - TM_YEAR_BASE;
+ tmp.tm_mon= time_tmp.month - 1;
+ tmp.tm_mday= time_tmp.day;
+ tmp.tm_hour= time_tmp.hour;
+ tmp.tm_min= time_tmp.minute;
+ tmp.tm_sec= time_tmp.second;
+ tmp.tm_isdst= 1;
+
+ t2= mktime(&tmp);
+
+ if (t1 == t2)
+ continue;
+
+ printf("Problem: %u/%u/%u %u:%u:%u with times t=%d, t1=%d\n",
+ time_tmp.year, time_tmp.month, time_tmp.day,
+ time_tmp.hour, time_tmp.minute, time_tmp.second,
+ (int)t,(int)t1);
+
+ free_root(&tz_storage, MYF(0));
+ return 1;
+ }
+ }
+
+ printf("TIME_to_gmt_sec = my_system_gmt_sec for test range\n");
+
+ free_root(&tz_storage, MYF(0));
+ return 0;
+}
+
+#endif /* defined(TESTTIME) */
diff --git a/sql/tztime.h b/sql/tztime.h
new file mode 100644
index 00000000000..ed92441d790
--- /dev/null
+++ b/sql/tztime.h
@@ -0,0 +1,71 @@
+/* Copyright (C) 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#ifdef __GNUC__
+#pragma interface /* gcc class interface */
+#endif
+
+#if !defined(TESTTIME) && !defined(TZINFO2SQL)
+/*
+ This class represents abstract time zone and provides
+ basic interface for TIME <-> my_time_t conversion.
+ Actual time zones which are specified by DB, or via offset
+ or use system functions are its descendants.
+*/
+class Time_zone: public Sql_alloc
+{
+public:
+ /*
+ Converts local time in broken down TIME representation to
+ my_time_t (UTC seconds since Epoch) represenation.
+ Returns 0 in case of error. Sets in_dst_time_gap to true if date provided
+ falls into spring time-gap (or lefts it untouched otherwise).
+ */
+ virtual my_time_t TIME_to_gmt_sec(const TIME *t,
+ bool *in_dst_time_gap) const = 0;
+ /*
+ Converts time in my_time_t representation to local time in
+ broken down TIME representation.
+ */
+ virtual void gmt_sec_to_TIME(TIME *tmp, my_time_t t) const = 0;
+ /*
+ Because of constness of String returned by get_name() time zone name
+ have to be already zeroended to be able to use String::ptr() instead
+ of c_ptr().
+ */
+ virtual const String * get_name() const = 0;
+
+ /*
+ We need this only for surpressing warnings, objects of this type are
+ allocated on MEM_ROOT and should not require destruction.
+ */
+ virtual ~Time_zone() {};
+};
+
+extern Time_zone * my_tz_UTC;
+extern Time_zone * my_tz_SYSTEM;
+extern Time_zone * my_tz_find(THD *thd, const String *name);
+extern my_bool my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap);
+extern void my_tz_free();
+
+/*
+ Maximum length of time zone name that we support
+ (Time zone name is char(64) in db)
+*/
+#define MAX_TIME_ZONE_NAME_LENGTH 72
+
+#endif /* !defined(TESTTIME) && !defined(TZINFO2SQL) */
diff --git a/sql/unireg.cc b/sql/unireg.cc
index bab021aed59..c2666be804d 100644
--- a/sql/unireg.cc
+++ b/sql/unireg.cc
@@ -633,6 +633,7 @@ static bool make_empty_rec(File file,enum db_type table_type,
DBUG_RETURN(1);
}
+ table.in_use= current_thd;
table.db_low_byte_first= handler->low_byte_first();
table.blob_ptr_size=portable_sizeof_char_ptr;