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

3
Copyright (c) 1997, 2010, Innobase Oy. 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 178 179
{
	btr_pcur_t*	pcur;
	btr_cur_t*	btr_cur;
	ulint		err;
	ibool		success;
180

181
	ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
osku's avatar
osku committed
182 183 184 185 186 187 188 189 190 191 192 193 194
	pcur = &(node->pcur);
	btr_cur = btr_pcur_get_btr_cur(pcur);

	success = btr_pcur_restore_position(mode, pcur, mtr);

	if (!success) {

		return(DB_SUCCESS);
	}

	/* Find out if we can remove the whole clustered index record */

	if (node->rec_type == TRX_UNDO_UPD_DEL_REC
195
	    && !row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
osku's avatar
osku committed
196 197 198 199 200

		/* Ok, we can remove */
	} else {
		return(DB_SUCCESS);
	}
201

osku's avatar
osku committed
202 203 204 205 206 207 208 209 210 211 212
	if (mode == BTR_MODIFY_LEAF) {
		success = btr_cur_optimistic_delete(btr_cur, mtr);

		if (success) {
			err = DB_SUCCESS;
		} else {
			err = DB_FAIL;
		}
	} else {
		ut_ad(mode == BTR_MODIFY_TREE);

213 214
		/* This operation is analogous to purge, we can free also
		inherited externally stored fields */
osku's avatar
osku committed
215

216 217 218 219
		btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
					   thr_is_recv(thr)
					   ? RB_RECOVERY_PURGE_REC
					   : RB_NONE, mtr);
osku's avatar
osku committed
220 221 222 223 224 225 226 227

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

229
/***********************************************************//**
osku's avatar
osku committed
230
Undoes a modify in a clustered index record. Sets also the node state for the
231 232
next round of undo.
@return	DB_SUCCESS or error code: we may run out of file space */
osku's avatar
osku committed
233 234 235 236
static
ulint
row_undo_mod_clust(
/*===============*/
237 238
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
239 240 241 242 243 244
{
	btr_pcur_t*	pcur;
	mtr_t		mtr;
	ulint		err;
	ibool		success;
	ibool		more_vers;
245
	undo_no_t	new_undo_no;
246

osku's avatar
osku committed
247 248
	ut_ad(node && thr);

Inaam Rana's avatar
Inaam Rana committed
249 250
	log_free_check();

osku's avatar
osku committed
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
	/* 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) {
279

osku's avatar
osku committed
280 281 282
		mtr_start(&mtr);

		err = row_undo_mod_remove_clust_low(node, thr, &mtr,
283
						    BTR_MODIFY_LEAF);
osku's avatar
osku committed
284 285 286 287 288 289 290 291 292
		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,
293
							    BTR_MODIFY_TREE);
osku's avatar
osku committed
294 295 296 297 298 299
		}

		btr_pcur_commit_specify_mtr(pcur, &mtr);
	}

	node->state = UNDO_NODE_FETCH_NEXT;
300 301

	trx_undo_rec_release(node->trx, node->undo_no);
osku's avatar
osku committed
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319

	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);
}

320
/***********************************************************//**
321 322
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
323 324 325 326
static
ulint
row_undo_mod_del_mark_or_remove_sec_low(
/*====================================*/
327 328 329 330 331
	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
332
				BTR_MODIFY_TREE */
osku's avatar
osku committed
333
{
334 335 336 337 338 339 340 341
	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;
342

osku's avatar
osku committed
343 344
	log_free_check();
	mtr_start(&mtr);
345

osku's avatar
osku committed
346 347
	btr_cur = btr_pcur_get_btr_cur(&pcur);

348 349 350 351 352
	ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);

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

353
	switch (UNIV_EXPECT(search_result, ROW_FOUND)) {
354
	case ROW_NOT_FOUND:
355 356 357 358
		/* 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
359 360 361 362 363
		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. */
364

365 366 367 368 369
		err = DB_SUCCESS;
		goto func_exit;
	case ROW_FOUND:
		break;
	case ROW_BUFFERED:
370
	case ROW_NOT_DELETED_REF:
371 372
		/* These are invalid outcomes, because the mode passed
		to row_search_index_entry() did not include any of the
373
		flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
374
		ut_error;
osku's avatar
osku committed
375 376 377 378 379 380 381
	}

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

osku's avatar
osku committed
383
	success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
384
					    &mtr_vers);
osku's avatar
osku committed
385
	ut_a(success);
386

osku's avatar
osku committed
387
	old_has = row_vers_old_has_index_entry(FALSE,
388 389
					       btr_pcur_get_rec(&(node->pcur)),
					       &mtr_vers, index, entry);
osku's avatar
osku committed
390 391
	if (old_has) {
		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
392
						   btr_cur, TRUE, thr, &mtr);
osku's avatar
osku committed
393 394 395 396
		ut_ad(err == DB_SUCCESS);
	} else {
		/* Remove the index record */

397
		if (mode == BTR_MODIFY_LEAF) {
osku's avatar
osku committed
398 399 400 401 402 403 404 405 406
			success = btr_cur_optimistic_delete(btr_cur, &mtr);
			if (success) {
				err = DB_SUCCESS;
			} else {
				err = DB_FAIL;
			}
		} else {
			ut_ad(mode == BTR_MODIFY_TREE);

407 408 409 410 411
			/* 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
412 413
			columns. */
			ut_ad(!dict_index_is_clust(index));
osku's avatar
osku committed
414
			btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
415
						   RB_NORMAL, &mtr);
osku's avatar
osku committed
416 417 418 419 420 421 422 423

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

func_exit:
osku's avatar
osku committed
426 427 428 429 430 431
	btr_pcur_close(&pcur);
	mtr_commit(&mtr);

	return(err);
}

432
/***********************************************************//**
osku's avatar
osku committed
433 434 435 436 437 438
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
439 440
record through which we do the search is delete-marked.
@return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
osku's avatar
osku committed
441 442 443 444
static
ulint
row_undo_mod_del_mark_or_remove_sec(
/*================================*/
445 446 447 448
	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
449 450
{
	ulint	err;
451

osku's avatar
osku committed
452
	err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
453
						      entry, BTR_MODIFY_LEAF);
osku's avatar
osku committed
454 455 456 457 458 459
	if (err == DB_SUCCESS) {

		return(err);
	}

	err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
460
						      entry, BTR_MODIFY_TREE);
461
	return(err);
osku's avatar
osku committed
462 463
}

464
/***********************************************************//**
osku's avatar
osku committed
465 466 467
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
468 469
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
470 471 472 473
static
ulint
row_undo_mod_del_unmark_sec_and_undo_update(
/*========================================*/
474
	ulint		mode,	/*!< in: search mode: BTR_MODIFY_LEAF or
osku's avatar
osku committed
475
				BTR_MODIFY_TREE */
476 477
	que_thr_t*	thr,	/*!< in: query thread */
	dict_index_t*	index,	/*!< in: index */
478
	const dtuple_t*	entry)	/*!< in: index entry */
osku's avatar
osku committed
479
{
480 481 482 483 484 485 486 487 488
	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
489

490
	/* Ignore indexes that are being created. */
491
	if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {
492

493
		return(DB_SUCCESS);
494
	}
osku's avatar
osku committed
495

496 497 498
	log_free_check();
	mtr_start(&mtr);

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

534 535
		update = row_upd_build_sec_rec_difference_binary(
			index, entry, btr_cur_get_rec(btr_cur), trx, heap);
536
		if (upd_get_n_fields(update) == 0) {
osku's avatar
osku committed
537 538

			/* Do nothing */
539

osku's avatar
osku committed
540
		} else if (mode == BTR_MODIFY_LEAF) {
541
			/* Try an optimistic updating of the record, keeping
osku's avatar
osku committed
542 543
			changes within the page */

544 545 546
			err = btr_cur_optimistic_update(
				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
				btr_cur, update, 0, thr, &mtr);
547 548 549 550 551 552
			switch (err) {
			case DB_OVERFLOW:
			case DB_UNDERFLOW:
			case DB_ZIP_OVERFLOW:
				err = DB_FAIL;
			}
553 554
		} else {
			ut_a(mode == BTR_MODIFY_TREE);
555 556
			err = btr_cur_pessimistic_update(
				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
557
				btr_cur, &heap, &dummy_big_rec,
558
				update, 0, thr, &mtr);
559
			ut_a(!dummy_big_rec);
560
		}
osku's avatar
osku committed
561 562 563 564 565 566 567 568 569 570

		mem_heap_free(heap);
	}

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

	return(err);
}

571
/***********************************************************//**
572 573
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
574 575 576 577
static
ulint
row_undo_mod_upd_del_sec(
/*=====================*/
578 579
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
580 581 582 583
{
	mem_heap_t*	heap;
	dtuple_t*	entry;
	dict_index_t*	index;
584
	ulint		err	= DB_SUCCESS;
585

586
	ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
osku's avatar
osku committed
587 588 589 590 591
	heap = mem_heap_create(1024);

	while (node->index != NULL) {
		index = node->index;

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

609
			if (err != DB_SUCCESS) {
osku's avatar
osku committed
610

611 612
				break;
			}
osku's avatar
osku committed
613
		}
614

615 616
		mem_heap_empty(heap);

osku's avatar
osku committed
617 618 619 620 621
		node->index = dict_table_get_next_index(node->index);
	}

	mem_heap_free(heap);

622
	return(err);
osku's avatar
osku committed
623 624
}

625
/***********************************************************//**
626 627
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
628 629 630 631
static
ulint
row_undo_mod_del_mark_sec(
/*======================*/
632 633
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
634 635 636 637 638 639 640 641 642 643 644
{
	mem_heap_t*	heap;
	dtuple_t*	entry;
	dict_index_t*	index;
	ulint		err;

	heap = mem_heap_create(1024);

	while (node->index != NULL) {
		index = node->index;

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

		if (err != DB_SUCCESS) {

			mem_heap_free(heap);

			return(err);
		}

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

665
	mem_heap_free(heap);
osku's avatar
osku committed
666 667 668 669

	return(DB_SUCCESS);
}

670
/***********************************************************//**
671 672
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
673 674 675 676
static
ulint
row_undo_mod_upd_exist_sec(
/*=======================*/
677 678
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
679 680 681 682 683 684 685 686
{
	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 */
687

osku's avatar
osku committed
688 689
		return(DB_SUCCESS);
	}
690

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

	while (node->index != NULL) {
		index = node->index;

696 697 698
		if (row_upd_changes_ord_field_binary(node->index, node->update,
						     thr,
						     node->row, node->ext)) {
osku's avatar
osku committed
699 700

			/* Build the newest version of the index entry */
701 702
			entry = row_build_index_entry(node->row, node->ext,
						      index, heap);
703 704
			if (UNIV_UNLIKELY(!entry)) {
				/* The server must have crashed in
705 706 707 708
				row_upd_clust_rec_by_insert() before
				the updated externally stored columns (BLOBs)
				of the new clustered index entry were
				written. */
709 710 711 712 713 714 715 716 717 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

				/* 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
751 752 753 754 755 756 757 758
			}

			/* 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'. */
759 760 761 762
			entry = row_build_index_entry(node->undo_row,
						      node->undo_ext,
						      index, heap);
			ut_a(entry);
osku's avatar
osku committed
763

764 765
			err = row_undo_mod_del_unmark_sec_and_undo_update(
				BTR_MODIFY_LEAF, thr, index, entry);
osku's avatar
osku committed
766
			if (err == DB_FAIL) {
767 768
				err = row_undo_mod_del_unmark_sec_and_undo_update(
					BTR_MODIFY_TREE, thr, index, entry);
osku's avatar
osku committed
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785
			}

			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);
}

786
/***********************************************************//**
osku's avatar
osku committed
787 788 789 790 791
Parses the row reference and other info in a modify undo log record. */
static
void
row_undo_mod_parse_undo_rec(
/*========================*/
792 793
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
794 795 796
{
	dict_index_t*	clust_index;
	byte*		ptr;
797
	undo_no_t	undo_no;
798
	table_id_t	table_id;
799 800
	trx_id_t	trx_id;
	roll_ptr_t	roll_ptr;
osku's avatar
osku committed
801 802 803 804 805 806 807 808 809
	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,
810
				    &dummy_extern, &undo_no, &table_id);
osku's avatar
osku committed
811
	node->rec_type = type;
812

osku's avatar
osku committed
813 814 815 816 817 818
	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) {
819 820
		/* Table was dropped */
		return;
osku's avatar
osku committed
821 822 823 824 825 826 827 828 829 830 831 832
	}

	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,
833
					       &info_bits);
osku's avatar
osku committed
834 835

	ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
836
				       node->heap);
osku's avatar
osku committed
837 838

	trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
839 840
				       roll_ptr, info_bits, trx,
				       node->heap, &(node->update));
osku's avatar
osku committed
841 842 843 844
	node->new_roll_ptr = roll_ptr;
	node->new_trx_id = trx_id;
	node->cmpl_info = cmpl_info;
}
845

846
/***********************************************************//**
847 848
Undoes a modify operation on a row of a table.
@return	DB_SUCCESS or error code */
849
UNIV_INTERN
osku's avatar
osku committed
850 851 852
ulint
row_undo_mod(
/*=========*/
853 854
	undo_node_t*	node,	/*!< in: row undo node */
	que_thr_t*	thr)	/*!< in: query thread */
osku's avatar
osku committed
855 856
{
	ulint	err;
857

osku's avatar
osku committed
858 859 860 861 862
	ut_ad(node && thr);
	ut_ad(node->state == UNDO_NODE_MODIFY);

	row_undo_mod_parse_undo_rec(node, thr);

863
	if (!node->table || !row_undo_search_clust_to_pcur(node)) {
osku's avatar
osku committed
864 865
		/* It is already undone, or will be undone by another query
		thread, or table was dropped */
866 867

		trx_undo_rec_release(node->trx, node->undo_no);
osku's avatar
osku committed
868 869 870 871 872
		node->state = UNDO_NODE_FETCH_NEXT;

		return(DB_SUCCESS);
	}

873 874
	node->index = dict_table_get_next_index(
		dict_table_get_first_index(node->table));
osku's avatar
osku committed
875 876

	if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
877

osku's avatar
osku committed
878 879 880 881 882 883 884 885 886 887 888 889 890 891
		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);
	}
892

osku's avatar
osku committed
893
	err = row_undo_mod_clust(node, thr);
894

osku's avatar
osku committed
895 896
	return(err);
}