void row_purge( /*======*/ purge_node_t* node, /*!< in: row purge node */ que_thr_t* thr) /*!< in: query thread */ { ibool updated_extern; ut_ad(node); ut_ad(thr); node->undo_rec = trx_purge_fetch_next_rec(&node->roll_ptr, &node->reservation, node->heap); if (!node->undo_rec) { /* Purge completed for this query thread */ thr->run_node = que_node_get_parent(node); return; } if (node->undo_rec != &trx_purge_dummy_rec && row_purge_parse_undo_rec(node, &updated_extern, thr)) { node->found_clust = FALSE; node->index = dict_table_get_next_index( dict_table_get_first_index(node->table)); if (node->rec_type == TRX_UNDO_DEL_MARK_REC) { row_purge_del_mark(node); } else if (updated_extern || node->rec_type == TRX_UNDO_UPD_EXIST_REC) { row_purge_upd_exist_or_extern(thr, node); } if (node->found_clust) { btr_pcur_close(&(node->pcur)); } row_mysql_unfreeze_data_dictionary(thr_get_trx(thr)); } /* Do some cleanup */ trx_purge_rec_release(node->reservation); mem_heap_empty(node->heap); thr->run_node = node; }
/***********************************************************//** 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); }
/*************************************************************** Fetches an undo log record and does the purge for the recorded operation. If none left, or the current purge completed, returns the control to the parent node, which is always a query thread node. */ static ulint row_purge( /*======*/ /* out: DB_SUCCESS if operation successfully completed, else error code */ purge_node_t* node, /* in: row purge node */ que_thr_t* thr) /* in: query thread */ { dulint roll_ptr; ibool purge_needed; ibool updated_extern; trx_t* trx; ut_ad(node && thr); trx = thr_get_trx(thr); node->undo_rec = trx_purge_fetch_next_rec(&roll_ptr, &(node->reservation), node->heap); if (!node->undo_rec) { /* Purge completed for this query thread */ thr->run_node = que_node_get_parent(node); return(DB_SUCCESS); } node->roll_ptr = roll_ptr; if (node->undo_rec == &trx_purge_dummy_rec) { purge_needed = FALSE; } else { purge_needed = row_purge_parse_undo_rec(node, &updated_extern, thr); /* If purge_needed == TRUE, we must also remember to unfreeze data dictionary! */ } if (purge_needed) { node->found_clust = FALSE; node->index = dict_table_get_next_index( dict_table_get_first_index(node->table)); if (node->rec_type == TRX_UNDO_DEL_MARK_REC) { row_purge_del_mark(node); } else if (updated_extern || node->rec_type == TRX_UNDO_UPD_EXIST_REC) { row_purge_upd_exist_or_extern(node); } if (node->found_clust) { btr_pcur_close(&(node->pcur)); } row_mysql_unfreeze_data_dictionary(trx); } /* Do some cleanup */ trx_purge_rec_release(node->reservation); mem_heap_empty(node->heap); thr->run_node = node; return(DB_SUCCESS); }