row0umod.c 24.4 KB
Newer Older
1 2
/*****************************************************************************

3
Copyright (c) 1997, 2012, Oracle and/or its affiliates. All Rights Reserved.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

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
Foundation; version 2 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place, Suite 330, Boston, MA 02111-1307 USA

*****************************************************************************/

19 20
/**************************************************//**
@file row/row0umod.c
osku's avatar
osku committed
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60
Undo modify of a row

Created 2/27/1997 Heikki Tuuri
*******************************************************/

#include "row0umod.h"

#ifdef UNIV_NONINL
#include "row0umod.ic"
#endif

#include "dict0dict.h"
#include "dict0boot.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 "log0log.h"

/* Considerations on undoing a modify operation.
(1) Undoing a delete marking: all index records should be found. Some of
them may have delete mark already FALSE, if the delete mark operation was
stopped underway, or if the undo operation ended prematurely because of a
system crash.
(2) Undoing an update of a delete unmarked record: the newer version of
an updated secondary index entry should be removed if no prior version
of the clustered index record requires its existence. Otherwise, it should
be delete marked.
(3) Undoing an update of a delete marked record. In this kind of update a
delete marked clustered index record was delete unmarked and possibly also
some of its fields were changed. Now, it is possible that the delete marked
version has become obsolete at the time the undo is started. */

Inaam Rana's avatar
Inaam Rana committed
61 62 63 64 65 66 67 68 69 70
/*************************************************************************
IMPORTANT NOTE: Any operation that generates redo MUST check that there
is enough space in the redo log before for that operation. This is
done by calling log_free_check(). The reason for checking the
availability of the redo log space before the start of the operation is
that we MUST not hold any synchonization objects when performing the
check.
If you make a change in this module make sure that no codepath is
introduced where a call to log_free_check() is bypassed. */

71
/***********************************************************//**
osku's avatar
osku committed
72 73
Checks if also the previous version of the clustered index record was
modified or inserted by the same transaction, and its undo number is such
74 75
that it should be undone in the same rollback.
@return	TRUE if also previous modify or insert of this row should be undone */
Inaam Rana's avatar
Inaam Rana committed
76
static
osku's avatar
osku committed
77 78 79
ibool
row_undo_mod_undo_also_prev_vers(
/*=============================*/
80 81
	undo_node_t*	node,	/*!< in: row undo node */
	undo_no_t*	undo_no)/*!< out: the undo number */
osku's avatar
osku committed
82 83 84 85 86
{
	trx_undo_rec_t*	undo_rec;
	trx_t*		trx;

	trx = node->trx;
87

88
	if (node->new_trx_id != trx->id) {
osku's avatar
osku committed
89

90
		*undo_no = 0;
osku's avatar
osku committed
91 92 93 94 95 96 97
		return(FALSE);
	}

	undo_rec = trx_undo_get_undo_rec_low(node->new_roll_ptr, node->heap);

	*undo_no = trx_undo_rec_get_undo_no(undo_rec);

98
	return(trx->roll_limit <= *undo_no);
osku's avatar
osku committed
99
}
100

101
/***********************************************************//**
102 103
Undoes a modify in a clustered index record.
@return	DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
osku's avatar
osku committed
104 105 106 107
static
ulint
row_undo_mod_clust_low(
/*===================*/
108 109 110
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr,	/*!< in: query thread */
	mtr_t*		mtr,	/*!< in: mtr; must be committed before
111
				latching any further pages */
112
	ulint		mode)	/*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
osku's avatar
osku committed
113 114 115 116
{
	btr_pcur_t*	pcur;
	btr_cur_t*	btr_cur;
	ulint		err;
117
#ifdef UNIV_DEBUG
osku's avatar
osku committed
118
	ibool		success;
119
#endif /* UNIV_DEBUG */
osku's avatar
osku committed
120 121 122 123

	pcur = &(node->pcur);
	btr_cur = btr_pcur_get_btr_cur(pcur);

124 125 126 127
#ifdef UNIV_DEBUG
	success =
#endif /* UNIV_DEBUG */
	btr_pcur_restore_position(mode, pcur, mtr);
osku's avatar
osku committed
128 129 130 131 132 133

	ut_ad(success);

	if (mode == BTR_MODIFY_LEAF) {

		err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG
134 135 136 137
						| BTR_NO_UNDO_LOG_FLAG
						| BTR_KEEP_SYS_FLAG,
						btr_cur, node->update,
						node->cmpl_info, thr, mtr);
osku's avatar
osku committed
138
	} else {
139 140 141
		mem_heap_t*	heap		= NULL;
		big_rec_t*	dummy_big_rec;

osku's avatar
osku committed
142 143
		ut_ad(mode == BTR_MODIFY_TREE);

144 145 146 147
		err = btr_cur_pessimistic_update(
			BTR_NO_LOCKING_FLAG
			| BTR_NO_UNDO_LOG_FLAG
			| BTR_KEEP_SYS_FLAG,
148
			btr_cur, &heap, &dummy_big_rec, node->update,
149
			node->cmpl_info, thr, mtr);
150 151 152 153 154

		ut_a(!dummy_big_rec);
		if (UNIV_LIKELY_NULL(heap)) {
			mem_heap_free(heap);
		}
osku's avatar
osku committed
155 156 157 158
	}

	return(err);
}
159

160
/***********************************************************//**
161
Removes a clustered index record after undo if possible.
162 163 164 165
This is attempted when the record was inserted by updating a
delete-marked record and there no longer exist transactions
that would see the delete-marked record.  In other words, we
roll back the insert by purging the record.
166
@return	DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
osku's avatar
osku committed
167 168 169 170
static
ulint
row_undo_mod_remove_clust_low(
/*==========================*/
171
	undo_node_t*	node,	/*!< in: row undo node */
172
	que_thr_t*	thr,	/*!< in: query thread */
173 174
	mtr_t*		mtr,	/*!< in: mtr */
	ulint		mode)	/*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
osku's avatar
osku committed
175 176 177
{
	btr_cur_t*	btr_cur;
	ulint		err;
178

179
	ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
osku's avatar
osku committed
180

181 182
	/* Find out if the record has been purged already
	or if we can remove it. */
osku's avatar
osku committed
183

184 185
	if (!btr_pcur_restore_position(mode, &node->pcur, mtr)
	    || row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
osku's avatar
osku committed
186 187 188 189

		return(DB_SUCCESS);
	}

190
	btr_cur = btr_pcur_get_btr_cur(&node->pcur);
191

osku's avatar
osku committed
192
	if (mode == BTR_MODIFY_LEAF) {
193 194 195
		err = btr_cur_optimistic_delete(btr_cur, mtr)
			? DB_SUCCESS
			: DB_FAIL;
osku's avatar
osku committed
196 197 198
	} else {
		ut_ad(mode == BTR_MODIFY_TREE);

199 200
		/* This operation is analogous to purge, we can free also
		inherited externally stored fields */
osku's avatar
osku committed
201

202 203 204 205
		btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
					   thr_is_recv(thr)
					   ? RB_RECOVERY_PURGE_REC
					   : RB_NONE, mtr);
osku's avatar
osku committed
206 207 208 209 210 211 212 213

		/* The delete operation may fail if we have little
		file space left: TODO: easiest to crash the database
		and restart with more file space */
	}

	return(err);
}
214

215
/***********************************************************//**
osku's avatar
osku committed
216
Undoes a modify in a clustered index record. Sets also the node state for the
217 218
next round of undo.
@return	DB_SUCCESS or error code: we may run out of file space */
osku's avatar
osku committed
219 220 221 222
static
ulint
row_undo_mod_clust(
/*===============*/
223 224
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
225 226 227 228 229 230
{
	btr_pcur_t*	pcur;
	mtr_t		mtr;
	ulint		err;
	ibool		success;
	ibool		more_vers;
231
	undo_no_t	new_undo_no;
232

osku's avatar
osku committed
233 234
	ut_ad(node && thr);

Inaam Rana's avatar
Inaam Rana committed
235 236
	log_free_check();

osku's avatar
osku committed
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
	/* Check if also the previous version of the clustered index record
	should be undone in this same rollback operation */

	more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no);

	pcur = &(node->pcur);

	mtr_start(&mtr);

	/* Try optimistic processing of the record, keeping changes within
	the index page */

	err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);

	if (err != DB_SUCCESS) {
		btr_pcur_commit_specify_mtr(pcur, &mtr);

		/* We may have to modify tree structure: do a pessimistic
		descent down the index tree */

		mtr_start(&mtr);

		err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
	}

	btr_pcur_commit_specify_mtr(pcur, &mtr);

	if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
265

osku's avatar
osku committed
266 267 268
		mtr_start(&mtr);

		err = row_undo_mod_remove_clust_low(node, thr, &mtr,
269
						    BTR_MODIFY_LEAF);
osku's avatar
osku committed
270 271 272 273 274 275 276 277 278
		if (err != DB_SUCCESS) {
			btr_pcur_commit_specify_mtr(pcur, &mtr);

			/* We may have to modify tree structure: do a
			pessimistic descent down the index tree */

			mtr_start(&mtr);

			err = row_undo_mod_remove_clust_low(node, thr, &mtr,
279
							    BTR_MODIFY_TREE);
osku's avatar
osku committed
280 281 282 283 284 285
		}

		btr_pcur_commit_specify_mtr(pcur, &mtr);
	}

	node->state = UNDO_NODE_FETCH_NEXT;
286 287

	trx_undo_rec_release(node->trx, node->undo_no);
osku's avatar
osku committed
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305

	if (more_vers && err == DB_SUCCESS) {

		/* Reserve the undo log record to the prior version after
		committing &mtr: this is necessary to comply with the latching
		order, as &mtr may contain the fsp latch which is lower in
		the latch hierarchy than trx->undo_mutex. */

		success = trx_undo_rec_reserve(node->trx, new_undo_no);

		if (success) {
			node->state = UNDO_NODE_PREV_VERS;
		}
	}

	return(err);
}

306
/***********************************************************//**
307 308
Delete marks or removes a secondary index entry if found.
@return	DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */
osku's avatar
osku committed
309 310 311 312
static
ulint
row_undo_mod_del_mark_or_remove_sec_low(
/*====================================*/
313 314 315 316 317
	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
318
				BTR_MODIFY_TREE */
osku's avatar
osku committed
319
{
320 321 322 323 324 325 326 327
	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;
328

osku's avatar
osku committed
329 330
	log_free_check();
	mtr_start(&mtr);
331

osku's avatar
osku committed
332 333
	btr_cur = btr_pcur_get_btr_cur(&pcur);

334 335 336 337 338
	ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);

	search_result = row_search_index_entry(index, entry, mode,
					       &pcur, &mtr);

339
	switch (UNIV_EXPECT(search_result, ROW_FOUND)) {
340
	case ROW_NOT_FOUND:
341 342 343 344
		/* 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
345 346 347 348 349
		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. */
350

351 352 353 354 355
		err = DB_SUCCESS;
		goto func_exit;
	case ROW_FOUND:
		break;
	case ROW_BUFFERED:
356
	case ROW_NOT_DELETED_REF:
357 358
		/* These are invalid outcomes, because the mode passed
		to row_search_index_entry() did not include any of the
359
		flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
360
		ut_error;
osku's avatar
osku committed
361 362 363 364 365 366 367
	}

	/* 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);
368

osku's avatar
osku committed
369
	success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
370
					    &mtr_vers);
osku's avatar
osku committed
371
	ut_a(success);
372

osku's avatar
osku committed
373
	old_has = row_vers_old_has_index_entry(FALSE,
374 375
					       btr_pcur_get_rec(&(node->pcur)),
					       &mtr_vers, index, entry);
osku's avatar
osku committed
376 377
	if (old_has) {
		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
378
						   btr_cur, TRUE, thr, &mtr);
osku's avatar
osku committed
379 380 381 382
		ut_ad(err == DB_SUCCESS);
	} else {
		/* Remove the index record */

383
		if (mode == BTR_MODIFY_LEAF) {
osku's avatar
osku committed
384 385 386 387 388 389 390 391 392
			success = btr_cur_optimistic_delete(btr_cur, &mtr);
			if (success) {
				err = DB_SUCCESS;
			} else {
				err = DB_FAIL;
			}
		} else {
			ut_ad(mode == BTR_MODIFY_TREE);

393 394 395 396 397
			/* 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
398 399
			columns. */
			ut_ad(!dict_index_is_clust(index));
osku's avatar
osku committed
400
			btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
401
						   RB_NORMAL, &mtr);
osku's avatar
osku committed
402 403 404 405 406 407 408 409

			/* 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);
410 411

func_exit:
osku's avatar
osku committed
412 413 414 415 416 417
	btr_pcur_close(&pcur);
	mtr_commit(&mtr);

	return(err);
}

418
/***********************************************************//**
osku's avatar
osku committed
419 420 421 422 423 424
Delete marks or removes a secondary index entry if found.
NOTE that if we updated the fields of a delete-marked secondary index record
so that alphabetically they stayed the same, e.g., 'abc' -> 'aBc', we cannot
return to the original values because we do not know them. But this should
not cause problems because in row0sel.c, in queries we always retrieve the
clustered index record or an earlier version of it, if the secondary index
425 426
record through which we do the search is delete-marked.
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
osku's avatar
osku committed
427 428 429 430
static
ulint
row_undo_mod_del_mark_or_remove_sec(
/*================================*/
431 432 433 434
	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 */
osku's avatar
osku committed
435 436
{
	ulint	err;
437

osku's avatar
osku committed
438
	err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
439
						      entry, BTR_MODIFY_LEAF);
osku's avatar
osku committed
440 441 442 443 444 445
	if (err == DB_SUCCESS) {

		return(err);
	}

	err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
446
						      entry, BTR_MODIFY_TREE);
447
	return(err);
osku's avatar
osku committed
448 449
}

450
/***********************************************************//**
osku's avatar
osku committed
451 452 453
Delete unmarks a secondary index entry which must be found. It might not be
delete-marked at the moment, but it does not harm to unmark it anyway. We also
need to update the fields of the secondary index record if we updated its
454 455
fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'.
@return	DB_FAIL or DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
osku's avatar
osku committed
456 457 458 459
static
ulint
row_undo_mod_del_unmark_sec_and_undo_update(
/*========================================*/
460
	ulint		mode,	/*!< in: search mode: BTR_MODIFY_LEAF or
osku's avatar
osku committed
461
				BTR_MODIFY_TREE */
462 463
	que_thr_t*	thr,	/*!< in: query thread */
	dict_index_t*	index,	/*!< in: index */
464
	const dtuple_t*	entry)	/*!< in: index entry */
osku's avatar
osku committed
465
{
466 467 468 469 470 471 472 473 474
	mem_heap_t*		heap;
	btr_pcur_t		pcur;
	btr_cur_t*		btr_cur;
	upd_t*			update;
	ulint			err		= DB_SUCCESS;
	big_rec_t*		dummy_big_rec;
	mtr_t			mtr;
	trx_t*			trx		= thr_get_trx(thr);
	enum row_search_result	search_result;
osku's avatar
osku committed
475

476
	/* Ignore indexes that are being created. */
477
	if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {
478

479
		return(DB_SUCCESS);
480
	}
osku's avatar
osku committed
481

482 483 484
	log_free_check();
	mtr_start(&mtr);

485 486 487 488 489 490 491
	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_BUFFERED:
492
	case ROW_NOT_DELETED_REF:
493 494
		/* These are invalid outcomes, because the mode passed
		to row_search_index_entry() did not include any of the
495
		flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
496 497
		ut_error;
	case ROW_NOT_FOUND:
osku's avatar
osku committed
498
		fputs("InnoDB: error in sec index entry del undo in\n"
499
		      "InnoDB: ", stderr);
osku's avatar
osku committed
500 501
		dict_index_name_print(stderr, trx, index);
		fputs("\n"
502
		      "InnoDB: tuple ", stderr);
osku's avatar
osku committed
503 504
		dtuple_print(stderr, entry);
		fputs("\n"
505
		      "InnoDB: record ", stderr);
osku's avatar
osku committed
506 507 508 509
		rec_print(stderr, btr_pcur_get_rec(&pcur), index);
		putc('\n', stderr);
		trx_print(stderr, trx, 0);
		fputs("\n"
510 511
		      "InnoDB: Submit a detailed bug report"
		      " to http://bugs.mysql.com\n", stderr);
512
		ut_ad(0);
513 514 515
		break;
	case ROW_FOUND:
		btr_cur = btr_pcur_get_btr_cur(&pcur);
516
		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
517
						   btr_cur, FALSE, thr, &mtr);
518
		ut_a(err == DB_SUCCESS);
osku's avatar
osku committed
519 520
		heap = mem_heap_create(100);

521 522
		update = row_upd_build_sec_rec_difference_binary(
			index, entry, btr_cur_get_rec(btr_cur), trx, heap);
523
		if (upd_get_n_fields(update) == 0) {
osku's avatar
osku committed
524 525

			/* Do nothing */
526

osku's avatar
osku committed
527
		} else if (mode == BTR_MODIFY_LEAF) {
528
			/* Try an optimistic updating of the record, keeping
osku's avatar
osku committed
529 530
			changes within the page */

531 532 533
			err = btr_cur_optimistic_update(
				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
				btr_cur, update, 0, thr, &mtr);
534 535 536 537 538 539
			switch (err) {
			case DB_OVERFLOW:
			case DB_UNDERFLOW:
			case DB_ZIP_OVERFLOW:
				err = DB_FAIL;
			}
540 541
		} else {
			ut_a(mode == BTR_MODIFY_TREE);
542 543
			err = btr_cur_pessimistic_update(
				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
544
				btr_cur, &heap, &dummy_big_rec,
545
				update, 0, thr, &mtr);
546
			ut_a(!dummy_big_rec);
547
		}
osku's avatar
osku committed
548 549 550 551 552 553 554 555 556 557

		mem_heap_free(heap);
	}

	btr_pcur_close(&pcur);
	mtr_commit(&mtr);

	return(err);
}

558
/***********************************************************//**
559 560
Undoes a modify in secondary indexes when undo record type is UPD_DEL.
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
osku's avatar
osku committed
561 562 563 564
static
ulint
row_undo_mod_upd_del_sec(
/*=====================*/
565 566
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
567 568 569 570
{
	mem_heap_t*	heap;
	dtuple_t*	entry;
	dict_index_t*	index;
571
	ulint		err	= DB_SUCCESS;
572

573
	ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
osku's avatar
osku committed
574 575 576
	heap = mem_heap_create(1024);

	while (node->index != NULL) {
577 578 579 580 581 582 583 584

		/* Skip all corrupted secondary index */
		dict_table_skip_corrupt_index(node->index);

		if (!node->index) {
			break;
		}

osku's avatar
osku committed
585 586
		index = node->index;

587 588
		entry = row_build_index_entry(node->row, node->ext,
					      index, heap);
589 590 591 592 593 594 595 596 597 598
		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. */
599
			ut_a(thr_is_recv(thr));
600 601 602
		} else {
			err = row_undo_mod_del_mark_or_remove_sec(
				node, thr, index, entry);
osku's avatar
osku committed
603

604
			if (err != DB_SUCCESS) {
osku's avatar
osku committed
605

606 607
				break;
			}
osku's avatar
osku committed
608
		}
609

610 611
		mem_heap_empty(heap);

osku's avatar
osku committed
612 613 614 615 616
		node->index = dict_table_get_next_index(node->index);
	}

	mem_heap_free(heap);

617
	return(err);
osku's avatar
osku committed
618 619
}

620
/***********************************************************//**
621 622
Undoes a modify in secondary indexes when undo record type is DEL_MARK.
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
osku's avatar
osku committed
623 624 625 626
static
ulint
row_undo_mod_del_mark_sec(
/*======================*/
627 628
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
629 630 631 632 633 634 635 636 637
{
	mem_heap_t*	heap;
	dtuple_t*	entry;
	dict_index_t*	index;
	ulint		err;

	heap = mem_heap_create(1024);

	while (node->index != NULL) {
638 639 640 641 642 643 644
		/* Skip all corrupted secondary index */
		dict_table_skip_corrupt_index(node->index);

		if (!node->index) {
			break;
		}

osku's avatar
osku committed
645 646
		index = node->index;

647 648
		entry = row_build_index_entry(node->row, node->ext,
					      index, heap);
649
		ut_a(entry);
650 651
		err = row_undo_mod_del_unmark_sec_and_undo_update(
			BTR_MODIFY_LEAF, thr, index, entry);
osku's avatar
osku committed
652
		if (err == DB_FAIL) {
653 654
			err = row_undo_mod_del_unmark_sec_and_undo_update(
				BTR_MODIFY_TREE, thr, index, entry);
osku's avatar
osku committed
655 656 657 658 659 660 661 662 663 664 665 666
		}

		if (err != DB_SUCCESS) {

			mem_heap_free(heap);

			return(err);
		}

		node->index = dict_table_get_next_index(node->index);
	}

667
	mem_heap_free(heap);
osku's avatar
osku committed
668 669 670 671

	return(DB_SUCCESS);
}

672
/***********************************************************//**
673 674
Undoes a modify in secondary indexes when undo record type is UPD_EXIST.
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
osku's avatar
osku committed
675 676 677 678
static
ulint
row_undo_mod_upd_exist_sec(
/*=======================*/
679 680
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
681 682 683 684 685 686 687 688
{
	mem_heap_t*	heap;
	dtuple_t*	entry;
	dict_index_t*	index;
	ulint		err;

	if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
		/* No change in secondary indexes */
689

osku's avatar
osku committed
690 691
		return(DB_SUCCESS);
	}
692

osku's avatar
osku committed
693 694 695
	heap = mem_heap_create(1024);

	while (node->index != NULL) {
696 697 698 699 700 701 702
		/* Skip all corrupted secondary index */
		dict_table_skip_corrupt_index(node->index);

		if (!node->index) {
			break;
		}

osku's avatar
osku committed
703 704
		index = node->index;

705 706 707
		if (row_upd_changes_ord_field_binary(node->index, node->update,
						     thr,
						     node->row, node->ext)) {
osku's avatar
osku committed
708 709

			/* Build the newest version of the index entry */
710 711
			entry = row_build_index_entry(node->row, node->ext,
						      index, heap);
712 713
			if (UNIV_UNLIKELY(!entry)) {
				/* The server must have crashed in
714 715 716 717
				row_upd_clust_rec_by_insert() before
				the updated externally stored columns (BLOBs)
				of the new clustered index entry were
				written. */
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759

				/* The table must be in DYNAMIC or COMPRESSED
				format.  REDUNDANT and COMPACT formats
				store a local 768-byte prefix of each
				externally stored column. */
				ut_a(dict_table_get_format(index->table)
				     >= DICT_TF_FORMAT_ZIP);

				/* This is only legitimate when
				rolling back an incomplete transaction
				after crash recovery. */
				ut_a(thr_get_trx(thr)->is_recovered);

				/* The server must have crashed before
				completing the insert of the new
				clustered index entry and before
				inserting to the secondary indexes.
				Because node->row was not yet written
				to this index, we can ignore it.  But
				we must restore node->undo_row. */
			} else {
				/* NOTE that if we updated the fields of a
				delete-marked secondary index record so that
				alphabetically they stayed the same, e.g.,
				'abc' -> 'aBc', we cannot return to the
				original values because we do not know them.
				But this should not cause problems because
				in row0sel.c, in queries we always retrieve
				the clustered index record or an earlier
				version of it, if the secondary index record
				through which we do the search is
				delete-marked. */

				err = row_undo_mod_del_mark_or_remove_sec(
					node, thr, index, entry);
				if (err != DB_SUCCESS) {
					mem_heap_free(heap);

					return(err);
				}

				mem_heap_empty(heap);
osku's avatar
osku committed
760 761 762 763 764 765 766 767
			}

			/* We may have to update the delete mark in the
			secondary index record of the previous version of
			the row. We also need to update the fields of
			the secondary index record if we updated its fields
			but alphabetically they stayed the same, e.g.,
			'abc' -> 'aBc'. */
768 769 770 771
			entry = row_build_index_entry(node->undo_row,
						      node->undo_ext,
						      index, heap);
			ut_a(entry);
osku's avatar
osku committed
772

773 774
			err = row_undo_mod_del_unmark_sec_and_undo_update(
				BTR_MODIFY_LEAF, thr, index, entry);
osku's avatar
osku committed
775
			if (err == DB_FAIL) {
776 777
				err = row_undo_mod_del_unmark_sec_and_undo_update(
					BTR_MODIFY_TREE, thr, index, entry);
osku's avatar
osku committed
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
			}

			if (err != DB_SUCCESS) {
				mem_heap_free(heap);

				return(err);
			}
		}

		node->index = dict_table_get_next_index(node->index);
	}

	mem_heap_free(heap);

	return(DB_SUCCESS);
}

795
/***********************************************************//**
osku's avatar
osku committed
796 797 798 799 800
Parses the row reference and other info in a modify undo log record. */
static
void
row_undo_mod_parse_undo_rec(
/*========================*/
801 802
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
803 804 805
{
	dict_index_t*	clust_index;
	byte*		ptr;
806
	undo_no_t	undo_no;
807
	table_id_t	table_id;
808 809
	trx_id_t	trx_id;
	roll_ptr_t	roll_ptr;
osku's avatar
osku committed
810 811 812 813 814 815 816 817 818
	ulint		info_bits;
	ulint		type;
	ulint		cmpl_info;
	ibool		dummy_extern;
	trx_t*		trx;

	ut_ad(node && thr);
	trx = thr_get_trx(thr);
	ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
819
				    &dummy_extern, &undo_no, &table_id);
osku's avatar
osku committed
820
	node->rec_type = type;
821

osku's avatar
osku committed
822 823 824 825 826 827
	node->table = dict_table_get_on_id(table_id, trx);

	/* TODO: other fixes associated with DROP TABLE + rollback in the
	same table by another user */

	if (node->table == NULL) {
828 829
		/* Table was dropped */
		return;
osku's avatar
osku committed
830 831 832 833 834 835 836 837 838 839 840 841
	}

	if (node->table->ibd_file_missing) {
		/* We skip undo operations to missing .ibd files */
		node->table = NULL;

		return;
	}

	clust_index = dict_table_get_first_index(node->table);

	ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
842
					       &info_bits);
osku's avatar
osku committed
843 844

	ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
845
				       node->heap);
osku's avatar
osku committed
846 847

	trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
848 849
				       roll_ptr, info_bits, trx,
				       node->heap, &(node->update));
osku's avatar
osku committed
850 851 852 853
	node->new_roll_ptr = roll_ptr;
	node->new_trx_id = trx_id;
	node->cmpl_info = cmpl_info;
}
854

855
/***********************************************************//**
856 857
Undoes a modify operation on a row of a table.
@return	DB_SUCCESS or error code */
858
UNIV_INTERN
osku's avatar
osku committed
859 860 861
ulint
row_undo_mod(
/*=========*/
862 863
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
864 865
{
	ulint	err;
866

osku's avatar
osku committed
867 868 869 870 871
	ut_ad(node && thr);
	ut_ad(node->state == UNDO_NODE_MODIFY);

	row_undo_mod_parse_undo_rec(node, thr);

872
	if (!node->table || !row_undo_search_clust_to_pcur(node)) {
osku's avatar
osku committed
873 874
		/* It is already undone, or will be undone by another query
		thread, or table was dropped */
875 876

		trx_undo_rec_release(node->trx, node->undo_no);
osku's avatar
osku committed
877 878 879 880 881
		node->state = UNDO_NODE_FETCH_NEXT;

		return(DB_SUCCESS);
	}

882 883
	node->index = dict_table_get_next_index(
		dict_table_get_first_index(node->table));
osku's avatar
osku committed
884

885 886 887
	/* Skip all corrupted secondary index */
	dict_table_skip_corrupt_index(node->index);

osku's avatar
osku committed
888
	if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
889

osku's avatar
osku committed
890 891 892 893 894 895 896 897 898 899 900 901 902 903
		err = row_undo_mod_upd_exist_sec(node, thr);

	} else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {

		err = row_undo_mod_del_mark_sec(node, thr);
	} else {
		ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
		err = row_undo_mod_upd_del_sec(node, thr);
	}

	if (err != DB_SUCCESS) {

		return(err);
	}
904

osku's avatar
osku committed
905
	err = row_undo_mod_clust(node, thr);
906

osku's avatar
osku committed
907 908
	return(err);
}