From 16567e5242ee3e0e2ba9c92714b375cdf251aa35 Mon Sep 17 00:00:00 2001 From: Vicent Marti Date: Wed, 10 Jul 2024 17:54:00 +0200 Subject: [PATCH] innobase: respect transaction isolation for innodb_api When performing a row search via the internal InnoDB API, `row_search_mvcc` locates the relevant record in the B-tree and, if there are one or more concurrent transactions modifying such record, it reconstructs the correct (previous) version of the record that the local transaction should see given its transaction isolation level. This reconstructed record is stored in `prebuilt->innodb_api_rec` so it can be accessed by the internal InnoDB API in `ib_cursor_read_row`. Commit d9bc5e03d fixed a use-after-free with the stored record, but it also introduced an extra check for `rec_get_deleted_flag` before attempting to fetch the reconstructed record. This check appears to be incorrect: the flags on the record are not meaningful at this point because the record as it comes out of `pcur` could be a newer version of the record that the current transaction should **not** be able to see. Hence, by checking for a deleted flag on the record, we're instantly materializing any DELETE operations performed by concurrent transactions, breaking transaction isolation, whilst only UPDATEs to the underlying record are properly isolated. Signed-off-by: Vicent Marti --- storage/innobase/api/api0api.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/storage/innobase/api/api0api.cc b/storage/innobase/api/api0api.cc index 6372b0464ded..9f6fc9f78f45 100644 --- a/storage/innobase/api/api0api.cc +++ b/storage/innobase/api/api0api.cc @@ -1642,10 +1642,8 @@ ib_err_t ib_cursor_read_row( bool page_format = dict_table_is_comp(tuple->index->table); rec = pcur->get_rec(); - if (!rec_get_deleted_flag(rec, page_format)) { - if (prebuilt->innodb_api && prebuilt->innodb_api_rec != nullptr) { - rec = prebuilt->innodb_api_rec; - } + if (prebuilt->innodb_api && prebuilt->innodb_api_rec != nullptr) { + rec = prebuilt->innodb_api_rec; } if (!rec_get_deleted_flag(rec, page_format)) {