Commit f8a800be authored by Sergei Golubchik's avatar Sergei Golubchik

bugfix: copy timestamps correctly in INSERT...SELECT

don't do it via MYSQL_TIME, that conversion is lossy
around DST change dates.
parent f4f48e06
...@@ -149,4 +149,26 @@ a unix_timestamp(a) ...@@ -149,4 +149,26 @@ a unix_timestamp(a)
2010-10-31 02:25:26 1288477526 2010-10-31 02:25:26 1288477526
2010-10-31 02:25:26 1288481126 2010-10-31 02:25:26 1288481126
drop table t1; drop table t1;
set global mysql56_temporal_format=false;
create table t1 (a timestamp);
set timestamp=1288477526;
insert t1 values (null);
set timestamp=1288481126;
insert t1 values (null);
select a, unix_timestamp(a) from t1;
a unix_timestamp(a)
2010-10-31 02:25:26 1288477526
2010-10-31 02:25:26 1288481126
set global mysql56_temporal_format=true;
select a, unix_timestamp(a) from t1;
a unix_timestamp(a)
2010-10-31 02:25:26 1288477526
2010-10-31 02:25:26 1288481126
create table t2 (a timestamp);
insert t2 select a from t1;
select a, unix_timestamp(a) from t2;
a unix_timestamp(a)
2010-10-31 02:25:26 1288477526
2010-10-31 02:25:26 1288481126
drop table t1, t2;
set time_zone=DEFAULT; set time_zone=DEFAULT;
...@@ -86,6 +86,8 @@ SET @@global.mysql56_temporal_format=DEFAULT; ...@@ -86,6 +86,8 @@ SET @@global.mysql56_temporal_format=DEFAULT;
# #
# MDEV-12672 Replicated TIMESTAMP fields given wrong value near DST change # MDEV-12672 Replicated TIMESTAMP fields given wrong value near DST change
# #
# Copy_field
set time_zone='Europe/Moscow'; set time_zone='Europe/Moscow';
set global mysql56_temporal_format=false; set global mysql56_temporal_format=false;
create table t1 (a timestamp); create table t1 (a timestamp);
...@@ -99,4 +101,19 @@ select a, unix_timestamp(a) from t1; ...@@ -99,4 +101,19 @@ select a, unix_timestamp(a) from t1;
alter table t1 modify a timestamp; alter table t1 modify a timestamp;
select a, unix_timestamp(a) from t1; select a, unix_timestamp(a) from t1;
drop table t1; drop table t1;
# field_conv_incompatible()
set global mysql56_temporal_format=false;
create table t1 (a timestamp);
set timestamp=1288477526;
insert t1 values (null);
set timestamp=1288481126;
insert t1 values (null);
select a, unix_timestamp(a) from t1;
set global mysql56_temporal_format=true;
select a, unix_timestamp(a) from t1;
create table t2 (a timestamp);
insert t2 select a from t1;
select a, unix_timestamp(a) from t2;
drop table t1, t2;
set time_zone=DEFAULT; set time_zone=DEFAULT;
...@@ -5064,6 +5064,23 @@ int Field_timestamp::store(longlong nr, bool unsigned_val) ...@@ -5064,6 +5064,23 @@ int Field_timestamp::store(longlong nr, bool unsigned_val)
} }
int Field_timestamp::store_timestamp(Field_timestamp *from)
{
ulong sec_part;
my_time_t ts= from->get_timestamp(&sec_part);
store_TIME(ts, sec_part);
if (!ts && !sec_part && get_thd()->variables.sql_mode & MODE_NO_ZERO_DATE)
{
ErrConvString s(
STRING_WITH_LEN("0000-00-00 00:00:00.000000") - (decimals() ? 6 - decimals() : 7),
system_charset_info);
set_datetime_warning(WARN_DATA_TRUNCATED, &s, MYSQL_TIMESTAMP_DATETIME, 1);
return 1;
}
return 0;
}
double Field_timestamp::val_real(void) double Field_timestamp::val_real(void)
{ {
return (double) Field_timestamp::val_int(); return (double) Field_timestamp::val_int();
......
...@@ -2186,6 +2186,7 @@ class Field_timestamp :public Field_temporal { ...@@ -2186,6 +2186,7 @@ class Field_timestamp :public Field_temporal {
int store(longlong nr, bool unsigned_val); int store(longlong nr, bool unsigned_val);
int store_time_dec(MYSQL_TIME *ltime, uint dec); int store_time_dec(MYSQL_TIME *ltime, uint dec);
int store_decimal(const my_decimal *); int store_decimal(const my_decimal *);
int store_timestamp(Field_timestamp *from);
double val_real(void); double val_real(void);
longlong val_int(void); longlong val_int(void);
String *val_str(String*,String *); String *val_str(String*,String *);
......
...@@ -219,6 +219,13 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) ...@@ -219,6 +219,13 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions)
} }
static int copy_timestamp_fields(Field *from, Field *to)
{
DBUG_ASSERT(from->type() == MYSQL_TYPE_TIMESTAMP);
DBUG_ASSERT(to->type() == MYSQL_TYPE_TIMESTAMP);
return ((Field_timestamp*)to)->store_timestamp((Field_timestamp*)from);
}
static void do_skip(Copy_field *copy __attribute__((unused))) static void do_skip(Copy_field *copy __attribute__((unused)))
{ {
} }
...@@ -419,13 +426,7 @@ static void do_field_decimal(Copy_field *copy) ...@@ -419,13 +426,7 @@ static void do_field_decimal(Copy_field *copy)
static void do_field_timestamp(Copy_field *copy) static void do_field_timestamp(Copy_field *copy)
{ {
DBUG_ASSERT(copy->from_field->type() == MYSQL_TYPE_TIMESTAMP); copy_timestamp_fields(copy->from_field, copy->to_field);
DBUG_ASSERT(copy->to_field->type() == MYSQL_TYPE_TIMESTAMP);
ulong sec_part;
Field_timestamp *f= static_cast<Field_timestamp*>(copy->from_field);
Field_timestamp *t= static_cast<Field_timestamp*>(copy->to_field);
my_time_t ts= f->get_timestamp(&sec_part);
t->store_TIME(ts, sec_part);
} }
...@@ -938,6 +939,10 @@ int field_conv_incompatible(Field *to, Field *from) ...@@ -938,6 +939,10 @@ int field_conv_incompatible(Field *to, Field *from)
my_decimal buff; my_decimal buff;
return to->store_decimal(from->val_decimal(&buff)); return to->store_decimal(from->val_decimal(&buff));
} }
if (from->type() == MYSQL_TYPE_TIMESTAMP && to->type() == MYSQL_TYPE_TIMESTAMP)
{
return copy_timestamp_fields(from, to);
}
if (from->cmp_type() == TIME_RESULT) if (from->cmp_type() == TIME_RESULT)
{ {
MYSQL_TIME ltime; MYSQL_TIME ltime;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment