summaryrefslogtreecommitdiff
path: root/storage/innobase/row/row0purge.c
diff options
context:
space:
mode:
Diffstat (limited to 'storage/innobase/row/row0purge.c')
-rw-r--r--storage/innobase/row/row0purge.c201
1 files changed, 152 insertions, 49 deletions
diff --git a/storage/innobase/row/row0purge.c b/storage/innobase/row/row0purge.c
index 500ebe571ab..da9d31f333f 100644
--- a/storage/innobase/row/row0purge.c
+++ b/storage/innobase/row/row0purge.c
@@ -1,6 +1,6 @@
/*****************************************************************************
-Copyright (c) 1997, 2009, Innobase Oy. All Rights Reserved.
+Copyright (c) 1997, 2010, Innobase Oy. All Rights Reserved.
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
@@ -215,33 +215,71 @@ retry:
}
/***********************************************************//**
-Removes a secondary index entry if possible.
+Determines if it is possible to remove a secondary index entry.
+Removal is possible if the secondary index entry does not refer to any
+not delete marked version of a clustered index record where DB_TRX_ID
+is newer than the purge view.
+
+NOTE: This function should only be called by the purge thread, only
+while holding a latch on the leaf page of the secondary index entry
+(or keeping the buffer pool watch on the page). It is possible that
+this function first returns TRUE and then FALSE, if a user transaction
+inserts a record that the secondary index entry would refer to.
+However, in that case, the user transaction would also re-insert the
+secondary index entry after purge has removed it and released the leaf
+page latch.
+@return TRUE if the secondary index record can be purged */
+UNIV_INTERN
+ibool
+row_purge_poss_sec(
+/*===============*/
+ purge_node_t* node, /*!< in/out: row purge node */
+ dict_index_t* index, /*!< in: secondary index */
+ const dtuple_t* entry) /*!< in: secondary index entry */
+{
+ ibool can_delete;
+ mtr_t mtr;
+
+ ut_ad(!dict_index_is_clust(index));
+ mtr_start(&mtr);
+
+ can_delete = !row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr)
+ || !row_vers_old_has_index_entry(TRUE,
+ btr_pcur_get_rec(&node->pcur),
+ &mtr, index, entry);
+
+ btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
+
+ return(can_delete);
+}
+
+/***************************************************************
+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_low(
-/*=============================*/
+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 */
- ulint mode) /*!< in: latch mode BTR_MODIFY_LEAF or
- BTR_MODIFY_TREE */
+ const dtuple_t* entry) /*!< in: index entry */
{
- btr_pcur_t pcur;
- btr_cur_t* btr_cur;
- ibool success;
- ibool old_has = 0; /* remove warning */
- ibool found;
- ulint err;
- mtr_t mtr;
- mtr_t mtr_vers;
+ 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);
- found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
+ search_result = row_search_index_entry(index, entry, BTR_MODIFY_TREE,
+ &pcur, &mtr);
- if (!found) {
+ 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
@@ -254,11 +292,15 @@ row_purge_remove_sec_if_poss_low(
/* fputs("PURGE:........sec entry not found\n", stderr); */
/* dtuple_print(stderr, entry); */
-
- btr_pcur_close(&pcur);
- mtr_commit(&mtr);
-
- return(TRUE);
+ 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);
@@ -267,38 +309,102 @@ row_purge_remove_sec_if_poss_low(
which cannot be purged yet, requires its existence. If some requires,
we should do nothing. */
- mtr_start(&mtr_vers);
-
- success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr_vers);
-
- if (success) {
- old_has = row_vers_old_has_index_entry(
- TRUE, btr_pcur_get_rec(&(node->pcur)),
- &mtr_vers, index, entry);
- }
-
- btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
+ 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)));
- if (!success || !old_has) {
- /* Remove the index record */
-
- if (mode == BTR_MODIFY_LEAF) {
- success = btr_cur_optimistic_delete(btr_cur, &mtr);
- } else {
- ut_ad(mode == BTR_MODIFY_TREE);
- btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
- RB_NONE, &mtr);
- success = err == DB_SUCCESS;
- ut_a(success || err == DB_OUT_OF_FILE_SPACE);
+ 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);
}
+/***************************************************************
+Removes a secondary index entry without modifying the index tree,
+if possible.
+@return TRUE if success or if not found */
+static
+ibool
+row_purge_remove_sec_if_poss_leaf(
+/*==============================*/
+ purge_node_t* node, /*!< in: row purge node */
+ dict_index_t* index, /*!< in: index */
+ const dtuple_t* entry) /*!< in: index entry */
+{
+ mtr_t mtr;
+ btr_pcur_t pcur;
+ enum row_search_result search_result;
+
+ log_free_check();
+
+ mtr_start(&mtr);
+
+ /* Set the purge node for the call to row_purge_poss_sec(). */
+ pcur.btr_cur.purge_node = node;
+ /* Set the query thread, so that ibuf_insert_low() will be
+ able to invoke thd_get_trx(). */
+ pcur.btr_cur.thr = que_node_get_parent(node);
+
+ search_result = row_search_index_entry(
+ index, entry, BTR_MODIFY_LEAF | BTR_DELETE, &pcur, &mtr);
+
+ switch (search_result) {
+ ibool success;
+ case ROW_FOUND:
+ /* Before attempting to purge a record, check
+ if it is safe to do so. */
+ if (row_purge_poss_sec(node, index, entry)) {
+ btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur);
+
+ /* Only delete-marked records should be purged. */
+ ut_ad(REC_INFO_DELETED_FLAG
+ & rec_get_info_bits(
+ btr_cur_get_rec(btr_cur),
+ dict_table_is_comp(index->table)));
+
+ if (!btr_cur_optimistic_delete(btr_cur, &mtr)) {
+
+ /* The index entry could not be deleted. */
+ success = FALSE;
+ goto func_exit;
+ }
+ }
+ /* fall through (the index entry is still needed,
+ or the deletion succeeded) */
+ case ROW_NOT_DELETED_REF:
+ /* The index entry is still needed. */
+ case ROW_BUFFERED:
+ /* The deletion was buffered. */
+ case ROW_NOT_FOUND:
+ /* The index entry does not exist, nothing to do. */
+ success = TRUE;
+ func_exit:
+ btr_pcur_close(&pcur);
+ mtr_commit(&mtr);
+ return(success);
+ }
+
+ ut_error;
+ return(FALSE);
+}
+
/***********************************************************//**
Removes a secondary index entry if possible. */
UNIV_INLINE
@@ -314,15 +420,12 @@ row_purge_remove_sec_if_poss(
/* fputs("Purge: Removing secondary record\n", stderr); */
- success = row_purge_remove_sec_if_poss_low(node, index, entry,
- BTR_MODIFY_LEAF);
- if (success) {
+ if (row_purge_remove_sec_if_poss_leaf(node, index, entry)) {
return;
}
retry:
- success = row_purge_remove_sec_if_poss_low(node, index, entry,
- BTR_MODIFY_TREE);
+ success = row_purge_remove_sec_if_poss_tree(node, 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 */