1 /*****************************************************************************
2 
3 Copyright (c) 1997, 2013, Oracle and/or its affiliates. All Rights Reserved.
4 
5 This program is free software; you can redistribute it and/or modify it under
6 the terms of the GNU General Public License as published by the Free Software
7 Foundation; version 2 of the License.
8 
9 This program is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 
13 You should have received a copy of the GNU General Public License along with
14 this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 
17 *****************************************************************************/
18 
19 /**************************************************//**
20 @file row/row0umod.c
21 Undo modify of a row
22 
23 Created 2/27/1997 Heikki Tuuri
24 *******************************************************/
25 
26 #include "row0umod.h"
27 
28 #ifdef UNIV_NONINL
29 #include "row0umod.ic"
30 #endif
31 
32 #include "dict0dict.h"
33 #include "dict0boot.h"
34 #include "trx0undo.h"
35 #include "trx0roll.h"
36 #include "btr0btr.h"
37 #include "mach0data.h"
38 #include "row0undo.h"
39 #include "row0vers.h"
40 #include "trx0trx.h"
41 #include "trx0rec.h"
42 #include "row0row.h"
43 #include "row0upd.h"
44 #include "que0que.h"
45 #include "log0log.h"
46 
47 /* Considerations on undoing a modify operation.
48 (1) Undoing a delete marking: all index records should be found. Some of
49 them may have delete mark already FALSE, if the delete mark operation was
50 stopped underway, or if the undo operation ended prematurely because of a
51 system crash.
52 (2) Undoing an update of a delete unmarked record: the newer version of
53 an updated secondary index entry should be removed if no prior version
54 of the clustered index record requires its existence. Otherwise, it should
55 be delete marked.
56 (3) Undoing an update of a delete marked record. In this kind of update a
57 delete marked clustered index record was delete unmarked and possibly also
58 some of its fields were changed. Now, it is possible that the delete marked
59 version has become obsolete at the time the undo is started. */
60 
61 /*************************************************************************
62 IMPORTANT NOTE: Any operation that generates redo MUST check that there
63 is enough space in the redo log before for that operation. This is
64 done by calling log_free_check(). The reason for checking the
65 availability of the redo log space before the start of the operation is
66 that we MUST not hold any synchonization objects when performing the
67 check.
68 If you make a change in this module make sure that no codepath is
69 introduced where a call to log_free_check() is bypassed. */
70 
71 /***********************************************************//**
72 Undoes a modify in a clustered index record.
73 @return	DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
74 static
75 ulint
row_undo_mod_clust_low(undo_node_t * node,que_thr_t * thr,mtr_t * mtr,ulint mode)76 row_undo_mod_clust_low(
77 /*===================*/
78 	undo_node_t*	node,	/*!< in: row undo node */
79 	que_thr_t*	thr,	/*!< in: query thread */
80 	mtr_t*		mtr,	/*!< in: mtr; must be committed before
81 				latching any further pages */
82 	ulint		mode)	/*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
83 {
84 	btr_pcur_t*	pcur;
85 	btr_cur_t*	btr_cur;
86 	ulint		err;
87 #ifdef UNIV_DEBUG
88 	ibool		success;
89 #endif /* UNIV_DEBUG */
90 
91 	pcur = &(node->pcur);
92 	btr_cur = btr_pcur_get_btr_cur(pcur);
93 
94 #ifdef UNIV_DEBUG
95 	success =
96 #endif /* UNIV_DEBUG */
97 	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 		mem_heap_t*	heap		= NULL;
110 		big_rec_t*	dummy_big_rec;
111 
112 		ut_ad(mode == BTR_MODIFY_TREE);
113 
114 		err = btr_cur_pessimistic_update(
115 			BTR_NO_LOCKING_FLAG
116 			| BTR_NO_UNDO_LOG_FLAG
117 			| BTR_KEEP_SYS_FLAG,
118 			btr_cur, &heap, &dummy_big_rec, node->update,
119 			node->cmpl_info, thr, mtr);
120 
121 		ut_a(!dummy_big_rec);
122 		if (UNIV_LIKELY_NULL(heap)) {
123 			mem_heap_free(heap);
124 		}
125 	}
126 
127 	return(err);
128 }
129 
130 /***********************************************************//**
131 Purges a clustered index record after undo if possible.
132 This is attempted when the record was inserted by updating a
133 delete-marked record and there no longer exist transactions
134 that would see the delete-marked record.
135 @return	DB_SUCCESS, DB_FAIL, or error code: we may run out of file space */
136 static
137 ulint
row_undo_mod_remove_clust_low(undo_node_t * node,que_thr_t * thr,mtr_t * mtr,ulint mode)138 row_undo_mod_remove_clust_low(
139 /*==========================*/
140 	undo_node_t*	node,	/*!< in: row undo node */
141 	que_thr_t*	thr,	/*!< in: query thread */
142 	mtr_t*		mtr,	/*!< in/out: mini-transaction */
143 	ulint		mode)	/*!< in: BTR_MODIFY_LEAF or BTR_MODIFY_TREE */
144 {
145 	btr_cur_t*	btr_cur;
146 	ulint		err;
147 	ulint		trx_id_offset;
148 
149 	ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
150 
151 	/* Find out if the record has been purged already
152 	or if we can remove it. */
153 
154 	if (!btr_pcur_restore_position(mode, &node->pcur, mtr)
155 	    || row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
156 
157 		return(DB_SUCCESS);
158 	}
159 
160 	btr_cur = btr_pcur_get_btr_cur(&node->pcur);
161 
162 	trx_id_offset = btr_cur_get_index(btr_cur)->trx_id_offset;
163 
164 	if (!trx_id_offset) {
165 		mem_heap_t*	heap	= NULL;
166 		ulint		trx_id_col;
167 		ulint*		offsets;
168 		ulint		len;
169 
170 		trx_id_col = dict_index_get_sys_col_pos(
171 			btr_cur_get_index(btr_cur), DATA_TRX_ID);
172 		ut_ad(trx_id_col > 0);
173 		ut_ad(trx_id_col != ULINT_UNDEFINED);
174 
175 		offsets = rec_get_offsets(
176 			btr_cur_get_rec(btr_cur), btr_cur_get_index(btr_cur),
177 			NULL, trx_id_col + 1, &heap);
178 
179 		trx_id_offset = rec_get_nth_field_offs(
180 			offsets, trx_id_col, &len);
181 		ut_ad(len == DATA_TRX_ID_LEN);
182 		mem_heap_free(heap);
183 	}
184 
185 	if (trx_read_trx_id(btr_cur_get_rec(btr_cur) + trx_id_offset)
186 	    != node->new_trx_id) {
187 		/* The record must have been purged and then replaced
188 		with a different one. */
189 		return(DB_SUCCESS);
190 	}
191 
192 	/* We are about to remove an old, delete-marked version of the
193 	record that may have been delete-marked by a different transaction
194 	than the rolling-back one. */
195 	ut_ad(rec_get_deleted_flag(btr_cur_get_rec(btr_cur),
196 				   dict_table_is_comp(node->table)));
197 
198 	if (mode == BTR_MODIFY_LEAF) {
199 		err = btr_cur_optimistic_delete(btr_cur, mtr)
200 			? DB_SUCCESS
201 			: DB_FAIL;
202 	} else {
203 		ut_ad(mode == BTR_MODIFY_TREE);
204 
205 		/* This operation is analogous to purge, we can free also
206 		inherited externally stored fields */
207 
208 		btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
209 					   thr_is_recv(thr)
210 					   ? RB_RECOVERY_PURGE_REC
211 					   : RB_NONE, mtr);
212 
213 		/* The delete operation may fail if we have little
214 		file space left: TODO: easiest to crash the database
215 		and restart with more file space */
216 	}
217 
218 	return(err);
219 }
220 
221 /***********************************************************//**
222 Undoes a modify in a clustered index record. Sets also the node state for the
223 next round of undo.
224 @return	DB_SUCCESS or error code: we may run out of file space */
225 static
226 ulint
row_undo_mod_clust(undo_node_t * node,que_thr_t * thr)227 row_undo_mod_clust(
228 /*===============*/
229 	undo_node_t*	node,	/*!< in: row undo node */
230 	que_thr_t*	thr)	/*!< in: query thread */
231 {
232 	btr_pcur_t*	pcur;
233 	mtr_t		mtr;
234 	ulint		err;
235 
236 	ut_ad(node && thr);
237 
238 	log_free_check();
239 
240 	pcur = &(node->pcur);
241 
242 	mtr_start(&mtr);
243 
244 	/* Try optimistic processing of the record, keeping changes within
245 	the index page */
246 
247 	err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_LEAF);
248 
249 	if (err != DB_SUCCESS) {
250 		btr_pcur_commit_specify_mtr(pcur, &mtr);
251 
252 		/* We may have to modify tree structure: do a pessimistic
253 		descent down the index tree */
254 
255 		mtr_start(&mtr);
256 
257 		err = row_undo_mod_clust_low(node, thr, &mtr, BTR_MODIFY_TREE);
258 	}
259 
260 	btr_pcur_commit_specify_mtr(pcur, &mtr);
261 
262 	if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
263 
264 		mtr_start(&mtr);
265 
266 		err = row_undo_mod_remove_clust_low(node, thr, &mtr,
267 						    BTR_MODIFY_LEAF);
268 		if (err != DB_SUCCESS) {
269 			btr_pcur_commit_specify_mtr(pcur, &mtr);
270 
271 			/* We may have to modify tree structure: do a
272 			pessimistic descent down the index tree */
273 
274 			mtr_start(&mtr);
275 
276 			err = row_undo_mod_remove_clust_low(node, thr, &mtr,
277 							    BTR_MODIFY_TREE);
278 		}
279 
280 		btr_pcur_commit_specify_mtr(pcur, &mtr);
281 	}
282 
283 	node->state = UNDO_NODE_FETCH_NEXT;
284 
285 	trx_undo_rec_release(node->trx, node->undo_no);
286 
287 	return(err);
288 }
289 
290 /***********************************************************//**
291 Delete marks or removes a secondary index entry if found.
292 @return	DB_SUCCESS, DB_FAIL, or DB_OUT_OF_FILE_SPACE */
293 static
294 ulint
row_undo_mod_del_mark_or_remove_sec_low(undo_node_t * node,que_thr_t * thr,dict_index_t * index,dtuple_t * entry,ulint mode)295 row_undo_mod_del_mark_or_remove_sec_low(
296 /*====================================*/
297 	undo_node_t*	node,	/*!< in: row undo node */
298 	que_thr_t*	thr,	/*!< in: query thread */
299 	dict_index_t*	index,	/*!< in: index */
300 	dtuple_t*	entry,	/*!< in: index entry */
301 	ulint		mode)	/*!< in: latch mode BTR_MODIFY_LEAF or
302 				BTR_MODIFY_TREE */
303 {
304 	btr_pcur_t		pcur;
305 	btr_cur_t*		btr_cur;
306 	ibool			success;
307 	ibool			old_has;
308 	ulint			err;
309 	mtr_t			mtr;
310 	mtr_t			mtr_vers;
311 	enum row_search_result	search_result;
312 
313 	log_free_check();
314 	mtr_start(&mtr);
315 
316 	btr_cur = btr_pcur_get_btr_cur(&pcur);
317 
318 	ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
319 
320 	search_result = row_search_index_entry(index, entry, mode,
321 					       &pcur, &mtr);
322 
323 	switch (UNIV_EXPECT(search_result, ROW_FOUND)) {
324 	case ROW_NOT_FOUND:
325 		/* In crash recovery, the secondary index record may
326 		be missing if the UPDATE did not have time to insert
327 		the secondary index records before the crash.  When we
328 		are undoing that UPDATE in crash recovery, the record
329 		may be missing.
330 
331 		In normal processing, if an update ends in a deadlock
332 		before it has inserted all updated secondary index
333 		records, then the undo will not find those records. */
334 
335 		err = DB_SUCCESS;
336 		goto func_exit;
337 	case ROW_FOUND:
338 		break;
339 	case ROW_BUFFERED:
340 	case ROW_NOT_DELETED_REF:
341 		/* These are invalid outcomes, because the mode passed
342 		to row_search_index_entry() did not include any of the
343 		flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
344 		ut_error;
345 	}
346 
347 	/* We should remove the index record if no prior version of the row,
348 	which cannot be purged yet, requires its existence. If some requires,
349 	we should delete mark the record. */
350 
351 	mtr_start(&mtr_vers);
352 
353 	success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
354 					    &mtr_vers);
355 	ut_a(success);
356 
357 	old_has = row_vers_old_has_index_entry(FALSE,
358 					       btr_pcur_get_rec(&(node->pcur)),
359 					       &mtr_vers, index, entry);
360 	if (old_has) {
361 		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
362 						   btr_cur, TRUE, thr, &mtr);
363 		ut_ad(err == DB_SUCCESS);
364 	} else {
365 		/* Remove the index record */
366 
367 		if (mode == BTR_MODIFY_LEAF) {
368 			success = btr_cur_optimistic_delete(btr_cur, &mtr);
369 			if (success) {
370 				err = DB_SUCCESS;
371 			} else {
372 				err = DB_FAIL;
373 			}
374 		} else {
375 			ut_ad(mode == BTR_MODIFY_TREE);
376 
377 			/* No need to distinguish RB_RECOVERY_PURGE here,
378 			because we are deleting a secondary index record:
379 			the distinction between RB_NORMAL and
380 			RB_RECOVERY_PURGE only matters when deleting a
381 			record that contains externally stored
382 			columns. */
383 			ut_ad(!dict_index_is_clust(index));
384 			btr_cur_pessimistic_delete(&err, FALSE, btr_cur,
385 						   RB_NORMAL, &mtr);
386 
387 			/* The delete operation may fail if we have little
388 			file space left: TODO: easiest to crash the database
389 			and restart with more file space */
390 		}
391 	}
392 
393 	btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
394 
395 func_exit:
396 	btr_pcur_close(&pcur);
397 	mtr_commit(&mtr);
398 
399 	return(err);
400 }
401 
402 /***********************************************************//**
403 Delete marks or removes a secondary index entry if found.
404 NOTE that if we updated the fields of a delete-marked secondary index record
405 so that alphabetically they stayed the same, e.g., 'abc' -> 'aBc', we cannot
406 return to the original values because we do not know them. But this should
407 not cause problems because in row0sel.c, in queries we always retrieve the
408 clustered index record or an earlier version of it, if the secondary index
409 record through which we do the search is delete-marked.
410 @return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
411 static
412 ulint
row_undo_mod_del_mark_or_remove_sec(undo_node_t * node,que_thr_t * thr,dict_index_t * index,dtuple_t * entry)413 row_undo_mod_del_mark_or_remove_sec(
414 /*================================*/
415 	undo_node_t*	node,	/*!< in: row undo node */
416 	que_thr_t*	thr,	/*!< in: query thread */
417 	dict_index_t*	index,	/*!< in: index */
418 	dtuple_t*	entry)	/*!< in: index entry */
419 {
420 	ulint	err;
421 
422 	err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
423 						      entry, BTR_MODIFY_LEAF);
424 	if (err == DB_SUCCESS) {
425 
426 		return(err);
427 	}
428 
429 	err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
430 						      entry, BTR_MODIFY_TREE);
431 	return(err);
432 }
433 
434 /***********************************************************//**
435 Delete unmarks a secondary index entry which must be found. It might not be
436 delete-marked at the moment, but it does not harm to unmark it anyway. We also
437 need to update the fields of the secondary index record if we updated its
438 fields but alphabetically they stayed the same, e.g., 'abc' -> 'aBc'.
439 @return	DB_FAIL or DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
440 static
441 ulint
row_undo_mod_del_unmark_sec_and_undo_update(ulint mode,que_thr_t * thr,dict_index_t * index,const dtuple_t * entry)442 row_undo_mod_del_unmark_sec_and_undo_update(
443 /*========================================*/
444 	ulint		mode,	/*!< in: search mode: BTR_MODIFY_LEAF or
445 				BTR_MODIFY_TREE */
446 	que_thr_t*	thr,	/*!< in: query thread */
447 	dict_index_t*	index,	/*!< in: index */
448 	const dtuple_t*	entry)	/*!< in: index entry */
449 {
450 	mem_heap_t*		heap;
451 	btr_pcur_t		pcur;
452 	btr_cur_t*		btr_cur;
453 	upd_t*			update;
454 	ulint			err		= DB_SUCCESS;
455 	big_rec_t*		dummy_big_rec;
456 	mtr_t			mtr;
457 	trx_t*			trx		= thr_get_trx(thr);
458 	enum row_search_result	search_result;
459 
460 	/* Ignore indexes that are being created. */
461 	if (UNIV_UNLIKELY(*index->name == TEMP_INDEX_PREFIX)) {
462 
463 		return(DB_SUCCESS);
464 	}
465 
466 	log_free_check();
467 	mtr_start(&mtr);
468 
469 	ut_ad(mode == BTR_MODIFY_TREE || mode == BTR_MODIFY_LEAF);
470 
471 	search_result = row_search_index_entry(index, entry, mode,
472 					       &pcur, &mtr);
473 
474 	switch (search_result) {
475 	case ROW_BUFFERED:
476 	case ROW_NOT_DELETED_REF:
477 		/* These are invalid outcomes, because the mode passed
478 		to row_search_index_entry() did not include any of the
479 		flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
480 		ut_error;
481 	case ROW_NOT_FOUND:
482 		fputs("InnoDB: error in sec index entry del undo in\n"
483 		      "InnoDB: ", stderr);
484 		dict_index_name_print(stderr, trx, index);
485 		fputs("\n"
486 		      "InnoDB: tuple ", stderr);
487 		dtuple_print(stderr, entry);
488 		fputs("\n"
489 		      "InnoDB: record ", stderr);
490 		rec_print(stderr, btr_pcur_get_rec(&pcur), index);
491 		putc('\n', stderr);
492 		trx_print(stderr, trx, 0);
493 		fputs("\n"
494 		      "InnoDB: Submit a detailed bug report"
495 		      " to http://bugs.mysql.com\n", stderr);
496 		ut_ad(0);
497 		break;
498 	case ROW_FOUND:
499 		btr_cur = btr_pcur_get_btr_cur(&pcur);
500 		err = btr_cur_del_mark_set_sec_rec(BTR_NO_LOCKING_FLAG,
501 						   btr_cur, FALSE, thr, &mtr);
502 		ut_a(err == DB_SUCCESS);
503 		heap = mem_heap_create(100);
504 
505 		update = row_upd_build_sec_rec_difference_binary(
506 			index, entry, btr_cur_get_rec(btr_cur), trx, heap);
507 		if (upd_get_n_fields(update) == 0) {
508 
509 			/* Do nothing */
510 
511 		} else if (mode == BTR_MODIFY_LEAF) {
512 			/* Try an optimistic updating of the record, keeping
513 			changes within the page */
514 
515 			err = btr_cur_optimistic_update(
516 				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
517 				btr_cur, update, 0, thr, &mtr);
518 			switch (err) {
519 			case DB_OVERFLOW:
520 			case DB_UNDERFLOW:
521 			case DB_ZIP_OVERFLOW:
522 				err = DB_FAIL;
523 			}
524 		} else {
525 			ut_a(mode == BTR_MODIFY_TREE);
526 			err = btr_cur_pessimistic_update(
527 				BTR_KEEP_SYS_FLAG | BTR_NO_LOCKING_FLAG,
528 				btr_cur, &heap, &dummy_big_rec,
529 				update, 0, thr, &mtr);
530 			ut_a(!dummy_big_rec);
531 		}
532 
533 		mem_heap_free(heap);
534 	}
535 
536 	btr_pcur_close(&pcur);
537 	mtr_commit(&mtr);
538 
539 	return(err);
540 }
541 
542 /***********************************************************//**
543 Undoes a modify in secondary indexes when undo record type is UPD_DEL.
544 @return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
545 static
546 ulint
row_undo_mod_upd_del_sec(undo_node_t * node,que_thr_t * thr)547 row_undo_mod_upd_del_sec(
548 /*=====================*/
549 	undo_node_t*	node,	/*!< in: row undo node */
550 	que_thr_t*	thr)	/*!< in: query thread */
551 {
552 	mem_heap_t*	heap;
553 	dtuple_t*	entry;
554 	dict_index_t*	index;
555 	ulint		err	= DB_SUCCESS;
556 
557 	ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
558 	ut_ad(!node->undo_row);
559 	heap = mem_heap_create(1024);
560 
561 	while (node->index != NULL) {
562 
563 		/* Skip all corrupted secondary index */
564 		dict_table_skip_corrupt_index(node->index);
565 
566 		if (!node->index) {
567 			break;
568 		}
569 
570 		index = node->index;
571 
572 		entry = row_build_index_entry(node->row, node->ext,
573 					      index, heap);
574 		if (UNIV_UNLIKELY(!entry)) {
575 			/* The database must have crashed after
576 			inserting a clustered index record but before
577 			writing all the externally stored columns of
578 			that record.  Because secondary index entries
579 			are inserted after the clustered index record,
580 			we may assume that the secondary index record
581 			does not exist.  However, this situation may
582 			only occur during the rollback of incomplete
583 			transactions. */
584 			ut_a(thr_is_recv(thr));
585 		} else {
586 			err = row_undo_mod_del_mark_or_remove_sec(
587 				node, thr, index, entry);
588 
589 			if (err != DB_SUCCESS) {
590 
591 				break;
592 			}
593 		}
594 
595 		mem_heap_empty(heap);
596 
597 		node->index = dict_table_get_next_index(node->index);
598 	}
599 
600 	mem_heap_free(heap);
601 
602 	return(err);
603 }
604 
605 /***********************************************************//**
606 Undoes a modify in secondary indexes when undo record type is DEL_MARK.
607 @return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
608 static
609 ulint
row_undo_mod_del_mark_sec(undo_node_t * node,que_thr_t * thr)610 row_undo_mod_del_mark_sec(
611 /*======================*/
612 	undo_node_t*	node,	/*!< in: row undo node */
613 	que_thr_t*	thr)	/*!< in: query thread */
614 {
615 	mem_heap_t*	heap;
616 	dtuple_t*	entry;
617 	dict_index_t*	index;
618 	ulint		err;
619 
620 	ut_ad(!node->undo_row);
621 
622 	heap = mem_heap_create(1024);
623 
624 	while (node->index != NULL) {
625 		/* Skip all corrupted secondary index */
626 		dict_table_skip_corrupt_index(node->index);
627 
628 		if (!node->index) {
629 			break;
630 		}
631 
632 		index = node->index;
633 
634 		entry = row_build_index_entry(node->row, node->ext,
635 					      index, heap);
636 		ut_a(entry);
637 		err = row_undo_mod_del_unmark_sec_and_undo_update(
638 			BTR_MODIFY_LEAF, thr, index, entry);
639 		if (err == DB_FAIL) {
640 			err = row_undo_mod_del_unmark_sec_and_undo_update(
641 				BTR_MODIFY_TREE, thr, index, entry);
642 		}
643 
644 		if (err != DB_SUCCESS) {
645 
646 			mem_heap_free(heap);
647 
648 			return(err);
649 		}
650 
651 		node->index = dict_table_get_next_index(node->index);
652 	}
653 
654 	mem_heap_free(heap);
655 
656 	return(DB_SUCCESS);
657 }
658 
659 /***********************************************************//**
660 Undoes a modify in secondary indexes when undo record type is UPD_EXIST.
661 @return	DB_SUCCESS or DB_OUT_OF_FILE_SPACE */
662 static
663 ulint
row_undo_mod_upd_exist_sec(undo_node_t * node,que_thr_t * thr)664 row_undo_mod_upd_exist_sec(
665 /*=======================*/
666 	undo_node_t*	node,	/*!< in: row undo node */
667 	que_thr_t*	thr)	/*!< in: query thread */
668 {
669 	mem_heap_t*	heap;
670 	dtuple_t*	entry;
671 	dict_index_t*	index;
672 	ulint		err;
673 
674 	if (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE) {
675 		/* No change in secondary indexes */
676 
677 		return(DB_SUCCESS);
678 	}
679 
680 	heap = mem_heap_create(1024);
681 
682 	while (node->index != NULL) {
683 		/* Skip all corrupted secondary index */
684 		dict_table_skip_corrupt_index(node->index);
685 
686 		if (!node->index) {
687 			break;
688 		}
689 
690 		index = node->index;
691 
692 		if (row_upd_changes_ord_field_binary(node->index, node->update,
693 						     thr,
694 						     node->row, node->ext)) {
695 
696 			/* Build the newest version of the index entry */
697 			entry = row_build_index_entry(node->row, node->ext,
698 						      index, heap);
699 			if (UNIV_UNLIKELY(!entry)) {
700 				/* The server must have crashed in
701 				row_upd_clust_rec_by_insert() before
702 				the updated externally stored columns (BLOBs)
703 				of the new clustered index entry were
704 				written. */
705 
706 				/* The table must be in DYNAMIC or COMPRESSED
707 				format.  REDUNDANT and COMPACT formats
708 				store a local 768-byte prefix of each
709 				externally stored column. */
710 				ut_a(dict_table_get_format(index->table)
711 				     >= DICT_TF_FORMAT_ZIP);
712 
713 				/* This is only legitimate when
714 				rolling back an incomplete transaction
715 				after crash recovery. */
716 				ut_a(thr_get_trx(thr)->is_recovered);
717 
718 				/* The server must have crashed before
719 				completing the insert of the new
720 				clustered index entry and before
721 				inserting to the secondary indexes.
722 				Because node->row was not yet written
723 				to this index, we can ignore it.  But
724 				we must restore node->undo_row. */
725 			} else {
726 				/* NOTE that if we updated the fields of a
727 				delete-marked secondary index record so that
728 				alphabetically they stayed the same, e.g.,
729 				'abc' -> 'aBc', we cannot return to the
730 				original values because we do not know them.
731 				But this should not cause problems because
732 				in row0sel.c, in queries we always retrieve
733 				the clustered index record or an earlier
734 				version of it, if the secondary index record
735 				through which we do the search is
736 				delete-marked. */
737 
738 				err = row_undo_mod_del_mark_or_remove_sec(
739 					node, thr, index, entry);
740 				if (err != DB_SUCCESS) {
741 					mem_heap_free(heap);
742 
743 					return(err);
744 				}
745 
746 				mem_heap_empty(heap);
747 			}
748 
749 			/* We may have to update the delete mark in the
750 			secondary index record of the previous version of
751 			the row. We also need to update the fields of
752 			the secondary index record if we updated its fields
753 			but alphabetically they stayed the same, e.g.,
754 			'abc' -> 'aBc'. */
755 			entry = row_build_index_entry(node->undo_row,
756 						      node->undo_ext,
757 						      index, heap);
758 			ut_a(entry);
759 
760 			err = row_undo_mod_del_unmark_sec_and_undo_update(
761 				BTR_MODIFY_LEAF, thr, index, entry);
762 			if (err == DB_FAIL) {
763 				err = row_undo_mod_del_unmark_sec_and_undo_update(
764 					BTR_MODIFY_TREE, thr, index, entry);
765 			}
766 
767 			if (err != DB_SUCCESS) {
768 				mem_heap_free(heap);
769 
770 				return(err);
771 			}
772 		}
773 
774 		node->index = dict_table_get_next_index(node->index);
775 	}
776 
777 	mem_heap_free(heap);
778 
779 	return(DB_SUCCESS);
780 }
781 
782 /***********************************************************//**
783 Parses the row reference and other info in a modify undo log record. */
784 static
785 void
row_undo_mod_parse_undo_rec(undo_node_t * node,que_thr_t * thr)786 row_undo_mod_parse_undo_rec(
787 /*========================*/
788 	undo_node_t*	node,	/*!< in: row undo node */
789 	que_thr_t*	thr)	/*!< in: query thread */
790 {
791 	dict_index_t*	clust_index;
792 	byte*		ptr;
793 	undo_no_t	undo_no;
794 	table_id_t	table_id;
795 	trx_id_t	trx_id;
796 	roll_ptr_t	roll_ptr;
797 	ulint		info_bits;
798 	ulint		type;
799 	ulint		cmpl_info;
800 	ibool		dummy_extern;
801 	trx_t*		trx;
802 
803 	ut_ad(node && thr);
804 	trx = thr_get_trx(thr);
805 	ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
806 				    &dummy_extern, &undo_no, &table_id);
807 	node->rec_type = type;
808 
809 	node->table = dict_table_get_on_id(table_id, trx);
810 
811 	/* TODO: other fixes associated with DROP TABLE + rollback in the
812 	same table by another user */
813 
814 	if (node->table == NULL) {
815 		/* Table was dropped */
816 		return;
817 	}
818 
819 	if (node->table->ibd_file_missing) {
820 		/* We skip undo operations to missing .ibd files */
821 		node->table = NULL;
822 
823 		return;
824 	}
825 
826 	clust_index = dict_table_get_first_index(node->table);
827 
828 	ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
829 					       &info_bits);
830 
831 	ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
832 				       node->heap);
833 
834 	trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
835 				       roll_ptr, info_bits, trx,
836 				       node->heap, &(node->update));
837 	node->new_trx_id = trx_id;
838 	node->cmpl_info = cmpl_info;
839 }
840 
841 /***********************************************************//**
842 Undoes a modify operation on a row of a table.
843 @return	DB_SUCCESS or error code */
844 UNIV_INTERN
845 ulint
row_undo_mod(undo_node_t * node,que_thr_t * thr)846 row_undo_mod(
847 /*=========*/
848 	undo_node_t*	node,	/*!< in: row undo node */
849 	que_thr_t*	thr)	/*!< in: query thread */
850 {
851 	ulint	err;
852 
853 	ut_ad(node && thr);
854 	ut_ad(node->state == UNDO_NODE_MODIFY);
855 
856 	row_undo_mod_parse_undo_rec(node, thr);
857 
858 	if (!node->table || !row_undo_search_clust_to_pcur(node)) {
859 		/* It is already undone, or will be undone by another query
860 		thread, or table was dropped */
861 
862 		trx_undo_rec_release(node->trx, node->undo_no);
863 		node->state = UNDO_NODE_FETCH_NEXT;
864 
865 		return(DB_SUCCESS);
866 	}
867 
868 	node->index = dict_table_get_next_index(
869 		dict_table_get_first_index(node->table));
870 
871 	/* Skip all corrupted secondary index */
872 	dict_table_skip_corrupt_index(node->index);
873 
874 	if (node->rec_type == TRX_UNDO_UPD_EXIST_REC) {
875 
876 		err = row_undo_mod_upd_exist_sec(node, thr);
877 
878 	} else if (node->rec_type == TRX_UNDO_DEL_MARK_REC) {
879 
880 		err = row_undo_mod_del_mark_sec(node, thr);
881 	} else {
882 		ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
883 		err = row_undo_mod_upd_del_sec(node, thr);
884 	}
885 
886 	if (err != DB_SUCCESS) {
887 
888 		return(err);
889 	}
890 
891 	err = row_undo_mod_clust(node, thr);
892 
893 	return(err);
894 }
895