Commit be6d48fd authored by Libing Song's avatar Libing Song Committed by Andrew Hutchings

MDEV-33049 Assertion `marked_for_write_or_computed()' failed in bool

           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.
parent d136169e
...@@ -4752,6 +4752,21 @@ include/diff_tables.inc [server_1:test.t, server_2:test.t, server_3:test.t] ...@@ -4752,6 +4752,21 @@ include/diff_tables.inc [server_1:test.t, server_2:test.t, server_3:test.t]
connection server_1; connection server_1;
DROP TABLE t; DROP TABLE t;
include/rpl_sync.inc 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' CON: 'server_1', IMG: 'FULL', RESTART SLAVE: 'N'
connection server_1; connection server_1;
SET SESSION binlog_row_image= 'FULL'; SET SESSION binlog_row_image= 'FULL';
......
...@@ -27,8 +27,41 @@ ...@@ -27,8 +27,41 @@
-- let $row_img_test_script= include/rpl_row_img.test -- let $row_img_test_script= include/rpl_row_img.test
-- source include/rpl_row_img_general_loop.inc -- 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 -- 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_row_img_set.inc
--source include/rpl_end.inc --source include/rpl_end.inc
...@@ -201,7 +201,29 @@ static bool unpack_field(const table_def *tabledef, Field *f, ...@@ -201,7 +201,29 @@ static bool unpack_field(const table_def *tabledef, Field *f,
the assertions triggered need to be the assertions triggered need to be
addressed/revisited. 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(); f->reset();
#ifndef DBUG_OFF
if (was_not_set)
bitmap_clear_bit(f->table->write_set, f->field_index);
#endif
f->set_null(); f->set_null();
} }
else else
......
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