Commit 269da4bf authored by Alexander Barkov's avatar Alexander Barkov

MDEV-5377 Row-based replication of MariaDB temporal data types with FSP>0 into...

MDEV-5377 Row-based replication of MariaDB temporal data types with FSP>0 into a different column type
parent 88a480ce
#
# 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
#
# 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
--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
--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= ...@@ -101,16 +101,11 @@ TYPELIB binlog_checksum_typelib=
TODO: correct the constant when it has been determined TODO: correct the constant when it has been determined
(which main tree to push and when) (which main tree to push and when)
*/ */
const uchar checksum_version_split_mysql[3]= {5, 6, 1}; const Version checksum_version_split_mysql(5, 6, 1);
const ulong checksum_version_product_mysql= const Version checksum_version_split_mariadb(5, 3, 0);
(checksum_version_split_mysql[0] * 256 +
checksum_version_split_mysql[1]) * 256 + // First MySQL version with fraction seconds
checksum_version_split_mysql[2]; const Version fsp_version_split_mysql(5, 6, 0);
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];
#if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION) #if !defined(MYSQL_CLIENT) && defined(HAVE_REPLICATION)
static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD* thd); static int rows_event_stmt_cleanup(rpl_group_info *rgi, THD* thd);
...@@ -4541,6 +4536,7 @@ code_name(int code) ...@@ -4541,6 +4536,7 @@ code_name(int code)
} }
#endif #endif
/** /**
Macro to check that there is enough space to read from memory. 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, ...@@ -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 Layout for the data buffer is as follows
+--------+-----------+------+------+---------+----+-------+ +--------+-----------+------+------+---------+----+-------+
...@@ -6549,26 +6569,24 @@ bool Format_description_log_event::start_decryption(Start_encryption_log_event* ...@@ -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); return crypto_data.init(sele->crypto_scheme, sele->key_version);
} }
static inline void
do_server_version_split(char* version, Version::Version(const char *version, const char **endptr)
Format_description_log_event::master_version_split *split_versions)
{ {
char *p= version, *r; const char *p= version;
ulong number; ulong number;
for (uint i= 0; i<=2; i++) for (uint i= 0; i<=2; i++)
{ {
char *r;
number= strtoul(p, &r, 10); number= strtoul(p, &r, 10);
/* /*
It is an invalid version if any version number greater than 255 or It is an invalid version if any version number greater than 255 or
first number is not followed by '.'. first number is not followed by '.'.
*/ */
if (number < 256 && (*r == '.' || i != 0)) if (number < 256 && (*r == '.' || i != 0))
split_versions->ver[i]= (uchar) number; m_ver[i]= (uchar) number;
else else
{ {
split_versions->ver[0]= 0; *this= Version();
split_versions->ver[1]= 0;
split_versions->ver[2]= 0;
break; break;
} }
...@@ -6576,12 +6594,19 @@ do_server_version_split(char* version, ...@@ -6576,12 +6594,19 @@ do_server_version_split(char* version,
if (*r == '.') if (*r == '.')
p++; // skip the dot 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) if (strstr(p, "MariaDB") != 0 || strstr(p, "-maria-") != 0)
split_versions->kind= kind= KIND_MARIADB;
Format_description_log_event::master_version_split::KIND_MARIADB;
else else
split_versions->kind= kind= KIND_MYSQL;
Format_description_log_event::master_version_split::KIND_MYSQL;
} }
...@@ -6595,20 +6620,14 @@ do_server_version_split(char* version, ...@@ -6595,20 +6620,14 @@ do_server_version_split(char* version,
*/ */
void Format_description_log_event::calc_server_version_split() 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:" DBUG_PRINT("info",("Format_description_log_event::server_version_split:"
" '%s' %d %d %d", server_version, " '%s' %d %d %d", server_version,
server_version_split.ver[0], server_version_split[0],
server_version_split.ver[1], server_version_split.ver[2])); 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 @return TRUE is the event's version is earlier than one that introduced
...@@ -6618,9 +6637,9 @@ bool ...@@ -6618,9 +6637,9 @@ bool
Format_description_log_event::is_version_before_checksum(const master_version_split Format_description_log_event::is_version_before_checksum(const master_version_split
*version_split) *version_split)
{ {
return version_product(version_split) < return *version_split <
(version_split->kind == master_version_split::KIND_MARIADB ? (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) ...@@ -6636,7 +6655,6 @@ enum enum_binlog_checksum_alg get_checksum_alg(const char* buf, ulong len)
{ {
enum enum_binlog_checksum_alg ret; enum enum_binlog_checksum_alg ret;
char version[ST_SERVER_VER_LEN]; char version[ST_SERVER_VER_LEN];
Format_description_log_event::master_version_split version_split;
DBUG_ENTER("get_checksum_alg"); DBUG_ENTER("get_checksum_alg");
DBUG_ASSERT(buf[EVENT_TYPE_OFFSET] == FORMAT_DESCRIPTION_EVENT); 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) ...@@ -6646,7 +6664,7 @@ enum enum_binlog_checksum_alg get_checksum_alg(const char* buf, ulong len)
ST_SERVER_VER_LEN); ST_SERVER_VER_LEN);
version[ST_SERVER_VER_LEN - 1]= 0; 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) ret= Format_description_log_event::is_version_before_checksum(&version_split)
? BINLOG_CHECKSUM_ALG_UNDEF ? BINLOG_CHECKSUM_ALG_UNDEF
: (enum_binlog_checksum_alg)buf[len - BINLOG_CHECKSUM_LEN - BINLOG_CHECKSUM_ALG_DESC_LEN]; : (enum_binlog_checksum_alg)buf[len - BINLOG_CHECKSUM_LEN - BINLOG_CHECKSUM_ALG_DESC_LEN];
......
...@@ -2728,6 +2728,38 @@ class Start_encryption_log_event : public Log_event ...@@ -2728,6 +2728,38 @@ class Start_encryption_log_event : public Log_event
}; };
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 @class Format_description_log_event
...@@ -2754,10 +2786,17 @@ class Format_description_log_event: public Start_log_event_v3 ...@@ -2754,10 +2786,17 @@ class Format_description_log_event: public Start_log_event_v3
by the checksum alg decription byte by the checksum alg decription byte
*/ */
uint8 *post_header_len; uint8 *post_header_len;
struct master_version_split { class master_version_split: public Version {
public:
enum {KIND_MYSQL, KIND_MARIADB}; enum {KIND_MYSQL, KIND_MARIADB};
int kind; 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; master_version_split server_version_split;
const uint8 *event_type_permutation; const uint8 *event_type_permutation;
...@@ -2781,17 +2820,9 @@ class Format_description_log_event: public Start_log_event_v3 ...@@ -2781,17 +2820,9 @@ class Format_description_log_event: public Start_log_event_v3
(post_header_len != NULL)); (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 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() int get_data_size()
......
...@@ -7910,8 +7910,8 @@ bool rpl_master_has_bug(const Relay_log_info *rli, uint bug_id, bool report, ...@@ -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 { struct st_version_range_for_one_bug {
uint bug_id; uint bug_id;
const uchar introduced_in[3]; // first version with bug Version introduced_in; // first version with bug
const uchar fixed_in[3]; // first version with fix Version fixed_in; // first version with fix
}; };
static struct st_version_range_for_one_bug versions_for_all_bugs[]= 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, ...@@ -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 } }, {33029, { 5, 1, 0 }, { 5, 1, 12 } },
{37426, { 5, 1, 0 }, { 5, 1, 26 } }, {37426, { 5, 1, 0 }, { 5, 1, 26 } },
}; };
const uchar *master_ver= const Version &master_ver=
rli->relay_log.description_event_for_exec->server_version_split.ver; rli->relay_log.description_event_for_exec->server_version_split;
DBUG_ASSERT(sizeof(rli->relay_log.description_event_for_exec->server_version_split.ver) == 3);
for (uint i= 0; for (uint i= 0;
i < sizeof(versions_for_all_bugs)/sizeof(*versions_for_all_bugs);i++) i < sizeof(versions_for_all_bugs)/sizeof(*versions_for_all_bugs);i++)
{ {
const uchar *introduced_in= versions_for_all_bugs[i].introduced_in, const Version &introduced_in= versions_for_all_bugs[i].introduced_in;
*fixed_in= versions_for_all_bugs[i].fixed_in; const Version &fixed_in= versions_for_all_bugs[i].fixed_in;
if ((versions_for_all_bugs[i].bug_id == bug_id) && if ((versions_for_all_bugs[i].bug_id == bug_id) &&
(memcmp(introduced_in, master_ver, 3) <= 0) && introduced_in <= master_ver &&
(memcmp(fixed_in, master_ver, 3) > 0) && fixed_in > master_ver &&
(pred == NULL || (*pred)(param))) (pred == NULL || (*pred)(param)))
{ {
if (!report) if (!report)
......
...@@ -156,9 +156,15 @@ enum enum_binlog_row_image { ...@@ -156,9 +156,15 @@ enum enum_binlog_row_image {
#define MODE_HIGH_NOT_PRECEDENCE (1ULL << 29) #define MODE_HIGH_NOT_PRECEDENCE (1ULL << 29)
#define MODE_NO_ENGINE_SUBSTITUTION (1ULL << 30) #define MODE_NO_ENGINE_SUBSTITUTION (1ULL << 30)
#define MODE_PAD_CHAR_TO_FULL_LENGTH (1ULL << 31) #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_EMPTY_STRING_IS_NULL (1ULL << 32)
#define MODE_SIMULTANEOUS_ASSIGNMENT (1ULL << 33) #define MODE_SIMULTANEOUS_ASSIGNMENT (1ULL << 33)
#define MODE_TIME_ROUND_FRACTIONAL (1ULL << 34) #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 */ /* Bits for different old style modes */
#define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0) #define OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE (1 << 0)
......
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