MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
row0purge.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 "row0purge.h"
27 
28 #ifdef UNIV_NONINL
29 #include "row0purge.ic"
30 #endif
31 
32 #include "fsp0fsp.h"
33 #include "mach0data.h"
34 #include "trx0rseg.h"
35 #include "trx0trx.h"
36 #include "trx0roll.h"
37 #include "trx0undo.h"
38 #include "trx0purge.h"
39 #include "trx0rec.h"
40 #include "que0que.h"
41 #include "row0row.h"
42 #include "row0upd.h"
43 #include "row0vers.h"
44 #include "row0mysql.h"
45 #include "row0log.h"
46 #include "log0log.h"
47 #include "srv0mon.h"
48 #include "srv0start.h"
49 
50 /*************************************************************************
51 IMPORTANT NOTE: Any operation that generates redo MUST check that there
52 is enough space in the redo log before for that operation. This is
53 done by calling log_free_check(). The reason for checking the
54 availability of the redo log space before the start of the operation is
55 that we MUST not hold any synchonization objects when performing the
56 check.
57 If you make a change in this module make sure that no codepath is
58 introduced where a call to log_free_check() is bypassed. */
59 
60 /********************************************************************/
63 UNIV_INTERN
66 /*==================*/
67  que_thr_t* parent,
68  mem_heap_t* heap)
69 {
70  purge_node_t* node;
71 
72  ut_ad(parent && heap);
73 
74  node = static_cast<purge_node_t*>(
75  mem_heap_zalloc(heap, sizeof(*node)));
76 
77  node->common.type = QUE_NODE_PURGE;
78  node->common.parent = parent;
79  node->done = TRUE;
80  node->heap = mem_heap_create(256);
81 
82  return(node);
83 }
84 
85 /***********************************************************/
89 static
90 ibool
91 row_purge_reposition_pcur(
92 /*======================*/
93  ulint mode,
94  purge_node_t* node,
95  mtr_t* mtr)
96 {
97  if (node->found_clust) {
98  ibool found;
99 
100  found = btr_pcur_restore_position(mode, &node->pcur, mtr);
101 
102  return(found);
103  } else {
104  node->found_clust = row_search_on_row_ref(
105  &node->pcur, mode, node->table, node->ref, mtr);
106 
107  if (node->found_clust) {
108  btr_pcur_store_position(&node->pcur, mtr);
109  }
110  }
111 
112  return(node->found_clust);
113 }
114 
115 /***********************************************************/
119 static __attribute__((nonnull, warn_unused_result))
120 bool
121 row_purge_remove_clust_if_poss_low(
122 /*===============================*/
123  purge_node_t* node,
124  ulint mode)
125 {
127  bool success = true;
128  mtr_t mtr;
129  rec_t* rec;
130  mem_heap_t* heap = NULL;
131  ulint* offsets;
132  ulint offsets_[REC_OFFS_NORMAL_SIZE];
133  rec_offs_init(offsets_);
134 
135 #ifdef UNIV_SYNC_DEBUG
136  ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED));
137 #endif /* UNIV_SYNC_DEBUG */
138 
139  index = dict_table_get_first_index(node->table);
140 
141  log_free_check();
142  mtr_start(&mtr);
143 
144  if (!row_purge_reposition_pcur(mode, node, &mtr)) {
145  /* The record was already removed. */
146  goto func_exit;
147  }
148 
149  rec = btr_pcur_get_rec(&node->pcur);
150 
151  offsets = rec_get_offsets(
152  rec, index, offsets_, ULINT_UNDEFINED, &heap);
153 
154  if (node->roll_ptr != row_get_rec_roll_ptr(rec, index, offsets)) {
155  /* Someone else has modified the record later: do not remove */
156  goto func_exit;
157  }
158 
159  if (mode == BTR_MODIFY_LEAF) {
160  success = btr_cur_optimistic_delete(
161  btr_pcur_get_btr_cur(&node->pcur), 0, &mtr);
162  } else {
163  dberr_t err;
164  ut_ad(mode == BTR_MODIFY_TREE);
166  &err, FALSE, btr_pcur_get_btr_cur(&node->pcur), 0,
167  RB_NONE, &mtr);
168 
169  switch (err) {
170  case DB_SUCCESS:
171  break;
172  case DB_OUT_OF_FILE_SPACE:
173  success = false;
174  break;
175  default:
176  ut_error;
177  }
178  }
179 
180 func_exit:
181  if (heap) {
182  mem_heap_free(heap);
183  }
184 
185  btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
186 
187  return(success);
188 }
189 
190 /***********************************************************/
196 static __attribute__((nonnull, warn_unused_result))
197 bool
198 row_purge_remove_clust_if_poss(
199 /*===========================*/
200  purge_node_t* node)
201 {
202  if (row_purge_remove_clust_if_poss_low(node, BTR_MODIFY_LEAF)) {
203  return(true);
204  }
205 
206  for (ulint n_tries = 0;
208  n_tries++) {
209  if (row_purge_remove_clust_if_poss_low(
210  node, BTR_MODIFY_TREE)) {
211  return(true);
212  }
213 
215  }
216 
217  return(false);
218 }
219 
220 /***********************************************************/
235 UNIV_INTERN
236 bool
238 /*===============*/
239  purge_node_t* node,
240  dict_index_t* index,
241  const dtuple_t* entry)
242 {
243  bool can_delete;
244  mtr_t mtr;
245 
246  ut_ad(!dict_index_is_clust(index));
247  mtr_start(&mtr);
248 
249  can_delete = !row_purge_reposition_pcur(BTR_SEARCH_LEAF, node, &mtr)
251  btr_pcur_get_rec(&node->pcur),
252  &mtr, index, entry);
253 
254  btr_pcur_commit_specify_mtr(&node->pcur, &mtr);
255 
256  return(can_delete);
257 }
258 
259 /***************************************************************
260 Removes a secondary index entry if possible, by modifying the
261 index tree. Does not try to buffer the delete.
262 @return TRUE if success or if not found */
263 static __attribute__((nonnull, warn_unused_result))
264 ibool
265 row_purge_remove_sec_if_poss_tree(
266 /*==============================*/
267  purge_node_t* node,
268  dict_index_t* index,
269  const dtuple_t* entry)
270 {
272  btr_cur_t* btr_cur;
273  ibool success = TRUE;
274  dberr_t err;
275  mtr_t mtr;
276  enum row_search_result search_result;
277 
278  log_free_check();
279  mtr_start(&mtr);
280 
281  if (*index->name == TEMP_INDEX_PREFIX) {
282  /* The index->online_status may change if the
283  index->name starts with TEMP_INDEX_PREFIX (meaning
284  that the index is or was being created online). It is
285  protected by index->lock. */
286  mtr_x_lock(dict_index_get_lock(index), &mtr);
287 
288  if (dict_index_is_online_ddl(index)) {
289  /* Online secondary index creation will not
290  copy any delete-marked records. Therefore
291  there is nothing to be purged. We must also
292  skip the purge when a completed index is
293  dropped by rollback_inplace_alter_table(). */
294  goto func_exit_no_pcur;
295  }
296  } else {
297  /* For secondary indexes,
298  index->online_status==ONLINE_INDEX_CREATION unless
299  index->name starts with TEMP_INDEX_PREFIX. */
301  }
302 
303  search_result = row_search_index_entry(index, entry, BTR_MODIFY_TREE,
304  &pcur, &mtr);
305 
306  switch (search_result) {
307  case ROW_NOT_FOUND:
308  /* Not found. This is a legitimate condition. In a
309  rollback, InnoDB will remove secondary recs that would
310  be purged anyway. Then the actual purge will not find
311  the secondary index record. Also, the purge itself is
312  eager: if it comes to consider a secondary index
313  record, and notices it does not need to exist in the
314  index, it will remove it. Then if/when the purge
315  comes to consider the secondary index record a second
316  time, it will not exist any more in the index. */
317 
318  /* fputs("PURGE:........sec entry not found\n", stderr); */
319  /* dtuple_print(stderr, entry); */
320  goto func_exit;
321  case ROW_FOUND:
322  break;
323  case ROW_BUFFERED:
324  case ROW_NOT_DELETED_REF:
325  /* These are invalid outcomes, because the mode passed
326  to row_search_index_entry() did not include any of the
327  flags BTR_INSERT, BTR_DELETE, or BTR_DELETE_MARK. */
328  ut_error;
329  }
330 
331  btr_cur = btr_pcur_get_btr_cur(&pcur);
332 
333  /* We should remove the index record if no later version of the row,
334  which cannot be purged yet, requires its existence. If some requires,
335  we should do nothing. */
336 
337  if (row_purge_poss_sec(node, index, entry)) {
338  /* Remove the index record, which should have been
339  marked for deletion. */
340  ut_ad(REC_INFO_DELETED_FLAG
341  & rec_get_info_bits(btr_cur_get_rec(btr_cur),
342  dict_table_is_comp(index->table)));
343 
344  btr_cur_pessimistic_delete(&err, FALSE, btr_cur, 0,
345  RB_NONE, &mtr);
346  switch (UNIV_EXPECT(err, DB_SUCCESS)) {
347  case DB_SUCCESS:
348  break;
349  case DB_OUT_OF_FILE_SPACE:
350  success = FALSE;
351  break;
352  default:
353  ut_error;
354  }
355  }
356 
357 func_exit:
358  btr_pcur_close(&pcur);
359 func_exit_no_pcur:
360  mtr_commit(&mtr);
361 
362  return(success);
363 }
364 
365 /***************************************************************
366 Removes a secondary index entry without modifying the index tree,
367 if possible.
368 @retval true if success or if not found
369 @retval false if row_purge_remove_sec_if_poss_tree() should be invoked */
370 static __attribute__((nonnull, warn_unused_result))
371 bool
372 row_purge_remove_sec_if_poss_leaf(
373 /*==============================*/
374  purge_node_t* node,
375  dict_index_t* index,
376  const dtuple_t* entry)
377 {
378  mtr_t mtr;
380  ulint mode;
381  enum row_search_result search_result;
382  bool success = true;
383 
384  log_free_check();
385 
386  mtr_start(&mtr);
387 
388  if (*index->name == TEMP_INDEX_PREFIX) {
389  /* The index->online_status may change if the
390  index->name starts with TEMP_INDEX_PREFIX (meaning
391  that the index is or was being created online). It is
392  protected by index->lock. */
393  mtr_s_lock(dict_index_get_lock(index), &mtr);
394 
395  if (dict_index_is_online_ddl(index)) {
396  /* Online secondary index creation will not
397  copy any delete-marked records. Therefore
398  there is nothing to be purged. We must also
399  skip the purge when a completed index is
400  dropped by rollback_inplace_alter_table(). */
401  goto func_exit_no_pcur;
402  }
403 
405  } else {
406  /* For secondary indexes,
407  index->online_status==ONLINE_INDEX_CREATION unless
408  index->name starts with TEMP_INDEX_PREFIX. */
410 
411  mode = BTR_MODIFY_LEAF | BTR_DELETE;
412  }
413 
414  /* Set the purge node for the call to row_purge_poss_sec(). */
415  pcur.btr_cur.purge_node = node;
416  /* Set the query thread, so that ibuf_insert_low() will be
417  able to invoke thd_get_trx(). */
418  pcur.btr_cur.thr = static_cast<que_thr_t*>(que_node_get_parent(node));
419 
420  search_result = row_search_index_entry(
421  index, entry, mode, &pcur, &mtr);
422 
423  switch (search_result) {
424  case ROW_FOUND:
425  /* Before attempting to purge a record, check
426  if it is safe to do so. */
427  if (row_purge_poss_sec(node, index, entry)) {
428  btr_cur_t* btr_cur = btr_pcur_get_btr_cur(&pcur);
429 
430  /* Only delete-marked records should be purged. */
431  ut_ad(REC_INFO_DELETED_FLAG
433  btr_cur_get_rec(btr_cur),
434  dict_table_is_comp(index->table)));
435 
436  if (!btr_cur_optimistic_delete(btr_cur, 0, &mtr)) {
437 
438  /* The index entry could not be deleted. */
439  success = false;
440  }
441  }
442  /* fall through (the index entry is still needed,
443  or the deletion succeeded) */
444  case ROW_NOT_DELETED_REF:
445  /* The index entry is still needed. */
446  case ROW_BUFFERED:
447  /* The deletion was buffered. */
448  case ROW_NOT_FOUND:
449  /* The index entry does not exist, nothing to do. */
450  btr_pcur_close(&pcur);
451  func_exit_no_pcur:
452  mtr_commit(&mtr);
453  return(success);
454  }
455 
456  ut_error;
457  return(FALSE);
458 }
459 
460 /***********************************************************/
462 UNIV_INLINE __attribute__((nonnull(1,2)))
463 void
464 row_purge_remove_sec_if_poss(
465 /*=========================*/
466  purge_node_t* node,
467  dict_index_t* index,
468  const dtuple_t* entry)
469 {
470  ibool success;
471  ulint n_tries = 0;
472 
473  /* fputs("Purge: Removing secondary record\n", stderr); */
474 
475  if (!entry) {
476  /* The node->row must have lacked some fields of this
477  index. This is possible when the undo log record was
478  written before this index was created. */
479  return;
480  }
481 
482  if (row_purge_remove_sec_if_poss_leaf(node, index, entry)) {
483 
484  return;
485  }
486 retry:
487  success = row_purge_remove_sec_if_poss_tree(node, index, entry);
488  /* The delete operation may fail if we have little
489  file space left: TODO: easiest to crash the database
490  and restart with more file space */
491 
492  if (!success && n_tries < BTR_CUR_RETRY_DELETE_N_TIMES) {
493 
494  n_tries++;
495 
497 
498  goto retry;
499  }
500 
501  ut_a(success);
502 }
503 
504 /***********************************************************/
509 static __attribute__((nonnull, warn_unused_result))
510 bool
511 row_purge_del_mark(
512 /*===============*/
513  purge_node_t* node)
514 {
515  mem_heap_t* heap;
516 
517  heap = mem_heap_create(1024);
518 
519  while (node->index != NULL) {
520  /* skip corrupted secondary index */
521  dict_table_skip_corrupt_index(node->index);
522 
523  if (!node->index) {
524  break;
525  }
526 
527  if (node->index->type != DICT_FTS) {
529  node->row, NULL, node->index, heap);
530  row_purge_remove_sec_if_poss(node, node->index, entry);
531  mem_heap_empty(heap);
532  }
533 
534  node->index = dict_table_get_next_index(node->index);
535  }
536 
537  mem_heap_free(heap);
538 
539  return(row_purge_remove_clust_if_poss(node));
540 }
541 
542 /***********************************************************/
545 static
546 void
547 row_purge_upd_exist_or_extern_func(
548 /*===============================*/
549 #ifdef UNIV_DEBUG
550  const que_thr_t*thr,
551 #endif /* UNIV_DEBUG */
552  purge_node_t* node,
553  trx_undo_rec_t* undo_rec)
554 {
555  mem_heap_t* heap;
556 
557 #ifdef UNIV_SYNC_DEBUG
558  ut_ad(rw_lock_own(&dict_operation_lock, RW_LOCK_SHARED));
559 #endif /* UNIV_SYNC_DEBUG */
560 
561  if (node->rec_type == TRX_UNDO_UPD_DEL_REC
562  || (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
563 
564  goto skip_secondaries;
565  }
566 
567  heap = mem_heap_create(1024);
568 
569  while (node->index != NULL) {
570  dict_table_skip_corrupt_index(node->index);
571 
572  if (!node->index) {
573  break;
574  }
575 
576  if (row_upd_changes_ord_field_binary(node->index, node->update,
577  thr, NULL, NULL)) {
578  /* Build the older version of the index entry */
580  node->row, NULL, node->index, heap);
581  row_purge_remove_sec_if_poss(node, node->index, entry);
582  mem_heap_empty(heap);
583  }
584 
585  node->index = dict_table_get_next_index(node->index);
586  }
587 
588  mem_heap_free(heap);
589 
590 skip_secondaries:
591  /* Free possible externally stored fields */
592  for (ulint i = 0; i < upd_get_n_fields(node->update); i++) {
593 
594  const upd_field_t* ufield
595  = upd_get_nth_field(node->update, i);
596 
597  if (dfield_is_ext(&ufield->new_val)) {
598  trx_rseg_t* rseg;
600  ulint internal_offset;
601  byte* data_field;
603  ibool is_insert;
604  ulint rseg_id;
605  ulint page_no;
606  ulint offset;
607  mtr_t mtr;
608 
609  /* We use the fact that new_val points to
610  undo_rec and get thus the offset of
611  dfield data inside the undo record. Then we
612  can calculate from node->roll_ptr the file
613  address of the new_val data */
614 
615  internal_offset
616  = ((const byte*)
617  dfield_get_data(&ufield->new_val))
618  - undo_rec;
619 
620  ut_a(internal_offset < UNIV_PAGE_SIZE);
621 
622  trx_undo_decode_roll_ptr(node->roll_ptr,
623  &is_insert, &rseg_id,
624  &page_no, &offset);
625 
626  rseg = trx_sys_get_nth_rseg(trx_sys, rseg_id);
627  ut_a(rseg != NULL);
628  ut_a(rseg->id == rseg_id);
629 
630  mtr_start(&mtr);
631 
632  /* We have to acquire an X-latch to the clustered
633  index tree */
634 
635  index = dict_table_get_first_index(node->table);
636  mtr_x_lock(dict_index_get_lock(index), &mtr);
637 
638  /* NOTE: we must also acquire an X-latch to the
639  root page of the tree. We will need it when we
640  free pages from the tree. If the tree is of height 1,
641  the tree X-latch does NOT protect the root page,
642  because it is also a leaf page. Since we will have a
643  latch on an undo log page, we would break the
644  latching order if we would only later latch the
645  root page of such a tree! */
646 
647  btr_root_get(index, &mtr);
648 
649  block = buf_page_get(
650  rseg->space, 0, page_no, RW_X_LATCH, &mtr);
651 
652  buf_block_dbg_add_level(block, SYNC_TRX_UNDO_PAGE);
653 
654  data_field = buf_block_get_frame(block)
655  + offset + internal_offset;
656 
657  ut_a(dfield_get_len(&ufield->new_val)
660  index,
661  data_field + dfield_get_len(&ufield->new_val)
663  NULL, NULL, NULL, 0, RB_NONE, &mtr);
664  mtr_commit(&mtr);
665  }
666  }
667 }
668 
669 #ifdef UNIV_DEBUG
670 # define row_purge_upd_exist_or_extern(thr,node,undo_rec) \
671  row_purge_upd_exist_or_extern_func(thr,node,undo_rec)
672 #else /* UNIV_DEBUG */
673 # define row_purge_upd_exist_or_extern(thr,node,undo_rec) \
674  row_purge_upd_exist_or_extern_func(node,undo_rec)
675 #endif /* UNIV_DEBUG */
676 
677 /***********************************************************/
680 static
681 bool
682 row_purge_parse_undo_rec(
683 /*=====================*/
684  purge_node_t* node,
685  trx_undo_rec_t* undo_rec,
686  bool* updated_extern,
688  que_thr_t* thr)
689 {
690  dict_index_t* clust_index;
691  byte* ptr;
692  trx_t* trx;
693  undo_no_t undo_no;
694  table_id_t table_id;
695  trx_id_t trx_id;
696  roll_ptr_t roll_ptr;
697  ulint info_bits;
698  ulint type;
699 
700  ut_ad(node && thr);
701 
702  ptr = trx_undo_rec_get_pars(
703  undo_rec, &type, &node->cmpl_info,
704  updated_extern, &undo_no, &table_id);
705 
706  node->rec_type = type;
707 
708  if (type == TRX_UNDO_UPD_DEL_REC && !*updated_extern) {
709 
710  return(false);
711  }
712 
713  ptr = trx_undo_update_rec_get_sys_cols(ptr, &trx_id, &roll_ptr,
714  &info_bits);
715  node->table = NULL;
716 
717  /* Prevent DROP TABLE etc. from running when we are doing the purge
718  for this row */
719 
720  rw_lock_s_lock_inline(&dict_operation_lock, 0, __FILE__, __LINE__);
721 
723  table_id, FALSE, DICT_TABLE_OP_NORMAL);
724 
725  if (node->table == NULL) {
726  /* The table has been dropped: no need to do purge */
727  goto err_exit;
728  }
729 
730  if (node->table->ibd_file_missing) {
731  /* We skip purge of missing .ibd files */
732 
733  dict_table_close(node->table, FALSE, FALSE);
734 
735  node->table = NULL;
736 
737  goto err_exit;
738  }
739 
740  clust_index = dict_table_get_first_index(node->table);
741 
742  if (clust_index == NULL) {
743  /* The table was corrupt in the data dictionary.
744  dict_set_corrupted() works on an index, and
745  we do not have an index to call it with. */
746 close_exit:
747  dict_table_close(node->table, FALSE, FALSE);
748 err_exit:
749  rw_lock_s_unlock(&dict_operation_lock);
750  return(false);
751  }
752 
753  if (type == TRX_UNDO_UPD_EXIST_REC
754  && (node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)
755  && !*updated_extern) {
756 
757  /* Purge requires no changes to indexes: we may return */
758  goto close_exit;
759  }
760 
761  ptr = trx_undo_rec_get_row_ref(ptr, clust_index, &(node->ref),
762  node->heap);
763 
764  trx = thr_get_trx(thr);
765 
766  ptr = trx_undo_update_rec_get_update(ptr, clust_index, type, trx_id,
767  roll_ptr, info_bits, trx,
768  node->heap, &(node->update));
769 
770  /* Read to the partial row the fields that occur in indexes */
771 
772  if (!(node->cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
774  ptr, clust_index, &node->row,
775  type == TRX_UNDO_UPD_DEL_REC,
776  node->heap);
777  }
778 
779  return(true);
780 }
781 
782 /***********************************************************/
785 static __attribute__((nonnull, warn_unused_result))
786 bool
787 row_purge_record_func(
788 /*==================*/
789  purge_node_t* node,
790  trx_undo_rec_t* undo_rec,
791 #ifdef UNIV_DEBUG
792  const que_thr_t*thr,
793 #endif /* UNIV_DEBUG */
794  bool updated_extern)
796 {
797  dict_index_t* clust_index;
798  bool purged = true;
799 
800  clust_index = dict_table_get_first_index(node->table);
801 
802  node->index = dict_table_get_next_index(clust_index);
803 
804  switch (node->rec_type) {
805  case TRX_UNDO_DEL_MARK_REC:
806  purged = row_purge_del_mark(node);
807  if (!purged) {
808  break;
809  }
810  MONITOR_INC(MONITOR_N_DEL_ROW_PURGE);
811  break;
812  default:
813  if (!updated_extern) {
814  break;
815  }
816  /* fall through */
817  case TRX_UNDO_UPD_EXIST_REC:
818  row_purge_upd_exist_or_extern(thr, node, undo_rec);
819  MONITOR_INC(MONITOR_N_UPD_EXIST_EXTERN);
820  break;
821  }
822 
823  if (node->found_clust) {
824  btr_pcur_close(&node->pcur);
825  node->found_clust = FALSE;
826  }
827 
828  if (node->table != NULL) {
829  dict_table_close(node->table, FALSE, FALSE);
830  node->table = NULL;
831  }
832 
833  return(purged);
834 }
835 
836 #ifdef UNIV_DEBUG
837 # define row_purge_record(node,undo_rec,thr,updated_extern) \
838  row_purge_record_func(node,undo_rec,thr,updated_extern)
839 #else /* UNIV_DEBUG */
840 # define row_purge_record(node,undo_rec,thr,updated_extern) \
841  row_purge_record_func(node,undo_rec,updated_extern)
842 #endif /* UNIV_DEBUG */
843 
844 /***********************************************************/
848 static __attribute__((nonnull))
849 void
850 row_purge(
851 /*======*/
852  purge_node_t* node,
853  trx_undo_rec_t* undo_rec,
854  que_thr_t* thr)
855 {
856  if (undo_rec != &trx_purge_dummy_rec) {
857  bool updated_extern;
858 
859  while (row_purge_parse_undo_rec(
860  node, undo_rec, &updated_extern, thr)) {
861 
862  bool purged = row_purge_record(
863  node, undo_rec, thr, updated_extern);
864 
865  rw_lock_s_unlock(&dict_operation_lock);
866 
867  if (purged
869  return;
870  }
871 
872  /* Retry the purge in a second. */
873  os_thread_sleep(1000000);
874  }
875  }
876 }
877 
878 /***********************************************************/
880 UNIV_INLINE
881 void
882 row_purge_end(
883 /*==========*/
884  que_thr_t* thr)
885 {
886  purge_node_t* node;
887 
888  ut_ad(thr);
889 
890  node = static_cast<purge_node_t*>(thr->run_node);
891 
892  ut_ad(que_node_get_type(node) == QUE_NODE_PURGE);
893 
894  thr->run_node = que_node_get_parent(node);
895 
896  node->undo_recs = NULL;
897 
898  node->done = TRUE;
899 
900  ut_a(thr->run_node != NULL);
901 
902  mem_heap_empty(node->heap);
903 }
904 
905 /***********************************************************/
909 UNIV_INTERN
910 que_thr_t*
912 /*===========*/
913  que_thr_t* thr)
914 {
915  purge_node_t* node;
916 
917  ut_ad(thr);
918 
919  node = static_cast<purge_node_t*>(thr->run_node);
920 
921  node->table = NULL;
922  node->row = NULL;
923  node->ref = NULL;
924  node->index = NULL;
925  node->update = NULL;
926  node->found_clust = FALSE;
927  node->rec_type = ULINT_UNDEFINED;
928  node->cmpl_info = ULINT_UNDEFINED;
929 
930  ut_a(!node->done);
931 
932  ut_ad(que_node_get_type(node) == QUE_NODE_PURGE);
933 
934  if (!(node->undo_recs == NULL || ib_vector_is_empty(node->undo_recs))) {
935  trx_purge_rec_t*purge_rec;
936 
937  purge_rec = static_cast<trx_purge_rec_t*>(
938  ib_vector_pop(node->undo_recs));
939 
940  node->roll_ptr = purge_rec->roll_ptr;
941 
942  row_purge(node, purge_rec->undo_rec, thr);
943 
944  if (ib_vector_is_empty(node->undo_recs)) {
945  row_purge_end(thr);
946  } else {
947  thr->run_node = node;
948  }
949  } else {
950  row_purge_end(thr);
951  }
952 
953  return(thr);
954 }