1
0
mirror of https://github.com/MariaDB/server.git synced 2026-10-23 18:49:29 +03:00
Files
mariadb/sql/sql_update.cc
Aleksey Midenkov 599416569d MDEV-39306 Index corruption on UPDATE when using versioned InnoDB table (on UPDATE)
Duplicated by:

MDEV-39711 UPDATE on System-Versioned Tables uses the after-value
           in Row-format Binlog's before-image
MDEV-24646 ASAN heap-use-after-free in Field_blob::pack /
           THD::binlog_update_row upon DML on table with virtual column

vers_update_fields() recomputed virtual fields with VCOL_UPDATE_FOR_READ.
For BLOB virtual columns this mode swaps Field_blob::value and read_value
after the computation (swap_values). After the swap, Field_blob::value
holds the buffer that record[1] (the old row image) points to. The next
VCOL_UPDATE_FOR_WRITE computation, done by fill_record() right after
vers_update_fields(), stores the new value into that buffer and so
overwrites the old row image. As a result, InnoDB builds the old
secondary-index key from the new value (MDEV-39306), and the row-format
binlog writes the new value into the before-image (MDEV-39711), or a
freed buffer (MDEV-24646).

REPLACE had the same problem with record[2]. TABLE::delete_row() saves
the new row into record[2] and turns the conflicting row into history
with vers_update_end(). Its VCOL_UPDATE_FOR_WRITE computation stored
the virtual blobs of the old row into Field_blob::value, the buffer
record[2] points to, so the new row was written with them. Versioned
DELETE had the same problem with record[1]: it points to the virtual
blobs computed with VCOL_UPDATE_FOR_DELETE in Field_blob::value, and
vers_update_end() overwrote them.

The fix removes the virtual fields computation from vers_update_fields().
Its callers now recompute virtual fields with VCOL_UPDATE_FOR_WRITE after
the system fields are set: fill_record() already did, while
update_generated_fields(), multi_update::do_updates() and the RBR
appliers do it explicitly. multi_update::do_updates() still computes
virtual fields twice per row: before BEFORE triggers and after
vers_update_fields(), since the system fields cannot be set before
compare_record(). For REPLACE and DELETE, TABLE::delete_row() keeps
Field_blob::value with remember_blob_values() while it updates the
history row.

In the RBR appliers no VCOL_UPDATE_FOR_WRITE followed
vers_update_fields(), so record[1] was not overwritten there. Their
explicit computation replaces the one removed from vers_update_fields().
It uses VCOL_UPDATE_FOR_WRITE, so an error in a virtual field
expression now stops the slave instead of being turned into a warning.

In the Update_rows applier, vers_insert_history_row() also overwrote
the error of ha_update_row(), so a failed update on the slave was
ignored and its history row was written. The history row is now
written only when the update succeeded.

Based on the MDEV-24646/MDEV-39711 fix by ParadoxV5 (0c2f3de1a2),
which switched vers_update_fields() to VCOL_UPDATE_FOR_WRITE.
2026-10-22 18:26:14 +03:00

103 KiB