mirror of
https://github.com/MariaDB/server.git
synced 2025-01-16 03:52:35 +01:00
MDEV-5377 Row-based replication of MariaDB temporal data types with FSP>0 into a different column type
This commit is contained in:
parent
88a480cecb
commit
269da4bf19
10 changed files with 256 additions and 56 deletions
Binary file not shown.
Binary file not shown.
21
mysql-test/suite/rpl/r/rpl_mysql57_stm_temporal_round.result
Normal file
21
mysql-test/suite/rpl/r/rpl_mysql57_stm_temporal_round.result
Normal file
|
@ -0,0 +1,21 @@
|
|||
#
|
||||
# MDEV-8894 Inserting fractional seconds into MySQL 5.6 master breaks consistency on MariaDB 10 slave
|
||||
#
|
||||
include/master-slave.inc
|
||||
[connection master]
|
||||
connection slave;
|
||||
include/stop_slave.inc
|
||||
connection master;
|
||||
include/rpl_stop_server.inc [server_number=1]
|
||||
include/rpl_start_server.inc [server_number=1]
|
||||
connection slave;
|
||||
CHANGE MASTER TO master_host='127.0.0.1', master_port=SERVER_MYPORT_1, master_user='root', master_log_file='master-bin.000001', master_log_pos=4;
|
||||
include/start_slave.inc
|
||||
connection master;
|
||||
connection slave;
|
||||
SELECT * FROM t1 ORDER BY id;
|
||||
id a
|
||||
1 2001-01-01 00:00:01.000
|
||||
include/stop_slave.inc
|
||||
DROP TABLE t1;
|
||||
include/rpl_end.inc
|
22
mysql-test/suite/rpl/r/rpl_mysql80_stm_temporal_round.result
Normal file
22
mysql-test/suite/rpl/r/rpl_mysql80_stm_temporal_round.result
Normal file
|
@ -0,0 +1,22 @@
|
|||
#
|
||||
# MDEV-8894 Inserting fractional seconds into MySQL 5.6 master breaks consistency on MariaDB 10 slave
|
||||
#
|
||||
include/master-slave.inc
|
||||
[connection master]
|
||||
connection slave;
|
||||
include/stop_slave.inc
|
||||
connection master;
|
||||
include/rpl_stop_server.inc [server_number=1]
|
||||
include/rpl_start_server.inc [server_number=1]
|
||||
connection slave;
|
||||
CHANGE MASTER TO master_host='127.0.0.1', master_port=SERVER_MYPORT_1, master_user='root', master_log_file='master-bin.000001', master_log_pos=4;
|
||||
include/start_slave.inc
|
||||
connection master;
|
||||
connection slave;
|
||||
SELECT * FROM t1 ORDER BY id;
|
||||
id a
|
||||
1 2001-01-01 00:00:01.000
|
||||
2 2001-01-01 00:00:00.999
|
||||
include/stop_slave.inc
|
||||
DROP TABLE t1;
|
||||
include/rpl_end.inc
|
50
mysql-test/suite/rpl/t/rpl_mysql57_stm_temporal_round.test
Normal file
50
mysql-test/suite/rpl/t/rpl_mysql57_stm_temporal_round.test
Normal file
|
@ -0,0 +1,50 @@
|
|||
--echo #
|
||||
--echo # MDEV-8894 Inserting fractional seconds into MySQL 5.6 master breaks consistency on MariaDB 10 slave
|
||||
--echo #
|
||||
|
||||
--source include/have_innodb.inc
|
||||
--source include/master-slave.inc
|
||||
|
||||
--connection slave
|
||||
--source include/stop_slave.inc
|
||||
|
||||
--connection master
|
||||
--let $datadir= `SELECT @@datadir`
|
||||
|
||||
--let $rpl_server_number= 1
|
||||
--source include/rpl_stop_server.inc
|
||||
|
||||
--remove_file $datadir/master-bin.000001
|
||||
|
||||
#
|
||||
# Simulate MySQL 5.7.x master
|
||||
#
|
||||
# mysql-5.7.11-stm-temporal-round-binlog.000001 was recorded with
|
||||
# "mysqld --log-bin --binlog-format=statement", with the following SQL script:
|
||||
#
|
||||
#CREATE TABLE t1 (id SERIAL, a DATETIME(3));
|
||||
#INSERT INTO t1 (a) VALUES ('2001-01-01 00:00:00.999999');
|
||||
#
|
||||
|
||||
--copy_file $MYSQL_TEST_DIR/std_data/rpl/mysql-5.7.11-stm-temporal-round-binlog.000001 $datadir/master-bin.000001
|
||||
|
||||
--let $rpl_server_number= 1
|
||||
--source include/rpl_start_server.inc
|
||||
|
||||
--source include/wait_until_connected_again.inc
|
||||
|
||||
--connection slave
|
||||
--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
|
||||
eval CHANGE MASTER TO master_host='127.0.0.1', master_port=$SERVER_MYPORT_1, master_user='root', master_log_file='master-bin.000001', master_log_pos=4;
|
||||
|
||||
--source include/start_slave.inc
|
||||
|
||||
--connection master
|
||||
--sync_slave_with_master
|
||||
SELECT * FROM t1 ORDER BY id;
|
||||
|
||||
--source include/stop_slave.inc
|
||||
DROP TABLE t1;
|
||||
|
||||
--let $rpl_only_running_threads= 1
|
||||
--source include/rpl_end.inc
|
54
mysql-test/suite/rpl/t/rpl_mysql80_stm_temporal_round.test
Normal file
54
mysql-test/suite/rpl/t/rpl_mysql80_stm_temporal_round.test
Normal file
|
@ -0,0 +1,54 @@
|
|||
--echo #
|
||||
--echo # MDEV-8894 Inserting fractional seconds into MySQL 5.6 master breaks consistency on MariaDB 10 slave
|
||||
--echo #
|
||||
|
||||
--source include/have_innodb.inc
|
||||
--source include/master-slave.inc
|
||||
|
||||
--connection slave
|
||||
--source include/stop_slave.inc
|
||||
|
||||
--connection master
|
||||
--let $datadir= `SELECT @@datadir`
|
||||
|
||||
--let $rpl_server_number= 1
|
||||
--source include/rpl_stop_server.inc
|
||||
|
||||
--remove_file $datadir/master-bin.000001
|
||||
|
||||
#
|
||||
# Simulate MySQL 8.0.x master
|
||||
#
|
||||
# mysql-8.0.13-stm-temporal-round-binlog.000001 was recorded with
|
||||
# "mysqld --log-bin --binlog-format=statement", with the following SQL script:
|
||||
#
|
||||
#SET NAMES utf8mb4 COLLATE utf8mb4_general_ci;
|
||||
#SET sql_mode='';
|
||||
#CREATE TABLE t1 (id SERIAL, a DATETIME(3));
|
||||
#INSERT INTO t1 (a) VALUES ('2001-01-01 00:00:00.999999');
|
||||
#SET sql_mode=TIME_TRUNCATE_FRACTIONAL;
|
||||
#INSERT INTO t1 (a) VALUES ('2001-01-01 00:00:00.999999');
|
||||
#
|
||||
|
||||
--copy_file $MYSQL_TEST_DIR/std_data/rpl/mysql-8.0.13-stm-temporal-round-binlog.000001 $datadir/master-bin.000001
|
||||
|
||||
--let $rpl_server_number= 1
|
||||
--source include/rpl_start_server.inc
|
||||
|
||||
--source include/wait_until_connected_again.inc
|
||||
|
||||
--connection slave
|
||||
--replace_result $SERVER_MYPORT_1 SERVER_MYPORT_1
|
||||
eval CHANGE MASTER TO master_host='127.0.0.1', master_port=$SERVER_MYPORT_1, master_user='root', master_log_file='master-bin.000001', master_log_pos=4;
|
||||
|
||||
--source include/start_slave.inc
|
||||
|
||||
--connection master
|
||||
--sync_slave_with_master
|
||||
SELECT * FROM t1 ORDER BY id;
|
||||
|
||||
--source include/stop_slave.inc
|
||||
DROP TABLE t1;
|
||||
|
||||
--let $rpl_only_running_threads= 1
|
||||
--source include/rpl_end.inc
|
|
@ -101,16 +101,11 @@ TYPELIB binlog_checksum_typelib=
|
|||
TODO: correct the constant when it has been determined
|
||||
(which main tree to push and when)
|
||||
*/
|
||||
const uchar checksum_version_split_mysql[3]= {5, 6, 1};
|
||||
const ulong checksum_version_product_mysql=
|
||||
(checksum_version_split_mysql[0] * 256 +
|
||||
checksum_version_split_mysql[1]) * 256 +
|
||||
checksum_version_split_mysql[2];
|
||||
const uchar checksum_version_split_mariadb[3]= {5, 3, 0};
|
||||
const ulong checksum_version_product_mariadb=
|
||||
(checksum_version_split_mariadb[0] * 256 +
|
||||
checksum_version_split_mariadb[1]) * 256 +
|
||||
checksum_version_split_mariadb[2];
|
||||
const Version checksum_version_split_mysql(5, 6, 1);
|
||||
const Version checksum_version_split_mariadb(5, 3, 0);
|
||||
|
||||
// First MySQL version with fraction seconds
|
||||
const Version fsp_version_split_mysql(5, 6, 0);
|
||||
|
||||
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
|
||||
static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD* thd);
|
||||
|
@ -4541,6 +4536,7 @@ code_name(int code)
|
|||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
Macro to check that there is enough space to read from memory.
|
||||
|
||||
|
@ -4764,6 +4760,30 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(MYSQL_CLIENT)
|
||||
if (description_event->server_version_split.kind ==
|
||||
Format_description_log_event::master_version_split::KIND_MYSQL)
|
||||
{
|
||||
// Handle MariaDB/MySQL incompatible sql_mode bits
|
||||
sql_mode_t mysql_sql_mode= sql_mode;
|
||||
sql_mode&= MODE_MASK_MYSQL_COMPATIBLE; // Unset MySQL specific bits
|
||||
|
||||
/*
|
||||
sql_mode flags related to fraction second rounding/truncation
|
||||
have opposite meaning in MySQL vs MariaDB.
|
||||
MySQL:
|
||||
- rounds fractional seconds by default
|
||||
- truncates if TIME_TRUNCATE_FRACTIONAL is set
|
||||
MariaDB:
|
||||
- truncates fractional seconds by default
|
||||
- rounds if TIME_ROUND_FRACTIONAL is set
|
||||
*/
|
||||
if (description_event->server_version_split >= fsp_version_split_mysql &&
|
||||
!(mysql_sql_mode & MODE_MYSQL80_TIME_TRUNCATE_FRACTIONAL))
|
||||
sql_mode|= MODE_TIME_ROUND_FRACTIONAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
Layout for the data buffer is as follows
|
||||
+--------+-----------+------+------+---------+----+-------+
|
||||
|
@ -6549,26 +6569,24 @@ bool Format_description_log_event::start_decryption(Start_encryption_log_event*
|
|||
return crypto_data.init(sele->crypto_scheme, sele->key_version);
|
||||
}
|
||||
|
||||
static inline void
|
||||
do_server_version_split(char* version,
|
||||
Format_description_log_event::master_version_split *split_versions)
|
||||
|
||||
Version::Version(const char *version, const char **endptr)
|
||||
{
|
||||
char *p= version, *r;
|
||||
const char *p= version;
|
||||
ulong number;
|
||||
for (uint i= 0; i<=2; i++)
|
||||
{
|
||||
char *r;
|
||||
number= strtoul(p, &r, 10);
|
||||
/*
|
||||
It is an invalid version if any version number greater than 255 or
|
||||
first number is not followed by '.'.
|
||||
*/
|
||||
if (number < 256 && (*r == '.' || i != 0))
|
||||
split_versions->ver[i]= (uchar) number;
|
||||
m_ver[i]= (uchar) number;
|
||||
else
|
||||
{
|
||||
split_versions->ver[0]= 0;
|
||||
split_versions->ver[1]= 0;
|
||||
split_versions->ver[2]= 0;
|
||||
*this= Version();
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -6576,12 +6594,19 @@ do_server_version_split(char* version,
|
|||
if (*r == '.')
|
||||
p++; // skip the dot
|
||||
}
|
||||
endptr[0]= p;
|
||||
}
|
||||
|
||||
|
||||
Format_description_log_event::
|
||||
master_version_split::master_version_split(const char *version)
|
||||
{
|
||||
const char *p;
|
||||
static_cast<Version*>(this)[0]= Version(version, &p);
|
||||
if (strstr(p, "MariaDB") != 0 || strstr(p, "-maria-") != 0)
|
||||
split_versions->kind=
|
||||
Format_description_log_event::master_version_split::KIND_MARIADB;
|
||||
kind= KIND_MARIADB;
|
||||
else
|
||||
split_versions->kind=
|
||||
Format_description_log_event::master_version_split::KIND_MYSQL;
|
||||
kind= KIND_MYSQL;
|
||||
}
|
||||
|
||||
|
||||
|
@ -6595,20 +6620,14 @@ do_server_version_split(char* version,
|
|||
*/
|
||||
void Format_description_log_event::calc_server_version_split()
|
||||
{
|
||||
do_server_version_split(server_version, &server_version_split);
|
||||
server_version_split= master_version_split(server_version);
|
||||
|
||||
DBUG_PRINT("info",("Format_description_log_event::server_version_split:"
|
||||
" '%s' %d %d %d", server_version,
|
||||
server_version_split.ver[0],
|
||||
server_version_split.ver[1], server_version_split.ver[2]));
|
||||
server_version_split[0],
|
||||
server_version_split[1], server_version_split[2]));
|
||||
}
|
||||
|
||||
static inline ulong
|
||||
version_product(const Format_description_log_event::master_version_split* version_split)
|
||||
{
|
||||
return ((version_split->ver[0] * 256 + version_split->ver[1]) * 256
|
||||
+ version_split->ver[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
@return TRUE is the event's version is earlier than one that introduced
|
||||
|
@ -6618,9 +6637,9 @@ bool
|
|||
Format_description_log_event::is_version_before_checksum(const master_version_split
|
||||
*version_split)
|
||||
{
|
||||
return version_product(version_split) <
|
||||
return *version_split <
|
||||
(version_split->kind == master_version_split::KIND_MARIADB ?
|
||||
checksum_version_product_mariadb : checksum_version_product_mysql);
|
||||
checksum_version_split_mariadb : checksum_version_split_mysql);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -6636,7 +6655,6 @@ enum enum_binlog_checksum_alg get_checksum_alg(const char* buf, ulong len)
|
|||
{
|
||||
enum enum_binlog_checksum_alg ret;
|
||||
char version[ST_SERVER_VER_LEN];
|
||||
Format_description_log_event::master_version_split version_split;
|
||||
|
||||
DBUG_ENTER("get_checksum_alg");
|
||||
DBUG_ASSERT(buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT);
|
||||
|
@ -6646,7 +6664,7 @@ enum enum_binlog_checksum_alg get_checksum_alg(const char* buf, ulong len)
|
|||
ST_SERVER_VER_LEN);
|
||||
version[ST_SERVER_VER_LEN - 1]= 0;
|
||||
|
||||
do_server_version_split(version, &version_split);
|
||||
Format_description_log_event::master_version_split version_split(version);
|
||||
ret= Format_description_log_event::is_version_before_checksum(&version_split)
|
||||
? BINLOG_CHECKSUM_ALG_UNDEF
|
||||
: (enum_binlog_checksum_alg)buf[len - BINLOG_CHECKSUM_LEN - BINLOG_CHECKSUM_ALG_DESC_LEN];
|
||||
|
|
|
@ -2728,6 +2728,38 @@ protected:
|
|||
};
|
||||
|
||||
|
||||
class Version
|
||||
{
|
||||
protected:
|
||||
uchar m_ver[3];
|
||||
int cmp(const Version &other) const
|
||||
{
|
||||
return memcmp(m_ver, other.m_ver, 3);
|
||||
}
|
||||
public:
|
||||
Version()
|
||||
{
|
||||
m_ver[0]= m_ver[1]= m_ver[2]= '\0';
|
||||
}
|
||||
Version(uchar v0, uchar v1, uchar v2)
|
||||
{
|
||||
m_ver[0]= v0;
|
||||
m_ver[1]= v1;
|
||||
m_ver[2]= v2;
|
||||
}
|
||||
Version(const char *version, const char **endptr);
|
||||
const uchar& operator [] (size_t i) const
|
||||
{
|
||||
DBUG_ASSERT(i < 3);
|
||||
return m_ver[i];
|
||||
}
|
||||
bool operator<(const Version &other) const { return cmp(other) < 0; }
|
||||
bool operator>(const Version &other) const { return cmp(other) > 0; }
|
||||
bool operator<=(const Version &other) const { return cmp(other) <= 0; }
|
||||
bool operator>=(const Version &other) const { return cmp(other) >= 0; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
@class Format_description_log_event
|
||||
|
||||
|
@ -2754,10 +2786,17 @@ public:
|
|||
by the checksum alg decription byte
|
||||
*/
|
||||
uint8 *post_header_len;
|
||||
struct master_version_split {
|
||||
class master_version_split: public Version {
|
||||
public:
|
||||
enum {KIND_MYSQL, KIND_MARIADB};
|
||||
int kind;
|
||||
uchar ver[3];
|
||||
master_version_split() :kind(KIND_MARIADB) { }
|
||||
master_version_split(const char *version);
|
||||
bool version_is_valid() const
|
||||
{
|
||||
/* It is invalid only when all version numbers are 0 */
|
||||
return !(m_ver[0] == 0 && m_ver[1] == 0 && m_ver[2] == 0);
|
||||
}
|
||||
};
|
||||
master_version_split server_version_split;
|
||||
const uint8 *event_type_permutation;
|
||||
|
@ -2781,17 +2820,9 @@ public:
|
|||
(post_header_len != NULL));
|
||||
}
|
||||
|
||||
bool version_is_valid() const
|
||||
{
|
||||
/* It is invalid only when all version numbers are 0 */
|
||||
return !(server_version_split.ver[0] == 0 &&
|
||||
server_version_split.ver[1] == 0 &&
|
||||
server_version_split.ver[2] == 0);
|
||||
}
|
||||
|
||||
bool is_valid() const
|
||||
{
|
||||
return header_is_valid() && version_is_valid();
|
||||
return header_is_valid() && server_version_split.version_is_valid();
|
||||
}
|
||||
|
||||
int get_data_size()
|
||||
|
|
18
sql/slave.cc
18
sql/slave.cc
|
@ -7910,8 +7910,8 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
|
|||
{
|
||||
struct st_version_range_for_one_bug {
|
||||
uint bug_id;
|
||||
const uchar introduced_in[3]; // first version with bug
|
||||
const uchar fixed_in[3]; // first version with fix
|
||||
Version introduced_in; // first version with bug
|
||||
Version fixed_in; // first version with fix
|
||||
};
|
||||
static struct st_version_range_for_one_bug versions_for_all_bugs[]=
|
||||
{
|
||||
|
@ -7921,19 +7921,17 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report,
|
|||
{33029, { 5, 1, 0 }, { 5, 1, 12 } },
|
||||
{37426, { 5, 1, 0 }, { 5, 1, 26 } },
|
||||
};
|
||||
const uchar *master_ver=
|
||||
rli->relay_log.description_event_for_exec->server_version_split.ver;
|
||||
|
||||
DBUG_ASSERT(sizeof(rli->relay_log.description_event_for_exec->server_version_split.ver) == 3);
|
||||
const Version &master_ver=
|
||||
rli->relay_log.description_event_for_exec->server_version_split;
|
||||
|
||||
for (uint i= 0;
|
||||
i < sizeof(versions_for_all_bugs)/sizeof(*versions_for_all_bugs);i++)
|
||||
{
|
||||
const uchar *introduced_in= versions_for_all_bugs[i].introduced_in,
|
||||
*fixed_in= versions_for_all_bugs[i].fixed_in;
|
||||
const Version &introduced_in= versions_for_all_bugs[i].introduced_in;
|
||||
const Version &fixed_in= versions_for_all_bugs[i].fixed_in;
|
||||
if ((versions_for_all_bugs[i].bug_id == bug_id) &&
|
||||
(memcmp(introduced_in, master_ver, 3) <= 0) &&
|
||||
(memcmp(fixed_in, master_ver, 3) > 0) &&
|
||||
introduced_in <= master_ver &&
|
||||
fixed_in > master_ver &&
|
||||
(pred == NULL || (*pred)(param)))
|
||||
{
|
||||
if (!report)
|
||||
|
|
|
@ -156,9 +156,15 @@ enum enum_binlog_row_image {
|
|||
#define MODE_HIGH_NOT_PRECEDENCE (1ULL << 29)
|
||||
#define MODE_NO_ENGINE_SUBSTITUTION (1ULL << 30)
|
||||
#define MODE_PAD_CHAR_TO_FULL_LENGTH (1ULL << 31)
|
||||
/* SQL mode bits defined above are common for MariaDB and MySQL */
|
||||
#define MODE_MASK_MYSQL_COMPATIBLE 0xFFFFFFFFULL
|
||||
/* The following modes are specific to MariaDB */
|
||||
#define MODE_EMPTY_STRING_IS_NULL (1ULL << 32)
|
||||
#define MODE_SIMULTANEOUS_ASSIGNMENT (1ULL << 33)
|
||||
#define MODE_TIME_ROUND_FRACTIONAL (1ULL << 34)
|
||||
/* The following modes are specific to MySQL */
|
||||
#define MODE_MYSQL80_TIME_TRUNCATE_FRACTIONAL (1ULL << 32)
|
||||
|
||||
|
||||
/* Bits for different old style modes */
|
||||
#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0)
|
||||
|
|
Loading…
Reference in a new issue