Skip to content

Commit 9f00ff0

Browse files
MDEV-39081 InnoDB: tried to purge non-delete-marked record, assertion fails in row_purge_del_mark_error
Reason: ======= Following the changes in MDEV-38734, the server no longer marks all indexed virtual columns during an UPDATE operation. Consequently, ha_innobase::update() populates the upd_t vector with old_vrow but omits the actual data for these virtual columns. Despite this omission, trx_undo_page_report_modify() continues to write metadata for indexed virtual columns into the undo log. Because the actual values are missing from the update vector, the undo log entry is recorded without the historical data for these columns. When the purge thread processes the undo log to reconstruct a previous record state for MVCC, it identifies an indexed virtual column but finds no associated data. The purge thread incorrectly interprets this missing data as a NULL value, rather than a "missing/unrecorded" value. The historical record is reconstructed with an incorrect NULL for the virtual column. This causes the purge thread to incorrectly identify and purge records that are not actually delete-marked, leading to abort of server. Solution: ========= ha_innobase::column_bitmaps_signal(): Revert the column-marking logic to the state prior to commit a4e4a56, ensuring all indexed virtual columns are unconditionally marked during an UPDATE. The previous "optimization" attempted to manually detect indexed column changes before marking virtual columns. The manual check for indexed column modifications is redundant. InnoDB already provides the UPD_NODE_NO_ORD_CHANGE flag within row_upd_step(). This flag is being used in trx_undo_page_report_modify() and trx_undo_read_undo_rec() should log or read virtual column values. Refactored column_bitmaps_signal() to accept a mark_for_update parameter that controls when indexed virtual columns are marked. TABLE::mark_columns_needed_for_update() is the only place that needs mark_for_update=true because only UPDATE operations need to mark indexed virtual columns for InnoDB's undo logging mechanism. INSERT operation is already handled by TABLE::mark_virtual_columns_for_write(insert_fl=true). Even the commit a4e4a56 changes are going to affect TABLE::mark_virtual_column_for_write(false) and It is called during UPDATE operation, and that's when column_bitmaps_signal() needs to mark indexed virtual columns. Online DDL has separate code path which is handled by row_log_mark_virtual_cols() for all DML operations
1 parent 11f408b commit 9f00ff0

File tree

19 files changed

+114
-78
lines changed

19 files changed

+114
-78
lines changed

mysql-test/suite/vcol/r/vcol_keys_innodb.result

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,3 +303,23 @@ IF(@@innodb_sort_buffer_size < count(*)*200, 'GOOD', 'WRONG SIZE')
303303
GOOD
304304
alter table t1 drop column va;
305305
drop table t1;
306+
#
307+
# MDEV-39081 InnoDB: tried to purge non-delete-marked record,
308+
# assertion fails in row_purge_del_mark_error
309+
#
310+
CREATE TABLE t1(
311+
pk INT AUTO_INCREMENT,
312+
f1 INT DEFAULT 0,
313+
f2 INT as (1 + 1) VIRTUAL,
314+
KEY (f1), PRIMARY KEY (pk), UNIQUE (f2)) ENGINE=InnoDB;
315+
START TRANSACTION WITH CONSISTENT SNAPSHOT;
316+
CONNECT con1,localhost,root,,;
317+
INSERT INTO t1 (pk) VALUES(1);
318+
DELETE FROM t1;
319+
INSERT INTO t1 (pk) VALUES(1);
320+
UPDATE t1 set f1 = 2;
321+
disconnect con1;
322+
connection default;
323+
COMMIT;
324+
InnoDB 0 transactions not purged
325+
DROP TABLE t1;

mysql-test/suite/vcol/t/vcol_keys_innodb.test

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,3 +135,25 @@ insert t1 (id,a,c) select seq,seq,seq from seq_1_to_330;
135135
select IF(@@innodb_sort_buffer_size < count(*)*200, 'GOOD', 'WRONG SIZE') from t1;
136136
alter table t1 drop column va;
137137
drop table t1;
138+
139+
--echo #
140+
--echo # MDEV-39081 InnoDB: tried to purge non-delete-marked record,
141+
--echo # assertion fails in row_purge_del_mark_error
142+
--echo #
143+
CREATE TABLE t1(
144+
pk INT AUTO_INCREMENT,
145+
f1 INT DEFAULT 0,
146+
f2 INT as (1 + 1) VIRTUAL,
147+
KEY (f1), PRIMARY KEY (pk), UNIQUE (f2)) ENGINE=InnoDB;
148+
START TRANSACTION WITH CONSISTENT SNAPSHOT;
149+
150+
CONNECT(con1,localhost,root,,);
151+
INSERT INTO t1 (pk) VALUES(1);
152+
DELETE FROM t1;
153+
INSERT INTO t1 (pk) VALUES(1);
154+
UPDATE t1 set f1 = 2;
155+
DISCONNECT con1;
156+
CONNECTION default;
157+
COMMIT;
158+
--source ../innodb/include/wait_all_purged.inc
159+
DROP TABLE t1;

sql/filesort.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -962,7 +962,7 @@ static ha_rows find_all_keys(THD *thd, Sort_param *param, SQL_SELECT *select,
962962
select->cond : select->pre_idx_push_select_cond));
963963
if (sort_cond)
964964
sort_cond->walk(&Item::register_field_in_read_map, 1, sort_form);
965-
sort_form->file->column_bitmaps_signal();
965+
sort_form->file->column_bitmaps_signal(false);
966966

967967
if (quick_select)
968968
{

sql/ha_partition.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -577,13 +577,13 @@ class ha_partition final :public handler
577577
m_file[part_id]->update_create_info(create_info);
578578
}
579579

580-
void column_bitmaps_signal() override
580+
void column_bitmaps_signal(bool mark_for_update) override
581581
{
582582
for (uint i= bitmap_get_first_set(&m_opened_partitions);
583583
i < m_tot_parts;
584584
i= bitmap_get_next_set(&m_opened_partitions, i))
585585
{
586-
m_file[i]->column_bitmaps_signal();
586+
m_file[i]->column_bitmaps_signal(mark_for_update);
587587
}
588588
}
589589

sql/ha_sequence.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,8 @@ class ha_sequence :public handler
157157
{ return file->check_and_repair(thd); }
158158
bool is_crashed() const override
159159
{ return file->is_crashed(); }
160-
void column_bitmaps_signal() override
161-
{ return file->column_bitmaps_signal(); }
160+
void column_bitmaps_signal(bool mark_for_update) override
161+
{ return file->column_bitmaps_signal(mark_for_update); }
162162

163163
/* New methods */
164164
void register_original_handler(handler *file_arg)

sql/handler.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4482,6 +4482,8 @@ int handler::update_auto_increment()
44824482
/** @brief
44834483
MySQL signal that it changed the column bitmap
44844484
4485+
@param mark_for_update whether to mark indexed virtual columns
4486+
for UPDATE operations
44854487
USAGE
44864488
This is for handlers that needs to setup their own column bitmaps.
44874489
Normally the handler should set up their own column bitmaps in
@@ -4492,7 +4494,7 @@ int handler::update_auto_increment()
44924494
rnd_init() call is made as after this, MySQL will not use the bitmap
44934495
for any program logic checking.
44944496
*/
4495-
void handler::column_bitmaps_signal()
4497+
void handler::column_bitmaps_signal(bool mark_for_update)
44964498
{
44974499
DBUG_ENTER("column_bitmaps_signal");
44984500
if (table)

sql/handler.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4054,8 +4054,11 @@ class handler :public Sql_alloc
40544054
call. Normally the handler should ignore all calls until we have done
40554055
a ha_rnd_init() or ha_index_init(), write_row(), update_row or delete_row()
40564056
as there may be several calls to this routine.
4057+
4058+
@param mark_for_update whether to mark indexed virtual columns
4059+
for UPDATE operations
40574060
*/
4058-
virtual void column_bitmaps_signal();
4061+
virtual void column_bitmaps_signal(bool mark_for_update);
40594062
/*
40604063
We have to check for inited as some engines, like innodb, sets
40614064
active_index during table scan.

sql/sql_admin.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1042,7 +1042,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
10421042
ER_THD(thd, ER_NO_EIS_FOR_FIELD),
10431043
column_name->str);
10441044
}
1045-
tab->file->column_bitmaps_signal();
1045+
tab->file->column_bitmaps_signal(false);
10461046
}
10471047
if (!lex->index_list)
10481048
tab->keys_in_use_for_query.init(tab->s->keys);

sql/sql_sequence.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -695,14 +695,14 @@ int sequence_definition::write(TABLE *table, bool all_fields)
695695
save_write_set= table->write_set;
696696
save_read_set= table->read_set;
697697
table->read_set= table->write_set= &table->s->all_set;
698-
table->file->column_bitmaps_signal();
698+
table->file->column_bitmaps_signal(false);
699699
store_fields(table);
700700
if (unlikely((error= table->file->ha_write_row(table->record[0]))))
701701
table->file->print_error(error, MYF(0));
702702
table->rpl_write_set= save_rpl_write_set;
703703
table->read_set= save_read_set;
704704
table->write_set= save_write_set;
705-
table->file->column_bitmaps_signal();
705+
table->file->column_bitmaps_signal(false);
706706
return error;
707707
}
708708

sql/sql_table.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12621,7 +12621,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to,
1262112621
if (from_row_end)
1262212622
bitmap_set_bit(from->read_set, from_row_end->field_index);
1262312623

12624-
from->file->column_bitmaps_signal();
12624+
from->file->column_bitmaps_signal(false);
1262512625

1262612626
to->file->prepare_for_insert(0);
1262712627
DBUG_ASSERT(to->file->inited == handler::NONE);

0 commit comments

Comments
 (0)