mariadb/row/row0uins.c
marko b6679b1d14 branches/innodb+: Merge revisions 3180:3312 from branches/zip:
------------------------------------------------------------------------
  r3254 | marko | 2008-11-24 18:01:42 +0200 (Mon, 24 Nov 2008) | 4 lines

  branches/zip: Note that it is legitimate for a secondary index record not
  to be found during purge.  This tries to address Issue #129.  The comments
  were supplied by Heikki.
  ------------------------------------------------------------------------
  r3286 | marko | 2008-11-26 10:00:28 +0200 (Wed, 26 Nov 2008) | 18 lines

  branches/zip: row_merge_drop_temp_indexes(): Replace the WHILE 1 with
  WHILE 1=1 in the SQL procedure, so that the loop will actually be
  entered and temporary indexes be dropped during crash recovery.
  Thanks to Sunny Bains for pointing this out.

  Tested as follows:

  Set a breakpoint in row_merge_rename_indexes.

  CREATE TABLE t(a INT)ENGINE=InnoDB;
  CREATE INDEX a ON t(a);

  -- The breakpoint will be reached.  Kill and restart mysqld.
  SHOW CREATE TABLE t;
  -- This shows the MySQL .frm file, without and index.
  CREATE TABLE innodb_table_monitor(a INT)ENGINE=InnoDB;
  -- This will dump the InnoDB dictionary to the error log, without the index.
  ------------------------------------------------------------------------
  r3302 | vasil | 2008-11-27 23:26:39 +0200 (Thu, 27 Nov 2008) | 12 lines

  branches/zip:

  Fix Mantis issue#130 wdl: does not handle 64-bit address

  - Change the call from strtoul() to strtoull()
  - Change "%16X" to "%16llx" when scanning preferred load address

  rb://58

  Submitted by:   Calvin
  Approved by:    Marko
  ------------------------------------------------------------------------
  r3303 | vasil | 2008-11-27 23:31:18 +0200 (Thu, 27 Nov 2008) | 10 lines

  branches/zip:

  * Remove a change from win-plugin/win-plugin.diff about time_t because
    MySQL has used VS2005 for building 5.1.30.

  * Adjust the line numbers so the patch applies cleanly without fuzz and
    offset messages.

  Submitted by:   Calvin
  ------------------------------------------------------------------------
  r3304 | vasil | 2008-11-27 23:33:48 +0200 (Thu, 27 Nov 2008) | 6 lines

  branches/zip:

  Non-functional change in win-plugin/win-plugin.diff: fix the file name
  before the diff, this is irrelevant but it is nice to be the same as
  the file name on the following line.
  ------------------------------------------------------------------------
  r3312 | marko | 2008-11-28 16:18:43 +0200 (Fri, 28 Nov 2008) | 5 lines

  branches/zip: row_undo_mod_del_mark_or_remove_sec_low(): Complain if
  the secondary index entry cannot be found, and this is not an incomplete
  transaction that is being rolled back in crash recovery.  The source code
  comments were suggested by Heikki.
  ------------------------------------------------------------------------
2008-11-28 14:58:51 +00:00

337 lines
8.3 KiB
C

/******************************************************
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) {
ut_ad(node->trx->dict_operation_lock_mode == RW_X_LATCH);
/* 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,
trx_is_recv(node->trx)
? RB_RECOVERY
: 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 */
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;
ulint err;
mtr_t mtr;
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 (search_result) {
case ROW_NOT_FOUND:
err = DB_SUCCESS;
goto func_exit;
case ROW_FOUND:
break;
case ROW_BUFFERED:
case ROW_NOT_IN_POOL:
/* These are invalid outcomes, because the mode passed
to row_search_index_entry() did not include any of the
flags BTR_INSERT, BTR_DELETE, BTR_DELETE_MARK, or
BTR_WATCH_LEAF. */
ut_error;
}
if (mode == BTR_MODIFY_LEAF) {
err = btr_cur_optimistic_delete(btr_cur, &mtr)
? DB_SUCCESS : DB_FAIL;
} else {
ut_ad(mode == BTR_MODIFY_TREE);
/* No need to distinguish RB_RECOVERY here, because we
are deleting a secondary index record: the distinction
between RB_NORMAL and RB_RECOVERY 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);
}
func_exit:
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/out: 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->update = NULL;
node->table = dict_table_get_on_id(table_id, node->trx);
/* Skip the UNDO if we can't find the table or the .ibd file. */
if (UNIV_UNLIKELY(node->table == NULL)) {
} else if (UNIV_UNLIKELY(node->table->ibd_file_missing)) {
node->table = NULL;
} else {
clust_index = dict_table_get_first_index(node->table);
if (clust_index != NULL) {
ptr = trx_undo_rec_get_row_ref(
ptr, clust_index, &node->ref, node->heap);
} else {
ut_print_timestamp(stderr);
fprintf(stderr, " InnoDB: table ");
ut_print_name(stderr, node->trx, TRUE,
node->table->name);
fprintf(stderr, " has no indexes, "
"ignoring the table\n");
node->table = NULL;
}
}
}
/***************************************************************
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. InnoDB is eager in a rollback:
if it figures out that an index record will be removed in the purge
anyway, it will remove it in the rollback. */
UNIV_INTERN
ulint
row_undo_ins(
/*=========*/
/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
undo_node_t* node) /* in: row undo node */
{
ut_ad(node);
ut_ad(node->state == UNDO_NODE_INSERT);
row_undo_ins_parse_undo_rec(node);
if (!node->table || !row_undo_search_clust_to_pcur(node)) {
trx_undo_rec_release(node->trx, node->undo_no);
return(DB_SUCCESS);
}
/* Iterate over all the indexes and undo the insert.*/
/* Skip the clustered index (the first index) */
node->index = dict_table_get_next_index(
dict_table_get_first_index(node->table));
while (node->index != NULL) {
dtuple_t* entry;
ulint err;
entry = row_build_index_entry(node->row, node->ext,
node->index, node->heap);
if (UNIV_UNLIKELY(!entry)) {
/* The database must have crashed after
inserting a clustered index record but before
writing all the externally stored columns of
that record. Because secondary index entries
are inserted after the clustered index record,
we may assume that the secondary index record
does not exist. However, this situation may
only occur during the rollback of incomplete
transactions. */
ut_a(trx_is_recv(node->trx));
} else {
err = row_undo_ins_remove_sec(node->index, entry);
if (err != DB_SUCCESS) {
return(err);
}
}
node->index = dict_table_get_next_index(node->index);
}
return(row_undo_ins_remove_clust_rec(node));
}