What happens here is the following sequence of events:
- There's an update, it modifies the value of a bob virtual column
- The column is indexed, so it's recalculated
- The pointer to the value (it's a blob) is stored in the record. As it's a virtual calculated column, the value is owned by the Field_blob and stored in the Field_blob::value.
- The previous value of the field (also calculated) is in Field_blob::read_value, and the pointer is in record.
- Now comes check_duplicate_long_entry_key(), it calculates the hash value, saves record in the lookup_buffer, and performs an index_read() using a separate lookup_handler.
- In this case we have a hash collision, so a row is found, it's read into the record
- Virtual columns are computed and the new value is stored in Field_blob::value, replacing the old one
- The row from the hash collision isn't identical to the one in the lookup_buffer, so check_duplicate_long_entry_key() decides it's not a duplicate key, restores record from lookup_buffer and proceeds with the update
- but the blob value in Field_blob::value is lost
This isn't exactly a new kind of problems, this has happened before a few times in partitioning (
MDEV-18734, 160d97a4aaac), in innodb ( MDEV-15114, ab194666564a), in REPLACE and UPDATE (ea1b25046c81, that's how Field_blob::read_value was born). The fix is to detach temporarily the pointer in the record from Field_blob::value and reattach it back later.