summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarko Mäkelä <marko.makela@mariadb.com>2023-03-22 15:19:52 +0200
committerMarko Mäkelä <marko.makela@mariadb.com>2023-03-22 15:19:52 +0200
commit701399ad375fe71daded4fc7f18f347594352aa1 (patch)
tree84dbdef10a48e2224f689e4334fdddf78cf981f8
parentdccbb5a6dba21b241e1796af82a0db85de28d195 (diff)
downloadmariadb-git-701399ad375fe71daded4fc7f18f347594352aa1.tar.gz
MDEV-30882 Crash on ROLLBACK in a ROW_FORMAT=COMPRESSED table
btr_cur_upd_rec_in_place(): Avoid calling page_zip_write_rec() if we are not modifying any fields that are stored in compressed format. btr_cur_update_in_place_zip_check(): New function to check if a ROW_FORMAT=COMPRESSED record can actually be updated in place. btr_cur_pessimistic_update(): If the BTR_KEEP_POS_FLAG is not set (we are in a ROLLBACK and cannot write any BLOBs), ignore the potential overflow and let page_zip_reorganize() or page_zip_compress() handle it. This avoids a failure when an attempted UPDATE of an NULL column to 0 is rolled back. During the ROLLBACK, we would try to move a non-updated long column to off-page storage in order to avoid a compression failure of the ROW_FORMAT=COMPRESSED page. page_zip_write_trx_id_and_roll_ptr(): Remove an assertion that would fail in row_upd_rec_in_place() because the uncompressed page would already have been modified there. This is a 10.5 version of commit ff3d4395d808b6421d2e0714e10d48c7aa2f3c3a (different because of commit 08ba388713946c03aa591899cd3a446a6202f882).
-rw-r--r--storage/innobase/btr/btr0cur.cc103
-rw-r--r--storage/innobase/page/page0zip.cc3
2 files changed, 89 insertions, 17 deletions
diff --git a/storage/innobase/btr/btr0cur.cc b/storage/innobase/btr/btr0cur.cc
index 463b9da9744..a14874ec011 100644
--- a/storage/innobase/btr/btr0cur.cc
+++ b/storage/innobase/btr/btr0cur.cc
@@ -4218,9 +4218,85 @@ void btr_cur_upd_rec_in_place(rec_t *rec, const dict_index_t *index,
}
}
- if (UNIV_LIKELY_NULL(block->page.zip.data)) {
- page_zip_write_rec(block, rec, index, offsets, 0, mtr);
+ if (UNIV_LIKELY(!block->page.zip.data)) {
+ return;
}
+
+ switch (update->n_fields) {
+ case 0:
+ /* We only changed the delete-mark flag. */
+ return;
+ case 1:
+ if (!index->is_clust()
+ || update->fields[0].field_no != index->db_roll_ptr()) {
+ break;
+ }
+ goto update_sys;
+ case 2:
+ if (!index->is_clust()
+ || update->fields[0].field_no != index->db_trx_id()
+ || update->fields[1].field_no != index->db_roll_ptr()) {
+ break;
+ }
+ update_sys:
+ ulint len;
+ const byte* sys = rec_get_nth_field(rec, offsets,
+ index->db_trx_id(), &len);
+ ut_ad(len == DATA_TRX_ID_LEN);
+ page_zip_write_trx_id_and_roll_ptr(
+ block, rec, offsets, index->db_trx_id(),
+ trx_read_trx_id(sys),
+ trx_read_roll_ptr(sys + DATA_TRX_ID_LEN), mtr);
+ return;
+ }
+
+ page_zip_write_rec(block, rec, index, offsets, 0, mtr);
+}
+
+/** Check if a ROW_FORMAT=COMPRESSED page can be updated in place
+@param cur cursor pointing to ROW_FORMAT=COMPRESSED page
+@param offsets rec_get_offsets(btr_cur_get_rec(cur))
+@param update index fields being updated
+@param mtr mini-transaction
+@return the record in the ROW_FORMAT=COMPRESSED page
+@retval nullptr if the page cannot be updated in place */
+ATTRIBUTE_COLD static
+rec_t *btr_cur_update_in_place_zip_check(btr_cur_t *cur, rec_offs *offsets,
+ const upd_t& update, mtr_t *mtr)
+{
+ dict_index_t *index= cur->index;
+ ut_ad(!index->table->is_temporary());
+
+ switch (update.n_fields) {
+ case 0:
+ /* We are only changing the delete-mark flag. */
+ break;
+ case 1:
+ if (!index->is_clust() ||
+ update.fields[0].field_no != index->db_roll_ptr())
+ goto check_for_overflow;
+ /* We are only changing the delete-mark flag and DB_ROLL_PTR. */
+ break;
+ case 2:
+ if (!index->is_clust() ||
+ update.fields[0].field_no != index->db_trx_id() ||
+ update.fields[1].field_no != index->db_roll_ptr())
+ goto check_for_overflow;
+ /* We are only changing DB_TRX_ID, DB_ROLL_PTR, and the delete-mark.
+ They can be updated in place in the uncompressed part of the
+ ROW_FORMAT=COMPRESSED page. */
+ break;
+ check_for_overflow:
+ default:
+ if (!btr_cur_update_alloc_zip(btr_cur_get_page_zip(cur),
+ btr_cur_get_page_cur(cur),
+ index,
+ offsets, rec_offs_size(offsets),
+ false, mtr))
+ return nullptr;
+ }
+
+ return btr_cur_get_rec(cur);
}
/*************************************************************//**
@@ -4282,17 +4358,10 @@ btr_cur_update_in_place(
page_zip_des_t* page_zip = buf_block_get_page_zip(block);
/* Check that enough space is available on the compressed page. */
- if (UNIV_LIKELY_NULL(page_zip)) {
- ut_ad(!index->table->is_temporary());
-
- if (!btr_cur_update_alloc_zip(
- page_zip, btr_cur_get_page_cur(cursor),
- index, offsets, rec_offs_size(offsets),
- false, mtr)) {
- return(DB_ZIP_OVERFLOW);
- }
-
- rec = btr_cur_get_rec(cursor);
+ if (UNIV_LIKELY_NULL(page_zip)
+ && !(rec = btr_cur_update_in_place_zip_check(
+ cursor, offsets, *update, mtr))) {
+ return DB_ZIP_OVERFLOW;
}
/* Do lock checking and undo logging */
@@ -5040,7 +5109,13 @@ btr_cur_pessimistic_update(
ut_ad(page_is_leaf(block->frame));
ut_ad(dict_index_is_clust(index));
- ut_ad(flags & BTR_KEEP_POS_FLAG);
+ if (UNIV_UNLIKELY(!(flags & BTR_KEEP_POS_FLAG))) {
+ ut_ad(page_zip != NULL);
+ dtuple_convert_back_big_rec(index, new_entry,
+ big_rec_vec);
+ big_rec_vec = NULL;
+ n_ext = dtuple_get_n_ext(new_entry);
+ }
}
/* Do lock checking and undo logging */
diff --git a/storage/innobase/page/page0zip.cc b/storage/innobase/page/page0zip.cc
index 647bc381755..8ed4863b3fb 100644
--- a/storage/innobase/page/page0zip.cc
+++ b/storage/innobase/page/page0zip.cc
@@ -3980,9 +3980,6 @@ page_zip_write_trx_id_and_roll_ptr(
ut_ad(field + DATA_TRX_ID_LEN
== rec_get_nth_field(rec, offsets, trx_id_col + 1, &len));
ut_ad(len == DATA_ROLL_PTR_LEN);
-#if defined UNIV_DEBUG || defined UNIV_ZIP_DEBUG
- ut_a(!memcmp(storage, field, sys_len));
-#endif /* UNIV_DEBUG || UNIV_ZIP_DEBUG */
compile_time_assert(DATA_TRX_ID_LEN == 6);
mach_write_to_6(field, trx_id);
compile_time_assert(DATA_ROLL_PTR_LEN == 7);