Skip to content

Commit

Permalink
MDEV-33049 Assertion `marked_for_write_or_computed()' failed in bool
Browse files Browse the repository at this point in the history
           Field_new_decimal::store_value(const my_decimal*, int*)

Analysis
========
When rpl applier is unpacking a before row image, Field::reset() will be
called before setting a field to null if null bit of the field is set in
the row image. For Field_new_decimal::reset(), it calls
Field_new_decimal::store_value() to reset the value. store_value() asserts
that the field is in the write_set bitmap since it thinks the field is
updating.

But that is not true for the row image generated in FULL_NODUP
mode. In the mode, the before image includes all fields and the after
image includes only updated fields.

Fix
===
In the case unpacking binlog row images, the assertion is meaningless.
So the unpacking field is marked in write_set temporarily to avoid the
assertion failure.
  • Loading branch information
SongLibing authored and LinuxJedi committed Jan 12, 2024
1 parent d136169 commit be6d48f
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 1 deletion.
15 changes: 15 additions & 0 deletions mysql-test/suite/rpl/r/rpl_row_img_eng_full_nodup.result
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
35 changes: 34 additions & 1 deletion mysql-test/suite/rpl/t/rpl_row_img_eng_full_nodup.test
Original file line number Diff line number Diff line change
Expand Up @@ -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
22 changes: 22 additions & 0 deletions sql/rpl_record.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit be6d48f

Please sign in to comment.