diff options
Diffstat (limited to 'storage/innobase/row/row0undo.cc')
-rw-r--r-- | storage/innobase/row/row0undo.cc | 197 |
1 files changed, 166 insertions, 31 deletions
diff --git a/storage/innobase/row/row0undo.cc b/storage/innobase/row/row0undo.cc index a85b817d5cb..2b8187171f9 100644 --- a/storage/innobase/row/row0undo.cc +++ b/storage/innobase/row/row0undo.cc @@ -24,8 +24,6 @@ Row undo Created 1/8/1997 Heikki Tuuri *******************************************************/ -#include "ha_prototypes.h" - #include "row0undo.h" #include "fsp0fsp.h" #include "mach0data.h" @@ -219,7 +217,8 @@ row_undo_search_clust_to_pcur( log, first mark them DATA_MISSING. So we will know if the value gets updated */ if (node->table->n_v_cols - && node->state != UNDO_NODE_INSERT + && (node->state == UNDO_UPDATE_PERSISTENT + || node->state == UNDO_UPDATE_TEMPORARY) && !(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { for (ulint i = 0; i < dict_table_get_n_v_cols(node->table); i++) { @@ -229,14 +228,16 @@ row_undo_search_clust_to_pcur( } if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) { - ut_ad(node->row->info_bits == REC_INFO_MIN_REC_FLAG + ut_ad((node->row->info_bits & ~REC_INFO_DELETED_FLAG) + == REC_INFO_MIN_REC_FLAG || node->row->info_bits == 0); node->undo_row = dtuple_copy(node->row, node->heap); row_upd_replace(node->undo_row, &node->undo_ext, clust_index, node->update, node->heap); } else { - ut_ad((node->row->info_bits == REC_INFO_MIN_REC_FLAG) - == (node->rec_type == TRX_UNDO_INSERT_DEFAULT)); + ut_ad(((node->row->info_bits & ~REC_INFO_DELETED_FLAG) + == REC_INFO_MIN_REC_FLAG) + == (node->rec_type == TRX_UNDO_INSERT_METADATA)); node->undo_row = NULL; node->undo_ext = NULL; } @@ -253,6 +254,149 @@ func_exit: return(found); } +/** Try to truncate the undo logs. +@param[in,out] trx transaction */ +static void row_undo_try_truncate(trx_t* trx) +{ + if (trx_undo_t* undo = trx->rsegs.m_redo.undo) { + ut_ad(undo->rseg == trx->rsegs.m_redo.rseg); + trx_undo_truncate_end(*undo, trx->undo_no, false); + } + + if (trx_undo_t* undo = trx->rsegs.m_noredo.undo) { + ut_ad(undo->rseg == trx->rsegs.m_noredo.rseg); + trx_undo_truncate_end(*undo, trx->undo_no, true); + } +} + +/** Get the latest undo log record for rollback. +@param[in,out] node rollback context +@return whether an undo log record was fetched */ +static bool row_undo_rec_get(undo_node_t* node) +{ + trx_t* trx = node->trx; + + if (trx->pages_undone) { + trx->pages_undone = 0; + row_undo_try_truncate(trx); + } + + trx_undo_t* undo = NULL; + trx_undo_t* insert = trx->rsegs.m_redo.old_insert; + trx_undo_t* update = trx->rsegs.m_redo.undo; + trx_undo_t* temp = trx->rsegs.m_noredo.undo; + const undo_no_t limit = trx->roll_limit; + + ut_ad(!insert || !update || insert->empty() || update->empty() + || insert->top_undo_no != update->top_undo_no); + ut_ad(!insert || !temp || insert->empty() || temp->empty() + || insert->top_undo_no != temp->top_undo_no); + ut_ad(!update || !temp || update->empty() || temp->empty() + || update->top_undo_no != temp->top_undo_no); + + if (UNIV_LIKELY_NULL(insert) + && !insert->empty() && limit <= insert->top_undo_no) { + undo = insert; + } + + if (update && !update->empty() && update->top_undo_no >= limit) { + if (!undo) { + undo = update; + } else if (undo->top_undo_no < update->top_undo_no) { + undo = update; + } + } + + if (temp && !temp->empty() && temp->top_undo_no >= limit) { + if (!undo) { + undo = temp; + } else if (undo->top_undo_no < temp->top_undo_no) { + undo = temp; + } + } + + if (undo == NULL) { + row_undo_try_truncate(trx); + /* Mark any ROLLBACK TO SAVEPOINT completed, so that + if the transaction object is committed and reused + later, we will default to a full ROLLBACK. */ + trx->roll_limit = 0; + trx->in_rollback = false; + return false; + } + + ut_ad(!undo->empty()); + ut_ad(limit <= undo->top_undo_no); + + node->roll_ptr = trx_undo_build_roll_ptr( + false, undo->rseg->id, undo->top_page_no, undo->top_offset); + + mtr_t mtr; + mtr.start(); + + page_t* undo_page = trx_undo_page_get_s_latched( + page_id_t(undo->rseg->space->id, undo->top_page_no), &mtr); + + ulint offset = undo->top_offset; + + trx_undo_rec_t* prev_rec = trx_undo_get_prev_rec( + undo_page + offset, undo->hdr_page_no, undo->hdr_offset, + true, &mtr); + + if (prev_rec == NULL) { + undo->top_undo_no = IB_ID_MAX; + ut_ad(undo->empty()); + } else { + page_t* prev_rec_page = page_align(prev_rec); + + if (prev_rec_page != undo_page) { + + trx->pages_undone++; + } + + undo->top_page_no = page_get_page_no(prev_rec_page); + undo->top_offset = ulint(prev_rec - prev_rec_page); + undo->top_undo_no = trx_undo_rec_get_undo_no(prev_rec); + ut_ad(!undo->empty()); + } + + { + const trx_undo_rec_t* undo_rec = undo_page + offset; + node->undo_rec = trx_undo_rec_copy(undo_rec, node->heap); + } + + mtr.commit(); + + switch (trx_undo_rec_get_type(node->undo_rec)) { + case TRX_UNDO_INSERT_METADATA: + /* This record type was introduced in MDEV-11369 + instant ADD COLUMN, which was implemented after + MDEV-12288 removed the insert_undo log. There is no + instant ADD COLUMN for temporary tables. Therefore, + this record can only be present in the main undo log. */ + ut_ad(undo == update); + /* fall through */ + case TRX_UNDO_RENAME_TABLE: + ut_ad(undo == insert || undo == update); + /* fall through */ + case TRX_UNDO_INSERT_REC: + ut_ad(undo == insert || undo == update || undo == temp); + node->roll_ptr |= 1ULL << ROLL_PTR_INSERT_FLAG_POS; + node->state = undo == temp + ? UNDO_INSERT_TEMPORARY : UNDO_INSERT_PERSISTENT; + break; + default: + ut_ad(undo == update || undo == temp); + node->state = undo == temp + ? UNDO_UPDATE_TEMPORARY : UNDO_UPDATE_PERSISTENT; + break; + } + + trx->undo_no = node->undo_no = trx_undo_rec_get_undo_no( + node->undo_rec); + return true; +} + /***********************************************************//** Fetches an undo log record and does the undo for the recorded operation. If none left, or a partial rollback completed, returns control to the @@ -265,23 +409,12 @@ row_undo( undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr) /*!< in: query thread */ { - trx_t* trx = node->trx; - ut_ad(trx->in_rollback); - - if (node->state == UNDO_NODE_FETCH_NEXT) { - - node->undo_rec = trx_roll_pop_top_rec_of_trx( - trx, &node->roll_ptr, node->heap); - - if (!node->undo_rec) { - /* Rollback completed for this query thread */ - thr->run_node = que_node_get_parent(node); - return(DB_SUCCESS); - } + ut_ad(node->trx->in_rollback); - node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec); - node->state = trx_undo_roll_ptr_is_insert(node->roll_ptr) - ? UNDO_NODE_INSERT : UNDO_NODE_MODIFY; + if (node->state == UNDO_NODE_FETCH_NEXT && !row_undo_rec_get(node)) { + /* Rollback completed for this query thread */ + thr->run_node = que_node_get_parent(node); + return DB_SUCCESS; } /* Prevent DROP TABLE etc. while we are rolling back this row. @@ -289,31 +422,33 @@ row_undo( then we already have dict_operation_lock locked in x-mode. Do not try to lock again, because that would cause a hang. */ + trx_t* trx = node->trx; const bool locked_data_dict = (trx->dict_operation_lock_mode == 0); if (locked_data_dict) { - row_mysql_freeze_data_dictionary(trx); } dberr_t err; - if (node->state == UNDO_NODE_INSERT) { - + switch (node->state) { + case UNDO_INSERT_PERSISTENT: + case UNDO_INSERT_TEMPORARY: err = row_undo_ins(node, thr); - - node->state = UNDO_NODE_FETCH_NEXT; - } else { - ut_ad(node->state == UNDO_NODE_MODIFY); + break; + case UNDO_UPDATE_PERSISTENT: + case UNDO_UPDATE_TEMPORARY: err = row_undo_mod(node, thr); + break; + case UNDO_NODE_FETCH_NEXT: + ut_ad(!"wrong state"); } if (locked_data_dict) { - row_mysql_unfreeze_data_dictionary(trx); } - /* Do some cleanup */ + node->state = UNDO_NODE_FETCH_NEXT; btr_pcur_close(&(node->pcur)); mem_heap_empty(node->heap); |