MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
row0umod.cc
Go to the documentation of this file.
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 Street, Suite 500, Boston, MA 02110-1335 USA
16 
17 *****************************************************************************/
18 
19 /**************************************************/
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 "row0log.h"
41 #include "trx0trx.h"
42 #include "trx0rec.h"
43 #include "row0row.h"
44 #include "row0upd.h"
45 #include "que0que.h"
46 #include "log0log.h"
47 
48 /* Considerations on undoing a modify operation.
49 (1) Undoing a delete marking: all index records should be found. Some of
50 them may have delete mark already FALSE, if the delete mark operation was
51 stopped underway, or if the undo operation ended prematurely because of a
52 system crash.
53 (2) Undoing an update of a delete unmarked record: the newer version of
54 an updated secondary index entry should be removed if no prior version
55 of the clustered index record requires its existence. Otherwise, it should
56 be delete marked.
57 (3) Undoing an update of a delete marked record. In this kind of update a
58 delete marked clustered index record was delete unmarked and possibly also
59 some of its fields were changed. Now, it is possible that the delete marked
60 version has become obsolete at the time the undo is started. */
61 
62 /*************************************************************************
63 IMPORTANT NOTE: Any operation that generates redo MUST check that there
64 is enough space in the redo log before for that operation. This is
65 done by calling log_free_check(). The reason for checking the
66 availability of the redo log space before the start of the operation is
67 that we MUST not hold any synchonization objects when performing the
68 check.
69 If you make a change in this module make sure that no codepath is
70 introduced where a call to log_free_check() is bypassed. */
71 
72 /***********************************************************/
75 static __attribute__((nonnull, warn_unused_result))
76 dberr_t
77 row_undo_mod_clust_low(
78 /*===================*/
79  undo_node_t* node,
80  ulint** offsets,
83  mem_heap_t* heap,
84  const dtuple_t**rebuilt_old_pk,
89  que_thr_t* thr,
90  mtr_t* mtr,
92  ulint mode)
93 {
95  btr_cur_t* btr_cur;
96  dberr_t err;
97 #ifdef UNIV_DEBUG
98  ibool success;
99 #endif /* UNIV_DEBUG */
100 
101  pcur = &node->pcur;
102  btr_cur = btr_pcur_get_btr_cur(pcur);
103 
104 #ifdef UNIV_DEBUG
105  success =
106 #endif /* UNIV_DEBUG */
107  btr_pcur_restore_position(mode, pcur, mtr);
108 
109  ut_ad(success);
110  ut_ad(rec_get_trx_id(btr_cur_get_rec(btr_cur),
111  btr_cur_get_index(btr_cur))
112  == thr_get_trx(thr)->id);
113 
114  if (mode != BTR_MODIFY_LEAF
116  *rebuilt_old_pk = row_log_table_get_pk(
117  btr_cur_get_rec(btr_cur),
118  btr_cur_get_index(btr_cur), NULL, &heap);
119  } else {
120  *rebuilt_old_pk = NULL;
121  }
122 
123  if (mode != BTR_MODIFY_TREE) {
125 
129  btr_cur, offsets, offsets_heap,
130  node->update, node->cmpl_info,
131  thr, thr_get_trx(thr)->id, mtr);
132  } else {
133  big_rec_t* dummy_big_rec;
134 
139  btr_cur, offsets, offsets_heap, heap,
140  &dummy_big_rec, node->update,
141  node->cmpl_info, thr, thr_get_trx(thr)->id, mtr);
142 
143  ut_a(!dummy_big_rec);
144  }
145 
146  return(err);
147 }
148 
149 /***********************************************************/
155 static __attribute__((nonnull, warn_unused_result))
156 dberr_t
157 row_undo_mod_remove_clust_low(
158 /*==========================*/
159  undo_node_t* node,
160  que_thr_t* thr,
161  mtr_t* mtr,
162  ulint mode)
163 {
164  btr_cur_t* btr_cur;
165  dberr_t err;
166  ulint trx_id_offset;
167 
168  ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
169 
170  /* Find out if the record has been purged already
171  or if we can remove it. */
172 
173  if (!btr_pcur_restore_position(mode, &node->pcur, mtr)
174  || row_vers_must_preserve_del_marked(node->new_trx_id, mtr)) {
175 
176  return(DB_SUCCESS);
177  }
178 
179  btr_cur = btr_pcur_get_btr_cur(&node->pcur);
180 
181  trx_id_offset = btr_cur_get_index(btr_cur)->trx_id_offset;
182 
183  if (!trx_id_offset) {
184  mem_heap_t* heap = NULL;
185  ulint trx_id_col;
186  const ulint* offsets;
187  ulint len;
188 
189  trx_id_col = dict_index_get_sys_col_pos(
190  btr_cur_get_index(btr_cur), DATA_TRX_ID);
191  ut_ad(trx_id_col > 0);
192  ut_ad(trx_id_col != ULINT_UNDEFINED);
193 
194  offsets = rec_get_offsets(
195  btr_cur_get_rec(btr_cur), btr_cur_get_index(btr_cur),
196  NULL, trx_id_col + 1, &heap);
197 
198  trx_id_offset = rec_get_nth_field_offs(
199  offsets, trx_id_col, &len);
200  ut_ad(len == DATA_TRX_ID_LEN);
201  mem_heap_free(heap);
202  }
203 
204  if (trx_read_trx_id(btr_cur_get_rec(btr_cur) + trx_id_offset)
205  != node->new_trx_id) {
206  /* The record must have been purged and then replaced
207  with a different one. */
208  return(DB_SUCCESS);
209  }
210 
211  /* We are about to remove an old, delete-marked version of the
212  record that may have been delete-marked by a different transaction
213  than the rolling-back one. */
214  ut_ad(rec_get_deleted_flag(btr_cur_get_rec(btr_cur),
215  dict_table_is_comp(node->table)));
216 
217  if (mode == BTR_MODIFY_LEAF) {
218  err = btr_cur_optimistic_delete(btr_cur, 0, mtr)
219  ? DB_SUCCESS
220  : DB_FAIL;
221  } else {
222  ut_ad(mode == BTR_MODIFY_TREE);
223 
224  /* This operation is analogous to purge, we can free also
225  inherited externally stored fields */
226 
227  btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0,
228  thr_is_recv(thr)
230  : RB_NONE, mtr);
231 
232  /* The delete operation may fail if we have little
233  file space left: TODO: easiest to crash the database
234  and restart with more file space */
235  }
236 
237  return(err);
238 }
239 
240 /***********************************************************/
244 static __attribute__((nonnull, warn_unused_result))
245 dberr_t
246 row_undo_mod_clust(
247 /*===============*/
248  undo_node_t* node,
249  que_thr_t* thr)
250 {
251  btr_pcur_t* pcur;
252  mtr_t mtr;
253  dberr_t err;
255  bool online;
256 
257  ut_ad(thr_get_trx(thr) == node->trx);
258  ut_ad(node->trx->dict_operation_lock_mode);
259 #ifdef UNIV_SYNC_DEBUG
260  ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED)
261  || rw_lock_own(&dict_operation_lock, RW_LOCK_EX));
262 #endif /* UNIV_SYNC_DEBUG */
263 
264  log_free_check();
265  pcur = &node->pcur;
266  index = btr_cur_get_index(btr_pcur_get_btr_cur(pcur));
267 
268  mtr_start(&mtr);
269 
270  online = dict_index_is_online_ddl(index);
271  if (online) {
272  ut_ad(node->trx->dict_operation_lock_mode != RW_X_LATCH);
273  mtr_s_lock(dict_index_get_lock(index), &mtr);
274  }
275 
276  mem_heap_t* heap = mem_heap_create(1024);
277  mem_heap_t* offsets_heap = NULL;
278  ulint* offsets = NULL;
279  const dtuple_t* rebuilt_old_pk;
280 
281  /* Try optimistic processing of the record, keeping changes within
282  the index page */
283 
284  err = row_undo_mod_clust_low(node, &offsets, &offsets_heap,
285  heap, &rebuilt_old_pk,
286  thr, &mtr, online
288  : BTR_MODIFY_LEAF);
289 
290  if (err != DB_SUCCESS) {
291  btr_pcur_commit_specify_mtr(pcur, &mtr);
292 
293  /* We may have to modify tree structure: do a pessimistic
294  descent down the index tree */
295 
296  mtr_start(&mtr);
297 
298  err = row_undo_mod_clust_low(
299  node, &offsets, &offsets_heap, heap, &rebuilt_old_pk,
300  thr, &mtr, BTR_MODIFY_TREE);
301  ut_ad(err == DB_SUCCESS || err == DB_OUT_OF_FILE_SPACE);
302  }
303 
304  /* Online rebuild cannot be initiated while we are holding
305  dict_operation_lock and index->lock. (It can be aborted.) */
306  ut_ad(online || !dict_index_is_online_ddl(index));
307 
308  if (err == DB_SUCCESS && online) {
309 #ifdef UNIV_SYNC_DEBUG
310  ut_ad(rw_lock_own(&index->lock, RW_LOCK_SHARED)
311  || rw_lock_own(&index->lock, RW_LOCK_EX));
312 #endif /* UNIV_SYNC_DEBUG */
313  switch (node->rec_type) {
314  case TRX_UNDO_DEL_MARK_REC:
316  btr_pcur_get_rec(pcur), index, offsets);
317  break;
318  case TRX_UNDO_UPD_EXIST_REC:
320  btr_pcur_get_rec(pcur), index, offsets,
321  rebuilt_old_pk);
322  break;
323  case TRX_UNDO_UPD_DEL_REC:
325  btr_pcur_get_rec(pcur), index, offsets,
326  true, node->trx->id);
327  break;
328  default:
329  ut_ad(0);
330  break;
331  }
332  }
333 
334  ut_ad(rec_get_trx_id(btr_pcur_get_rec(pcur), index)
335  == node->new_trx_id);
336 
337  btr_pcur_commit_specify_mtr(pcur, &mtr);
338 
339  if (err == DB_SUCCESS && node->rec_type == TRX_UNDO_UPD_DEL_REC) {
340 
341  mtr_start(&mtr);
342 
343  /* It is not necessary to call row_log_table,
344  because the record is delete-marked and would thus
345  be omitted from the rebuilt copy of the table. */
346  err = row_undo_mod_remove_clust_low(
347  node, thr, &mtr, BTR_MODIFY_LEAF);
348  if (err != DB_SUCCESS) {
349  btr_pcur_commit_specify_mtr(pcur, &mtr);
350 
351  /* We may have to modify tree structure: do a
352  pessimistic descent down the index tree */
353 
354  mtr_start(&mtr);
355 
356  err = row_undo_mod_remove_clust_low(node, thr, &mtr,
358 
359  ut_ad(err == DB_SUCCESS
360  || err == DB_OUT_OF_FILE_SPACE);
361  }
362 
363  btr_pcur_commit_specify_mtr(pcur, &mtr);
364  }
365 
366  node->state = UNDO_NODE_FETCH_NEXT;
367 
368  trx_undo_rec_release(node->trx, node->undo_no);
369 
370  if (offsets_heap) {
371  mem_heap_free(offsets_heap);
372  }
373  mem_heap_free(heap);
374  return(err);
375 }
376 
377 /***********************************************************/
380 static __attribute__((nonnull, warn_unused_result))
381 dberr_t
382 row_undo_mod_del_mark_or_remove_sec_low(
383 /*====================================*/
384  undo_node_t* node,
385  que_thr_t* thr,
386  dict_index_t* index,
387  dtuple_t* entry,
388  ulint mode)
390 {
392  btr_cur_t* btr_cur;
393  ibool success;
394  ibool old_has;
395  dberr_t err = DB_SUCCESS;
396  mtr_t mtr;
397  mtr_t mtr_vers;
398  enum row_search_result search_result;
399 
400  log_free_check();
401  mtr_start(&mtr);
402 
403  if (*index->name == TEMP_INDEX_PREFIX) {
404  /* The index->online_status may change if the
405  index->name starts with TEMP_INDEX_PREFIX (meaning
406  that the index is or was being created online). It is
407  protected by index->lock. */
408  if (mode == BTR_MODIFY_LEAF) {
410  mtr_s_lock(dict_index_get_lock(index), &mtr);
411  } else {
412  ut_ad(mode == BTR_MODIFY_TREE);
413  mtr_x_lock(dict_index_get_lock(index), &mtr);
414  }
415 
416  if (row_log_online_op_try(index, entry, 0)) {
417  goto func_exit_no_pcur;
418  }
419  } else {
420  /* For secondary indexes,
421  index->online_status==ONLINE_INDEX_CREATION unless
422  index->name starts with TEMP_INDEX_PREFIX. */
424  }
425 
426  btr_cur = btr_pcur_get_btr_cur(&pcur);
427 
428  search_result = row_search_index_entry(index, entry, mode,
429  &pcur, &mtr);
430 
431  switch (UNIV_EXPECT(search_result, ROW_FOUND)) {
432  case ROW_NOT_FOUND:
433  /* In crash recovery, the secondary index record may
434  be missing if the UPDATE did not have time to insert
435  the secondary index records before the crash. When we
436  are undoing that UPDATE in crash recovery, the record
437  may be missing.
438 
439  In normal processing, if an update ends in a deadlock
440  before it has inserted all updated secondary index
441  records, then the undo will not find those records. */
442  goto func_exit;
443  case ROW_FOUND:
444  break;
445  case ROW_BUFFERED:
446  case ROW_NOT_DELETED_REF:
447  /* These are invalid outcomes, because the mode passed
448  to row_search_index_entry() did not include any of the
449  flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
450  ut_error;
451  }
452 
453  /* We should remove the index record if no prior version of the row,
454  which cannot be purged yet, requires its existence. If some requires,
455  we should delete mark the record. */
456 
457  mtr_start(&mtr_vers);
458 
459  success = btr_pcur_restore_position(BTR_SEARCH_LEAF, &(node->pcur),
460  &mtr_vers);
461  ut_a(success);
462 
463  old_has = row_vers_old_has_index_entry(FALSE,
464  btr_pcur_get_rec(&(node->pcur)),
465  &mtr_vers, index, entry);
466  if (old_has) {
468  btr_cur, TRUE, thr, &mtr);
469  ut_ad(err == DB_SUCCESS);
470  } else {
471  /* Remove the index record */
472 
473  if (mode != BTR_MODIFY_TREE) {
474  success = btr_cur_optimistic_delete(btr_cur, 0, &mtr);
475  if (success) {
476  err = DB_SUCCESS;
477  } else {
478  err = DB_FAIL;
479  }
480  } else {
481  /* No need to distinguish RB_RECOVERY_PURGE here,
482  because we are deleting a secondary index record:
483  the distinction between RB_NORMAL and
484  RB_RECOVERY_PURGE only matters when deleting a
485  record that contains externally stored
486  columns. */
487  ut_ad(!dict_index_is_clust(index));
488  btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0,
489  RB_NORMAL, &mtr);
490 
491  /* The delete operation may fail if we have little
492  file space left: TODO: easiest to crash the database
493  and restart with more file space */
494  }
495  }
496 
497  btr_pcur_commit_specify_mtr(&(node->pcur), &mtr_vers);
498 
499 func_exit:
500  btr_pcur_close(&pcur);
501 func_exit_no_pcur:
502  mtr_commit(&mtr);
503 
504  return(err);
505 }
506 
507 /***********************************************************/
516 static __attribute__((nonnull, warn_unused_result))
517 dberr_t
518 row_undo_mod_del_mark_or_remove_sec(
519 /*================================*/
520  undo_node_t* node,
521  que_thr_t* thr,
522  dict_index_t* index,
523  dtuple_t* entry)
524 {
525  dberr_t err;
526 
527  err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
528  entry, BTR_MODIFY_LEAF);
529  if (err == DB_SUCCESS) {
530 
531  return(err);
532  }
533 
534  err = row_undo_mod_del_mark_or_remove_sec_low(node, thr, index,
535  entry, BTR_MODIFY_TREE);
536  return(err);
537 }
538 
539 /***********************************************************/
549 static __attribute__((nonnull, warn_unused_result))
550 dberr_t
551 row_undo_mod_del_unmark_sec_and_undo_update(
552 /*========================================*/
553  ulint mode,
555  que_thr_t* thr,
556  dict_index_t* index,
557  dtuple_t* entry)
558 {
560  btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur);
561  upd_t* update;
562  dberr_t err = DB_SUCCESS;
563  big_rec_t* dummy_big_rec;
564  mtr_t mtr;
565  trx_t* trx = thr_get_trx(thr);
566  const ulint flags
568  enum row_search_result search_result;
569 
570  ut_ad(trx->id);
571 
572  log_free_check();
573  mtr_start(&mtr);
574 
575  if (*index->name == TEMP_INDEX_PREFIX) {
576  /* The index->online_status may change if the
577  index->name starts with TEMP_INDEX_PREFIX (meaning
578  that the index is or was being created online). It is
579  protected by index->lock. */
580  if (mode == BTR_MODIFY_LEAF) {
582  mtr_s_lock(dict_index_get_lock(index), &mtr);
583  } else {
584  ut_ad(mode == BTR_MODIFY_TREE);
585  mtr_x_lock(dict_index_get_lock(index), &mtr);
586  }
587 
588  if (row_log_online_op_try(index, entry, trx->id)) {
589  goto func_exit_no_pcur;
590  }
591  } else {
592  /* For secondary indexes,
593  index->online_status==ONLINE_INDEX_CREATION unless
594  index->name starts with TEMP_INDEX_PREFIX. */
596  }
597 
598  search_result = row_search_index_entry(index, entry, mode,
599  &pcur, &mtr);
600 
601  switch (search_result) {
602  mem_heap_t* heap;
604  ulint* offsets;
605  case ROW_BUFFERED:
606  case ROW_NOT_DELETED_REF:
607  /* These are invalid outcomes, because the mode passed
608  to row_search_index_entry() did not include any of the
609  flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
610  ut_error;
611  case ROW_NOT_FOUND:
612  if (*index->name != TEMP_INDEX_PREFIX) {
613  /* During online secondary index creation, it
614  is possible that MySQL is waiting for a
615  meta-data lock upgrade before invoking
616  ha_innobase::commit_inplace_alter_table()
617  while this ROLLBACK is executing. InnoDB has
618  finished building the index, but it does not
619  yet exist in MySQL. In this case, we suppress
620  the printout to the error log. */
621  fputs("InnoDB: error in sec index entry del undo in\n"
622  "InnoDB: ", stderr);
623  dict_index_name_print(stderr, trx, index);
624  fputs("\n"
625  "InnoDB: tuple ", stderr);
626  dtuple_print(stderr, entry);
627  fputs("\n"
628  "InnoDB: record ", stderr);
629  rec_print(stderr, btr_pcur_get_rec(&pcur), index);
630  putc('\n', stderr);
631  trx_print(stderr, trx, 0);
632  fputs("\n"
633  "InnoDB: Submit a detailed bug report"
634  " to http://bugs.mysql.com\n", stderr);
635 
636  ib_logf(IB_LOG_LEVEL_WARN,
637  "record in index %s was not found"
638  " on rollback, trying to insert",
639  index->name);
640  }
641 
642  if (btr_cur->up_match >= dict_index_get_n_unique(index)
643  || btr_cur->low_match >= dict_index_get_n_unique(index)) {
644  if (*index->name != TEMP_INDEX_PREFIX) {
645  ib_logf(IB_LOG_LEVEL_WARN,
646  "record in index %s was not found on"
647  " rollback, and a duplicate exists",
648  index->name);
649  }
650  err = DB_DUPLICATE_KEY;
651  break;
652  }
653 
654  /* Insert the missing record that we were trying to
655  delete-unmark. */
656  big_rec_t* big_rec;
657  rec_t* insert_rec;
658  offsets = NULL;
659  offsets_heap = NULL;
660 
662  flags, btr_cur, &offsets, &offsets_heap,
663  entry, &insert_rec, &big_rec,
664  0, thr, &mtr);
665  ut_ad(!big_rec);
666 
667  if (err == DB_FAIL && mode == BTR_MODIFY_TREE) {
669  flags, btr_cur,
670  &offsets, &offsets_heap,
671  entry, &insert_rec, &big_rec,
672  0, thr, &mtr);
673  /* There are no off-page columns in
674  secondary indexes. */
675  ut_ad(!big_rec);
676  }
677 
678  if (err == DB_SUCCESS) {
680  btr_cur_get_block(btr_cur),
681  btr_cur_get_page_zip(btr_cur),
682  trx->id, &mtr);
683  }
684 
685  if (offsets_heap) {
686  mem_heap_free(offsets_heap);
687  }
688 
689  break;
690  case ROW_FOUND:
693  btr_cur, FALSE, thr, &mtr);
694  ut_a(err == DB_SUCCESS);
695  heap = mem_heap_create(
696  sizeof(upd_t)
697  + dtuple_get_n_fields(entry) * sizeof(upd_field_t));
698  offsets_heap = NULL;
699  offsets = rec_get_offsets(
700  btr_cur_get_rec(btr_cur),
701  index, NULL, ULINT_UNDEFINED, &offsets_heap);
703  btr_cur_get_rec(btr_cur), index, offsets, entry, heap);
704  if (upd_get_n_fields(update) == 0) {
705 
706  /* Do nothing */
707 
708  } else if (mode != BTR_MODIFY_TREE) {
709  /* Try an optimistic updating of the record, keeping
710  changes within the page */
711 
712  /* TODO: pass offsets, not &offsets */
714  flags, btr_cur, &offsets, &offsets_heap,
715  update, 0, thr, thr_get_trx(thr)->id, &mtr);
716  switch (err) {
717  case DB_OVERFLOW:
718  case DB_UNDERFLOW:
719  case DB_ZIP_OVERFLOW:
720  err = DB_FAIL;
721  default:
722  break;
723  }
724  } else {
726  flags, btr_cur, &offsets, &offsets_heap,
727  heap, &dummy_big_rec,
728  update, 0, thr, thr_get_trx(thr)->id, &mtr);
729  ut_a(!dummy_big_rec);
730  }
731 
732  mem_heap_free(heap);
733  mem_heap_free(offsets_heap);
734  }
735 
736  btr_pcur_close(&pcur);
737 func_exit_no_pcur:
738  mtr_commit(&mtr);
739 
740  return(err);
741 }
742 
743 /***********************************************************/
745 static __attribute__((nonnull))
746 void
747 row_undo_mod_sec_flag_corrupted(
748 /*============================*/
749  trx_t* trx,
750  dict_index_t* index)
751 {
752  ut_ad(!dict_index_is_clust(index));
753 
754  switch (trx->dict_operation_lock_mode) {
755  case RW_S_LATCH:
756  /* Because row_undo() is holding an S-latch
757  on the data dictionary during normal rollback,
758  we can only mark the index corrupted in the
759  data dictionary cache. TODO: fix this somehow.*/
760  mutex_enter(&dict_sys->mutex);
761  dict_set_corrupted_index_cache_only(index, index->table);
762  mutex_exit(&dict_sys->mutex);
763  break;
764  default:
765  ut_ad(0);
766  /* fall through */
767  case RW_X_LATCH:
768  /* This should be the rollback of a data dictionary
769  transaction. */
770  dict_set_corrupted(index, trx, "rollback");
771  }
772 }
773 
774 /***********************************************************/
777 static __attribute__((nonnull, warn_unused_result))
778 dberr_t
779 row_undo_mod_upd_del_sec(
780 /*=====================*/
781  undo_node_t* node,
782  que_thr_t* thr)
783 {
784  mem_heap_t* heap;
785  dberr_t err = DB_SUCCESS;
786 
787  ut_ad(node->rec_type == TRX_UNDO_UPD_DEL_REC);
788  ut_ad(!node->undo_row);
789 
790  heap = mem_heap_create(1024);
791 
792  while (node->index != NULL) {
793  dict_index_t* index = node->index;
794  dtuple_t* entry;
795 
796  if (index->type & DICT_FTS) {
797  dict_table_next_uncorrupted_index(node->index);
798  continue;
799  }
800 
801  /* During online index creation,
802  HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE should
803  guarantee that any active transaction has not modified
804  indexed columns such that col->ord_part was 0 at the
805  time when the undo log record was written. When we get
806  to roll back an undo log entry TRX_UNDO_DEL_MARK_REC,
807  it should always cover all affected indexes. */
808  entry = row_build_index_entry(
809  node->row, node->ext, index, heap);
810 
811  if (UNIV_UNLIKELY(!entry)) {
812  /* The database must have crashed after
813  inserting a clustered index record but before
814  writing all the externally stored columns of
815  that record. Because secondary index entries
816  are inserted after the clustered index record,
817  we may assume that the secondary index record
818  does not exist. However, this situation may
819  only occur during the rollback of incomplete
820  transactions. */
821  ut_a(thr_is_recv(thr));
822  } else {
823  err = row_undo_mod_del_mark_or_remove_sec(
824  node, thr, index, entry);
825 
826  if (UNIV_UNLIKELY(err != DB_SUCCESS)) {
827 
828  break;
829  }
830  }
831 
832  mem_heap_empty(heap);
833  dict_table_next_uncorrupted_index(node->index);
834  }
835 
836  mem_heap_free(heap);
837 
838  return(err);
839 }
840 
841 /***********************************************************/
844 static __attribute__((nonnull, warn_unused_result))
845 dberr_t
846 row_undo_mod_del_mark_sec(
847 /*======================*/
848  undo_node_t* node,
849  que_thr_t* thr)
850 {
851  mem_heap_t* heap;
852  dberr_t err = DB_SUCCESS;
853 
854  ut_ad(!node->undo_row);
855 
856  heap = mem_heap_create(1024);
857 
858  while (node->index != NULL) {
859  dict_index_t* index = node->index;
860  dtuple_t* entry;
861 
862  if (index->type == DICT_FTS) {
863  dict_table_next_uncorrupted_index(node->index);
864  continue;
865  }
866 
867  /* During online index creation,
868  HA_ALTER_INPLACE_NO_LOCK_AFTER_PREPARE should
869  guarantee that any active transaction has not modified
870  indexed columns such that col->ord_part was 0 at the
871  time when the undo log record was written. When we get
872  to roll back an undo log entry TRX_UNDO_DEL_MARK_REC,
873  it should always cover all affected indexes. */
874  entry = row_build_index_entry(
875  node->row, node->ext, index, heap);
876 
877  ut_a(entry);
878 
879  err = row_undo_mod_del_unmark_sec_and_undo_update(
880  BTR_MODIFY_LEAF, thr, index, entry);
881  if (err == DB_FAIL) {
882  err = row_undo_mod_del_unmark_sec_and_undo_update(
883  BTR_MODIFY_TREE, thr, index, entry);
884  }
885 
886  if (err == DB_DUPLICATE_KEY) {
887  row_undo_mod_sec_flag_corrupted(
888  thr_get_trx(thr), index);
889  err = DB_SUCCESS;
890  /* Do not return any error to the caller. The
891  duplicate will be reported by ALTER TABLE or
892  CREATE UNIQUE INDEX. Unfortunately we cannot
893  report the duplicate key value to the DDL
894  thread, because the altered_table object is
895  private to its call stack. */
896  } else if (err != DB_SUCCESS) {
897  break;
898  }
899 
900  mem_heap_empty(heap);
901  dict_table_next_uncorrupted_index(node->index);
902  }
903 
904  mem_heap_free(heap);
905 
906  return(err);
907 }
908 
909 /***********************************************************/
912 static __attribute__((nonnull, warn_unused_result))
913 dberr_t
914 row_undo_mod_upd_exist_sec(
915 /*=======================*/
916  undo_node_t* node,
917  que_thr_t* thr)
918 {
919  mem_heap_t* heap;
920  dberr_t err = DB_SUCCESS;
921 
922  if (node->index == NULL
923  || ((node->cmpl_info & UPD_NODE_NO_ORD_CHANGE))) {
924  /* No change in secondary indexes */
925 
926  return(err);
927  }
928 
929  heap = mem_heap_create(1024);
930 
931  while (node->index != NULL) {
932  dict_index_t* index = node->index;
933  dtuple_t* entry;
934 
935  if (index->type == DICT_FTS
936  || !row_upd_changes_ord_field_binary(
937  index, node->update, thr, node->row, node->ext)) {
938  dict_table_next_uncorrupted_index(node->index);
939  continue;
940  }
941 
942  /* Build the newest version of the index entry */
943  entry = row_build_index_entry(node->row, node->ext,
944  index, heap);
945  if (UNIV_UNLIKELY(!entry)) {
946  /* The server must have crashed in
947  row_upd_clust_rec_by_insert() before
948  the updated externally stored columns (BLOBs)
949  of the new clustered index entry were written. */
950 
951  /* The table must be in DYNAMIC or COMPRESSED
952  format. REDUNDANT and COMPACT formats
953  store a local 768-byte prefix of each
954  externally stored column. */
956  >= UNIV_FORMAT_B);
957 
958  /* This is only legitimate when
959  rolling back an incomplete transaction
960  after crash recovery. */
961  ut_a(thr_get_trx(thr)->is_recovered);
962 
963  /* The server must have crashed before
964  completing the insert of the new
965  clustered index entry and before
966  inserting to the secondary indexes.
967  Because node->row was not yet written
968  to this index, we can ignore it. But
969  we must restore node->undo_row. */
970  } else {
971  /* NOTE that if we updated the fields of a
972  delete-marked secondary index record so that
973  alphabetically they stayed the same, e.g.,
974  'abc' -> 'aBc', we cannot return to the
975  original values because we do not know them.
976  But this should not cause problems because
977  in row0sel.cc, in queries we always retrieve
978  the clustered index record or an earlier
979  version of it, if the secondary index record
980  through which we do the search is
981  delete-marked. */
982 
983  err = row_undo_mod_del_mark_or_remove_sec(
984  node, thr, index, entry);
985  if (err != DB_SUCCESS) {
986  break;
987  }
988  }
989 
990  mem_heap_empty(heap);
991  /* We may have to update the delete mark in the
992  secondary index record of the previous version of
993  the row. We also need to update the fields of
994  the secondary index record if we updated its fields
995  but alphabetically they stayed the same, e.g.,
996  'abc' -> 'aBc'. */
997  entry = row_build_index_entry(node->undo_row,
998  node->undo_ext,
999  index, heap);
1000  ut_a(entry);
1001 
1002  err = row_undo_mod_del_unmark_sec_and_undo_update(
1003  BTR_MODIFY_LEAF, thr, index, entry);
1004  if (err == DB_FAIL) {
1005  err = row_undo_mod_del_unmark_sec_and_undo_update(
1006  BTR_MODIFY_TREE, thr, index, entry);
1007  }
1008 
1009  if (err == DB_DUPLICATE_KEY) {
1010  row_undo_mod_sec_flag_corrupted(
1011  thr_get_trx(thr), index);
1012  err = DB_SUCCESS;
1013  } else if (err != DB_SUCCESS) {
1014  break;
1015  }
1016 
1017  mem_heap_empty(heap);
1018  dict_table_next_uncorrupted_index(node->index);
1019  }
1020 
1021  mem_heap_free(heap);
1022 
1023  return(err);
1024 }
1025 
1026 /***********************************************************/
1028 static __attribute__((nonnull))
1029 void
1030 row_undo_mod_parse_undo_rec(
1031 /*========================*/
1032  undo_node_t* node,
1033  ibool dict_locked)
1034 {
1035  dict_index_t* clust_index;
1036  byte* ptr;
1037  undo_no_t undo_no;
1038  table_id_t table_id;
1039  trx_id_t trx_id;
1040  roll_ptr_t roll_ptr;
1041  ulint info_bits;
1042  ulint type;
1043  ulint cmpl_info;
1044  bool dummy_extern;
1045 
1046  ptr = trx_undo_rec_get_pars(node->undo_rec, &type, &cmpl_info,
1047  &dummy_extern, &undo_no, &table_id);
1048  node->rec_type = type;
1049 
1050  node->table = dict_table_open_on_id(
1051  table_id, dict_locked, DICT_TABLE_OP_NORMAL);
1052 
1053  /* TODO: other fixes associated with DROP TABLE + rollback in the
1054  same table by another user */
1055 
1056  if (node->table == NULL) {
1057  /* Table was dropped */
1058  return;
1059  }
1060 
1061  if (node->table->ibd_file_missing) {
1062  dict_table_close(node->table, dict_locked, FALSE);
1063 
1064  /* We skip undo operations to missing .ibd files */
1065  node->table = NULL;
1066 
1067  return;
1068  }
1069 
1070  clust_index = dict_table_get_first_index(node->table);
1071 
1072  ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
1073  &info_bits);
1074 
1075  ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
1076  node->heap);
1077 
1078  trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
1079  roll_ptr, info_bits, node->trx,
1080  node->heap, &(node->update));
1081  node->new_trx_id = trx_id;
1082  node->cmpl_info = cmpl_info;
1083 
1084  if (!row_undo_search_clust_to_pcur(node)) {
1085 
1086  dict_table_close(node->table, dict_locked, FALSE);
1087 
1088  node->table = NULL;
1089  }
1090 }
1091 
1092 /***********************************************************/
1095 UNIV_INTERN
1096 dberr_t
1097 row_undo_mod(
1098 /*=========*/
1099  undo_node_t* node,
1100  que_thr_t* thr)
1101 {
1102  dberr_t err;
1103  ibool dict_locked;
1104 
1105  ut_ad(node && thr);
1106  ut_ad(node->state == UNDO_NODE_MODIFY);
1107 
1108  dict_locked = thr_get_trx(thr)->dict_operation_lock_mode == RW_X_LATCH;
1109 
1110  ut_ad(thr_get_trx(thr) == node->trx);
1111 
1112  row_undo_mod_parse_undo_rec(node, dict_locked);
1113 
1114  if (node->table == NULL) {
1115  /* It is already undone, or will be undone by another query
1116  thread, or table was dropped */
1117 
1118  trx_undo_rec_release(node->trx, node->undo_no);
1119  node->state = UNDO_NODE_FETCH_NEXT;
1120 
1121  return(DB_SUCCESS);
1122  }
1123 
1124  node->index = dict_table_get_first_index(node->table);
1126  /* Skip the clustered index (the first index) */
1127  node->index = dict_table_get_next_index(node->index);
1128 
1129  /* Skip all corrupted secondary index */
1130  dict_table_skip_corrupt_index(node->index);
1131 
1132  switch (node->rec_type) {
1133  case TRX_UNDO_UPD_EXIST_REC:
1134  err = row_undo_mod_upd_exist_sec(node, thr);
1135  break;
1136  case TRX_UNDO_DEL_MARK_REC:
1137  err = row_undo_mod_del_mark_sec(node, thr);
1138  break;
1139  case TRX_UNDO_UPD_DEL_REC:
1140  err = row_undo_mod_upd_del_sec(node, thr);
1141  break;
1142  default:
1143  ut_error;
1144  err = DB_ERROR;
1145  }
1146 
1147  if (err == DB_SUCCESS) {
1148 
1149  err = row_undo_mod_clust(node, thr);
1150  }
1151 
1152  dict_table_close(node->table, dict_locked, FALSE);
1153 
1154  node->table = NULL;
1155 
1156  return(err);
1157 }