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=
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 @@ 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
......@@ -2754,10 +2786,17 @@ class Format_description_log_event: public Start_log_event_v3
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 @@ class Format_description_log_event: public Start_log_event_v3
(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()
......
......@@ -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)
......
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