Skip to content

Commit

Permalink
MDEV-30882 Crash on ROLLBACK in a ROW_FORMAT=COMPRESSED table
Browse files Browse the repository at this point in the history
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 ff3d439
(different because of commit 08ba388).
  • Loading branch information
dr-m committed Mar 22, 2023
1 parent dccbb5a commit 701399a
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 17 deletions.
103 changes: 89 additions & 14 deletions storage/innobase/btr/btr0cur.cc
Expand Up @@ -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);
}

/*************************************************************//**
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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 */
Expand Down
3 changes: 0 additions & 3 deletions storage/innobase/page/page0zip.cc
Expand Up @@ -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);
Expand Down

0 comments on commit 701399a

Please sign in to comment.