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

MDEV-36896 : Assertion 'marked_for_read()' failed in virtual String *Field_varstring::val_str(String *, String *)

Problem:
Executing queries that require virtual/generated column evaluation during filesort trigger debug assertions due to missing columns in read_set.
Cause:
find_all_keys() temporarily assigns TABLE::tmp_set as both read_set and write_set. Later, TABLE::update_virtual_field() calls bitmap_clear_all(&tmp_set) before evaluating virtual column dependencies. Since all three pointers share the same underlying bitmap buffer, clearing tmp_set also clears the active read_set/write_set, causing required columns to appear missing during execution and triggering the assertion.
Fix:
Before clearing tmp_set in TABLE::update_virtual_field(), save its current state into a stack-allocated bitmap clone using my_safe_alloca + bitmap_copy. After virtual column evaluation is complete, restore tmp_set to its original state before returning. This preserves whatever bits were live in the shared buffer (i.e. the active read_set/write_set) across the call, while still allowing the dependency walk to use tmp_set as scratch space as before. my_safe_alloca is used instead of heap allocation to keep this save/restore overhead minimal on what is a per-row hot path.
This commit is contained in:
PranavKTiwari
2026-06-01 18:35:16 +05:30
committed by mariadb-PranavTiwari
parent d5813edd08
commit 1c397bccfb
4 changed files with 142 additions and 2 deletions

View File

@@ -303,3 +303,66 @@ IF(@@innodb_sort_buffer_size < count(*)*200, 'GOOD', 'WRONG SIZE')
GOOD
alter table t1 drop column va;
drop table t1;
#
# MDEV-36896 Assertion `marked_for_read()' failed in virtual String *Field_varstring::val_str(String *, String *)
#
SET @old_isolation_level= @@session.tx_isolation;
SET @@session.tx_isolation= 'READ-UNCOMMITTED';
CREATE TABLE t (a VARCHAR(10),b INT,c CHAR(10) GENERATED ALWAYS AS (a) VIRTUAL,KEY(b,c)) ENGINE=InnoDB;
INSERT INTO t (a) VALUES ('A');
SELECT * FROM t WHERE b IS NULL ORDER BY a;
a b c
A NULL A
DROP TABLE t;
CREATE TABLE t (
a VARCHAR(10),
b INT,
c CHAR(10) GENERATED ALWAYS AS (a) VIRTUAL,
d INT GENERATED ALWAYS AS (IF(a = c, 1, 0)) VIRTUAL,
KEY(b, c)
) ENGINE=InnoDB;
INSERT INTO t (a) VALUES ('A');
INSERT INTO t (a, b) VALUES ('B', 1);
SELECT * FROM t WHERE b IS NULL ORDER BY a;
a b c d
A NULL A 1
DROP TABLE t;
SET @@session.tx_isolation= @old_isolation_level;
#
# MDEV-41365 Wrong results from filesort over indexed virtual columns
# during a concurrent uncommitted update
#
create table t (
pk int primary key,
f int,
v1 int as (f & 1) virtual,
v2 int as (f & 2) virtual,
key (v1)
) engine=innodb;
insert into t (pk, f) values (1, 0);
explain select * from t force index (v1) where v1 = 0 and v2 = 0 order by f, pk;
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE t ref v1 v1 5 const 1 Using where; Using filesort
select * from t force index (v1) where v1 = 0 and v2 = 0 order by f, pk;
pk f v1 v2
1 0 0 0
connect con1, localhost, root;
begin;
update t set f = 1 where pk = 1;
connection default;
# No filesort: correct
select * from t force index (v1) where v1 = 0 and v2 = 0;
pk f v1 v2
1 0 0 0
# Filesort: must return pk=1, returns nothing
select * from t force index (v1) where v1 = 0 and v2 = 0 order by f, pk;
pk f v1 v2
1 0 0 0
connection con1;
rollback;
disconnect con1;
connection default;
drop table t;
#
# End of 10.11 tests
#

View File

@@ -135,3 +135,69 @@ insert t1 (id,a,c) select seq,seq,seq from seq_1_to_330;
select IF(@@innodb_sort_buffer_size < count(*)*200, 'GOOD', 'WRONG SIZE') from t1;
alter table t1 drop column va;
drop table t1;
--echo #
--echo # MDEV-36896 Assertion `marked_for_read()' failed in virtual String *Field_varstring::val_str(String *, String *)
--echo #
SET @old_isolation_level= @@session.tx_isolation;
SET @@session.tx_isolation= 'READ-UNCOMMITTED';
CREATE TABLE t (a VARCHAR(10),b INT,c CHAR(10) GENERATED ALWAYS AS (a) VIRTUAL,KEY(b,c)) ENGINE=InnoDB;
INSERT INTO t (a) VALUES ('A');
SELECT * FROM t WHERE b IS NULL ORDER BY a;
DROP TABLE t;
CREATE TABLE t (
a VARCHAR(10),
b INT,
c CHAR(10) GENERATED ALWAYS AS (a) VIRTUAL,
d INT GENERATED ALWAYS AS (IF(a = c, 1, 0)) VIRTUAL,
KEY(b, c)
) ENGINE=InnoDB;
INSERT INTO t (a) VALUES ('A');
INSERT INTO t (a, b) VALUES ('B', 1);
SELECT * FROM t WHERE b IS NULL ORDER BY a;
DROP TABLE t;
SET @@session.tx_isolation= @old_isolation_level;
--echo #
--echo # MDEV-41365 Wrong results from filesort over indexed virtual columns
--echo # during a concurrent uncommitted update
--echo #
create table t (
pk int primary key,
f int,
v1 int as (f & 1) virtual,
v2 int as (f & 2) virtual,
key (v1)
) engine=innodb;
insert into t (pk, f) values (1, 0);
explain select * from t force index (v1) where v1 = 0 and v2 = 0 order by f, pk;
select * from t force index (v1) where v1 = 0 and v2 = 0 order by f, pk;
connect con1, localhost, root;
begin;
update t set f = 1 where pk = 1;
connection default;
--echo # No filesort: correct
select * from t force index (v1) where v1 = 0 and v2 = 0;
--echo # Filesort: must return pk=1, returns nothing
select * from t force index (v1) where v1 = 0 and v2 = 0 order by f, pk;
connection con1;
rollback;
disconnect con1;
connection default;
drop table t;
--echo #
--echo # End of 10.11 tests
--echo #

View File

@@ -980,12 +980,13 @@ bool Item_field::check_field_expression_processor(void *arg)
bool Item_field::update_vcol_processor(void *arg)
{
MY_BITMAP *map= (MY_BITMAP *) arg;
if (field->vcol_info &&
!bitmap_fast_test_and_set(map, field->field_index))
if (!bitmap_fast_test_and_set(map, field->field_index) && field->vcol_info)
{
field->vcol_info->expr->walk(&Item::update_vcol_processor, 0, arg);
field->vcol_info->expr->save_in_field(field, 0);
}
return 0;
}

View File

@@ -9342,11 +9342,21 @@ int TABLE::update_virtual_field(Field *vf, bool ignore_warnings)
TABLE::update_virtual_fields(handler *, enum_vcol_update_mode).
*/
in_use->set_n_backup_active_arena(expr_arena, &backup_arena);
MY_BITMAP saved_tmp_set;
uint bitmap_size= bitmap_buffer_size(s->fields);
my_bitmap_map *saved_buf= (my_bitmap_map *) my_safe_alloca(bitmap_size);
my_bitmap_init(&saved_tmp_set, saved_buf, s->fields);
bitmap_copy(&saved_tmp_set, &tmp_set);
bitmap_clear_all(&tmp_set);
vf->vcol_info->expr->walk(&Item::update_vcol_processor, 0, &tmp_set);
DBUG_FIX_WRITE_SET(vf);
vf->vcol_info->expr->save_in_field(vf, 0);
DBUG_RESTORE_WRITE_SET(vf);
bitmap_copy(&tmp_set, &saved_tmp_set); /* restore original state */
my_safe_afree(saved_buf, bitmap_size);
in_use->restore_active_arena(expr_arena, &backup_arena);
in_use->pop_internal_handler();
if (ignore_warnings)