MDEV-30882 Crash on ROLLBACK in a ROW_FORMAT=COMPRESSED table
row_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. Thanks to Jean-François Gagné for providing a copy of a page that triggered these bugs on the ROLLBACK of UPDATE and DELETE. A 10.6 version of this was tested by Matthias Leich using cmake -DWITH_INNODB_EXTRA_DEBUG=ON a.k.a. UNIV_ZIP_DEBUG.
This commit is contained in:
parent
7c91082e39
commit
ff3d4395d8
@ -4210,6 +4210,52 @@ out_of_space:
|
||||
return(false);
|
||||
}
|
||||
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/*************************************************************//**
|
||||
Updates a record when the update causes no size changes in its fields.
|
||||
We assume here that the ordering fields of the record do not change.
|
||||
@ -4271,17 +4317,10 @@ btr_cur_update_in_place(
|
||||
page_zip = buf_block_get_page_zip(block);
|
||||
|
||||
/* Check that enough space is available on the compressed page. */
|
||||
if (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 */
|
||||
@ -5034,7 +5073,13 @@ btr_cur_pessimistic_update(
|
||||
|
||||
ut_ad(page_is_leaf(page));
|
||||
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 */
|
||||
|
@ -4132,9 +4132,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, DATA_TRX_ID_LEN + DATA_ROLL_PTR_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);
|
||||
|
@ -692,9 +692,42 @@ row_upd_rec_in_place(
|
||||
dfield_get_len(new_val));
|
||||
}
|
||||
|
||||
if (page_zip) {
|
||||
page_zip_write_rec(page_zip, rec, index, offsets, 0);
|
||||
if (UNIV_LIKELY(!page_zip)) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (update->n_fields) {
|
||||
case 0:
|
||||
/* We only changed the delete-mark flag. */
|
||||
update_del_mark:
|
||||
page_zip_rec_set_deleted(page_zip, rec,
|
||||
rec_get_deleted_flag(rec, true));
|
||||
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(
|
||||
page_zip, rec, offsets, index->db_trx_id(),
|
||||
trx_read_trx_id(sys),
|
||||
trx_read_roll_ptr(sys + DATA_TRX_ID_LEN));
|
||||
goto update_del_mark;
|
||||
}
|
||||
|
||||
page_zip_write_rec(page_zip, rec, index, offsets, 0);
|
||||
}
|
||||
|
||||
/*********************************************************************//**
|
||||
|
Loading…
x
Reference in New Issue
Block a user