Drizzled Public API Documentation

trx0purge.cc
1 /*****************************************************************************
2 
3 Copyright (c) 1996, 2011, Innobase Oy. 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., 51 Franklin
15 St, Fifth Floor, Boston, MA 02110-1301 USA
16 
17 *****************************************************************************/
18 
19 /**************************************************/
26 #include "trx0purge.h"
27 
28 #ifdef UNIV_NONINL
29 #include "trx0purge.ic"
30 #endif
31 
32 #include "fsp0fsp.h"
33 #include "mach0data.h"
34 #include "mtr0log.h"
35 #include "trx0rseg.h"
36 #include "trx0trx.h"
37 #include "trx0roll.h"
38 #include "read0read.h"
39 #include "fut0fut.h"
40 #include "que0que.h"
41 #include "row0purge.h"
42 #include "row0upd.h"
43 #include "trx0rec.h"
44 #include "srv0srv.h"
45 #include "os0thread.h"
46 
48 UNIV_INTERN trx_purge_t* purge_sys = NULL;
49 
53 
54 #ifdef UNIV_PFS_RWLOCK
55 /* Key to register trx_purge_latch with performance schema */
56 UNIV_INTERN mysql_pfs_key_t trx_purge_latch_key;
57 #endif /* UNIV_PFS_RWLOCK */
58 
59 #ifdef UNIV_PFS_MUTEX
60 /* Key to register purge_sys_bh_mutex with performance schema */
61 UNIV_INTERN mysql_pfs_key_t purge_sys_bh_mutex_key;
62 #endif /* UNIV_PFS_MUTEX */
63 
64 /*****************************************************************/
70 UNIV_INTERN
71 ibool
73 /*=============================*/
74  trx_id_t trx_id)
75 {
76 #ifdef UNIV_SYNC_DEBUG
77  ut_ad(rw_lock_own(&(purge_sys->latch), RW_LOCK_SHARED));
78 #endif /* UNIV_SYNC_DEBUG */
79 
80  if (!read_view_sees_trx_id(purge_sys->view, trx_id)) {
81 
82  return(TRUE);
83  }
84 
85  return(FALSE);
86 }
87 
88 /*=================== PURGE RECORD ARRAY =============================*/
89 
90 /*******************************************************************/
93 static
95 trx_purge_arr_store_info(
96 /*=====================*/
97  trx_id_t trx_no,
98  undo_no_t undo_no)
99 {
100  trx_undo_inf_t* cell;
101  trx_undo_arr_t* arr;
102  ulint i;
103 
104  arr = purge_sys->arr;
105 
106  for (i = 0;; i++) {
107  cell = trx_undo_arr_get_nth_info(arr, i);
108 
109  if (!(cell->in_use)) {
110  /* Not in use, we may store here */
111  cell->undo_no = undo_no;
112  cell->trx_no = trx_no;
113  cell->in_use = TRUE;
114 
115  arr->n_used++;
116 
117  return(cell);
118  }
119  }
120 }
121 
122 /*******************************************************************/
124 UNIV_INLINE
125 void
126 trx_purge_arr_remove_info(
127 /*======================*/
128  trx_undo_inf_t* cell)
129 {
130  trx_undo_arr_t* arr;
131 
132  arr = purge_sys->arr;
133 
134  cell->in_use = FALSE;
135 
136  ut_ad(arr->n_used > 0);
137 
138  arr->n_used--;
139 }
140 
141 /*******************************************************************/
143 static
144 void
145 trx_purge_arr_get_biggest(
146 /*======================*/
147  trx_undo_arr_t* arr,
148  trx_id_t* trx_no,
150  undo_no_t* undo_no)
151 {
152  trx_undo_inf_t* cell;
153  trx_id_t pair_trx_no;
154  undo_no_t pair_undo_no;
155  ulint i;
156  ulint n;
157 
158  n = arr->n_used;
159  pair_trx_no = 0;
160  pair_undo_no = 0;
161 
162  if (n) {
163  for (i = 0;; i++) {
164  cell = trx_undo_arr_get_nth_info(arr, i);
165 
166  if (!cell->in_use) {
167  continue;
168  }
169 
170  if ((cell->trx_no > pair_trx_no)
171  || ((cell->trx_no == pair_trx_no)
172  && cell->undo_no >= pair_undo_no)) {
173 
174  pair_trx_no = cell->trx_no;
175  pair_undo_no = cell->undo_no;
176  }
177 
178  if (!--n) {
179  break;
180  }
181  }
182  }
183 
184  *trx_no = pair_trx_no;
185  *undo_no = pair_undo_no;
186 }
187 
188 /****************************************************************/
192 static
193 que_t*
194 trx_purge_graph_build(void)
195 /*=======================*/
196 {
197  mem_heap_t* heap;
198  que_fork_t* fork;
199  que_thr_t* thr;
200  /* que_thr_t* thr2; */
201 
202  heap = mem_heap_create(512);
203  fork = que_fork_create(NULL, NULL, QUE_FORK_PURGE, heap);
204  fork->trx = purge_sys->trx;
205 
206  thr = que_thr_create(fork, heap);
207 
208  thr->child = row_purge_node_create(thr, heap);
209 
210  /* thr2 = que_thr_create(fork, fork, heap);
211 
212  thr2->child = row_purge_node_create(fork, thr2, heap); */
213 
214  return(fork);
215 }
216 
217 /********************************************************************/
220 UNIV_INTERN
221 void
223 /*=================*/
224  ib_bh_t* ib_bh)
225 {
226  ut_ad(mutex_own(&kernel_mutex));
227 
229 
230  /* Take ownership of ib_bh, we are responsible for freeing it. */
231  purge_sys->ib_bh = ib_bh;
232  purge_sys->state = TRX_STOP_PURGE;
233 
235 
236  purge_sys->purge_trx_no = 0;
238  purge_sys->next_stored = FALSE;
239 
240  rw_lock_create(trx_purge_latch_key,
241  &purge_sys->latch, SYNC_PURGE_LATCH);
242 
243  mutex_create(
244  purge_sys_bh_mutex_key, &purge_sys->bh_mutex,
245  SYNC_PURGE_QUEUE);
246 
248 
250 
251  purge_sys->sess = sess_open();
252 
254 
255  purge_sys->trx->is_purge = 1;
256 
257  ut_a(trx_start_low(purge_sys->trx, ULINT_UNDEFINED));
258 
259  purge_sys->query = trx_purge_graph_build();
260 
262  purge_sys->heap);
263 }
264 
265 /************************************************************************
266 Frees the global purge system control structure. */
267 UNIV_INTERN
268 void
270 /*======================*/
271 {
272  ut_ad(!mutex_own(&kernel_mutex));
273 
275 
277  purge_sys->sess->trx->conc_state = TRX_NOT_STARTED;
279  purge_sys->sess = NULL;
280 
281  if (purge_sys->view != NULL) {
282  /* Because acquiring the kernel mutex is a pre-condition
283  of read_view_close(). We don't really need it here. */
284  mutex_enter(&kernel_mutex);
285 
287  purge_sys->view = NULL;
288 
289  mutex_exit(&kernel_mutex);
290  }
291 
293 
294  rw_lock_free(&purge_sys->latch);
295  mutex_free(&purge_sys->bh_mutex);
296 
298 
300 
302 
303  purge_sys = NULL;
304 }
305 
306 /*================ UNDO LOG HISTORY LIST =============================*/
307 
308 /********************************************************************/
311 UNIV_INTERN
312 void
314 /*=================================*/
315  trx_t* trx,
316  page_t* undo_page,
318  mtr_t* mtr)
319 {
320  trx_undo_t* undo;
321  trx_rsegf_t* rseg_header;
322  trx_ulogf_t* undo_header;
323 
324  undo = trx->update_undo;
325 
326  ut_ad(undo);
327 
328  ut_ad(mutex_own(&undo->rseg->mutex));
329 
330  rseg_header = trx_rsegf_get(
331  undo->rseg->space, undo->rseg->zip_size, undo->rseg->page_no,
332  mtr);
333 
334  undo_header = undo_page + undo->hdr_offset;
335  /* Add the log as the first in the history list */
336 
337  if (undo->state != TRX_UNDO_CACHED) {
338  ulint hist_size;
339 #ifdef UNIV_DEBUG
340  trx_usegf_t* seg_header = undo_page + TRX_UNDO_SEG_HDR;
341 #endif /* UNIV_DEBUG */
342 
343  /* The undo log segment will not be reused */
344 
345  if (UNIV_UNLIKELY(undo->id >= TRX_RSEG_N_SLOTS)) {
346  fprintf(stderr,
347  "InnoDB: Error: undo->id is %lu\n",
348  (ulong) undo->id);
349  ut_error;
350  }
351 
352  trx_rsegf_set_nth_undo(rseg_header, undo->id, FIL_NULL, mtr);
353 
354  hist_size = mtr_read_ulint(
355  rseg_header + TRX_RSEG_HISTORY_SIZE, MLOG_4BYTES, mtr);
356 
357  ut_ad(undo->size == flst_get_len(
358  seg_header + TRX_UNDO_PAGE_LIST, mtr));
359 
361  rseg_header + TRX_RSEG_HISTORY_SIZE,
362  hist_size + undo->size, MLOG_4BYTES, mtr);
363  }
364 
366  rseg_header + TRX_RSEG_HISTORY,
367  undo_header + TRX_UNDO_HISTORY_NODE, mtr);
368 
369  /* Write the trx number to the undo log header */
370 
371  mlog_write_ull(undo_header + TRX_UNDO_TRX_NO, trx->no, mtr);
372 
373  /* Write information about delete markings to the undo log header */
374 
375  if (!undo->del_marks) {
377  undo_header + TRX_UNDO_DEL_MARKS, FALSE,
378  MLOG_2BYTES, mtr);
379  }
380 
381  if (undo->rseg->last_page_no == FIL_NULL) {
382  undo->rseg->last_trx_no = trx->no;
383  undo->rseg->last_offset = undo->hdr_offset;
384  undo->rseg->last_page_no = undo->hdr_page_no;
385  undo->rseg->last_del_marks = undo->del_marks;
386 
387  /* FIXME: Add a bin heap validate function to check that
388  the rseg exists. */
389  }
390 
391  mutex_enter(&kernel_mutex);
393  mutex_exit(&kernel_mutex);
394 
395  if (!(trx_sys->rseg_history_len % srv_purge_batch_size)) {
396  /* Inform the purge thread that there is work to do. */
398  }
399 }
400 
401 /**********************************************************************/
404 static
405 void
406 trx_purge_free_segment(
407 /*===================*/
408  trx_rseg_t* rseg,
409  fil_addr_t hdr_addr,
410  ulint n_removed_logs)
413 {
414  page_t* undo_page;
415  trx_rsegf_t* rseg_hdr;
416  trx_ulogf_t* log_hdr;
417  trx_usegf_t* seg_hdr;
418  ibool freed;
419  ulint seg_size;
420  ulint hist_size;
421  ibool marked = FALSE;
422  mtr_t mtr;
423 
424  /* fputs("Freeing an update undo log segment\n", stderr); */
425 
426 loop:
427  mtr_start(&mtr);
428  mutex_enter(&(rseg->mutex));
429 
430  rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
431  rseg->page_no, &mtr);
432 
433  undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
434  hdr_addr.page, &mtr);
435  seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
436  log_hdr = undo_page + hdr_addr.boffset;
437 
438  /* Mark the last undo log totally purged, so that if the system
439  crashes, the tail of the undo log will not get accessed again. The
440  list of pages in the undo log tail gets inconsistent during the
441  freeing of the segment, and therefore purge should not try to access
442  them again. */
443 
444  if (!marked) {
445  mlog_write_ulint(log_hdr + TRX_UNDO_DEL_MARKS, FALSE,
446  MLOG_2BYTES, &mtr);
447  marked = TRUE;
448  }
449 
451  &mtr);
452  if (!freed) {
453  mutex_exit(&(rseg->mutex));
454  mtr_commit(&mtr);
455 
456  goto loop;
457  }
458 
459  /* The page list may now be inconsistent, but the length field
460  stored in the list base node tells us how big it was before we
461  started the freeing. */
462 
463  seg_size = flst_get_len(seg_hdr + TRX_UNDO_PAGE_LIST, &mtr);
464 
465  /* We may free the undo log segment header page; it must be freed
466  within the same mtr as the undo log header is removed from the
467  history list: otherwise, in case of a database crash, the segment
468  could become inaccessible garbage in the file space. */
469 
470  flst_cut_end(rseg_hdr + TRX_RSEG_HISTORY,
471  log_hdr + TRX_UNDO_HISTORY_NODE, n_removed_logs, &mtr);
472 
473  mutex_enter(&kernel_mutex);
474  ut_ad(trx_sys->rseg_history_len >= n_removed_logs);
475  trx_sys->rseg_history_len -= n_removed_logs;
476  mutex_exit(&kernel_mutex);
477 
478  freed = FALSE;
479 
480  while (!freed) {
481  /* Here we assume that a file segment with just the header
482  page can be freed in a few steps, so that the buffer pool
483  is not flooded with bufferfixed pages: see the note in
484  fsp0fsp.c. */
485 
486  freed = fseg_free_step(seg_hdr + TRX_UNDO_FSEG_HEADER,
487  &mtr);
488  }
489 
490  hist_size = mtr_read_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
491  MLOG_4BYTES, &mtr);
492  ut_ad(hist_size >= seg_size);
493 
494  mlog_write_ulint(rseg_hdr + TRX_RSEG_HISTORY_SIZE,
495  hist_size - seg_size, MLOG_4BYTES, &mtr);
496 
497  ut_ad(rseg->curr_size >= seg_size);
498 
499  rseg->curr_size -= seg_size;
500 
501  mutex_exit(&(rseg->mutex));
502 
503  mtr_commit(&mtr);
504 }
505 
506 /********************************************************************/
508 static
509 void
510 trx_purge_truncate_rseg_history(
511 /*============================*/
512  trx_rseg_t* rseg,
513  trx_id_t limit_trx_no,
515  undo_no_t limit_undo_no)
518 {
519  fil_addr_t hdr_addr;
520  fil_addr_t prev_hdr_addr;
521  trx_rsegf_t* rseg_hdr;
522  page_t* undo_page;
523  trx_ulogf_t* log_hdr;
524  trx_usegf_t* seg_hdr;
525  ulint n_removed_logs = 0;
526  mtr_t mtr;
527  trx_id_t undo_trx_no;
528 
529  mtr_start(&mtr);
530  mutex_enter(&(rseg->mutex));
531 
532  rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
533  rseg->page_no, &mtr);
534 
535  hdr_addr = trx_purge_get_log_from_hist(
536  flst_get_last(rseg_hdr + TRX_RSEG_HISTORY, &mtr));
537 loop:
538  if (hdr_addr.page == FIL_NULL) {
539 
540  mutex_exit(&(rseg->mutex));
541 
542  mtr_commit(&mtr);
543 
544  return;
545  }
546 
547  undo_page = trx_undo_page_get(rseg->space, rseg->zip_size,
548  hdr_addr.page, &mtr);
549 
550  log_hdr = undo_page + hdr_addr.boffset;
551  undo_trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
552 
553  if (undo_trx_no >= limit_trx_no) {
554  if (undo_trx_no == limit_trx_no) {
555  trx_undo_truncate_start(rseg, rseg->space,
556  hdr_addr.page,
557  hdr_addr.boffset,
558  limit_undo_no);
559  }
560 
561  mutex_enter(&kernel_mutex);
562  ut_a(trx_sys->rseg_history_len >= n_removed_logs);
563  trx_sys->rseg_history_len -= n_removed_logs;
564  mutex_exit(&kernel_mutex);
565 
566  flst_truncate_end(rseg_hdr + TRX_RSEG_HISTORY,
567  log_hdr + TRX_UNDO_HISTORY_NODE,
568  n_removed_logs, &mtr);
569 
570  mutex_exit(&(rseg->mutex));
571  mtr_commit(&mtr);
572 
573  return;
574  }
575 
576  prev_hdr_addr = trx_purge_get_log_from_hist(
577  flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
578  n_removed_logs++;
579 
580  seg_hdr = undo_page + TRX_UNDO_SEG_HDR;
581 
582  if ((mach_read_from_2(seg_hdr + TRX_UNDO_STATE) == TRX_UNDO_TO_PURGE)
583  && (mach_read_from_2(log_hdr + TRX_UNDO_NEXT_LOG) == 0)) {
584 
585  /* We can free the whole log segment */
586 
587  mutex_exit(&(rseg->mutex));
588  mtr_commit(&mtr);
589 
590  trx_purge_free_segment(rseg, hdr_addr, n_removed_logs);
591 
592  n_removed_logs = 0;
593  } else {
594  mutex_exit(&(rseg->mutex));
595  mtr_commit(&mtr);
596  }
597 
598  mtr_start(&mtr);
599  mutex_enter(&(rseg->mutex));
600 
601  rseg_hdr = trx_rsegf_get(rseg->space, rseg->zip_size,
602  rseg->page_no, &mtr);
603 
604  hdr_addr = prev_hdr_addr;
605 
606  goto loop;
607 }
608 
609 /********************************************************************/
612 static
613 void
614 trx_purge_truncate_history(void)
615 /*============================*/
616 {
617  trx_rseg_t* rseg;
618  trx_id_t limit_trx_no;
619  undo_no_t limit_undo_no;
620 
621  trx_purge_arr_get_biggest(
622  purge_sys->arr, &limit_trx_no, &limit_undo_no);
623 
624  if (limit_trx_no == 0) {
625 
626  limit_trx_no = purge_sys->purge_trx_no;
627  limit_undo_no = purge_sys->purge_undo_no;
628  }
629 
630  /* We play safe and set the truncate limit at most to the purge view
631  low_limit number, though this is not necessary */
632 
633  if (limit_trx_no >= purge_sys->view->low_limit_no) {
634  limit_trx_no = purge_sys->view->low_limit_no;
635  limit_undo_no = 0;
636  }
637 
638  ut_ad(limit_trx_no <= purge_sys->view->low_limit_no);
639 
640  for (rseg = UT_LIST_GET_FIRST(trx_sys->rseg_list);
641  rseg != NULL;
642  rseg = UT_LIST_GET_NEXT(rseg_list, rseg)) {
643 
644  trx_purge_truncate_rseg_history(
645  rseg, limit_trx_no, limit_undo_no);
646  }
647 }
648 
649 /********************************************************************/
652 UNIV_INLINE
653 void
654 trx_purge_truncate_if_arr_empty(void)
655 /*=================================*/
656 {
657  static ulint count;
658 
659  if (!(++count % TRX_SYS_N_RSEGS) && purge_sys->arr->n_used == 0) {
660 
661  trx_purge_truncate_history();
662  }
663 }
664 
665 /***********************************************************************/
668 static
669 void
670 trx_purge_rseg_get_next_history_log(
671 /*================================*/
672  trx_rseg_t* rseg)
673 {
674  page_t* undo_page;
675  trx_ulogf_t* log_hdr;
676  fil_addr_t prev_log_addr;
677  trx_id_t trx_no;
678  ibool del_marks;
679  mtr_t mtr;
680  rseg_queue_t rseg_queue;
681  const void* ptr;
682 
683  mutex_enter(&(rseg->mutex));
684 
685  ut_a(rseg->last_page_no != FIL_NULL);
686 
687  purge_sys->purge_trx_no = rseg->last_trx_no + 1;
689  purge_sys->next_stored = FALSE;
690 
691  mtr_start(&mtr);
692 
693  undo_page = trx_undo_page_get_s_latched(
694  rseg->space, rseg->zip_size, rseg->last_page_no, &mtr);
695 
696  log_hdr = undo_page + rseg->last_offset;
697 
698  /* Increase the purge page count by one for every handled log */
699 
701 
702  prev_log_addr = trx_purge_get_log_from_hist(
703  flst_get_prev_addr(log_hdr + TRX_UNDO_HISTORY_NODE, &mtr));
704 
705  if (prev_log_addr.page == FIL_NULL) {
706  /* No logs left in the history list */
707 
708  rseg->last_page_no = FIL_NULL;
709 
710  mutex_exit(&(rseg->mutex));
711  mtr_commit(&mtr);
712 
713  mutex_enter(&kernel_mutex);
714 
715  /* Add debug code to track history list corruption reported
716  on the MySQL mailing list on Nov 9, 2004. The fut0lst.c
717  file-based list was corrupt. The prev node pointer was
718  FIL_NULL, even though the list length was over 8 million nodes!
719  We assume that purge truncates the history list in large
720  size pieces, and if we here reach the head of the list, the
721  list cannot be longer than 2000 000 undo logs now. */
722 
723  if (trx_sys->rseg_history_len > 2000000) {
724  ut_print_timestamp(stderr);
725  fprintf(stderr,
726  " InnoDB: Warning: purge reached the"
727  " head of the history list,\n"
728  "InnoDB: but its length is still"
729  " reported as %lu! Make a detailed bug\n"
730  "InnoDB: report, and submit it"
731  " to http://bugs.mysql.com\n",
732  (ulong) trx_sys->rseg_history_len);
733  }
734 
735  mutex_exit(&kernel_mutex);
736 
737  return;
738  }
739 
740  mutex_exit(&(rseg->mutex));
741  mtr_commit(&mtr);
742 
743  /* Read the trx number and del marks from the previous log header */
744  mtr_start(&mtr);
745 
746  log_hdr = trx_undo_page_get_s_latched(rseg->space, rseg->zip_size,
747  prev_log_addr.page, &mtr)
748  + prev_log_addr.boffset;
749 
750  trx_no = mach_read_from_8(log_hdr + TRX_UNDO_TRX_NO);
751 
752  del_marks = mach_read_from_2(log_hdr + TRX_UNDO_DEL_MARKS);
753 
754  mtr_commit(&mtr);
755 
756  mutex_enter(&(rseg->mutex));
757 
758  rseg->last_page_no = prev_log_addr.page;
759  rseg->last_offset = prev_log_addr.boffset;
760  rseg->last_trx_no = trx_no;
761  rseg->last_del_marks = del_marks;
762 
763  rseg_queue.rseg = rseg;
764  rseg_queue.trx_no = rseg->last_trx_no;
765 
766  /* Purge can also produce events, however these are already ordered
767  in the rollback segment and any user generated event will be greater
768  than the events that Purge produces. ie. Purge can never produce
769  events from an empty rollback segment. */
770 
771  mutex_enter(&purge_sys->bh_mutex);
772 
773  ptr = ib_bh_push(purge_sys->ib_bh, &rseg_queue);
774  ut_a(ptr != NULL);
775 
776  mutex_exit(&purge_sys->bh_mutex);
777 
778  mutex_exit(&(rseg->mutex));
779 }
780 
781 /***********************************************************************/
785 static
786 ulint
787 trx_purge_get_rseg_with_min_trx_id(
788 /*===============================*/
789  trx_purge_t* purge_sys_instance)
791 {
792  ulint zip_size = 0;
793 
794  mutex_enter(&purge_sys_instance->bh_mutex);
795 
796  /* Only purge consumes events from the binary heap, user
797  threads only produce the events. */
798 
799  if (!ib_bh_is_empty(purge_sys_instance->ib_bh)) {
800  trx_rseg_t* rseg;
801 
802  rseg = ((rseg_queue_t*) ib_bh_first(purge_sys_instance->ib_bh))->rseg;
803  ib_bh_pop(purge_sys_instance->ib_bh);
804 
805  mutex_exit(&purge_sys_instance->bh_mutex);
806 
807  purge_sys_instance->rseg = rseg;
808  } else {
809  mutex_exit(&purge_sys_instance->bh_mutex);
810 
811  purge_sys_instance->rseg = NULL;
812 
813  return(ULINT_UNDEFINED);
814  }
815 
816  ut_a(purge_sys_instance->rseg != NULL);
817 
818  mutex_enter(&purge_sys_instance->rseg->mutex);
819 
820  ut_a(purge_sys_instance->rseg->last_page_no != FIL_NULL);
821 
822  /* We assume in purge of externally stored fields
823  that space id == 0 */
824  ut_a(purge_sys_instance->rseg->space == 0);
825 
826  zip_size = purge_sys_instance->rseg->zip_size;
827 
828  ut_a(purge_sys_instance->purge_trx_no <= purge_sys_instance->rseg->last_trx_no);
829 
830  purge_sys_instance->purge_trx_no = purge_sys_instance->rseg->last_trx_no;
831 
832  purge_sys_instance->hdr_offset = purge_sys_instance->rseg->last_offset;
833 
834  purge_sys_instance->hdr_page_no = purge_sys_instance->rseg->last_page_no;
835 
836  mutex_exit(&purge_sys_instance->rseg->mutex);
837 
838  return(zip_size);
839 }
840 
841 /***********************************************************************/
843 static
844 void
845 trx_purge_read_undo_rec(
846 /*====================*/
847  trx_purge_t* purge_sys_instance,
848  ulint zip_size)
849 {
850  ulint page_no;
851  ulint offset = 0;
852  ib_uint64_t undo_no = 0;
853 
854  purge_sys_instance->hdr_offset = purge_sys_instance->rseg->last_offset;
855  page_no = purge_sys_instance->hdr_page_no = purge_sys_instance->rseg->last_page_no;
856 
857  if (purge_sys_instance->rseg->last_del_marks) {
858  mtr_t mtr;
859  trx_undo_rec_t* undo_rec;
860 
861  mtr_start(&mtr);
862 
863  undo_rec = trx_undo_get_first_rec(
864  0 /* System space id */, zip_size,
865  purge_sys_instance->hdr_page_no,
866  purge_sys_instance->hdr_offset, RW_S_LATCH, &mtr);
867 
868  if (undo_rec != NULL) {
869  offset = page_offset(undo_rec);
870  undo_no = trx_undo_rec_get_undo_no(undo_rec);
871  page_no = page_get_page_no(page_align(undo_rec));
872  }
873 
874  mtr_commit(&mtr);
875  }
876 
877  purge_sys_instance->offset = offset;
878  purge_sys_instance->page_no = page_no;
879  purge_sys_instance->purge_undo_no = undo_no;
880 
881  purge_sys_instance->next_stored = TRUE;
882 }
883 
884 /***********************************************************************/
889 static
890 void
891 trx_purge_choose_next_log(void)
892 /*===========================*/
893 {
894  ulint zip_size;
895 
896  ut_ad(purge_sys->next_stored == FALSE);
897 
898  zip_size = trx_purge_get_rseg_with_min_trx_id(purge_sys);
899 
900  if (purge_sys->rseg != NULL) {
901 
902  trx_purge_read_undo_rec(purge_sys, zip_size);
903  } else {
904  /* There is nothing to do yet. */
905  os_thread_yield();
906  }
907 }
908 
909 /***********************************************************************/
912 static
914 trx_purge_get_next_rec(
915 /*===================*/
916  mem_heap_t* heap)
917 {
918  trx_undo_rec_t* rec;
920  trx_undo_rec_t* rec2;
921  trx_undo_rec_t* next_rec;
922  page_t* undo_page;
923  page_t* page;
924  ulint offset;
925  ulint page_no;
926  ulint space;
927  ulint zip_size;
928  ulint type;
929  ulint cmpl_info;
930  mtr_t mtr;
931 
933 
934  space = purge_sys->rseg->space;
935  zip_size = purge_sys->rseg->zip_size;
936  page_no = purge_sys->page_no;
937  offset = purge_sys->offset;
938 
939  if (offset == 0) {
940  /* It is the dummy undo log record, which means that there is
941  no need to purge this undo log */
942 
943  trx_purge_rseg_get_next_history_log(purge_sys->rseg);
944 
945  /* Look for the next undo log and record to purge */
946 
947  trx_purge_choose_next_log();
948 
949  return(&trx_purge_dummy_rec);
950  }
951 
952  mtr_start(&mtr);
953 
954  undo_page = trx_undo_page_get_s_latched(space, zip_size, page_no, &mtr);
955 
956  rec = undo_page + offset;
957 
958  rec2 = rec;
959 
960  for (;;) {
961  /* Try first to find the next record which requires a purge
962  operation from the same page of the same undo log */
963 
964  next_rec = trx_undo_page_get_next_rec(
966 
967  if (next_rec == NULL) {
968  rec2 = trx_undo_get_next_rec(
969  rec2, purge_sys->hdr_page_no,
970  purge_sys->hdr_offset, &mtr);
971  break;
972  }
973 
974  rec2 = next_rec;
975 
976  type = trx_undo_rec_get_type(rec2);
977 
978  if (type == TRX_UNDO_DEL_MARK_REC) {
979 
980  break;
981  }
982 
983  cmpl_info = trx_undo_rec_get_cmpl_info(rec2);
984 
986  break;
987  }
988 
989  if ((type == TRX_UNDO_UPD_EXIST_REC)
990  && !(cmpl_info & UPD_NODE_NO_ORD_CHANGE)) {
991  break;
992  }
993  }
994 
995  if (rec2 == NULL) {
996  mtr_commit(&mtr);
997 
998  trx_purge_rseg_get_next_history_log(purge_sys->rseg);
999 
1000  /* Look for the next undo log and record to purge */
1001 
1002  trx_purge_choose_next_log();
1003 
1004  mtr_start(&mtr);
1005 
1006  undo_page = trx_undo_page_get_s_latched(space, zip_size,
1007  page_no, &mtr);
1008 
1009  rec = undo_page + offset;
1010  } else {
1011  page = page_align(rec2);
1012 
1015  purge_sys->offset = rec2 - page;
1016 
1017  if (undo_page != page) {
1018  /* We advance to a new page of the undo log: */
1020  }
1021  }
1022 
1023  rec_copy = trx_undo_rec_copy(rec, heap);
1024 
1025  mtr_commit(&mtr);
1026 
1027  return(rec_copy);
1028 }
1029 
1030 /********************************************************************/
1035 UNIV_INTERN
1038 /*=====================*/
1039  roll_ptr_t* roll_ptr,
1040  trx_undo_inf_t** cell,
1042  mem_heap_t* heap)
1043 {
1044  trx_undo_rec_t* undo_rec;
1045 
1046 
1047  if (purge_sys->state == TRX_STOP_PURGE) {
1048  trx_purge_truncate_if_arr_empty();
1049 
1050  return(NULL);
1051  } else if (!purge_sys->next_stored) {
1052  trx_purge_choose_next_log();
1053 
1054  if (!purge_sys->next_stored) {
1055  purge_sys->state = TRX_STOP_PURGE;
1056 
1057  trx_purge_truncate_if_arr_empty();
1058 
1059  if (srv_print_thread_releases) {
1060  fprintf(stderr,
1061  "Purge: No logs left in the"
1062  " history list; pages handled %lu\n",
1063  (ulong) purge_sys->n_pages_handled);
1064  }
1065 
1066  return(NULL);
1067  }
1068  }
1069 
1071 
1072  purge_sys->state = TRX_STOP_PURGE;
1073 
1074  trx_purge_truncate_if_arr_empty();
1075 
1076  return(NULL);
1077  } else if (purge_sys->purge_trx_no >= purge_sys->view->low_limit_no) {
1078  purge_sys->state = TRX_STOP_PURGE;
1079 
1080  trx_purge_truncate_if_arr_empty();
1081 
1082  return(NULL);
1083  }
1084 
1085  /* fprintf(stderr, "Thread %lu purging trx %llu undo record %llu\n",
1086  os_thread_get_curr_id(),
1087  (ullint) purge_sys->purge_trx_no,
1088  (ullint) purge_sys->purge_undo_no); */
1089 
1090 
1091  *roll_ptr = trx_undo_build_roll_ptr(
1092  FALSE, (purge_sys->rseg)->id, purge_sys->page_no,
1093  purge_sys->offset);
1094 
1095  *cell = trx_purge_arr_store_info(
1097 
1099 
1100  /* The following call will advance the stored values of purge_trx_no
1101  and purge_undo_no, therefore we had to store them first */
1102 
1103  undo_rec = trx_purge_get_next_rec(heap);
1104 
1105  return(undo_rec);
1106 }
1107 
1108 /*******************************************************************/
1110 UNIV_INTERN
1111 void
1113 /*==================*/
1114  trx_undo_inf_t* cell)
1115 {
1116  trx_purge_arr_remove_info(cell);
1117 }
1118 
1119 /*******************************************************************/
1122 UNIV_INTERN
1123 ulint
1125 /*======*/
1126  ulint limit)
1128 {
1129  que_thr_t* thr;
1130  ulint old_pages_handled;
1131 
1132  if (srv_fake_write)
1133  return(0);
1134 
1135  ut_a(purge_sys->trx->n_active_thrs == 0);
1136  rw_lock_x_lock(&purge_sys->latch);
1137 
1138  mutex_enter(&kernel_mutex);
1139 
1140  /* Close and free the old purge view */
1141 
1143  purge_sys->view = NULL;
1145 
1146  /* Determine how much data manipulation language (DML) statements
1147  need to be delayed in order to reduce the lagging of the purge
1148  thread. */
1149  srv_dml_needed_delay = 0; /* in microseconds; default: no delay */
1150 
1151  /* If we cannot advance the 'purge view' because of an old
1152  'consistent read view', then the DML statements cannot be delayed.
1153  Also, srv_max_purge_lag <= 0 means 'infinity'. */
1154  if (srv_max_purge_lag > 0
1155  && !UT_LIST_GET_LAST(trx_sys->view_list)) {
1156  float ratio = (float) trx_sys->rseg_history_len
1157  / srv_max_purge_lag;
1158  if (ratio > ULINT_MAX / 10000) {
1159  /* Avoid overflow: maximum delay is 4295 seconds */
1160  srv_dml_needed_delay = ULINT_MAX;
1161  } else if (ratio > 1) {
1162  /* If the history list length exceeds the
1163  innodb_max_purge_lag, the
1164  data manipulation statements are delayed
1165  by at least 5000 microseconds. */
1166  srv_dml_needed_delay = (ulint) ((ratio - .5) * 10000);
1167  }
1168  }
1169 
1171  0, purge_sys->heap);
1172 
1173  mutex_exit(&kernel_mutex);
1174 
1175  rw_lock_x_unlock(&(purge_sys->latch));
1176 
1177  purge_sys->state = TRX_PURGE_ON;
1178 
1180 
1181  old_pages_handled = purge_sys->n_pages_handled;
1182 
1183 
1184  mutex_enter(&kernel_mutex);
1185 
1187 
1188  ut_ad(thr);
1189 
1190  mutex_exit(&kernel_mutex);
1191 
1192  if (srv_print_thread_releases) {
1193 
1194  fputs("Starting purge\n", stderr);
1195  }
1196 
1197  que_run_threads(thr);
1198 
1199  if (srv_print_thread_releases) {
1200 
1201  fprintf(stderr,
1202  "Purge ends; pages handled %lu\n",
1203  (ulong) purge_sys->n_pages_handled);
1204  }
1205 
1206  return(purge_sys->n_pages_handled - old_pages_handled);
1207 }
1208 
1209 /******************************************************************/
1211 UNIV_INTERN
1212 void
1214 /*=====================*/
1215 {
1216  fprintf(stderr, "InnoDB: Purge system view:\n");
1218 
1219  fprintf(stderr, "InnoDB: Purge trx n:o " TRX_ID_FMT
1220  ", undo n:o " TRX_ID_FMT "\n",
1223  fprintf(stderr,
1224  "InnoDB: Purge next stored %lu, page_no %lu, offset %lu,\n"
1225  "InnoDB: Purge hdr_page_no %lu, hdr_offset %lu\n",
1226  (ulong) purge_sys->next_stored,
1227  (ulong) purge_sys->page_no,
1228  (ulong) purge_sys->offset,
1229  (ulong) purge_sys->hdr_page_no,
1230  (ulong) purge_sys->hdr_offset);
1231 }
trx_sys_t * trx_sys
Definition: trx0sys.cc:61
UNIV_INLINE ibool read_view_sees_trx_id(const read_view_t *view, trx_id_t trx_id)
trx_id_t trx_no
Definition: trx0rseg.h:188
#define rw_lock_create(K, L, level)
Definition: sync0rw.h:147
byte trx_undo_rec_t
Definition: trx0types.h:112
#define UT_LIST_GET_NEXT(NAME, N)
Definition: ut0lst.h:201
ib_id_t roll_ptr_t
Definition: trx0types.h:87
UNIV_INLINE fil_addr_t flst_get_last(const flst_base_node_t *base, mtr_t *mtr)
byte trx_usegf_t
Definition: trx0types.h:105
ulint n_active_thrs
Definition: trx0trx.h:619
UNIV_INLINE ulint trx_undo_rec_get_type(const trx_undo_rec_t *undo_rec)
UNIV_INLINE fil_addr_t flst_get_prev_addr(const flst_node_t *node, mtr_t *mtr)
UNIV_INLINE page_t * page_align(const void *ptr) __attribute__((const ))
UNIV_INLINE ulint page_get_page_no(const page_t *page)
undo_no_t purge_undo_no
Definition: trx0purge.h:158
byte trx_ulogf_t
Definition: trx0types.h:107
#define mem_free(PTR)
Definition: mem0mem.h:249
UNIV_INTERN void read_view_close(read_view_t *view)
Definition: read0read.cc:322
trx_rseg_t * rseg
Definition: trx0rseg.h:189
#define TRX_ID_FMT
Definition: trx0types.h:33
rw_lock_t latch
Definition: trx0purge.h:139
trx_rseg_t * rseg
Definition: trx0purge.h:166
UNIV_INTERN void flst_truncate_end(flst_base_node_t *base, flst_node_t *node2, ulint n_nodes, mtr_t *mtr)
Definition: fut0lst.cc:404
UNIV_INTERN void trx_purge_sys_print(void)
Definition: trx0purge.cc:1213
UNIV_INTERN void que_run_threads(que_thr_t *thr)
Definition: que0que.cc:1336
ulint hdr_offset
Definition: trx0undo.h:395
UNIV_INTERN void mlog_write_ulint(byte *ptr, ulint val, byte type, mtr_t *mtr)
Definition: mtr0log.cc:247
#define TRX_UNDO_NEXT_LOG
Definition: trx0undo.h:542
ulint conc_state
Definition: trx0trx.h:480
UNIV_INTERN void trx_undo_truncate_start(trx_rseg_t *rseg, ulint space, ulint hdr_page_no, ulint hdr_offset, undo_no_t limit)
Definition: trx0undo.cc:1135
UNIV_INTERN ulint trx_purge(ulint limit)
Definition: trx0purge.cc:1124
UNIV_INLINE rec_t * rec_copy(void *buf, const rec_t *rec, const ulint *offsets)
#define mem_heap_free(heap)
Definition: mem0mem.h:117
mutex_t mutex
Definition: trx0rseg.h:146
UNIV_INLINE ulint trx_undo_rec_get_cmpl_info(const trx_undo_rec_t *undo_rec)
UNIV_INTERN void sess_close(sess_t *sess)
Definition: usr0sess.cc:61
ibool del_marks
Definition: trx0undo.h:372
ulint boffset
Definition: fil0fil.h:68
#define TRX_UNDO_FSEG_HEADER
Definition: trx0undo.h:480
UNIV_INTERN void trx_purge_sys_create(ib_bh_t *ib_bh)
Definition: trx0purge.cc:222
UNIV_INTERN void trx_purge_rec_release(trx_undo_inf_t *cell)
Definition: trx0purge.cc:1112
#define mem_zalloc(N)
Definition: mem0mem.h:225
trx_rseg_t * rseg
Definition: trx0undo.h:387
trx_undo_arr_t * arr
Definition: trx0purge.h:178
#define TRX_UNDO_SEG_HDR
Definition: trx0undo.h:470
trx_undo_rec_t trx_purge_dummy_rec
Definition: trx0purge.cc:52
ib_bh_t * ib_bh
Definition: trx0purge.h:185
UNIV_INTERN void os_thread_yield(void)
Definition: os0thread.cc:244
UNIV_INTERN void mtr_commit(mtr_t *mtr) __attribute__((nonnull))
Definition: mtr0mtr.cc:247
UNIV_INLINE ibool ib_bh_is_empty(const ib_bh_t *ib_bh)
UNIV_INTERN ulint mtr_read_ulint(const byte *ptr, ulint type, mtr_t *mtr)
Definition: mtr0mtr.cc:362
UNIV_INLINE roll_ptr_t trx_undo_build_roll_ptr(ibool is_insert, ulint rseg_id, ulint page_no, ulint offset)
UNIV_INLINE fil_addr_t trx_purge_get_log_from_hist(fil_addr_t node_addr)
UNIV_INLINE page_t * trx_undo_page_get(ulint space, ulint zip_size, ulint page_no, mtr_t *mtr)
UNIV_INLINE trx_undo_inf_t * trx_undo_arr_get_nth_info(trx_undo_arr_t *arr, ulint n)
UNIV_INTERN void mlog_write_ull(byte *ptr, ib_uint64_t val, mtr_t *mtr)
Definition: mtr0log.cc:293
ulint n_pages_handled
Definition: trx0purge.h:147
UNIV_INTERN void que_graph_free(que_t *graph)
Definition: que0que.cc:679
UNIV_INTERN read_view_t * read_view_oldest_copy_or_open_new(trx_id_t cr_trx_id, mem_heap_t *heap)
Definition: read0read.cc:168
trx_t * trx
Definition: usr0sess.h:58
trx_id_t low_limit_no
Definition: read0read.h:131
UNIV_INTERN void * ib_bh_push(ib_bh_t *ib_bh, const void *elem)
Definition: ut0bh.cc:83
#define MLOG_2BYTES
Definition: mtr0mtr.h:74
ulint last_offset
Definition: trx0rseg.h:174
UNIV_INLINE ulint page_offset(const void *ptr) __attribute__((const ))
UNIV_INLINE trx_rsegf_t * trx_rsegf_get(ulint space, ulint zip_size, ulint page_no, mtr_t *mtr)
UNIV_INTERN ibool trx_purge_update_undo_must_exist(trx_id_t trx_id)
Definition: trx0purge.cc:72
ulint handle_limit
Definition: trx0purge.h:149
sess_t * sess
Definition: trx0purge.h:131
#define ut_a(EXPR)
Definition: ut0dbg.h:105
read_view_t * view
Definition: trx0purge.h:145
ulint rseg_history_len
Definition: trx0sys.h:599
#define mem_heap_create(N)
Definition: mem0mem.h:97
UNIV_INTERN trx_undo_rec_t * trx_purge_fetch_next_rec(roll_ptr_t *roll_ptr, trx_undo_inf_t **cell, mem_heap_t *heap)
Definition: trx0purge.cc:1037
UNIV_INTERN trx_undo_rec_t * trx_undo_get_next_rec(trx_undo_rec_t *rec, ulint page_no, ulint offset, mtr_t *mtr)
Definition: trx0undo.cc:273
#define TRX_UNDO_STATE
Definition: trx0undo.h:474
#define UT_LIST_GET_FIRST(BASE)
Definition: ut0lst.h:224
UNIV_INLINE trx_undo_rec_t * trx_undo_page_get_next_rec(trx_undo_rec_t *rec, ulint page_no, ulint offset)
#define MLOG_4BYTES
Definition: mtr0mtr.h:75
UNIV_INLINE void mem_heap_empty(mem_heap_t *heap)
UNIV_INLINE undo_no_t trx_undo_rec_get_undo_no(const trx_undo_rec_t *undo_rec)
#define ut_ad(EXPR)
Definition: ut0dbg.h:127
trx_id_t no
Definition: trx0trx.h:552
mutex_t bh_mutex
Definition: trx0purge.h:188
ibool last_del_marks
Definition: trx0rseg.h:178
UNIV_INTERN void flst_cut_end(flst_base_node_t *base, flst_node_t *node2, ulint n_nodes, mtr_t *mtr)
Definition: fut0lst.cc:347
UNIV_INTERN void ib_bh_pop(ib_bh_t *ib_bh)
Definition: ut0bh.cc:119
UNIV_INTERN trx_undo_arr_t * trx_undo_arr_create(void)
Definition: trx0roll.cc:636
ib_id_t trx_id_t
Definition: trx0types.h:85
UNIV_INTERN trx_undo_rec_t * trx_undo_get_first_rec(ulint space, ulint zip_size, ulint page_no, ulint offset, ulint mode, mtr_t *mtr)
Definition: trx0undo.cc:304
#define ut_error
Definition: ut0dbg.h:115
UNIV_INTERN void flst_add_first(flst_base_node_t *base, flst_node_t *node, mtr_t *mtr)
Definition: fut0lst.cc:118
trx_id_t purge_trx_no
Definition: trx0purge.h:155
UNIV_INTERN sess_t * sess_open(void)
Definition: usr0sess.cc:39
#define FIL_NULL
Definition: fil0fil.h:48
trx_id_t last_trx_no
Definition: trx0rseg.h:176
trx_t * trx
Definition: que0que.h:403
UNIV_INTERN void trx_purge_add_update_undo_to_history(trx_t *trx, page_t *undo_page, mtr_t *mtr)
Definition: trx0purge.cc:313
trx_purge_t * purge_sys
Definition: trx0purge.cc:48
UNIV_INLINE trx_undo_rec_t * trx_undo_rec_copy(const trx_undo_rec_t *undo_rec, mem_heap_t *heap)
ulint hdr_page_no
Definition: trx0undo.h:393
UNIV_INTERN purge_node_t * row_purge_node_create(que_thr_t *parent, mem_heap_t *heap)
Definition: row0purge.cc:72
#define TRX_UNDO_TRX_NO
Definition: trx0undo.h:498
UNIV_INTERN void ut_print_timestamp(FILE *file)
Definition: ut0ut.cc:247
UNIV_INTERN void read_view_print(const read_view_t *view)
Definition: read0read.cc:358
byte page_t
Definition: page0types.h:37
UNIV_INLINE void * ib_bh_first(ib_bh_t *ib_bh)
ulint page
Definition: fil0fil.h:67
UNIV_INTERN void trx_purge_sys_close(void)
Definition: trx0purge.cc:269
UNIV_INLINE void mtr_start(mtr_t *mtr) __attribute__((nonnull))
que_node_t * child
Definition: que0que.h:355
#define TRX_UNDO_DEL_MARKS
Definition: trx0undo.h:503
UNIV_INLINE ulint mach_read_from_2(const byte *b) __attribute__((nonnull
ulint is_purge
Definition: trx0trx.h:537
trx_undo_t * update_undo
Definition: trx0trx.h:698
UNIV_INTERN void srv_wake_purge_thread_if_not_active(void)
Definition: srv0srv.cc:2640
UNIV_INTERN que_thr_t * que_thr_create(que_fork_t *parent, mem_heap_t *heap)
Definition: que0que.cc:202
UNIV_INLINE ibool trx_undo_rec_get_extern_storage(const trx_undo_rec_t *undo_rec)
#define TRX_UNDO_PAGE_LIST
Definition: trx0undo.h:483
UNIV_INLINE ib_uint64_t mach_read_from_8(const byte *b) __attribute__((nonnull
UNIV_INTERN que_thr_t * que_fork_start_command(que_fork_t *fork)
Definition: que0que.cc:342
undo_no_t undo_no
Definition: trx0roll.h:301
UNIV_INLINE ulint flst_get_len(const flst_base_node_t *base, mtr_t *mtr)
ib_id_t undo_no_t
Definition: trx0types.h:89
mem_heap_t * heap
Definition: trx0purge.h:181
#define UT_LIST_GET_LAST(BASE)
Definition: ut0lst.h:235
UNIV_INTERN void ib_bh_free(ib_bh_t *ib_bh)
Definition: ut0bh.cc:71
UNIV_INTERN ibool trx_start_low(trx_t *trx, ulint rseg_id)
Definition: trx0trx.cc:628
byte trx_rsegf_t
Definition: trx0types.h:103
#define TRX_UNDO_HISTORY_NODE
Definition: trx0undo.h:548
UNIV_INLINE page_t * trx_undo_page_get_s_latched(ulint space, ulint zip_size, ulint page_no, mtr_t *mtr)
UNIV_INTERN void trx_undo_arr_free(trx_undo_arr_t *arr)
Definition: trx0roll.cc:666
UNIV_INTERN ibool fseg_free_step_not_header(fseg_header_t *header, mtr_t *mtr)
Definition: fsp0fsp.cc:3599
UNIV_INTERN ibool fseg_free_step(fseg_header_t *header, mtr_t *mtr)
Definition: fsp0fsp.cc:3512
UNIV_INLINE void trx_rsegf_set_nth_undo(trx_rsegf_t *rsegf, ulint n, ulint page_no, mtr_t *mtr)
UNIV_INTERN que_fork_t * que_fork_create(que_t *graph, que_node_t *parent, ulint fork_type, mem_heap_t *heap)
Definition: que0que.cc:156
ulint last_page_no
Definition: trx0rseg.h:171