/*************************************************************** Removes a secondary index entry if possible, by modifying the index tree. Does not try to buffer the delete. @return TRUE if success or if not found */ static ibool row_purge_remove_sec_if_poss_tree( /*==============================*/ purge_node_t* node, /*!< in: row purge node */ dict_index_t* index, /*!< in: index */ const dtuple_t* entry) /*!< in: index entry */ { btr_pcur_t pcur; btr_cur_t* btr_cur; ibool success = TRUE; ulint err; mtr_t mtr; enum row_search_result search_result; log_free_check(); mtr_start(&mtr); search_result = row_search_index_entry(index, entry, BTR_MODIFY_TREE, &pcur, &mtr); switch (search_result) { case ROW_NOT_FOUND: /* Not found. This is a legitimate condition. In a rollback, InnoDB will remove secondary recs that would be purged anyway. Then the actual purge will not find the secondary index record. Also, the purge itself is eager: if it comes to consider a secondary index record, and notices it does not need to exist in the index, it will remove it. Then if/when the purge comes to consider the secondary index record a second time, it will not exist any more in the index. */ /* fputs("PURGE:........sec entry not found\n", stderr); */ /* dtuple_print(stderr, entry); */ goto func_exit; case ROW_FOUND: break; case ROW_BUFFERED: case ROW_NOT_DELETED_REF: /* These are invalid outcomes, because the mode passed to row_search_index_entry() did not include any of the flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */ ut_error; } btr_cur = btr_pcur_get_btr_cur(&pcur); /* We should remove the index record if no later version of the row, which cannot be purged yet, requires its existence. If some requires, we should do nothing. */ if (row_purge_poss_sec(node, index, entry)) { /* Remove the index record, which should have been marked for deletion. */ ut_ad(REC_INFO_DELETED_FLAG & rec_get_info_bits(btr_cur_get_rec(btr_cur), dict_table_is_comp(index->table))); btr_cur_pessimistic_delete(&err, FALSE, btr_cur, RB_NONE, &mtr); switch (UNIV_EXPECT(err, DB_SUCCESS)) { case DB_SUCCESS: break; case DB_OUT_OF_FILE_SPACE: success = FALSE; break; default: ut_error; } } func_exit: btr_pcur_close(&pcur); mtr_commit(&mtr); return(success); }
/***********************************************************//** Delete marks or removes a secondary index entry if found. @return DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */ static ulint row_undo_mod_del_mark_or_remove_sec_low( /*====================================*/ undo_node_t* node, /*!< in: row undo node */ que_thr_t* thr, /*!< in: query thread */ dict_index_t* index, /*!< in: index */ dtuple_t* entry, /*!< in: index entry */ ulint mode) /*!< in: latch mode BTR_MODIFY_LEAF or BTR_MODIFY_TREE */ { btr_pcur_t pcur; btr_cur_t* btr_cur; ibool success; ibool old_has; ulint err; mtr_t mtr; mtr_t mtr_vers; enum row_search_result search_result; log_free_check(); mtr_start(&mtr); btr_cur = btr_pcur_get_btr_cur(&pcur); ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF); search_result = row_search_index_entry(index, entry, mode, &pcur, &mtr); switch (UNIV_EXPECT(search_result, ROW_FOUND)) { case ROW_NOT_FOUND: /* In crash recovery, the secondary index record may be missing if the UPDATE did not have time to insert the secondary index records before the crash. When we are undoing that UPDATE in crash recovery, the record may be missing. In normal processing, if an update ends in a deadlock before it has inserted all updated secondary index records, then the undo will not find those records. */ err = DB_SUCCESS; goto func_exit; case ROW_FOUND: break; case ROW_BUFFERED: case ROW_NOT_DELETED_REF: /* These are invalid outcomes, because the mode passed to row_search_index_entry() did not include any of the flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */ ut_error; } /* We should remove the index record if no prior version of the row, which cannot be purged yet, requires its existence. If some requires, we should delete mark the record. */ mtr_start(&mtr_vers); success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur), &mtr_vers); ut_a(success); old_has = row_vers_old_has_index_entry(FALSE, btr_pcur_get_rec(&(node->pcur)), &mtr_vers, index, entry); if (old_has) { err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG, btr_cur, TRUE, thr, &mtr); ut_ad(err == DB_SUCCESS); } else { /* Remove the index record */ 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); /* No need to distinguish RB_RECOVERY_PURGE here, because we are deleting a secondary index record: the distinction between RB_NORMAL and RB_RECOVERY_PURGE only matters when deleting a record that contains externally stored columns. */ ut_ad(!dict_index_is_clust(index)); btr_cur_pessimistic_delete(&err, FALSE, btr_cur, RB_NORMAL, &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 */ } } btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers); func_exit: btr_pcur_close(&pcur); mtr_commit(&mtr); return(err); }