MDEV-15374 Server hangs and aborts with long semaphore wait or assertion `len < ((ulint) srv_page_size)' fails in trx_undo_rec_copy upon ROLLBACK on temporary table

Problem:
=======
InnoDB cleans all temporary undo logs during commit. During rollback
of secondary index entry, InnoDB tries to build the previous version
of clustered index. It leads to access of freed undo page during
previous transaction commit and it leads to undo log corruption.

Solution:
=========
During rollback, temporary undo logs should not try to build
the previous version of the record.
This commit is contained in:
Thirunarayanan Balathandayuthapani 2018-04-23 11:22:58 +05:30
commit 211842dd86
2 changed files with 16 additions and 18 deletions

View file

@ -428,7 +428,7 @@ row_undo_mod_del_mark_or_remove_sec_low(
btr_pcur_t pcur;
btr_cur_t* btr_cur;
ibool success;
ibool old_has;
ibool old_has = FALSE;
dberr_t err = DB_SUCCESS;
mtr_t mtr;
mtr_t mtr_vers;
@ -504,10 +504,15 @@ row_undo_mod_del_mark_or_remove_sec_low(
&mtr_vers);
ut_a(success);
old_has = row_vers_old_has_index_entry(FALSE,
btr_pcur_get_rec(&(node->pcur)),
&mtr_vers, index, entry,
0, 0);
/* For temporary table, we can skip to check older version of
clustered index entry. Because the purge won't process
any no-redo rollback segment undo logs. */
if (!dict_table_is_temporary(node->table)) {
old_has = row_vers_old_has_index_entry(
FALSE, btr_pcur_get_rec(&(node->pcur)),
&mtr_vers, index, entry, 0, 0);
}
if (old_has) {
err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
btr_cur, TRUE, thr, &mtr);

View file

@ -2087,13 +2087,11 @@ err_exit:
/** Copy an undo record to heap.
@param[in] roll_ptr roll pointer to a record that exists
@param[in] is_temp whether this is a temporary table
@param[in,out] heap memory heap where copied */
static
trx_undo_rec_t*
trx_undo_get_undo_rec_low(
roll_ptr_t roll_ptr,
bool is_temp,
mem_heap_t* heap)
{
trx_undo_rec_t* undo_rec;
@ -2109,10 +2107,7 @@ trx_undo_get_undo_rec_low(
&offset);
ut_ad(page_no > FSP_FIRST_INODE_PAGE_NO);
ut_ad(offset >= TRX_UNDO_PAGE_HDR + TRX_UNDO_PAGE_HDR_SIZE);
rseg = is_temp
? trx_sys->temp_rsegs[rseg_id]
: trx_sys->rseg_array[rseg_id];
ut_ad(is_temp == !rseg->is_persistent());
rseg = trx_sys->rseg_array[rseg_id];
mtr_start(&mtr);
@ -2128,7 +2123,6 @@ trx_undo_get_undo_rec_low(
/** Copy an undo record to heap.
@param[in] roll_ptr roll pointer to record
@param[in] is_temp whether this is a temporary table
@param[in,out] heap memory heap where copied
@param[in] trx_id id of the trx that generated
the roll pointer: it points to an
@ -2143,7 +2137,6 @@ static MY_ATTRIBUTE((warn_unused_result))
bool
trx_undo_get_undo_rec(
roll_ptr_t roll_ptr,
bool is_temp,
mem_heap_t* heap,
trx_id_t trx_id,
const table_name_t& name,
@ -2155,7 +2148,7 @@ trx_undo_get_undo_rec(
missing_history = purge_sys->view.changes_visible(trx_id, name);
if (!missing_history) {
*undo_rec = trx_undo_get_undo_rec_low(roll_ptr, is_temp, heap);
*undo_rec = trx_undo_get_undo_rec_low(roll_ptr, heap);
}
rw_lock_s_unlock(&purge_sys->latch);
@ -2236,19 +2229,19 @@ trx_undo_prev_version_build(
return(true);
}
const bool is_temp = dict_table_is_temporary(index->table);
ut_ad(!dict_table_is_temporary(index->table));
rec_trx_id = row_get_rec_trx_id(rec, index, offsets);
ut_ad(!index->table->skip_alter_undo);
if (trx_undo_get_undo_rec(
roll_ptr, is_temp, heap, rec_trx_id, index->table->name,
roll_ptr, heap, rec_trx_id, index->table->name,
&undo_rec)) {
if (v_status & TRX_UNDO_PREV_IN_PURGE) {
/* We are fetching the record being purged */
ut_ad(!is_temp);
undo_rec = trx_undo_get_undo_rec_low(
roll_ptr, is_temp, heap);
roll_ptr, heap);
} else {
/* The undo record may already have been purged,
during purge or semi-consistent read. */