/****************************************************** Fresh insert undo (c) 1996 Innobase Oy Created 2/25/1997 Heikki Tuuri *******************************************************/ #include "row0uins.h" #ifdef UNIV_NONINL #include "row0uins.ic" #endif #include "dict0dict.h" #include "dict0boot.h" #include "dict0crea.h" #include "trx0undo.h" #include "trx0roll.h" #include "btr0btr.h" #include "mach0data.h" #include "row0undo.h" #include "row0vers.h" #include "trx0trx.h" #include "trx0rec.h" #include "row0row.h" #include "row0upd.h" #include "que0que.h" #include "ibuf0ibuf.h" #include "log0log.h" /******************************************************************* Removes a clustered index record. The pcur in node was positioned on the record, now it is detached. */ static ulint row_undo_ins_remove_clust_rec( /*==========================*/ /* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ undo_node_t* node) /* in: undo node */ { btr_cur_t* btr_cur; ibool success; ulint err; ulint n_tries = 0; mtr_t mtr; mtr_start(&mtr); success = btr_pcur_restore_position(BTR_MODIFY_LEAF, &(node->pcur), &mtr); ut_a(success); if (ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) { /* Drop the index tree associated with the row in SYS_INDEXES table: */ dict_drop_index_tree(btr_pcur_get_rec(&(node->pcur)), &mtr); mtr_commit(&mtr); mtr_start(&mtr); success = btr_pcur_restore_position(BTR_MODIFY_LEAF, &(node->pcur), &mtr); ut_a(success); } btr_cur = btr_pcur_get_btr_cur(&(node->pcur)); success = btr_cur_optimistic_delete(btr_cur, &mtr); btr_pcur_commit_specify_mtr(&(node->pcur), &mtr); if (success) { trx_undo_rec_release(node->trx, node->undo_no); return(DB_SUCCESS); } retry: /* If did not succeed, try pessimistic descent to tree */ mtr_start(&mtr); success = btr_pcur_restore_position(BTR_MODIFY_TREE, &(node->pcur), &mtr); ut_a(success); btr_cur_pessimistic_delete(&err, FALSE, btr_cur, TRUE, &mtr); /* The delete operation may fail if we have little file space left: TODO: easiest to crash the database and restart with more file space */ if (err == DB_OUT_OF_FILE_SPACE && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) { btr_pcur_commit_specify_mtr(&(node->pcur), &mtr); n_tries++; os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME); goto retry; } btr_pcur_commit_specify_mtr(&(node->pcur), &mtr); trx_undo_rec_release(node->trx, node->undo_no); return(err); } /******************************************************************* Removes a secondary index entry if found. */ static ulint row_undo_ins_remove_sec_low( /*========================*/ /* out: DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */ ulint mode, /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE, depending on whether we wish optimistic or pessimistic descent down the index tree */ dict_index_t* index, /* in: index */ dtuple_t* entry) /* in: index entry to remove */ { btr_pcur_t pcur; btr_cur_t* btr_cur; ibool found; ibool success; ulint err; mtr_t mtr; log_free_check(); mtr_start(&mtr); found = row_search_index_entry(index, entry, mode, &pcur, &mtr); btr_cur = btr_pcur_get_btr_cur(&pcur); if (!found) { /* Not found */ btr_pcur_close(&pcur); mtr_commit(&mtr); return(DB_SUCCESS); } if (mode == BTR_MODIFY_LEAF) { success = btr_cur_optimistic_delete(btr_cur, &mtr); if (success) { err = DB_SUCCESS; } else { err = DB_FAIL; } } else { ut_ad(mode == BTR_MODIFY_TREE); btr_cur_pessimistic_delete(&err, FALSE, btr_cur, TRUE, &mtr); } btr_pcur_close(&pcur); mtr_commit(&mtr); return(err); } /******************************************************************* Removes a secondary index entry from the index if found. Tries first optimistic, then pessimistic descent down the tree. */ static ulint row_undo_ins_remove_sec( /*====================*/ /* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ dict_index_t* index, /* in: index */ dtuple_t* entry) /* in: index entry to insert */ { ulint err; ulint n_tries = 0; /* Try first optimistic descent to the B-tree */ err = row_undo_ins_remove_sec_low(BTR_MODIFY_LEAF, index, entry); if (err == DB_SUCCESS) { return(err); } /* Try then pessimistic descent to the B-tree */ retry: err = row_undo_ins_remove_sec_low(BTR_MODIFY_TREE, index, entry); /* The delete operation may fail if we have little file space left: TODO: easiest to crash the database and restart with more file space */ if (err != DB_SUCCESS && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) { n_tries++; os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME); goto retry; } return(err); } /*************************************************************** Parses the row reference and other info in a fresh insert undo record. */ static void row_undo_ins_parse_undo_rec( /*========================*/ undo_node_t* node) /* in: row undo node */ { dict_index_t* clust_index; byte* ptr; dulint undo_no; dulint table_id; ulint type; ulint dummy; ibool dummy_extern; ut_ad(node); ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &dummy, &dummy_extern, &undo_no, &table_id); ut_ad(type == TRX_UNDO_INSERT_REC); node->rec_type = type; node->table = dict_table_get_on_id(table_id, node->trx); if (node->table == NULL) { return; } if (node->table->ibd_file_missing) { /* We skip undo operations to missing .ibd files */ node->table = NULL; return; } clust_index = dict_table_get_first_index(node->table); ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref), node->heap); } /*************************************************************** Undoes a fresh insert of a row to a table. A fresh insert means that the same clustered index unique key did not have any record, even delete marked, at the time of the insert. */ ulint row_undo_ins( /*=========*/ /* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */ undo_node_t* node) /* in: row undo node */ { dtuple_t* entry; ibool found; ulint err; ut_ad(node); ut_ad(node->state == UNDO_NODE_INSERT); row_undo_ins_parse_undo_rec(node); if (node->table == NULL) { found = FALSE; } else { found = row_undo_search_clust_to_pcur(node); } if (!found) { trx_undo_rec_release(node->trx, node->undo_no); return(DB_SUCCESS); } node->index = dict_table_get_next_index( dict_table_get_first_index(node->table)); while (node->index != NULL) { entry = row_build_index_entry(node->row, node->index, node->heap); err = row_undo_ins_remove_sec(node->index, entry); if (err != DB_SUCCESS) { return(err); } node->index = dict_table_get_next_index(node->index); } err = row_undo_ins_remove_clust_rec(node); return(err); }