1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00

MDEV-38734: Virtual columns wrongly included in binlog_row_image=MINIMAL

The original symptom of this was an assertion 'marked_for_read()' failing in
RBR with unique blobs and binlog_row_image=MINIMAL. The problem was that the
hidden DB_ROW_HASH_1 virtual column was included in the after-image of the
update, but the underlying blob column was not being updated, so it was not
in the read_set/write_set.

It seems clearly wrong to include the DB_ROW_HASH_1 in the after-image when
the underlying blob isn't even being updated. The cause of this is the
following commit:

Author: Monty <monty@mariadb.org>
Date:   Wed May 23 22:42:29 2018 +0300

    MDEV-15243 Crash with virtual fields and row based binary logging

That patch removed a check for if the underlying fields of a virtual column
were being updated, and just added them unconditionally. This seems wrong.

So revert that part of the commit, restoring the logic to only add a virtual
column if any underlying field is actually in the write_set.

Also fix a typo in that commit where a code reformat accidentally reversed a
condition.

Also fix an assertion when InnoDB goes to update secondary indexes: If any
part of the primary key is being updated, then add all virtual columns that
are part of secondary indexes to the read_set.

Signed-off-by: Kristian Nielsen <knielsen@knielsen-hq.org>
This commit is contained in:
Kristian Nielsen
2026-02-03 12:51:19 +01:00
parent 7cf6d317df
commit a4e4a56720
4 changed files with 84 additions and 8 deletions

View File

@@ -0,0 +1,17 @@
include/master-slave.inc
[connection master]
connection slave;
SET @row_image_save = @@global.binlog_row_image;
SET GLOBAL binlog_row_image=MINIMAL;
include/stop_slave.inc
include/start_slave.inc
connection master;
SET binlog_row_image=MINIMAL;
CREATE TABLE t (pk int primary key, a text, b text, unique(b));
INSERT INTO t VALUES (1,'foo', 'bar');
UPDATE t SET a = 'baz';
connection slave;
SET GLOBAL binlog_row_image = @row_image_save;
connection master;
DROP TABLE t;
include/rpl_end.inc

View File

@@ -0,0 +1,21 @@
--source include/have_binlog_format_row.inc
--source include/master-slave.inc
--connection slave
SET @row_image_save = @@global.binlog_row_image;
SET GLOBAL binlog_row_image=MINIMAL;
--source include/stop_slave.inc
--source include/start_slave.inc
--connection master
SET binlog_row_image=MINIMAL;
CREATE TABLE t (pk int primary key, a text, b text, unique(b));
INSERT INTO t VALUES (1,'foo', 'bar');
UPDATE t SET a = 'baz';
--sync_slave_with_master
SET GLOBAL binlog_row_image = @row_image_save;
--connection master
DROP TABLE t;
--source include/rpl_end.inc

View File

@@ -8187,8 +8187,7 @@ void TABLE::mark_columns_per_binlog_row_image()
be added to read_set either.
*/
bool TABLE::mark_virtual_columns_for_write(bool insert_fl
__attribute__((unused)))
bool TABLE::mark_virtual_columns_for_write(bool insert_fl)
{
Field **vfield_ptr, *tmp_vfield;
bool bitmap_updated= false;
@@ -8203,9 +8202,29 @@ bool TABLE::mark_virtual_columns_for_write(bool insert_fl
(tmp_vfield->flags & (PART_KEY_FLAG | FIELD_IN_PART_FUNC_FLAG |
PART_INDIRECT_KEY_FLAG)))
{
bitmap_set_bit(write_set, tmp_vfield->field_index);
mark_virtual_column_with_deps(tmp_vfield);
bitmap_updated= true;
if (insert_fl)
{
bitmap_set_bit(write_set, tmp_vfield->field_index);
mark_virtual_column_with_deps(tmp_vfield);
bitmap_updated= true;
}
else
{
MY_BITMAP *save_read_set= read_set;
Item *vcol_item= tmp_vfield->vcol_info->expr;
DBUG_ASSERT(vcol_item);
bitmap_clear_all(&tmp_set);
read_set= &tmp_set;
vcol_item->walk(&Item::register_field_in_read_map, 1, 0);
read_set= save_read_set;
if (bitmap_is_overlapping(&tmp_set, write_set))
{
bitmap_set_bit(write_set, tmp_vfield->field_index);
bitmap_set_bit(read_set, tmp_vfield->field_index);
bitmap_union(read_set, &tmp_set);
bitmap_updated= true;
}
}
}
}
if (bitmap_updated)
@@ -9269,7 +9288,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode)
/* Read indexed fields that was not updated in VCOL_UPDATE_FOR_READ */
update= (!vcol_info->is_stored() &&
(vf->flags & (PART_KEY_FLAG | PART_INDIRECT_KEY_FLAG)) &&
!bitmap_is_set(read_set, vf->field_index));
bitmap_is_set(read_set, vf->field_index));
swap_values= 1;
break;
}

View File

@@ -5052,6 +5052,7 @@ Ensure that indexed virtual columns will be computed.
Needs to be done for indexes that are being added with inplace ALTER
in a different thread, because from the server point of view these
columns are not yet indexed.
Also needed if the primary key is being updated.
*/
void ha_innobase::column_bitmaps_signal()
{
@@ -5059,7 +5060,25 @@ void ha_innobase::column_bitmaps_signal()
return;
dict_index_t* clust_index= dict_table_get_first_index(m_prebuilt->table);
if (!clust_index->online_log)
bool is_online_log= clust_index->online_log;
/*
Check if the clustered index is being updated.
If so, it is necessary to compute virtual columns that are part of
secondary indexes, to be able to re-compute those indexes.
*/
bool upd_pk= false;
for (uint j= 0; j < clust_index->n_user_defined_cols; ++j)
{
dict_col_t *col= clust_index->fields[j].col;
if (bitmap_is_set(table->write_set, static_cast<uint>(col->ind)))
{
upd_pk= 1;
break;
}
}
if (!is_online_log && !upd_pk)
return;
uint num_v= 0;
@@ -5070,7 +5089,7 @@ void ha_innobase::column_bitmaps_signal()
dict_col_t *col= &m_prebuilt->table->v_cols[num_v].m_col;
if (col->ord_part ||
(dict_index_is_online_ddl(clust_index) &&
(is_online_log && dict_index_is_online_ddl(clust_index) &&
row_log_col_is_indexed(clust_index, num_v)))
table->mark_virtual_column_with_deps(table->vfield[j]);
num_v++;