diff --git a/mysql-test/suite/rpl/r/rpl_row_img_eng_full_nodup.result b/mysql-test/suite/rpl/r/rpl_row_img_eng_full_nodup.result index 39c9ee03daa..17973c3a5b6 100644 --- a/mysql-test/suite/rpl/r/rpl_row_img_eng_full_nodup.result +++ b/mysql-test/suite/rpl/r/rpl_row_img_eng_full_nodup.result @@ -4752,6 +4752,21 @@ include/diff_tables.inc [server_1:test.t, server_2:test.t, server_3:test.t] connection server_1; DROP TABLE t; include/rpl_sync.inc +CREATE TABLE t1(c1 INT NOT NULL PRIMARY KEY, c2 varchar(10) DEFAULT NULL, +c3 decimal(12,4) DEFAULT NULL); +INSERT INTO t1(c1) VALUES (1); +include/save_master_pos.inc +UPDATE t1 SET c2 = 'c2'; +include/rpl_sync.inc +FLUSH BINARY LOGS; +UPDATE t1 SET c2 = "c2_new"; +# Use 'BINLOG' statement to apply the first update row event +# BINLOG statement is same to slave applier, it should work well. +# After 'BINLOG' statement c2's value should be "c2" +SELECT * FROM t1; +c1 c2 c3 +1 c2 NULL +DROP TABLE t1; CON: 'server_1', IMG: 'FULL', RESTART SLAVE: 'N' connection server_1; SET SESSION binlog_row_image= 'FULL'; diff --git a/mysql-test/suite/rpl/t/rpl_row_img_eng_full_nodup.test b/mysql-test/suite/rpl/t/rpl_row_img_eng_full_nodup.test index 12ec91bf381..f4a53a38baf 100644 --- a/mysql-test/suite/rpl/t/rpl_row_img_eng_full_nodup.test +++ b/mysql-test/suite/rpl/t/rpl_row_img_eng_full_nodup.test @@ -27,8 +27,41 @@ -- let $row_img_test_script= include/rpl_row_img.test -- source include/rpl_row_img_general_loop.inc +################################################################################ +# MDEV-33049 Assertion `marked_for_write_or_computed()' failed in bool +# Field_new_decimal::store_value(const my_decimal*, int*) +# +# In FULL_NODUP mode, the before image has all fields and the after image has +# only updated fields. Crash happened when slave was unpacking a row event +# if the new decimal field is null in the before image. +################################################################################ +CREATE TABLE t1(c1 INT NOT NULL PRIMARY KEY, c2 varchar(10) DEFAULT NULL, + c3 decimal(12,4) DEFAULT NULL); +INSERT INTO t1(c1) VALUES (1); +--source include/save_master_pos.inc +--let $datadir= `SELECT @@datadir` + +# It will generate a row event that c3 is only in before image and it is null. +UPDATE t1 SET c2 = 'c2'; + +# the UPDATE will crash the slave without this fix. +--source include/rpl_sync.inc + +FLUSH BINARY LOGS; +UPDATE t1 SET c2 = "c2_new"; + +--echo # Use 'BINLOG' statement to apply the first update row event +--echo # BINLOG statement is same to slave applier, it should work well. +--exec $MYSQL_BINLOG --start-position=$_master_pos $datadir$_master_file > $MYSQLTEST_VARDIR/tmp/binlog.sql +--exec $MYSQL < $MYSQLTEST_VARDIR/tmp/binlog.sql + +--echo # After 'BINLOG' statement c2's value should be "c2" +SELECT * FROM t1; + +--remove_file $MYSQLTEST_VARDIR/tmp/binlog.sql +DROP TABLE t1; + -- let $row_img_set=server_1:FULL:N,server_2:FULL:Y,server_3:FULL:Y -- source include/rpl_row_img_set.inc - --source include/rpl_end.inc diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index bb2b357a33a..dd02d97236f 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -201,7 +201,29 @@ static bool unpack_field(const table_def *tabledef, Field *f, the assertions triggered need to be addressed/revisited. */ + +#ifndef DBUG_OFF + /* + f->reset() may call store_value() to reset the value, for example + Field_new_decimal. store_value() has below assertion: + + DBUG_ASSERT(marked_for_write_or_computed()); + + It asserts write bitmap must be set. That caused an assertion + failure for row images generated by FULL_NODUP mode. + The assertion is meaningless for unpacking a row image, so + the field is marked in write_set temporarily to avoid the + assertion failure. + */ + bool was_not_set = !bitmap_is_set(f->table->write_set, f->field_index); + if (was_not_set) + bitmap_set_bit(f->table->write_set, f->field_index); +#endif f->reset(); +#ifndef DBUG_OFF + if (was_not_set) + bitmap_clear_bit(f->table->write_set, f->field_index); +#endif f->set_null(); } else