Commit 2a089557 authored by Mats Kindahl's avatar Mats Kindahl
Browse files

BUG#37426: RBR breaks for CHAR() UTF-8 fields > 85 chars

      
In order to handle CHAR() fields, 8 bits were reserved for
the size of the CHAR field. However, instead of denoting the
number of characters in the field, field_length was used which
denotes the number of bytes in the field.

Since UTF-8 fields can have three bytes per character (and
has been extended to have four bytes per character in 6.0),
an extra two bits have been encoded in the field metadata
work for fields of type Field_string (i.e., CHAR fields).

Since the metadata word is filled, the extra bits have been
encoded in the upper 4 bits of the real type (the most 
significant byte of the metadata word) by computing the
bitwise xor of the extra two bits. Since the upper 4 bits
of the real type always is 1111 for Field_string, this 
means that for fields of length <256, the encoding is
identical to the encoding used in pre-5.1.26 servers, but
for lengths of 256 or more, an unrecognized type is formed,
causing an old slave (that does not handle lengths of 256
or more) to stop.
parent 5672b953
Loading
Loading
Loading
Loading
+148 −1
Original line number Diff line number Diff line
@@ -259,7 +259,7 @@ DELETE FROM t1;
query_vertical SELECT COUNT(*) FROM t1 ORDER BY c1,c2;
sync_slave_with_master;
set @@global.slave_exec_mode= default;
let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Errno, 1);
let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
disable_query_log;
eval SELECT "$last_error" AS Last_SQL_Error;
enable_query_log;
@@ -272,3 +272,150 @@ query_vertical SELECT COUNT(*) FROM t1 ORDER BY c1,c2;
connection master;
DROP TABLE IF EXISTS t1,t2,t3,t4,t5,t6,t7,t8;
sync_slave_with_master;

#
# BUG#37426: RBR breaks for CHAR() UTF8 fields > 85 chars
#

# We have 4 combinations to test with respect to the field length
# (i.e., the number of bytes) of the CHAR fields:
#
# 1. Replicating from CHAR<256 to CHAR<256 
# 2. Replicating from CHAR<256 to CHAR>255
# 3. Replicating from CHAR>255 to CHAR<256
# 4. Replicating from CHAR>255 to CHAR>255

# We also make a special case of using the max size of a field on the
# master, i.e. CHAR(255) in UTF-8, giving another three cases.
#
# 5. Replicating UTF-8 CHAR(255) to CHAR(<256)
# 6. Replicating UTF-8 CHAR(255) to CHAR(>255)
# 7. Replicating UTF-8 CHAR(255) to CHAR(255) UTF-8

connection master;
CREATE TABLE t1 (i INT NOT NULL,
                 c CHAR(16) CHARACTER SET utf8 NOT NULL,
                 j INT NOT NULL);

CREATE TABLE t2 (i INT NOT NULL,
                 c CHAR(16) CHARACTER SET utf8 NOT NULL,
                 j INT NOT NULL);

sync_slave_with_master;
ALTER TABLE t2 MODIFY c CHAR(128) CHARACTER SET utf8 NOT NULL;

connection master;
CREATE TABLE t3 (i INT NOT NULL,
                 c CHAR(128) CHARACTER SET utf8 NOT NULL,
                 j INT NOT NULL);
sync_slave_with_master;
ALTER TABLE t3 MODIFY c CHAR(16) CHARACTER SET utf8 NOT NULL;

connection master;
CREATE TABLE t4 (i INT NOT NULL,
                 c CHAR(128) CHARACTER SET utf8 NOT NULL,
                 j INT NOT NULL);

CREATE TABLE t5 (i INT NOT NULL,
                 c CHAR(255) CHARACTER SET utf8 NOT NULL,
                 j INT NOT NULL);
sync_slave_with_master;
ALTER TABLE t5 MODIFY c CHAR(16) CHARACTER SET utf8 NOT NULL;

connection master;
CREATE TABLE t6 (i INT NOT NULL,
                 c CHAR(255) CHARACTER SET utf8 NOT NULL,
                 j INT NOT NULL);
sync_slave_with_master;
ALTER TABLE t6 MODIFY c CHAR(128) CHARACTER SET utf8 NOT NULL;

connection master;
CREATE TABLE t7 (i INT NOT NULL,
                 c CHAR(255) CHARACTER SET utf8 NOT NULL,
                 j INT NOT NULL);

--echo [expecting slave to replicate correctly]
connection master;
INSERT INTO t1 VALUES (1, "", 1);
INSERT INTO t1 VALUES (2, repeat(_utf8'a', 16), 2);

let $diff_table_1=master:test.t1;
let $diff_table_2=slave:test.t1;
source include/diff_tables.inc;

--echo [expecting slave to replicate correctly]
connection master;
INSERT INTO t2 VALUES (1, "", 1);
INSERT INTO t2 VALUES (2, repeat(_utf8'a', 16), 2);

let $diff_table_1=master:test.t2;
let $diff_table_2=slave:test.t2;
source include/diff_tables.inc;

--echo [expecting slave to stop]
connection master;
INSERT INTO t3 VALUES (1, "", 1);
INSERT INTO t3 VALUES (2, repeat(_utf8'a', 128), 2);

connection slave;
source include/wait_for_slave_sql_to_stop.inc;
let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
disable_query_log;
eval SELECT "$last_error" AS Last_SQL_Error;
enable_query_log;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
START SLAVE;
source include/wait_for_slave_to_start.inc;

--echo [expecting slave to replicate correctly]
connection master;
INSERT INTO t4 VALUES (1, "", 1);
INSERT INTO t4 VALUES (2, repeat(_utf8'a', 128), 2);

let $diff_table_1=master:test.t4;
let $diff_table_2=slave:test.t4;
source include/diff_tables.inc;

--echo [expecting slave to stop]
connection master;
INSERT INTO t5 VALUES (1, "", 1);
INSERT INTO t5 VALUES (2, repeat(_utf8'a', 255), 2);

connection slave;
source include/wait_for_slave_sql_to_stop.inc;
let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
disable_query_log;
eval SELECT "$last_error" AS Last_SQL_Error;
enable_query_log;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
START SLAVE;
source include/wait_for_slave_to_start.inc;

--echo [expecting slave to stop]
connection master;
INSERT INTO t6 VALUES (1, "", 1);
INSERT INTO t6 VALUES (2, repeat(_utf8'a', 255), 2);

connection slave;
source include/wait_for_slave_sql_to_stop.inc;
let $last_error = query_get_value("SHOW SLAVE STATUS", Last_SQL_Error, 1);
disable_query_log;
eval SELECT "$last_error" AS Last_SQL_Error;
enable_query_log;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER=8;
START SLAVE;
source include/wait_for_slave_to_start.inc;

--echo [expecting slave to replicate correctly]
connection master;
INSERT INTO t7 VALUES (1, "", 1);
INSERT INTO t7 VALUES (2, repeat(_utf8'a', 255), 2);

let $diff_table_1=master:test.t7;
let $diff_table_2=slave:test.t7;
source include/diff_tables.inc;

connection master;
drop table t1, t2, t3, t4, t5, t6, t7;
sync_slave_with_master;
+25 −1
Original line number Diff line number Diff line
@@ -66,4 +66,28 @@ a
1
1
3
drop table t1;
CREATE TABLE char128_utf8 ( 
i1 INT NOT NULL, 
c CHAR(128) CHARACTER SET utf8 NOT NULL, 
i2 INT NOT NULL);
CREATE TABLE char63_utf8 (
i1 INT NOT NULL,
c CHAR(63) CHARACTER SET utf8 NOT NULL,
i2 INT NOT NULL);
BINLOG '
MuNkSA8BAAAAZgAAAGoAAAAAAAQANS4xLjI1LXJjLWRlYnVnLWxvZwAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAy42RIEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC
';
BINLOG '
3u9kSBMBAAAANgAAAJYBAAAAABAAAAAAAAAABHRlc3QAC2NoYXI2M191dGY4AAMD/gMC/r0A
3u9kSBcBAAAAKgAAAMABAAAQABAAAAAAAAEAA//4AQAAAAMxMjMBAAAA
';
SELECT * FROM char63_utf8;
i1	c	i2
1	123	1
BINLOG '
iONkSBMBAAAANwAAAJkBAAAAABAAAAAAAAAABHRlc3QADGNoYXIxMjhfdXRmOAADA/4DAv6AAA==
iONkSBcBAAAAKwAAAMQBAAAQABAAAAAAAAEAA//4AQAAAAMAMTIzAQAAAA==
';
ERROR HY000: master may suffer from http://bugs.mysql.com/bug.php?id=37426 so slave stops; check error log on slave for more info
drop table t1, char63_utf8, char128_utf8;
+45 −2
Original line number Diff line number Diff line
@@ -104,6 +104,49 @@ Dl1YRxcBAAAAIgAAAFYBAAAQABAAAAAAAAEAAf/+BQAAAA==
# the above line should fail and 5 should not be in the binlog.
select * from t1;

# Test that BUG#37426 is triggered.

# clean up
drop table t1;
CREATE TABLE char128_utf8 ( 
    i1 INT NOT NULL, 
    c CHAR(128) CHARACTER SET utf8 NOT NULL, 
    i2 INT NOT NULL);
CREATE TABLE char63_utf8 (
      i1 INT NOT NULL,
      c CHAR(63) CHARACTER SET utf8 NOT NULL,
      i2 INT NOT NULL);

#
# This is the format description log event
#

BINLOG '
MuNkSA8BAAAAZgAAAGoAAAAAAAQANS4xLjI1LXJjLWRlYnVnLWxvZwAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAy42RIEzgNAAgAEgAEBAQEEgAAUwAEGggAAAAICAgC
';

# ... this event corresponding to
#
#    INSERT INTO char63_utf8 VALUES ( 1, "123", 1 )
#
# The binlog event below shall not trigger the bug check

BINLOG '
3u9kSBMBAAAANgAAAJYBAAAAABAAAAAAAAAABHRlc3QAC2NoYXI2M191dGY4AAMD/gMC/r0A
3u9kSBcBAAAAKgAAAMABAAAQABAAAAAAAAEAA//4AQAAAAMxMjMBAAAA
';
SELECT * FROM char63_utf8;

# ... and this is an event corresponding to
#
#    INSERT INTO char128_utf8 VALUES ( 1, "123", 1 )
#
# The binlog event below shall trigger the bug check and produce an error
#

error ER_UNKNOWN_ERROR;
BINLOG '
iONkSBMBAAAANwAAAJkBAAAAABAAAAAAAAAABHRlc3QADGNoYXIxMjhfdXRmOAADA/4DAv6AAA==
iONkSBcBAAAAKwAAAMQBAAAQABAAAAAAAAEAA//4AQAAAAMAMTIzAQAAAA==
';

drop table t1, char63_utf8, char128_utf8;
+8 −0
Original line number Diff line number Diff line
[row]
--binlog-format=row

[stmt]
--binlog-format=statement

[mix]
--binlog-format=mixed
+17 −0
Original line number Diff line number Diff line
stop slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
reset master;
reset slave;
drop table if exists t1,t2,t3,t4,t5,t6,t7,t8,t9;
start slave;
CREATE TABLE char128_utf8 ( 
i1 INT NOT NULL, 
c CHAR(128) CHARACTER SET utf8 NOT NULL, 
i2 INT NOT NULL);
INSERT INTO char128_utf8 VALUES ( 1, "123", 1 );
SELECT * FROM char128_utf8;
i1	c	i2
1	123	1
SELECT * FROM char128_utf8;
i1	c	i2
1	123	1
Loading