row0uins.c 8.29 KB
Newer Older
osku's avatar
osku committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
/******************************************************
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 */
{
42
	btr_cur_t*	btr_cur;
osku's avatar
osku committed
43 44 45 46
	ibool		success;
	ulint		err;
	ulint		n_tries		= 0;
	mtr_t		mtr;
47

osku's avatar
osku committed
48
	mtr_start(&mtr);
49

osku's avatar
osku committed
50
	success = btr_pcur_restore_position(BTR_MODIFY_LEAF, &(node->pcur),
51
					    &mtr);
osku's avatar
osku committed
52 53 54
	ut_a(success);

	if (ut_dulint_cmp(node->table->id, DICT_INDEXES_ID) == 0) {
55
		ut_ad(node->trx->dict_operation_lock_mode == RW_X_LATCH);
osku's avatar
osku committed
56 57 58

		/* Drop the index tree associated with the row in
		SYS_INDEXES table: */
59

osku's avatar
osku committed
60 61 62 63 64 65 66
		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,
67
						    &(node->pcur), &mtr);
osku's avatar
osku committed
68 69
		ut_a(success);
	}
70

osku's avatar
osku committed
71
	btr_cur = btr_pcur_get_btr_cur(&(node->pcur));
72

osku's avatar
osku committed
73 74 75 76 77 78 79 80 81 82 83 84
	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);
85

osku's avatar
osku committed
86
	success = btr_pcur_restore_position(BTR_MODIFY_TREE,
87
					    &(node->pcur), &mtr);
osku's avatar
osku committed
88 89
	ut_a(success);

90 91 92 93
	btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
				   trx_is_recv(node->trx)
				   ? RB_RECOVERY
				   : RB_NORMAL, &mtr);
osku's avatar
osku committed
94 95 96 97 98 99

	/* 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
100
	    && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
osku's avatar
osku committed
101 102 103 104 105 106

		btr_pcur_commit_specify_mtr(&(node->pcur), &mtr);

		n_tries++;

		os_thread_sleep(BTR_CUR_RETRY_SLEEP_TIME);
107

osku's avatar
osku committed
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
		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 */
{
132 133 134 135 136
	btr_pcur_t		pcur;
	btr_cur_t*		btr_cur;
	ulint			err;
	mtr_t			mtr;
	enum row_search_result	search_result;
137

osku's avatar
osku committed
138 139 140 141 142
	log_free_check();
	mtr_start(&mtr);

	btr_cur = btr_pcur_get_btr_cur(&pcur);

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
	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;
osku's avatar
osku committed
161 162 163
	}

	if (mode == BTR_MODIFY_LEAF) {
164 165
		err = btr_cur_optimistic_delete(btr_cur, &mtr)
			? DB_SUCCESS : DB_FAIL;
osku's avatar
osku committed
166 167 168
	} else {
		ut_ad(mode == BTR_MODIFY_TREE);

169 170 171 172 173 174 175 176
		/* 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);
osku's avatar
osku committed
177
	}
178
func_exit:
osku's avatar
osku committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
	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;
198

osku's avatar
osku committed
199 200 201
	/* Try first optimistic descent to the B-tree */

	err = row_undo_ins_remove_sec_low(BTR_MODIFY_LEAF, index, entry);
202

osku's avatar
osku committed
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
	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);
221

osku's avatar
osku committed
222 223 224 225 226 227 228 229 230 231 232 233
		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(
/*========================*/
234
	undo_node_t*	node)	/* in/out: row undo node */
osku's avatar
osku committed
235
{
236
	dict_index_t*	clust_index;
osku's avatar
osku committed
237
	byte*		ptr;
238
	dulint		undo_no;
osku's avatar
osku committed
239
	dulint		table_id;
240 241 242
	ulint		type;
	ulint		dummy;
	ibool		dummy_extern;
osku's avatar
osku committed
243 244

	ut_ad(node);
245

246 247 248 249
	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;
osku's avatar
osku committed
250

251
	node->update = NULL;
252
	node->table = dict_table_get_on_id(table_id, node->trx);
253

254 255 256 257 258 259
	/* 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);
260

261 262 263 264 265 266 267 268 269 270 271 272 273
		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;
		}
274
	}
osku's avatar
osku committed
275
}
276

osku's avatar
osku committed
277 278 279
/***************************************************************
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
280 281 282
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. */
283
UNIV_INTERN
osku's avatar
osku committed
284 285 286 287 288 289 290 291
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);
292

osku's avatar
osku committed
293 294
	row_undo_ins_parse_undo_rec(node);

295
	if (!node->table || !row_undo_search_clust_to_pcur(node)) {
296
		trx_undo_rec_release(node->trx, node->undo_no);
osku's avatar
osku committed
297

298 299
		return(DB_SUCCESS);
	}
osku's avatar
osku committed
300

301
	/* Iterate over all the indexes and undo the insert.*/
osku's avatar
osku committed
302

303 304 305
	/* Skip the clustered index (the first index) */
	node->index = dict_table_get_next_index(
		dict_table_get_first_index(node->table));
osku's avatar
osku committed
306

307 308 309
	while (node->index != NULL) {
		dtuple_t*	entry;
		ulint		err;
osku's avatar
osku committed
310

311 312
		entry = row_build_index_entry(node->row, node->ext,
					      node->index, node->heap);
313 314 315 316 317 318 319 320 321 322 323 324 325
		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);
326

327
			if (err != DB_SUCCESS) {
328

329 330
				return(err);
			}
osku's avatar
osku committed
331
		}
332

333
		node->index = dict_table_get_next_index(node->index);
osku's avatar
osku committed
334 335
	}

336
	return(row_undo_ins_remove_clust_rec(node));
osku's avatar
osku committed
337
}