Commit 617ea4d6 authored by sven@riska.(none)'s avatar sven@riska.(none)

BUG#27779: Slave cannot read old rows log events.

Problem: Replication fails when master is mysql-5.1-wl2325-5.0-drop6 and
slave is mysql-5.1-new-rpl. The reason is that, in
mysql-5.1-wl2325-5.0-drop6, the event type id's were different than in
mysql-5.1-new-rpl.
Fix (in mysql-5.1-new-rpl):
 (1) detect that the server that generated the events uses the old
format, by checking the server version of the format_description_log_event
This patch recognizes mysql-5.1-wl2325-5.0-drop6p13-alpha,
mysql-5.1-wl2325-5.0-drop6, mysql-5.1-wl2325-5.0, mysql-5.1-wl2325-no-dd.
 (2) if the generating server is old, map old event types to new event
types using a permutation array.

I've also added a test case which reads binlogs for four different
versions.
parent fabaa50c
DROP TABLE IF EXISTS t1, t2, t3;
==== Read modern binlog (version 5.1.23) ====
SELECT * FROM t1 ORDER BY a;
a b
0 last_insert_id
1 one
3 last stm in trx: next event should be xid
4 four
674568 random
SELECT * FROM t2 ORDER BY a;
a b
3 first stm in trx
SELECT COUNT(*) FROM t3;
COUNT(*)
17920
DROP TABLE t1, t2, t3;
==== Read binlog from version 5.1.17 ====
SELECT * FROM t1 ORDER BY a;
a b
0 last_insert_id
1 one
3 last stm in trx: next event should be xid
4 four
764247 random
SELECT * FROM t2 ORDER BY a;
a b
3 first stm in trx
SELECT COUNT(*) FROM t3;
COUNT(*)
17920
DROP TABLE t1, t2, t3;
==== Read binlog from alcatel tree (mysql-5.1-wl2325-5.0-drop6) ====
SELECT * FROM t1 ORDER BY a;
a b
0 last_insert_id
1 one
3 last stm in trx: next event should be xid
4 four
781729 random
SELECT * FROM t2 ORDER BY a;
a b
3 first stm in trx
SELECT COUNT(*) FROM t3;
COUNT(*)
17920
DROP TABLE t1, t2, t3;
==== Read binlog from ndb tree (mysql-5.1-telco-6.1) ====
SELECT * FROM t1 ORDER BY a;
a b
0 last_insert_id
1 one
3 last stm in trx: next event should be xid
4 four
703356 random
SELECT * FROM t2 ORDER BY a;
a b
3 first stm in trx
SELECT COUNT(*) FROM t3;
COUNT(*)
17920
DROP TABLE t1, t2, t3;
# Test that old binlog formats can be read.
# Some previous versions of MySQL use their own binlog format,
# especially in row-based replication. This test uses saved binlogs
# from those old versions to test that we can replicate from old
# versions to the present version.
# Replicating from old versions to new versions is necessary in an
# online upgrade scenario, where the .
# The previous versions we currently test are:
# - version 5.1.17 and earlier trees
# - mysql-5.1-wl2325-xxx trees (AKA alcatel trees)
# - mysql-5.1-telco-6.1 trees (AKA ndb trees)
# For completeness, we also test mysql-5.1-new_rpl, which is supposed
# to be the "correct" version.
# All binlogs were generated with the same commands (listed at the end
# of this test for reference). The binlogs contain the following
# events: Table_map, Write_rows, Update_rows, Delete_rows Query, Xid,
# User_var, Int_var, Rand, Begin_load, Append_file, Execute_load.
# Related bugs: BUG#27779, BUG#31581, BUG#31582, BUG#31583, BUG#32407
--disable_warnings
DROP TABLE IF EXISTS t1, t2, t3;
--echo ==== Read modern binlog (version 5.1.23) ====
# Read binlog.
--exec $MYSQL_BINLOG suite/binlog/std_data/binlog_old_version_5_1_23.000001 | $MYSQL
# Show result.
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT COUNT(*) FROM t3;
# Reset.
DROP TABLE t1, t2, t3;
--echo ==== Read binlog from version 5.1.17 ====
# Read binlog.
--exec $MYSQL_BINLOG suite/binlog/std_data/binlog_old_version_5_1_17.000001 | $MYSQL
# Show result.
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT COUNT(*) FROM t3;
# Reset.
DROP TABLE t1, t2, t3;
--echo ==== Read binlog from alcatel tree (mysql-5.1-wl2325-5.0-drop6) ====
# In this version, it was not possible to switch between row-based and
# statement-based binlogging without restarting the server. So, we
# have two binlogs; one for row based and one for statement based
# replication.
# Read rbr binlog.
--exec $MYSQL_BINLOG suite/binlog/std_data/binlog_old_version_5_1-wl2325_row.000001 | $MYSQL
# Read stm binlog.
--exec $MYSQL_BINLOG suite/binlog/std_data/binlog_old_version_5_1-wl2325_stm.000001 | $MYSQL
# Show result.
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT COUNT(*) FROM t3;
# Reset.
DROP TABLE t1, t2, t3;
--echo ==== Read binlog from ndb tree (mysql-5.1-telco-6.1) ====
# Read binlog.
--exec $MYSQL_BINLOG suite/binlog/std_data/binlog_old_version_5_1-telco.000001 | $MYSQL
# Show resulting tablea.
SELECT * FROM t1 ORDER BY a;
SELECT * FROM t2 ORDER BY a;
SELECT COUNT(*) FROM t3;
# Reset.
DROP TABLE t1, t2, t3;
#### The following commands were used to generate the binlogs ####
#
#source include/master-slave.inc;
#
## ==== initialize ====
#USE test;
#CREATE TABLE t1 (a int, b char(50)) ENGINE = MyISAM;
#CREATE TABLE t2 (a int, b char(50)) ENGINE = InnoDB;
#CREATE TABLE t3 (a char(20));
#
#
## ==== row based tests ====
#SET BINLOG_FORMAT='row';
#
## ---- get write, update, and delete rows events ----
#INSERT INTO t1 VALUES (0, 'one'), (1, 'two');
#UPDATE t1 SET a=a+1;
#DELETE FROM t1 WHERE a=2;
#
#
## ==== statement based tests ====
#SET BINLOG_FORMAT = 'statement';
#
## ---- get xid events ----
#BEGIN;
#INSERT INTO t2 VALUES (3, 'first stm in trx');
#INSERT INTO t1 VALUES (3, 'last stm in trx: next event should be xid');
#COMMIT;
#
## ---- get user var events ----
#SET @x = 4;
#INSERT INTO t1 VALUES (@x, 'four');
#
## ---- get rand event ----
#INSERT INTO t1 VALUES (RAND() * 1000000, 'random');
#
## ---- get intvar event ----
#INSERT INTO t1 VALUES (LAST_INSERT_ID(), 'last_insert_id');
#
## ---- get begin, append and execute load events ----
## double the file until we have more than 2^17 bytes, so that the
## event has to be split and we can use Append_file_log_event.
#
#SET SQL_LOG_BIN=0;
#CREATE TABLE temp (a char(20));
#LOAD DATA INFILE '../std_data_ln/words.dat' INTO TABLE temp;
#INSERT INTO temp SELECT * FROM temp;
#INSERT INTO temp SELECT * FROM temp;
#INSERT INTO temp SELECT * FROM temp;
#INSERT INTO temp SELECT * FROM temp;
#INSERT INTO temp SELECT * FROM temp;
#INSERT INTO temp SELECT * FROM temp;
#INSERT INTO temp SELECT * FROM temp;
#INSERT INTO temp SELECT * FROM temp;
#SELECT a FROM temp INTO OUTFILE 'big_file.dat';
#DROP TABLE temp;
#SET SQL_LOG_BIN=1;
#
#LOAD DATA INFILE 'big_file.dat' INTO TABLE t3;
#
#SELECT * FROM t1 ORDER BY a;
#SELECT * FROM t2 ORDER BY a;
#SELECT COUNT(*) FROM t3;
...@@ -1071,6 +1071,29 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len, ...@@ -1071,6 +1071,29 @@ Log_event* Log_event::read_log_event(const char* buf, uint event_len,
} }
else else
{ {
/*
In some previuos versions (see comment in
Format_description_log_event::Format_description_log_event(char*,...)),
event types were assigned different id numbers than in the
present version. In order to replicate from such versions to the
present version, we must map those event type id's to our event
type id's. The mapping is done with the event_type_permutation
array, which was set up when the Format_description_log_event
was read.
*/
if (description_event->event_type_permutation)
{
IF_DBUG({
int new_event_type=
description_event->event_type_permutation[event_type];
DBUG_PRINT("info",
("converting event type %d to %d (%s)",
event_type, new_event_type,
get_type_str((Log_event_type)new_event_type)));
});
event_type= description_event->event_type_permutation[event_type];
}
switch(event_type) { switch(event_type) {
case QUERY_EVENT: case QUERY_EVENT:
ev = new Query_log_event(buf, event_len, description_event, QUERY_EVENT); ev = new Query_log_event(buf, event_len, description_event, QUERY_EVENT);
...@@ -2771,7 +2794,7 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli) ...@@ -2771,7 +2794,7 @@ int Start_log_event_v3::do_apply_event(Relay_log_info const *rli)
Format_description_log_event:: Format_description_log_event::
Format_description_log_event(uint8 binlog_ver, const char* server_ver) Format_description_log_event(uint8 binlog_ver, const char* server_ver)
:Start_log_event_v3() :Start_log_event_v3(), event_type_permutation(0)
{ {
binlog_version= binlog_ver; binlog_version= binlog_ver;
switch (binlog_ver) { switch (binlog_ver) {
...@@ -2896,7 +2919,7 @@ Format_description_log_event(const char* buf, ...@@ -2896,7 +2919,7 @@ Format_description_log_event(const char* buf,
const const
Format_description_log_event* Format_description_log_event*
description_event) description_event)
:Start_log_event_v3(buf, description_event) :Start_log_event_v3(buf, description_event), event_type_permutation(0)
{ {
DBUG_ENTER("Format_description_log_event::Format_description_log_event(char*,...)"); DBUG_ENTER("Format_description_log_event::Format_description_log_event(char*,...)");
buf+= LOG_EVENT_MINIMAL_HEADER_LEN; buf+= LOG_EVENT_MINIMAL_HEADER_LEN;
...@@ -2911,6 +2934,65 @@ Format_description_log_event(const char* buf, ...@@ -2911,6 +2934,65 @@ Format_description_log_event(const char* buf,
number_of_event_types* number_of_event_types*
sizeof(*post_header_len), MYF(0)); sizeof(*post_header_len), MYF(0));
calc_server_version_split(); calc_server_version_split();
/*
In some previous versions, the events were given other event type
id numbers than in the present version. When replicating from such
a version, we therefore set up an array that maps those id numbers
to the id numbers of the present server.
If post_header_len is null, it means malloc failed, and is_valid
will fail, so there is no need to do anything.
The trees which have wrong event id's are:
mysql-5.1-wl2325-5.0-drop6p13-alpha, mysql-5.1-wl2325-5.0-drop6,
mysql-5.1-wl2325-5.0, mysql-5.1-wl2325-no-dd (`grep -C2
BEGIN_LOAD_QUERY_EVENT /home/bk/ * /sql/log_event.h`). The
corresponding version (`grep mysql, configure.in` in those trees)
strings are 5.2.2-a_drop6p13-alpha, 5.2.2-a_drop6p13c,
5.1.5-a_drop5p20, 5.1.2-a_drop5p5.
*/
if (post_header_len &&
(strncmp(server_version, "5.1.2-a_drop5", 13) == 0 ||
strncmp(server_version, "5.1.5-a_drop5", 13) == 0 ||
strncmp(server_version, "5.2.2-a_drop6", 13) == 0))
{
if (number_of_event_types != 22)
{
DBUG_PRINT("info", (" number_of_event_types=%d",
number_of_event_types));
/* this makes is_valid() return false. */
my_free(post_header_len, MYF(MY_ALLOW_ZERO_PTR));
post_header_len= NULL;
DBUG_VOID_RETURN;
}
static const uint8 perm[23]=
{
UNKNOWN_EVENT, START_EVENT_V3, QUERY_EVENT, STOP_EVENT, ROTATE_EVENT,
INTVAR_EVENT, LOAD_EVENT, SLAVE_EVENT, CREATE_FILE_EVENT,
APPEND_BLOCK_EVENT, EXEC_LOAD_EVENT, DELETE_FILE_EVENT,
NEW_LOAD_EVENT,
RAND_EVENT, USER_VAR_EVENT,
FORMAT_DESCRIPTION_EVENT,
TABLE_MAP_EVENT,
PRE_GA_WRITE_ROWS_EVENT,
PRE_GA_UPDATE_ROWS_EVENT,
PRE_GA_DELETE_ROWS_EVENT,
XID_EVENT,
BEGIN_LOAD_QUERY_EVENT,
EXECUTE_LOAD_QUERY_EVENT,
};
event_type_permutation= perm;
/*
Since we use (permuted) event id's to index the post_header_len
array, we need to permute the post_header_len array too.
*/
uint8 post_header_len_temp[23];
for (int i= 1; i < 23; i++)
post_header_len_temp[perm[i] - 1]= post_header_len[i - 1];
for (int i= 0; i < 22; i++)
post_header_len[i] = post_header_len_temp[i];
}
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
......
...@@ -2106,12 +2106,16 @@ class Format_description_log_event: public Start_log_event_v3 ...@@ -2106,12 +2106,16 @@ class Format_description_log_event: public Start_log_event_v3
/* The list of post-headers' lengthes */ /* The list of post-headers' lengthes */
uint8 *post_header_len; uint8 *post_header_len;
uchar server_version_split[3]; uchar server_version_split[3];
const uint8 *event_type_permutation;
Format_description_log_event(uint8 binlog_ver, const char* server_ver=0); Format_description_log_event(uint8 binlog_ver, const char* server_ver=0);
Format_description_log_event(const char* buf, uint event_len, Format_description_log_event(const char* buf, uint event_len,
const Format_description_log_event const Format_description_log_event
*description_event); *description_event);
~Format_description_log_event() { my_free((uchar*)post_header_len, MYF(0)); } ~Format_description_log_event()
{
my_free((uchar*)post_header_len, MYF(MY_ALLOW_ZERO_PTR));
}
Log_event_type get_type_code() { return FORMAT_DESCRIPTION_EVENT;} Log_event_type get_type_code() { return FORMAT_DESCRIPTION_EVENT;}
#ifndef MYSQL_CLIENT #ifndef MYSQL_CLIENT
bool write(IO_CACHE* file); bool write(IO_CACHE* file);
......
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