/***********************************************************//** 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 parent node, which is always a query thread node. @return DB_SUCCESS if operation successfully completed, else error code */ static ulint row_undo( /*=====*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr) /*!< in: query thread */ { ulint err; trx_t* trx; roll_ptr_t roll_ptr; ibool locked_data_dict; ut_ad(node && thr); trx = node->trx; if (node->state == UNDO_NODE_FETCH_NEXT) { node->undo_rec = trx_roll_pop_top_rec_of_trx(trx, trx->roll_limit, &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); } node->roll_ptr = roll_ptr; node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec); if (trx_undo_roll_ptr_is_insert(roll_ptr)) { node->state = UNDO_NODE_INSERT; } else { node->state = UNDO_NODE_MODIFY; } } else if (node->state == UNDO_NODE_PREV_VERS) { /* Undo should be done to the same clustered index record again in this same rollback, restoring the previous version */ roll_ptr = node->new_roll_ptr; node->undo_rec = trx_undo_get_undo_rec_low(roll_ptr, node->heap); node->roll_ptr = roll_ptr; node->undo_no = trx_undo_rec_get_undo_no(node->undo_rec); if (trx_undo_roll_ptr_is_insert(roll_ptr)) { node->state = UNDO_NODE_INSERT; } else { node->state = UNDO_NODE_MODIFY; } } /* Prevent DROP TABLE etc. while we are rolling back this row. If we are doing a TABLE CREATE or some other dictionary operation, then we already have dict_operation_lock locked in x-mode. Do not try to lock again, because that would cause a hang. */ locked_data_dict = (trx->dict_operation_lock_mode == 0); if (locked_data_dict) { row_mysql_freeze_data_dictionary(trx); } if (node->state == UNDO_NODE_INSERT) { err = row_undo_ins(node); node->state = UNDO_NODE_FETCH_NEXT; } else { ut_ad(node->state == UNDO_NODE_MODIFY); err = row_undo_mod(node, thr); } if (locked_data_dict) { row_mysql_unfreeze_data_dictionary(trx); } /* Do some cleanup */ btr_pcur_close(&(node->pcur)); mem_heap_empty(node->heap); thr->run_node = node; return(err); }
/***********************************************************//** Parses the row reference and other info in a modify undo log record. @return TRUE if purge operation required: NOTE that then the CALLER must unfreeze data dictionary! */ static ibool row_purge_parse_undo_rec( /*=====================*/ purge_node_t* node, /*!< in: row undo node */ ibool* updated_extern, /*!< out: TRUE if an externally stored field was updated */ que_thr_t* thr) /*!< in: query thread */ { dict_index_t* clust_index; byte* ptr; trx_t* trx; undo_no_t undo_no; table_id_t table_id; trx_id_t trx_id; roll_ptr_t roll_ptr; ulint info_bits; ulint type; ut_ad(node && thr); trx = thr_get_trx(thr); ptr = trx_undo_rec_get_pars( node->undo_rec, &type, &node->cmpl_info, updated_extern, &undo_no, &table_id); node->rec_type = type; if (type == TRX_UNDO_UPD_DEL_REC && !(*updated_extern)) { return(FALSE); } ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr, &info_bits); node->table = NULL; if (type == TRX_UNDO_UPD_EXIST_REC && node->cmpl_info & UPD_NODE_NO_ORD_CHANGE && !(*updated_extern)) { /* Purge requires no changes to indexes: we may return */ return(FALSE); } /* Prevent DROP TABLE etc. from running when we are doing the purge for this row */ row_mysql_freeze_data_dictionary(trx); mutex_enter(&(dict_sys->mutex)); node->table = dict_table_get_on_id_low(table_id); mutex_exit(&(dict_sys->mutex)); if (node->table == NULL) { /* The table has been dropped: no need to do purge */ err_exit: row_mysql_unfreeze_data_dictionary(trx); return(FALSE); } if (node->table->ibd_file_missing) { /* We skip purge of missing .ibd files */ node->table = NULL; goto err_exit; } clust_index = dict_table_get_first_index(node->table); if (clust_index == NULL) { /* The table was corrupt in the data dictionary */ goto err_exit; } ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref), node->heap); ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id, roll_ptr, info_bits, trx, node->heap, &(node->update)); /* Read to the partial row the fields that occur in indexes */ if (!(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) { ptr = trx_undo_rec_get_partial_row( ptr, clust_index, &node->row, type == TRX_UNDO_UPD_DEL_REC, node->heap); } return(TRUE); }