2005-10-27 07:29:40 +00:00
|
|
|
/******************************************************
|
|
|
|
Purge obsolete records
|
|
|
|
|
|
|
|
(c) 1997 Innobase Oy
|
|
|
|
|
|
|
|
Created 3/14/1997 Heikki Tuuri
|
|
|
|
*******************************************************/
|
|
|
|
|
|
|
|
#include "row0purge.h"
|
|
|
|
|
|
|
|
#ifdef UNIV_NONINL
|
|
|
|
#include "row0purge.ic"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "fsp0fsp.h"
|
|
|
|
#include "mach0data.h"
|
|
|
|
#include "trx0rseg.h"
|
|
|
|
#include "trx0trx.h"
|
|
|
|
#include "trx0roll.h"
|
|
|
|
#include "trx0undo.h"
|
|
|
|
#include "trx0purge.h"
|
|
|
|
#include "trx0rec.h"
|
|
|
|
#include "que0que.h"
|
|
|
|
#include "row0row.h"
|
|
|
|
#include "row0upd.h"
|
|
|
|
#include "row0vers.h"
|
|
|
|
#include "row0mysql.h"
|
|
|
|
#include "log0log.h"
|
|
|
|
|
|
|
|
/************************************************************************
|
|
|
|
Creates a purge node to a query graph. */
|
2008-02-06 14:17:36 +00:00
|
|
|
UNIV_INTERN
|
2005-10-27 07:29:40 +00:00
|
|
|
purge_node_t*
|
|
|
|
row_purge_node_create(
|
|
|
|
/*==================*/
|
|
|
|
/* out, own: purge node */
|
|
|
|
que_thr_t* parent, /* in: parent node, i.e., a thr node */
|
|
|
|
mem_heap_t* heap) /* in: memory heap where created */
|
|
|
|
{
|
|
|
|
purge_node_t* node;
|
|
|
|
|
|
|
|
ut_ad(parent && heap);
|
|
|
|
|
|
|
|
node = mem_heap_alloc(heap, sizeof(purge_node_t));
|
|
|
|
|
|
|
|
node->common.type = QUE_NODE_PURGE;
|
|
|
|
node->common.parent = parent;
|
|
|
|
|
|
|
|
node->heap = mem_heap_create(256);
|
|
|
|
|
|
|
|
return(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************
|
|
|
|
Repositions the pcur in the purge node on the clustered index record,
|
|
|
|
if found. */
|
|
|
|
static
|
|
|
|
ibool
|
|
|
|
row_purge_reposition_pcur(
|
|
|
|
/*======================*/
|
|
|
|
/* out: TRUE if the record was found */
|
|
|
|
ulint mode, /* in: latching mode */
|
|
|
|
purge_node_t* node, /* in: row purge node */
|
|
|
|
mtr_t* mtr) /* in: mtr */
|
|
|
|
{
|
|
|
|
ibool found;
|
|
|
|
|
|
|
|
if (node->found_clust) {
|
|
|
|
found = btr_pcur_restore_position(mode, &(node->pcur), mtr);
|
|
|
|
|
|
|
|
return(found);
|
|
|
|
}
|
|
|
|
|
|
|
|
found = row_search_on_row_ref(&(node->pcur), mode, node->table,
|
2006-08-29 09:30:31 +00:00
|
|
|
node->ref, mtr);
|
2005-10-27 07:29:40 +00:00
|
|
|
node->found_clust = found;
|
|
|
|
|
|
|
|
if (found) {
|
|
|
|
btr_pcur_store_position(&(node->pcur), mtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
return(found);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************
|
|
|
|
Removes a delete marked clustered index record if possible. */
|
|
|
|
static
|
|
|
|
ibool
|
|
|
|
row_purge_remove_clust_if_poss_low(
|
|
|
|
/*===============================*/
|
|
|
|
/* out: TRUE if success, or if not found, or
|
|
|
|
if modified after the delete marking */
|
|
|
|
purge_node_t* node, /* in: row purge node */
|
|
|
|
ulint mode) /* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
|
|
|
|
{
|
|
|
|
dict_index_t* index;
|
|
|
|
btr_pcur_t* pcur;
|
|
|
|
btr_cur_t* btr_cur;
|
|
|
|
ibool success;
|
|
|
|
ulint err;
|
|
|
|
mtr_t mtr;
|
|
|
|
rec_t* rec;
|
|
|
|
mem_heap_t* heap = NULL;
|
|
|
|
ulint offsets_[REC_OFFS_NORMAL_SIZE];
|
2007-09-28 07:05:57 +00:00
|
|
|
rec_offs_init(offsets_);
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
index = dict_table_get_first_index(node->table);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
pcur = &(node->pcur);
|
|
|
|
btr_cur = btr_pcur_get_btr_cur(pcur);
|
|
|
|
|
|
|
|
mtr_start(&mtr);
|
|
|
|
|
|
|
|
success = row_purge_reposition_pcur(mode, node, &mtr);
|
|
|
|
|
|
|
|
if (!success) {
|
|
|
|
/* The record is already removed */
|
|
|
|
|
|
|
|
btr_pcur_commit_specify_mtr(pcur, &mtr);
|
|
|
|
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
rec = btr_pcur_get_rec(pcur);
|
|
|
|
|
2006-09-19 10:14:07 +00:00
|
|
|
if (0 != ut_dulint_cmp(node->roll_ptr, row_get_rec_roll_ptr(
|
|
|
|
rec, index, rec_get_offsets(
|
|
|
|
rec, index, offsets_,
|
|
|
|
ULINT_UNDEFINED, &heap)))) {
|
2005-10-27 07:29:40 +00:00
|
|
|
if (UNIV_LIKELY_NULL(heap)) {
|
|
|
|
mem_heap_free(heap);
|
|
|
|
}
|
|
|
|
/* Someone else has modified the record later: do not remove */
|
|
|
|
btr_pcur_commit_specify_mtr(pcur, &mtr);
|
|
|
|
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (UNIV_LIKELY_NULL(heap)) {
|
|
|
|
mem_heap_free(heap);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mode == BTR_MODIFY_LEAF) {
|
|
|
|
success = btr_cur_optimistic_delete(btr_cur, &mtr);
|
|
|
|
} else {
|
|
|
|
ut_ad(mode == BTR_MODIFY_TREE);
|
branches/zip: In the rollback of incomplete transactions after crash
recovery, tolerate clustered index records whose externally stored
columns have not been written. This should remove the assertion failures
that were reported as Mantis issue#58, issue#62, issue#64.
trx_is_recv(): New function: TRUE if this transaction is rolling back
an incomplete transaction in crash recovery.
enum trx_rbmode: Rollback modes: no rollback, normal rollback, crash recovery.
btr_cur_pessimistic_delete(), btr_free_externally_stored_field(),
btr_rec_free_externally_stored_fields():
Replace the ibool parameter with enum trx_rbmode.
btr_free_externally_stored_field(): If field_ref is zero, return
but assert ut_a(rbmode == RB_RECOVERY). Unless InnoDB has crashed
while inserting a clustered index record, field_ref should not be zero.
btr_rec_free_updated_extern_fields(): Add the parameter enum trx_rbmode.
btr_cur_pessimistic_update(): Pass the rbmode parameter to
btr_rec_free_updated_extern_fields().
row_undo_ins(), row_undo_mod_upd_del_sec(): If row_build_index_entry()
fails, assert trx_is_recv() and skip this secondary index.
row_undo_mod_upd_del_sec(): Empty the heap at the end of each loop
iteration in order to conserve memory and to reduce the number of
low-level memory allocations.
2008-08-06 08:48:34 +00:00
|
|
|
btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
|
|
|
|
RB_NONE, &mtr);
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
if (err == DB_SUCCESS) {
|
|
|
|
success = TRUE;
|
|
|
|
} else if (err == DB_OUT_OF_FILE_SPACE) {
|
|
|
|
success = FALSE;
|
|
|
|
} else {
|
|
|
|
ut_error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
btr_pcur_commit_specify_mtr(pcur, &mtr);
|
|
|
|
|
|
|
|
return(success);
|
|
|
|
}
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
/***************************************************************
|
|
|
|
Removes a clustered index record if it has not been modified after the delete
|
|
|
|
marking. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
row_purge_remove_clust_if_poss(
|
|
|
|
/*===========================*/
|
|
|
|
purge_node_t* node) /* in: row purge node */
|
|
|
|
{
|
|
|
|
ibool success;
|
|
|
|
ulint n_tries = 0;
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2006-08-29 09:30:31 +00:00
|
|
|
/* fputs("Purge: Removing clustered record\n", stderr); */
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
success = row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_LEAF);
|
|
|
|
if (success) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
retry:
|
|
|
|
success = row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_TREE);
|
|
|
|
/* 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 (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
|
|
|
|
n_tries++;
|
|
|
|
|
|
|
|
os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_a(success);
|
|
|
|
}
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
/***************************************************************
|
|
|
|
Removes a secondary index entry if possible. */
|
|
|
|
static
|
|
|
|
ibool
|
|
|
|
row_purge_remove_sec_if_poss_low(
|
|
|
|
/*=============================*/
|
|
|
|
/* out: TRUE if success or if not found */
|
|
|
|
purge_node_t* node, /* in: row purge node */
|
|
|
|
dict_index_t* index, /* in: index */
|
|
|
|
dtuple_t* entry, /* in: index entry */
|
|
|
|
ulint mode) /* in: latch mode BTR_MODIFY_LEAF or
|
2006-02-23 19:25:29 +00:00
|
|
|
BTR_MODIFY_TREE */
|
2005-10-27 07:29:40 +00:00
|
|
|
{
|
|
|
|
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;
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
log_free_check();
|
|
|
|
mtr_start(&mtr);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
/* Not found */
|
|
|
|
|
|
|
|
/* fputs("PURGE:........sec entry not found\n", stderr); */
|
2007-04-04 11:05:33 +00:00
|
|
|
/* dtuple_print(stderr, entry); */
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
btr_cur = btr_pcur_get_btr_cur(&pcur);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
/* 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. */
|
|
|
|
|
|
|
|
mtr_vers = mem_alloc(sizeof(mtr_t));
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
mtr_start(mtr_vers);
|
|
|
|
|
|
|
|
success = row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, mtr_vers);
|
|
|
|
|
2006-02-23 19:25:29 +00:00
|
|
|
if (success) {
|
2006-09-19 10:14:07 +00:00
|
|
|
old_has = row_vers_old_has_index_entry(
|
|
|
|
TRUE, btr_pcur_get_rec(&(node->pcur)),
|
|
|
|
mtr_vers, index, entry);
|
2005-10-27 07:29:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
btr_pcur_commit_specify_mtr(&(node->pcur), mtr_vers);
|
|
|
|
|
|
|
|
mem_free(mtr_vers);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
if (!success || !old_has) {
|
|
|
|
/* Remove the index record */
|
|
|
|
|
2006-02-23 19:25:29 +00:00
|
|
|
if (mode == BTR_MODIFY_LEAF) {
|
2005-10-27 07:29:40 +00:00
|
|
|
success = btr_cur_optimistic_delete(btr_cur, &mtr);
|
|
|
|
} else {
|
|
|
|
ut_ad(mode == BTR_MODIFY_TREE);
|
|
|
|
btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
|
branches/zip: In the rollback of incomplete transactions after crash
recovery, tolerate clustered index records whose externally stored
columns have not been written. This should remove the assertion failures
that were reported as Mantis issue#58, issue#62, issue#64.
trx_is_recv(): New function: TRUE if this transaction is rolling back
an incomplete transaction in crash recovery.
enum trx_rbmode: Rollback modes: no rollback, normal rollback, crash recovery.
btr_cur_pessimistic_delete(), btr_free_externally_stored_field(),
btr_rec_free_externally_stored_fields():
Replace the ibool parameter with enum trx_rbmode.
btr_free_externally_stored_field(): If field_ref is zero, return
but assert ut_a(rbmode == RB_RECOVERY). Unless InnoDB has crashed
while inserting a clustered index record, field_ref should not be zero.
btr_rec_free_updated_extern_fields(): Add the parameter enum trx_rbmode.
btr_cur_pessimistic_update(): Pass the rbmode parameter to
btr_rec_free_updated_extern_fields().
row_undo_ins(), row_undo_mod_upd_del_sec(): If row_build_index_entry()
fails, assert trx_is_recv() and skip this secondary index.
row_undo_mod_upd_del_sec(): Empty the heap at the end of each loop
iteration in order to conserve memory and to reduce the number of
low-level memory allocations.
2008-08-06 08:48:34 +00:00
|
|
|
RB_NONE, &mtr);
|
2006-05-12 11:36:17 +00:00
|
|
|
success = err == DB_SUCCESS;
|
|
|
|
ut_a(success || err == DB_OUT_OF_FILE_SPACE);
|
2005-10-27 07:29:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
btr_pcur_close(&pcur);
|
|
|
|
mtr_commit(&mtr);
|
|
|
|
|
|
|
|
return(success);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************
|
|
|
|
Removes a secondary index entry if possible. */
|
|
|
|
UNIV_INLINE
|
|
|
|
void
|
|
|
|
row_purge_remove_sec_if_poss(
|
|
|
|
/*=========================*/
|
|
|
|
purge_node_t* node, /* in: row purge node */
|
|
|
|
dict_index_t* index, /* in: index */
|
|
|
|
dtuple_t* entry) /* in: index entry */
|
|
|
|
{
|
|
|
|
ibool success;
|
|
|
|
ulint n_tries = 0;
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2006-08-29 09:30:31 +00:00
|
|
|
/* fputs("Purge: Removing secondary record\n", stderr); */
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
success = row_purge_remove_sec_if_poss_low(node, index, entry,
|
2006-08-29 09:30:31 +00:00
|
|
|
BTR_MODIFY_LEAF);
|
2005-10-27 07:29:40 +00:00
|
|
|
if (success) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
retry:
|
|
|
|
success = row_purge_remove_sec_if_poss_low(node, index, entry,
|
2006-08-29 09:30:31 +00:00
|
|
|
BTR_MODIFY_TREE);
|
2005-10-27 07:29:40 +00:00
|
|
|
/* 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 (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
n_tries++;
|
|
|
|
|
|
|
|
os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
|
|
|
ut_a(success);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************
|
|
|
|
Purges a delete marking of a record. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
row_purge_del_mark(
|
|
|
|
/*===============*/
|
|
|
|
purge_node_t* node) /* in: row purge node */
|
|
|
|
{
|
|
|
|
mem_heap_t* heap;
|
|
|
|
dtuple_t* entry;
|
|
|
|
dict_index_t* index;
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
ut_ad(node);
|
|
|
|
|
|
|
|
heap = mem_heap_create(1024);
|
|
|
|
|
|
|
|
while (node->index != NULL) {
|
|
|
|
index = node->index;
|
|
|
|
|
|
|
|
/* Build the index entry */
|
2007-12-05 14:10:15 +00:00
|
|
|
entry = row_build_index_entry(node->row, NULL, index, heap);
|
2007-11-27 09:11:45 +00:00
|
|
|
ut_a(entry);
|
2005-10-27 07:29:40 +00:00
|
|
|
row_purge_remove_sec_if_poss(node, index, entry);
|
|
|
|
|
|
|
|
node->index = dict_table_get_next_index(node->index);
|
|
|
|
}
|
|
|
|
|
2006-02-23 19:25:29 +00:00
|
|
|
mem_heap_free(heap);
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
row_purge_remove_clust_if_poss(node);
|
|
|
|
}
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
/***************************************************************
|
|
|
|
Purges an update of an existing record. Also purges an update of a delete
|
|
|
|
marked record if that record contained an externally stored field. */
|
|
|
|
static
|
|
|
|
void
|
|
|
|
row_purge_upd_exist_or_extern(
|
|
|
|
/*==========================*/
|
|
|
|
purge_node_t* node) /* in: row purge node */
|
|
|
|
{
|
|
|
|
mem_heap_t* heap;
|
|
|
|
dtuple_t* entry;
|
|
|
|
dict_index_t* index;
|
|
|
|
ibool is_insert;
|
|
|
|
ulint rseg_id;
|
|
|
|
ulint page_no;
|
|
|
|
ulint offset;
|
|
|
|
ulint i;
|
|
|
|
mtr_t mtr;
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
ut_ad(node);
|
|
|
|
|
|
|
|
if (node->rec_type == TRX_UNDO_UPD_DEL_REC) {
|
|
|
|
|
|
|
|
goto skip_secondaries;
|
|
|
|
}
|
|
|
|
|
|
|
|
heap = mem_heap_create(1024);
|
|
|
|
|
|
|
|
while (node->index != NULL) {
|
|
|
|
index = node->index;
|
|
|
|
|
|
|
|
if (row_upd_changes_ord_field_binary(NULL, node->index,
|
2006-08-29 09:30:31 +00:00
|
|
|
node->update)) {
|
2005-10-27 07:29:40 +00:00
|
|
|
/* Build the older version of the index entry */
|
2007-12-05 14:10:15 +00:00
|
|
|
entry = row_build_index_entry(node->row, NULL,
|
2006-09-26 11:50:54 +00:00
|
|
|
index, heap);
|
2007-11-27 09:11:45 +00:00
|
|
|
ut_a(entry);
|
2005-10-27 07:29:40 +00:00
|
|
|
row_purge_remove_sec_if_poss(node, index, entry);
|
|
|
|
}
|
|
|
|
|
|
|
|
node->index = dict_table_get_next_index(node->index);
|
|
|
|
}
|
|
|
|
|
2006-02-23 19:25:29 +00:00
|
|
|
mem_heap_free(heap);
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
skip_secondaries:
|
|
|
|
/* Free possible externally stored fields */
|
|
|
|
for (i = 0; i < upd_get_n_fields(node->update); i++) {
|
|
|
|
|
branches/zip: Make merge sort handle externally stored columns.
Some things still fail in innodb-index.test, and there seems to be
a race condition (data dictionary lock wait) when running with --valgrind.
dfield_t: Add an "external storage" flag, dfield->ext.
dfield_is_null(), dfield_is_ext(), dfield_set_ext(), dfield_set_null():
New functions.
dfield_copy(), dfield_copy_data(): Add const qualifiers, fix in/out comments.
data_write_sql_null(): Use memset().
big_rec_field_t: Replace byte* data with const void* data.
ut_ulint_sort(): Remove.
upd_field_t: Remove extern_storage.
upd_node_t: Replace ext_vec, n_ext_vec with n_ext.
row_merge_copy_blobs(): New function.
row_ins_index_entry(): Add the parameter "ibool foreign" for suppressing
foreign key checks during fast index creation or when inserting into
secondary indexes.
btr_page_insert_fits(): Add const qualifiers.
btr_cur_add_ext(), upd_ext_vec_contains(): Remove.
dfield_print_also_hex(), dfield_print(): Replace if...else if with switch.
Observe dfield_is_ext().
2007-06-21 09:43:15 +00:00
|
|
|
const upd_field_t* ufield
|
|
|
|
= upd_get_nth_field(node->update, i);
|
2005-10-27 07:29:40 +00:00
|
|
|
|
branches/zip: Make merge sort handle externally stored columns.
Some things still fail in innodb-index.test, and there seems to be
a race condition (data dictionary lock wait) when running with --valgrind.
dfield_t: Add an "external storage" flag, dfield->ext.
dfield_is_null(), dfield_is_ext(), dfield_set_ext(), dfield_set_null():
New functions.
dfield_copy(), dfield_copy_data(): Add const qualifiers, fix in/out comments.
data_write_sql_null(): Use memset().
big_rec_field_t: Replace byte* data with const void* data.
ut_ulint_sort(): Remove.
upd_field_t: Remove extern_storage.
upd_node_t: Replace ext_vec, n_ext_vec with n_ext.
row_merge_copy_blobs(): New function.
row_ins_index_entry(): Add the parameter "ibool foreign" for suppressing
foreign key checks during fast index creation or when inserting into
secondary indexes.
btr_page_insert_fits(): Add const qualifiers.
btr_cur_add_ext(), upd_ext_vec_contains(): Remove.
dfield_print_also_hex(), dfield_print(): Replace if...else if with switch.
Observe dfield_is_ext().
2007-06-21 09:43:15 +00:00
|
|
|
if (dfield_is_ext(&ufield->new_val)) {
|
2006-10-12 11:05:22 +00:00
|
|
|
buf_block_t* block;
|
|
|
|
ulint internal_offset;
|
|
|
|
byte* data_field;
|
2006-02-10 15:06:17 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
/* We use the fact that new_val points to
|
|
|
|
node->undo_rec and get thus the offset of
|
2006-02-10 15:06:17 +00:00
|
|
|
dfield data inside the undo record. Then we
|
2005-10-27 07:29:40 +00:00
|
|
|
can calculate from node->roll_ptr the file
|
|
|
|
address of the new_val data */
|
|
|
|
|
2007-10-23 06:46:12 +00:00
|
|
|
internal_offset
|
|
|
|
= ((const byte*)
|
|
|
|
dfield_get_data(&ufield->new_val))
|
2006-08-29 09:30:31 +00:00
|
|
|
- node->undo_rec;
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
ut_a(internal_offset < UNIV_PAGE_SIZE);
|
|
|
|
|
|
|
|
trx_undo_decode_roll_ptr(node->roll_ptr,
|
2006-08-29 09:30:31 +00:00
|
|
|
&is_insert, &rseg_id,
|
|
|
|
&page_no, &offset);
|
2005-10-27 07:29:40 +00:00
|
|
|
mtr_start(&mtr);
|
|
|
|
|
|
|
|
/* We have to acquire an X-latch to the clustered
|
|
|
|
index tree */
|
|
|
|
|
|
|
|
index = dict_table_get_first_index(node->table);
|
|
|
|
|
2006-09-19 10:14:07 +00:00
|
|
|
mtr_x_lock(dict_index_get_lock(index), &mtr);
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
/* NOTE: we must also acquire an X-latch to the
|
|
|
|
root page of the tree. We will need it when we
|
|
|
|
free pages from the tree. If the tree is of height 1,
|
|
|
|
the tree X-latch does NOT protect the root page,
|
|
|
|
because it is also a leaf page. Since we will have a
|
|
|
|
latch on an undo log page, we would break the
|
|
|
|
latching order if we would only later latch the
|
|
|
|
root page of such a tree! */
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2006-09-19 10:14:07 +00:00
|
|
|
btr_root_get(index, &mtr);
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
/* We assume in purge of externally stored fields
|
|
|
|
that the space id of the undo log record is 0! */
|
|
|
|
|
2007-01-18 09:59:00 +00:00
|
|
|
block = buf_page_get(0, 0, page_no, RW_X_LATCH, &mtr);
|
2005-10-27 07:29:40 +00:00
|
|
|
#ifdef UNIV_SYNC_DEBUG
|
2006-10-12 11:05:22 +00:00
|
|
|
buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE);
|
2005-10-27 07:29:40 +00:00
|
|
|
#endif /* UNIV_SYNC_DEBUG */
|
2006-10-12 11:05:22 +00:00
|
|
|
data_field = buf_block_get_frame(block)
|
|
|
|
+ offset + internal_offset;
|
|
|
|
|
2007-10-23 06:46:12 +00:00
|
|
|
ut_a(dfield_get_len(&ufield->new_val)
|
|
|
|
>= BTR_EXTERN_FIELD_REF_SIZE);
|
2006-09-19 10:14:07 +00:00
|
|
|
btr_free_externally_stored_field(
|
2007-10-23 06:46:12 +00:00
|
|
|
index,
|
|
|
|
data_field + dfield_get_len(&ufield->new_val)
|
2006-09-19 10:14:07 +00:00
|
|
|
- BTR_EXTERN_FIELD_REF_SIZE,
|
branches/zip: In the rollback of incomplete transactions after crash
recovery, tolerate clustered index records whose externally stored
columns have not been written. This should remove the assertion failures
that were reported as Mantis issue#58, issue#62, issue#64.
trx_is_recv(): New function: TRUE if this transaction is rolling back
an incomplete transaction in crash recovery.
enum trx_rbmode: Rollback modes: no rollback, normal rollback, crash recovery.
btr_cur_pessimistic_delete(), btr_free_externally_stored_field(),
btr_rec_free_externally_stored_fields():
Replace the ibool parameter with enum trx_rbmode.
btr_free_externally_stored_field(): If field_ref is zero, return
but assert ut_a(rbmode == RB_RECOVERY). Unless InnoDB has crashed
while inserting a clustered index record, field_ref should not be zero.
btr_rec_free_updated_extern_fields(): Add the parameter enum trx_rbmode.
btr_cur_pessimistic_update(): Pass the rbmode parameter to
btr_rec_free_updated_extern_fields().
row_undo_ins(), row_undo_mod_upd_del_sec(): If row_build_index_entry()
fails, assert trx_is_recv() and skip this secondary index.
row_undo_mod_upd_del_sec(): Empty the heap at the end of each loop
iteration in order to conserve memory and to reduce the number of
low-level memory allocations.
2008-08-06 08:48:34 +00:00
|
|
|
NULL, NULL, NULL, 0, RB_NONE, &mtr);
|
2005-10-27 07:29:40 +00:00
|
|
|
mtr_commit(&mtr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************
|
|
|
|
Parses the row reference and other info in a modify undo log record. */
|
|
|
|
static
|
|
|
|
ibool
|
|
|
|
row_purge_parse_undo_rec(
|
|
|
|
/*=====================*/
|
|
|
|
/* out: TRUE if purge operation required:
|
|
|
|
NOTE that then the CALLER must unfreeze
|
|
|
|
data dictionary! */
|
|
|
|
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;
|
|
|
|
dulint undo_no;
|
|
|
|
dulint table_id;
|
|
|
|
dulint trx_id;
|
|
|
|
dulint roll_ptr;
|
|
|
|
ulint info_bits;
|
|
|
|
ulint type;
|
|
|
|
ulint cmpl_info;
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
ut_ad(node && thr);
|
|
|
|
|
|
|
|
trx = thr_get_trx(thr);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
|
2006-08-29 09:30:31 +00:00
|
|
|
updated_extern, &undo_no, &table_id);
|
2005-10-27 07:29:40 +00:00
|
|
|
node->rec_type = type;
|
|
|
|
|
|
|
|
if (type == TRX_UNDO_UPD_DEL_REC && !(*updated_extern)) {
|
|
|
|
|
|
|
|
return(FALSE);
|
2006-02-23 19:25:29 +00:00
|
|
|
}
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
|
2006-08-29 09:30:31 +00:00
|
|
|
&info_bits);
|
2005-10-27 07:29:40 +00:00
|
|
|
node->table = NULL;
|
|
|
|
|
|
|
|
if (type == TRX_UNDO_UPD_EXIST_REC
|
2006-08-29 09:30:31 +00:00
|
|
|
&& cmpl_info & UPD_NODE_NO_ORD_CHANGE && !(*updated_extern)) {
|
2005-10-27 07:29:40 +00:00
|
|
|
|
2006-02-23 19:25:29 +00:00
|
|
|
/* Purge requires no changes to indexes: we may return */
|
2005-10-27 07:29:40 +00:00
|
|
|
|
2006-02-23 19:25:29 +00:00
|
|
|
return(FALSE);
|
2005-10-27 07:29:40 +00:00
|
|
|
}
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
/* 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));
|
|
|
|
|
2006-05-08 06:18:59 +00:00
|
|
|
node->table = dict_table_get_on_id_low(table_id);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
mutex_exit(&(dict_sys->mutex));
|
|
|
|
|
|
|
|
if (node->table == NULL) {
|
|
|
|
/* The table has been dropped: no need to do purge */
|
2007-08-29 06:36:10 +00:00
|
|
|
err_exit:
|
2005-10-27 07:29:40 +00:00
|
|
|
row_mysql_unfreeze_data_dictionary(trx);
|
|
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node->table->ibd_file_missing) {
|
|
|
|
/* We skip purge of missing .ibd files */
|
|
|
|
|
|
|
|
node->table = NULL;
|
|
|
|
|
2007-08-29 06:36:10 +00:00
|
|
|
goto err_exit;
|
2005-10-27 07:29:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
clust_index = dict_table_get_first_index(node->table);
|
|
|
|
|
|
|
|
if (clust_index == NULL) {
|
|
|
|
/* The table was corrupt in the data dictionary */
|
|
|
|
|
2007-08-29 06:36:10 +00:00
|
|
|
goto err_exit;
|
2005-10-27 07:29:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
|
2006-08-29 09:30:31 +00:00
|
|
|
node->heap);
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
|
2006-08-29 09:30:31 +00:00
|
|
|
roll_ptr, info_bits, trx,
|
|
|
|
node->heap, &(node->update));
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
/* Read to the partial row the fields that occur in indexes */
|
|
|
|
|
2006-09-26 11:50:54 +00:00
|
|
|
if (!(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
|
2008-07-29 17:06:18 +00:00
|
|
|
ptr = trx_undo_rec_get_partial_row(
|
|
|
|
ptr, clust_index, &node->row,
|
|
|
|
type == TRX_UNDO_UPD_DEL_REC,
|
|
|
|
node->heap);
|
2005-10-27 07:29:40 +00:00
|
|
|
}
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
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;
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
ut_ad(node && thr);
|
|
|
|
|
|
|
|
trx = thr_get_trx(thr);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
node->undo_rec = trx_purge_fetch_next_rec(&roll_ptr,
|
2006-08-29 09:30:31 +00:00
|
|
|
&(node->reservation),
|
|
|
|
node->heap);
|
2005-10-27 07:29:40 +00:00
|
|
|
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,
|
2006-08-29 09:30:31 +00:00
|
|
|
thr);
|
2005-10-27 07:29:40 +00:00
|
|
|
/* If purge_needed == TRUE, we must also remember to unfreeze
|
|
|
|
data dictionary! */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (purge_needed) {
|
|
|
|
node->found_clust = FALSE;
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2006-09-19 10:14:07 +00:00
|
|
|
node->index = dict_table_get_next_index(
|
|
|
|
dict_table_get_first_index(node->table));
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
|
|
|
|
row_purge_del_mark(node);
|
|
|
|
|
|
|
|
} else if (updated_extern
|
2006-08-29 09:30:31 +00:00
|
|
|
|| node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
|
2005-10-27 07:29:40 +00:00
|
|
|
|
|
|
|
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);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
thr->run_node = node;
|
|
|
|
|
|
|
|
return(DB_SUCCESS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************
|
|
|
|
Does the purge operation for a single undo log record. This is a high-level
|
|
|
|
function used in an SQL execution graph. */
|
2008-02-06 14:17:36 +00:00
|
|
|
UNIV_INTERN
|
2005-10-27 07:29:40 +00:00
|
|
|
que_thr_t*
|
|
|
|
row_purge_step(
|
|
|
|
/*===========*/
|
|
|
|
/* out: query thread to run next or NULL */
|
|
|
|
que_thr_t* thr) /* in: query thread */
|
|
|
|
{
|
|
|
|
purge_node_t* node;
|
|
|
|
ulint err;
|
|
|
|
|
|
|
|
ut_ad(thr);
|
2006-02-23 19:25:29 +00:00
|
|
|
|
2005-10-27 07:29:40 +00:00
|
|
|
node = thr->run_node;
|
|
|
|
|
|
|
|
ut_ad(que_node_get_type(node) == QUE_NODE_PURGE);
|
|
|
|
|
|
|
|
err = row_purge(node, thr);
|
|
|
|
|
|
|
|
ut_ad(err == DB_SUCCESS);
|
|
|
|
|
|
|
|
return(thr);
|
2006-02-23 19:25:29 +00:00
|
|
|
}
|