~drizzle-trunk/drizzle/development

1 by brian
clean slate
1
/******************************************************
2
Undo modify of a row
3
4
(c) 1997 Innobase Oy
5
6
Created 2/27/1997 Heikki Tuuri
7
*******************************************************/
8
9
#include "row0umod.h"
10
11
#ifdef UNIV_NONINL
12
#include "row0umod.ic"
13
#endif
14
15
#include "dict0dict.h"
16
#include "dict0boot.h"
17
#include "trx0undo.h"
18
#include "trx0roll.h"
19
#include "btr0btr.h"
20
#include "mach0data.h"
21
#include "row0undo.h"
22
#include "row0vers.h"
23
#include "trx0trx.h"
24
#include "trx0rec.h"
25
#include "row0row.h"
26
#include "row0upd.h"
27
#include "que0que.h"
28
#include "log0log.h"
29
30
/* Considerations on undoing a modify operation.
31
(1) Undoing a delete marking: all index records should be found. Some of
32
them may have delete mark already FALSE, if the delete mark operation was
33
stopped underway, or if the undo operation ended prematurely because of a
34
system crash.
35
(2) Undoing an update of a delete unmarked record: the newer version of
36
an updated secondary index entry should be removed if no prior version
37
of the clustered index record requires its existence. Otherwise, it should
38
be delete marked.
39
(3) Undoing an update of a delete marked record. In this kind of update a
40
delete marked clustered index record was delete unmarked and possibly also
41
some of its fields were changed. Now, it is possible that the delete marked
42
version has become obsolete at the time the undo is started. */
43
44
/***************************************************************
45
Checks if also the previous version of the clustered index record was
46
modified or inserted by the same transaction, and its undo number is such
47
that it should be undone in the same rollback. */
48
UNIV_INLINE
49
ibool
50
row_undo_mod_undo_also_prev_vers(
51
/*=============================*/
52
				/* out: TRUE if also previous modify or
53
				insert of this row should be undone */
54
	undo_node_t*	node,	/* in: row undo node */
55
	dulint*		undo_no)/* out: the undo number */
56
{
57
	trx_undo_rec_t*	undo_rec;
58
	trx_t*		trx;
59
60
	trx = node->trx;
61
62
	if (0 != ut_dulint_cmp(node->new_trx_id, trx->id)) {
63
64
		*undo_no = ut_dulint_zero;
65
		return(FALSE);
66
	}
67
68
	undo_rec = trx_undo_get_undo_rec_low(node->new_roll_ptr, node->heap);
69
70
	*undo_no = trx_undo_rec_get_undo_no(undo_rec);
71
72
	return(ut_dulint_cmp(trx->roll_limit, *undo_no) <= 0);
73
}
74
75
/***************************************************************
76
Undoes a modify in a clustered index record. */
77
static
78
ulint
79
row_undo_mod_clust_low(
80
/*===================*/
81
				/* out: DB_SUCCESS, DB_FAIL, or error code:
82
				we may run out of file space */
83
	undo_node_t*	node,	/* in: row undo node */
84
	que_thr_t*	thr,	/* in: query thread */
85
	mtr_t*		mtr,	/* in: mtr */
86
	ulint		mode)	/* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
87
{
88
	big_rec_t*	dummy_big_rec;
89
	btr_pcur_t*	pcur;
90
	btr_cur_t*	btr_cur;
91
	ulint		err;
92
	ibool		success;
93
94
	pcur = &(node->pcur);
95
	btr_cur = btr_pcur_get_btr_cur(pcur);
96
97
	success = btr_pcur_restore_position(mode, pcur, mtr);
98
99
	ut_ad(success);
100
101
	if (mode == BTR_MODIFY_LEAF) {
102
103
		err = btr_cur_optimistic_update(BTR_NO_LOCKING_FLAG
104
						| BTR_NO_UNDO_LOG_FLAG
105
						| BTR_KEEP_SYS_FLAG,
106
						btr_cur, node->update,
107
						node->cmpl_info, thr, mtr);
108
	} else {
109
		ut_ad(mode == BTR_MODIFY_TREE);
110
111
		err = btr_cur_pessimistic_update(
112
			BTR_NO_LOCKING_FLAG
113
			| BTR_NO_UNDO_LOG_FLAG
114
			| BTR_KEEP_SYS_FLAG,
115
			btr_cur, &dummy_big_rec, node->update,
116
			node->cmpl_info, thr, mtr);
117
	}
118
119
	return(err);
120
}
121
122
/***************************************************************
123
Removes a clustered index record after undo if possible. */
124
static
125
ulint
126
row_undo_mod_remove_clust_low(
127
/*==========================*/
128
				/* out: DB_SUCCESS, DB_FAIL, or error code:
129
				we may run out of file space */
130
	undo_node_t*	node,	/* in: row undo node */
131
	que_thr_t*	thr __attribute__((unused)), /* in: query thread */
132
	mtr_t*		mtr,	/* in: mtr */
133
	ulint		mode)	/* in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
134
{
135
	btr_pcur_t*	pcur;
136
	btr_cur_t*	btr_cur;
137
	ulint		err;
138
	ibool		success;
139
140
	pcur = &(node->pcur);
141
	btr_cur = btr_pcur_get_btr_cur(pcur);
142
143
	success = btr_pcur_restore_position(mode, pcur, mtr);
144
145
	if (!success) {
146
147
		return(DB_SUCCESS);
148
	}
149
150
	/* Find out if we can remove the whole clustered index record */
151
152
	if (node->rec_type == TRX_UNDO_UPD_DEL_REC
153
	    && !row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
154
155
		/* Ok, we can remove */
156
	} else {
157
		return(DB_SUCCESS);
158
	}
159
160
	if (mode == BTR_MODIFY_LEAF) {
161
		success = btr_cur_optimistic_delete(btr_cur, mtr);
162
163
		if (success) {
164
			err = DB_SUCCESS;
165
		} else {
166
			err = DB_FAIL;
167
		}
168
	} else {
169
		ut_ad(mode == BTR_MODIFY_TREE);
170
171
		/* Note that since this operation is analogous to purge,
172
		we can free also inherited externally stored fields:
173
		hence the last FALSE in the call below */
174
175
		btr_cur_pessimistic_delete(&err, FALSE, btr_cur, FALSE, mtr);
176
177
		/* The delete operation may fail if we have little
178
		file space left: TODO: easiest to crash the database
179
		and restart with more file space */
180
	}
181
182
	return(err);
183
}
184
185
/***************************************************************
186
Undoes a modify in a clustered index record. Sets also the node state for the
187
next round of undo. */
188
static
189
ulint
190
row_undo_mod_clust(
191
/*===============*/
192
				/* out: DB_SUCCESS or error code: we may run
193
				out of file space */
194
	undo_node_t*	node,	/* in: row undo node */
195
	que_thr_t*	thr)	/* in: query thread */
196
{
197
	btr_pcur_t*	pcur;
198
	mtr_t		mtr;
199
	ulint		err;
200
	ibool		success;
201
	ibool		more_vers;
202
	dulint		new_undo_no;
203
204
	ut_ad(node && thr);
205
206
	/* Check if also the previous version of the clustered index record
207
	should be undone in this same rollback operation */
208
209
	more_vers = row_undo_mod_undo_also_prev_vers(node, &new_undo_no);
210
211
	pcur = &(node->pcur);
212
213
	mtr_start(&mtr);
214
215
	/* Try optimistic processing of the record, keeping changes within
216
	the index page */
217
218
	err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);
219
220
	if (err != DB_SUCCESS) {
221
		btr_pcur_commit_specify_mtr(pcur, &mtr);
222
223
		/* We may have to modify tree structure: do a pessimistic
224
		descent down the index tree */
225
226
		mtr_start(&mtr);
227
228
		err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
229
	}
230
231
	btr_pcur_commit_specify_mtr(pcur, &mtr);
232
233
	if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
234
235
		mtr_start(&mtr);
236
237
		err = row_undo_mod_remove_clust_low(node, thr, &mtr,
238
						    BTR_MODIFY_LEAF);
239
		if (err != DB_SUCCESS) {
240
			btr_pcur_commit_specify_mtr(pcur, &mtr);
241
242
			/* We may have to modify tree structure: do a
243
			pessimistic descent down the index tree */
244
245
			mtr_start(&mtr);
246
247
			err = row_undo_mod_remove_clust_low(node, thr, &mtr,
248
							    BTR_MODIFY_TREE);
249
		}
250
251
		btr_pcur_commit_specify_mtr(pcur, &mtr);
252
	}
253
254
	node->state = UNDO_NODE_FETCH_NEXT;
255
256
	trx_undo_rec_release(node->trx, node->undo_no);
257
258
	if (more_vers && err == DB_SUCCESS) {
259
260
		/* Reserve the undo log record to the prior version after
261
		committing &mtr: this is necessary to comply with the latching
262
		order, as &mtr may contain the fsp latch which is lower in
263
		the latch hierarchy than trx->undo_mutex. */
264
265
		success = trx_undo_rec_reserve(node->trx, new_undo_no);
266
267
		if (success) {
268
			node->state = UNDO_NODE_PREV_VERS;
269
		}
270
	}
271
272
	return(err);
273
}
274
275
/***************************************************************
276
Delete marks or removes a secondary index entry if found. */
277
static
278
ulint
279
row_undo_mod_del_mark_or_remove_sec_low(
280
/*====================================*/
281
				/* out: DB_SUCCESS, DB_FAIL, or
282
				DB_OUT_OF_FILE_SPACE */
283
	undo_node_t*	node,	/* in: row undo node */
284
	que_thr_t*	thr,	/* in: query thread */
285
	dict_index_t*	index,	/* in: index */
286
	dtuple_t*	entry,	/* in: index entry */
287
	ulint		mode)	/* in: latch mode BTR_MODIFY_LEAF or
288
				BTR_MODIFY_TREE */
289
{
290
	ibool		found;
291
	btr_pcur_t	pcur;
292
	btr_cur_t*	btr_cur;
293
	ibool		success;
294
	ibool		old_has;
295
	ulint		err;
296
	mtr_t		mtr;
297
	mtr_t		mtr_vers;
298
299
	log_free_check();
300
	mtr_start(&mtr);
301
302
	found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
303
304
	btr_cur = btr_pcur_get_btr_cur(&pcur);
305
306
	if (!found) {
307
		/* Not found */
308
309
		btr_pcur_close(&pcur);
310
		mtr_commit(&mtr);
311
312
		return(DB_SUCCESS);
313
	}
314
315
	/* We should remove the index record if no prior version of the row,
316
	which cannot be purged yet, requires its existence. If some requires,
317
	we should delete mark the record. */
318
319
	mtr_start(&mtr_vers);
320
321
	success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
322
					    &mtr_vers);
323
	ut_a(success);
324
325
	old_has = row_vers_old_has_index_entry(FALSE,
326
					       btr_pcur_get_rec(&(node->pcur)),
327
					       &mtr_vers, index, entry);
328
	if (old_has) {
329
		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
330
						   btr_cur, TRUE, thr, &mtr);
331
		ut_ad(err == DB_SUCCESS);
332
	} else {
333
		/* Remove the index record */
334
335
		if (mode == BTR_MODIFY_LEAF) {
336
			success = btr_cur_optimistic_delete(btr_cur, &mtr);
337
			if (success) {
338
				err = DB_SUCCESS;
339
			} else {
340
				err = DB_FAIL;
341
			}
342
		} else {
343
			ut_ad(mode == BTR_MODIFY_TREE);
344
345
			btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
346
						   TRUE, &mtr);
347
348
			/* The delete operation may fail if we have little
349
			file space left: TODO: easiest to crash the database
350
			and restart with more file space */
351
		}
352
	}
353
354
	btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
355
	btr_pcur_close(&pcur);
356
	mtr_commit(&mtr);
357
358
	return(err);
359
}
360
361
/***************************************************************
362
Delete marks or removes a secondary index entry if found.
363
NOTE that if we updated the fields of a delete-marked secondary index record
364
so that alphabetically they stayed the same, e.g., 'abc' -> 'aBc', we cannot
365
return to the original values because we do not know them. But this should
366
not cause problems because in row0sel.c, in queries we always retrieve the
367
clustered index record or an earlier version of it, if the secondary index
368
record through which we do the search is delete-marked. */
369
static
370
ulint
371
row_undo_mod_del_mark_or_remove_sec(
372
/*================================*/
373
				/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
374
	undo_node_t*	node,	/* in: row undo node */
375
	que_thr_t*	thr,	/* in: query thread */
376
	dict_index_t*	index,	/* in: index */
377
	dtuple_t*	entry)	/* in: index entry */
378
{
379
	ulint	err;
380
381
	err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
382
						      entry, BTR_MODIFY_LEAF);
383
	if (err == DB_SUCCESS) {
384
385
		return(err);
386
	}
387
388
	err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
389
						      entry, BTR_MODIFY_TREE);
390
	return(err);
391
}
392
393
/***************************************************************
394
Delete unmarks a secondary index entry which must be found. It might not be
395
delete-marked at the moment, but it does not harm to unmark it anyway. We also
396
need to update the fields of the secondary index record if we updated its
397
fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'. */
398
static
399
ulint
400
row_undo_mod_del_unmark_sec_and_undo_update(
401
/*========================================*/
402
				/* out: DB_FAIL or DB_SUCCESS or
403
				DB_OUT_OF_FILE_SPACE */
404
	ulint		mode,	/* in: search mode: BTR_MODIFY_LEAF or
405
				BTR_MODIFY_TREE */
406
	que_thr_t*	thr,	/* in: query thread */
407
	dict_index_t*	index,	/* in: index */
408
	dtuple_t*	entry)	/* in: index entry */
409
{
410
	mem_heap_t*	heap;
411
	btr_pcur_t	pcur;
412
	upd_t*		update;
413
	ulint		err		= DB_SUCCESS;
414
	ibool		found;
415
	big_rec_t*	dummy_big_rec;
416
	mtr_t		mtr;
417
	trx_t*		trx		= thr_get_trx(thr);
418
419
	log_free_check();
420
	mtr_start(&mtr);
421
422
	found = row_search_index_entry(index, entry, mode, &pcur, &mtr);
423
424
	if (!found) {
425
		fputs("InnoDB: error in sec index entry del undo in\n"
426
		      "InnoDB: ", stderr);
427
		dict_index_name_print(stderr, trx, index);
428
		fputs("\n"
429
		      "InnoDB: tuple ", stderr);
430
		dtuple_print(stderr, entry);
431
		fputs("\n"
432
		      "InnoDB: record ", stderr);
433
		rec_print(stderr, btr_pcur_get_rec(&pcur), index);
434
		putc('\n', stderr);
435
		trx_print(stderr, trx, 0);
436
		fputs("\n"
437
		      "InnoDB: Submit a detailed bug report"
438
		      " to http://bugs.mysql.com\n", stderr);
439
	} else {
440
		btr_cur_t*	btr_cur = btr_pcur_get_btr_cur(&pcur);
441
442
		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
443
						   btr_cur, FALSE, thr, &mtr);
444
		ut_a(err == DB_SUCCESS);
445
		heap = mem_heap_create(100);
446
447
		update = row_upd_build_sec_rec_difference_binary(
448
			index, entry, btr_cur_get_rec(btr_cur), trx, heap);
449
		if (upd_get_n_fields(update) == 0) {
450
451
			/* Do nothing */
452
453
		} else if (mode == BTR_MODIFY_LEAF) {
454
			/* Try an optimistic updating of the record, keeping
455
			changes within the page */
456
457
			err = btr_cur_optimistic_update(
458
				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
459
				btr_cur, update, 0, thr, &mtr);
460
			if (err == DB_OVERFLOW || err == DB_UNDERFLOW) {
461
				err = DB_FAIL;
462
			}
463
		} else {
464
			ut_a(mode == BTR_MODIFY_TREE);
465
			err = btr_cur_pessimistic_update(
466
				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
467
				btr_cur, &dummy_big_rec,
468
				update, 0, thr, &mtr);
469
		}
470
471
		mem_heap_free(heap);
472
	}
473
474
	btr_pcur_close(&pcur);
475
	mtr_commit(&mtr);
476
477
	return(err);
478
}
479
480
/***************************************************************
481
Undoes a modify in secondary indexes when undo record type is UPD_DEL. */
482
static
483
ulint
484
row_undo_mod_upd_del_sec(
485
/*=====================*/
486
				/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
487
	undo_node_t*	node,	/* in: row undo node */
488
	que_thr_t*	thr)	/* in: query thread */
489
{
490
	mem_heap_t*	heap;
491
	dtuple_t*	entry;
492
	dict_index_t*	index;
493
	ulint		err;
494
495
	heap = mem_heap_create(1024);
496
497
	while (node->index != NULL) {
498
		index = node->index;
499
500
		entry = row_build_index_entry(node->row, index, heap);
501
502
		err = row_undo_mod_del_mark_or_remove_sec(node, thr, index,
503
							  entry);
504
		if (err != DB_SUCCESS) {
505
506
			mem_heap_free(heap);
507
508
			return(err);
509
		}
510
511
		node->index = dict_table_get_next_index(node->index);
512
	}
513
514
	mem_heap_free(heap);
515
516
	return(DB_SUCCESS);
517
}
518
519
/***************************************************************
520
Undoes a modify in secondary indexes when undo record type is DEL_MARK. */
521
static
522
ulint
523
row_undo_mod_del_mark_sec(
524
/*======================*/
525
				/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
526
	undo_node_t*	node,	/* in: row undo node */
527
	que_thr_t*	thr)	/* in: query thread */
528
{
529
	mem_heap_t*	heap;
530
	dtuple_t*	entry;
531
	dict_index_t*	index;
532
	ulint		err;
533
534
	heap = mem_heap_create(1024);
535
536
	while (node->index != NULL) {
537
		index = node->index;
538
539
		entry = row_build_index_entry(node->row, index, heap);
540
541
		err = row_undo_mod_del_unmark_sec_and_undo_update(
542
			BTR_MODIFY_LEAF, thr, index, entry);
543
		if (err == DB_FAIL) {
544
			err = row_undo_mod_del_unmark_sec_and_undo_update(
545
				BTR_MODIFY_TREE, thr, index, entry);
546
		}
547
548
		if (err != DB_SUCCESS) {
549
550
			mem_heap_free(heap);
551
552
			return(err);
553
		}
554
555
		node->index = dict_table_get_next_index(node->index);
556
	}
557
558
	mem_heap_free(heap);
559
560
	return(DB_SUCCESS);
561
}
562
563
/***************************************************************
564
Undoes a modify in secondary indexes when undo record type is UPD_EXIST. */
565
static
566
ulint
567
row_undo_mod_upd_exist_sec(
568
/*=======================*/
569
				/* out: DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
570
	undo_node_t*	node,	/* in: row undo node */
571
	que_thr_t*	thr)	/* in: query thread */
572
{
573
	mem_heap_t*	heap;
574
	dtuple_t*	entry;
575
	dict_index_t*	index;
576
	ulint		err;
577
578
	if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
579
		/* No change in secondary indexes */
580
581
		return(DB_SUCCESS);
582
	}
583
584
	heap = mem_heap_create(1024);
585
586
	while (node->index != NULL) {
587
		index = node->index;
588
589
		if (row_upd_changes_ord_field_binary(node->row, node->index,
590
						     node->update)) {
591
592
			/* Build the newest version of the index entry */
593
			entry = row_build_index_entry(node->row, index, heap);
594
595
			/* NOTE that if we updated the fields of a
596
			delete-marked secondary index record so that
597
			alphabetically they stayed the same, e.g.,
598
			'abc' -> 'aBc', we cannot return to the original
599
			values because we do not know them. But this should
600
			not cause problems because in row0sel.c, in queries
601
			we always retrieve the clustered index record or an
602
			earlier version of it, if the secondary index record
603
			through which we do the search is delete-marked. */
604
605
			err = row_undo_mod_del_mark_or_remove_sec(node, thr,
606
								  index,
607
								  entry);
608
			if (err != DB_SUCCESS) {
609
				mem_heap_free(heap);
610
611
				return(err);
612
			}
613
614
			/* We may have to update the delete mark in the
615
			secondary index record of the previous version of
616
			the row. We also need to update the fields of
617
			the secondary index record if we updated its fields
618
			but alphabetically they stayed the same, e.g.,
619
			'abc' -> 'aBc'. */
620
621
			row_upd_index_replace_new_col_vals(entry, index,
622
							   node->update, NULL);
623
			err = row_undo_mod_del_unmark_sec_and_undo_update(
624
				BTR_MODIFY_LEAF, thr, index, entry);
625
			if (err == DB_FAIL) {
626
				err = row_undo_mod_del_unmark_sec_and_undo_update(
627
					BTR_MODIFY_TREE, thr, index, entry);
628
			}
629
630
			if (err != DB_SUCCESS) {
631
				mem_heap_free(heap);
632
633
				return(err);
634
			}
635
		}
636
637
		node->index = dict_table_get_next_index(node->index);
638
	}
639
640
	mem_heap_free(heap);
641
642
	return(DB_SUCCESS);
643
}
644
645
/***************************************************************
646
Parses the row reference and other info in a modify undo log record. */
647
static
648
void
649
row_undo_mod_parse_undo_rec(
650
/*========================*/
651
	undo_node_t*	node,	/* in: row undo node */
652
	que_thr_t*	thr)	/* in: query thread */
653
{
654
	dict_index_t*	clust_index;
655
	byte*		ptr;
656
	dulint		undo_no;
657
	dulint		table_id;
658
	dulint		trx_id;
659
	dulint		roll_ptr;
660
	ulint		info_bits;
661
	ulint		type;
662
	ulint		cmpl_info;
663
	ibool		dummy_extern;
664
	trx_t*		trx;
665
666
	ut_ad(node && thr);
667
	trx = thr_get_trx(thr);
668
	ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
669
				    &dummy_extern, &undo_no, &table_id);
670
	node->rec_type = type;
671
672
	node->table = dict_table_get_on_id(table_id, trx);
673
674
	/* TODO: other fixes associated with DROP TABLE + rollback in the
675
	same table by another user */
676
677
	if (node->table == NULL) {
678
		/* Table was dropped */
679
		return;
680
	}
681
682
	if (node->table->ibd_file_missing) {
683
		/* We skip undo operations to missing .ibd files */
684
		node->table = NULL;
685
686
		return;
687
	}
688
689
	clust_index = dict_table_get_first_index(node->table);
690
691
	ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
692
					       &info_bits);
693
694
	ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
695
				       node->heap);
696
697
	trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
698
				       roll_ptr, info_bits, trx,
699
				       node->heap, &(node->update));
700
	node->new_roll_ptr = roll_ptr;
701
	node->new_trx_id = trx_id;
702
	node->cmpl_info = cmpl_info;
703
}
704
705
/***************************************************************
706
Undoes a modify operation on a row of a table. */
707
708
ulint
709
row_undo_mod(
710
/*=========*/
711
				/* out: DB_SUCCESS or error code */
712
	undo_node_t*	node,	/* in: row undo node */
713
	que_thr_t*	thr)	/* in: query thread */
714
{
715
	ibool	found;
716
	ulint	err;
717
718
	ut_ad(node && thr);
719
	ut_ad(node->state == UNDO_NODE_MODIFY);
720
721
	row_undo_mod_parse_undo_rec(node, thr);
722
723
	if (node->table == NULL) {
724
		found = FALSE;
725
	} else {
726
		found = row_undo_search_clust_to_pcur(node);
727
	}
728
729
	if (!found) {
730
		/* It is already undone, or will be undone by another query
731
		thread, or table was dropped */
732
733
		trx_undo_rec_release(node->trx, node->undo_no);
734
		node->state = UNDO_NODE_FETCH_NEXT;
735
736
		return(DB_SUCCESS);
737
	}
738
739
	node->index = dict_table_get_next_index(
740
		dict_table_get_first_index(node->table));
741
742
	if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
743
744
		err = row_undo_mod_upd_exist_sec(node, thr);
745
746
	} else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
747
748
		err = row_undo_mod_del_mark_sec(node, thr);
749
	} else {
750
		ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
751
		err = row_undo_mod_upd_del_sec(node, thr);
752
	}
753
754
	if (err != DB_SUCCESS) {
755
756
		return(err);
757
	}
758
759
	err = row_undo_mod_clust(node, thr);
760
761
	return(err);
762
}