summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergei Golubchik <serg@mariadb.org>2017-09-19 11:18:45 +0200
committerSergei Golubchik <serg@mariadb.org>2017-09-21 22:03:21 +0200
commitf4f48e06215fe6717865ccbe27ddc388a2cb86b8 (patch)
treec4128f9caa12333de890e338a609f8bbe6f897fb
parent46a2917c0fd287755d9e357a98d04ed2c8b1ba0a (diff)
downloadmariadb-git-f4f48e06215fe6717865ccbe27ddc388a2cb86b8.tar.gz
MDEV-12672 Replicated TIMESTAMP fields given wrong value near DST change
Implement a special Copy_field method for timestamps, that copies timestamps without converting them to MYSQL_TIME (the conversion is lossy around DST change dates).
-rw-r--r--mysql-test/r/old-mode.result23
-rw-r--r--mysql-test/suite/rpl/r/rpl_temporal_format_mariadb53_to_mysql56_dst.result28
-rw-r--r--mysql-test/suite/rpl/t/rpl_temporal_format_mariadb53_to_mysql56_dst.test37
-rw-r--r--mysql-test/t/old-mode.test17
-rw-r--r--sql/field_conv.cc16
5 files changed, 120 insertions, 1 deletions
diff --git a/mysql-test/r/old-mode.result b/mysql-test/r/old-mode.result
index c2ee3324ede..398e719cf02 100644
--- a/mysql-test/r/old-mode.result
+++ b/mysql-test/r/old-mode.result
@@ -127,3 +127,26 @@ Warning 1264 Out of range value for column 'a' at row 1
Warning 1264 Out of range value for column 'b' at row 1
DROP TABLE t1;
SET @@global.mysql56_temporal_format=DEFAULT;
+set time_zone='Europe/Moscow';
+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
+alter table t1 modify a timestamp;
+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
+drop table t1;
+set time_zone=DEFAULT;
diff --git a/mysql-test/suite/rpl/r/rpl_temporal_format_mariadb53_to_mysql56_dst.result b/mysql-test/suite/rpl/r/rpl_temporal_format_mariadb53_to_mysql56_dst.result
new file mode 100644
index 00000000000..352101cb8cd
--- /dev/null
+++ b/mysql-test/suite/rpl/r/rpl_temporal_format_mariadb53_to_mysql56_dst.result
@@ -0,0 +1,28 @@
+include/master-slave.inc
+[connection master]
+set global time_zone='Europe/Moscow';
+set time_zone='UTC';
+stop slave;
+start slave;
+set global mysql56_temporal_format=false;
+set global time_zone='Europe/Moscow';
+set time_zone='UTC';
+create table t1 (pk int primary key, t timestamp not null);
+set timestamp = 1288477526;
+insert into t1 values (1,null);
+set timestamp = 1288481126;
+insert into t1 values (2,null);
+select pk, t, unix_timestamp(t) from t1;
+pk t unix_timestamp(t)
+1 2010-10-30 22:25:26 1288477526
+2 2010-10-30 23:25:26 1288481126
+set time_zone=default;
+select pk, t, unix_timestamp(t) from t1;
+pk t unix_timestamp(t)
+1 2010-10-31 02:25:26 1288477526
+2 2010-10-31 02:25:26 1288481126
+set global time_zone=default;
+drop table t1;
+set global time_zone=default;
+set global mysql56_temporal_format=default;
+include/rpl_end.inc
diff --git a/mysql-test/suite/rpl/t/rpl_temporal_format_mariadb53_to_mysql56_dst.test b/mysql-test/suite/rpl/t/rpl_temporal_format_mariadb53_to_mysql56_dst.test
new file mode 100644
index 00000000000..511bdc15184
--- /dev/null
+++ b/mysql-test/suite/rpl/t/rpl_temporal_format_mariadb53_to_mysql56_dst.test
@@ -0,0 +1,37 @@
+#
+# MDEV-12672 Replicated TIMESTAMP fields given wrong value near DST change
+#
+source include/have_binlog_format_row.inc;
+source include/master-slave.inc;
+
+connection slave;
+set global time_zone='Europe/Moscow';
+set time_zone='UTC';
+stop slave;
+start slave;
+
+connection master;
+set global mysql56_temporal_format=false;
+set global time_zone='Europe/Moscow';
+set time_zone='UTC';
+
+create table t1 (pk int primary key, t timestamp not null);
+set timestamp = 1288477526;
+insert into t1 values (1,null);
+set timestamp = 1288481126;
+insert into t1 values (2,null);
+
+sync_slave_with_master;
+
+select pk, t, unix_timestamp(t) from t1;
+set time_zone=default;
+select pk, t, unix_timestamp(t) from t1;
+
+set global time_zone=default;
+
+connection master;
+drop table t1;
+set global time_zone=default;
+set global mysql56_temporal_format=default;
+
+source include/rpl_end.inc;
diff --git a/mysql-test/t/old-mode.test b/mysql-test/t/old-mode.test
index 0572570d122..99c92f8093d 100644
--- a/mysql-test/t/old-mode.test
+++ b/mysql-test/t/old-mode.test
@@ -83,3 +83,20 @@ SELECT TO_DAYS(a), TO_DAYS(b) FROM t1;
DROP TABLE t1;
SET @@global.mysql56_temporal_format=DEFAULT;
+#
+# MDEV-12672 Replicated TIMESTAMP fields given wrong value near DST change
+#
+set time_zone='Europe/Moscow';
+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;
+alter table t1 modify a timestamp;
+select a, unix_timestamp(a) from t1;
+drop table t1;
+set time_zone=DEFAULT;
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index 7b57c7da104..74c5fb5b502 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -417,6 +417,18 @@ static void do_field_decimal(Copy_field *copy)
}
+static void do_field_timestamp(Copy_field *copy)
+{
+ DBUG_ASSERT(copy->from_field->type() == MYSQL_TYPE_TIMESTAMP);
+ 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);
+}
+
+
static void do_field_temporal(Copy_field *copy)
{
MYSQL_TIME ltime;
@@ -724,7 +736,9 @@ Copy_field::get_copy_func(Field *to,Field *from)
((to->table->in_use->variables.sql_mode &
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE)) &&
mysql_type_to_time_type(to->type()) != MYSQL_TIMESTAMP_TIME))
- return do_field_temporal;
+ return (from->type() == MYSQL_TYPE_TIMESTAMP &&
+ to->type() == MYSQL_TYPE_TIMESTAMP)
+ ? do_field_timestamp : do_field_temporal;
/* Do binary copy */
}
// Check if identical fields