PostgreSQL Source Code  git master
nbtxlog.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * nbtxlog.c
4  * WAL replay logic for btrees.
5  *
6  *
7  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
8  * Portions Copyright (c) 1994, Regents of the University of California
9  *
10  * IDENTIFICATION
11  * src/backend/access/nbtree/nbtxlog.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "access/bufmask.h"
18 #include "access/nbtree.h"
19 #include "access/nbtxlog.h"
20 #include "access/transam.h"
21 #include "access/xlog.h"
22 #include "access/xlogutils.h"
23 #include "miscadmin.h"
24 #include "storage/procarray.h"
25 #include "utils/memutils.h"
26 
27 static MemoryContext opCtx; /* working memory for operations */
28 
29 /*
30  * _bt_restore_page -- re-enter all the index tuples on a page
31  *
32  * The page is freshly init'd, and *from (length len) is a copy of what
33  * had been its upper part (pd_upper to pd_special). We assume that the
34  * tuples had been added to the page in item-number order, and therefore
35  * the one with highest item number appears first (lowest on the page).
36  */
37 static void
38 _bt_restore_page(Page page, char *from, int len)
39 {
40  IndexTupleData itupdata;
41  Size itemsz;
42  char *end = from + len;
44  uint16 itemsizes[MaxIndexTuplesPerPage];
45  int i;
46  int nitems;
47 
48  /*
49  * To get the items back in the original order, we add them to the page in
50  * reverse. To figure out where one tuple ends and another begins, we
51  * have to scan them in forward order first.
52  */
53  i = 0;
54  while (from < end)
55  {
56  /*
57  * As we step through the items, 'from' won't always be properly
58  * aligned, so we need to use memcpy(). Further, we use Item (which
59  * is just a char*) here for our items array for the same reason;
60  * wouldn't want the compiler or anyone thinking that an item is
61  * aligned when it isn't.
62  */
63  memcpy(&itupdata, from, sizeof(IndexTupleData));
64  itemsz = IndexTupleSize(&itupdata);
65  itemsz = MAXALIGN(itemsz);
66 
67  items[i] = (Item) from;
68  itemsizes[i] = itemsz;
69  i++;
70 
71  from += itemsz;
72  }
73  nitems = i;
74 
75  for (i = nitems - 1; i >= 0; i--)
76  {
77  if (PageAddItem(page, items[i], itemsizes[i], nitems - i,
78  false, false) == InvalidOffsetNumber)
79  elog(PANIC, "_bt_restore_page: cannot add item to page");
80  from += itemsz;
81  }
82 }
83 
84 static void
86 {
87  XLogRecPtr lsn = record->EndRecPtr;
88  Buffer metabuf;
89  Page metapg;
90  BTMetaPageData *md;
91  BTPageOpaque pageop;
92  xl_btree_metadata *xlrec;
93  char *ptr;
94  Size len;
95 
96  metabuf = XLogInitBufferForRedo(record, block_id);
97  ptr = XLogRecGetBlockData(record, block_id, &len);
98 
99  Assert(len == sizeof(xl_btree_metadata));
101  xlrec = (xl_btree_metadata *) ptr;
102  metapg = BufferGetPage(metabuf);
103 
104  _bt_pageinit(metapg, BufferGetPageSize(metabuf));
105 
106  md = BTPageGetMeta(metapg);
107  md->btm_magic = BTREE_MAGIC;
108  md->btm_version = xlrec->version;
109  md->btm_root = xlrec->root;
110  md->btm_level = xlrec->level;
111  md->btm_fastroot = xlrec->fastroot;
112  md->btm_fastlevel = xlrec->fastlevel;
113  /* Cannot log BTREE_MIN_VERSION index metapage without upgrade */
117  md->btm_allequalimage = xlrec->allequalimage;
118 
119  pageop = (BTPageOpaque) PageGetSpecialPointer(metapg);
120  pageop->btpo_flags = BTP_META;
121 
122  /*
123  * Set pd_lower just past the end of the metadata. This is essential,
124  * because without doing so, metadata will be lost if xlog.c compresses
125  * the page.
126  */
127  ((PageHeader) metapg)->pd_lower =
128  ((char *) md + sizeof(BTMetaPageData)) - (char *) metapg;
129 
130  PageSetLSN(metapg, lsn);
131  MarkBufferDirty(metabuf);
132  UnlockReleaseBuffer(metabuf);
133 }
134 
135 /*
136  * _bt_clear_incomplete_split -- clear INCOMPLETE_SPLIT flag on a page
137  *
138  * This is a common subroutine of the redo functions of all the WAL record
139  * types that can insert a downlink: insert, split, and newroot.
140  */
141 static void
143 {
144  XLogRecPtr lsn = record->EndRecPtr;
145  Buffer buf;
146 
147  if (XLogReadBufferForRedo(record, block_id, &buf) == BLK_NEEDS_REDO)
148  {
149  Page page = (Page) BufferGetPage(buf);
151 
152  Assert(P_INCOMPLETE_SPLIT(pageop));
153  pageop->btpo_flags &= ~BTP_INCOMPLETE_SPLIT;
154 
155  PageSetLSN(page, lsn);
156  MarkBufferDirty(buf);
157  }
158  if (BufferIsValid(buf))
159  UnlockReleaseBuffer(buf);
160 }
161 
162 static void
163 btree_xlog_insert(bool isleaf, bool ismeta, bool posting,
164  XLogReaderState *record)
165 {
166  XLogRecPtr lsn = record->EndRecPtr;
167  xl_btree_insert *xlrec = (xl_btree_insert *) XLogRecGetData(record);
168  Buffer buffer;
169  Page page;
170 
171  /*
172  * Insertion to an internal page finishes an incomplete split at the child
173  * level. Clear the incomplete-split flag in the child. Note: during
174  * normal operation, the child and parent pages are locked at the same
175  * time, so that clearing the flag and inserting the downlink appear
176  * atomic to other backends. We don't bother with that during replay,
177  * because readers don't care about the incomplete-split flag and there
178  * cannot be updates happening.
179  */
180  if (!isleaf)
181  _bt_clear_incomplete_split(record, 1);
182  if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
183  {
184  Size datalen;
185  char *datapos = XLogRecGetBlockData(record, 0, &datalen);
186 
187  page = BufferGetPage(buffer);
188 
189  if (!posting)
190  {
191  /* Simple retail insertion */
192  if (PageAddItem(page, (Item) datapos, datalen, xlrec->offnum,
193  false, false) == InvalidOffsetNumber)
194  elog(PANIC, "failed to add new item");
195  }
196  else
197  {
198  ItemId itemid;
199  IndexTuple oposting,
200  newitem,
201  nposting;
202  uint16 postingoff;
203 
204  /*
205  * A posting list split occurred during leaf page insertion. WAL
206  * record data will start with an offset number representing the
207  * point in an existing posting list that a split occurs at.
208  *
209  * Use _bt_swap_posting() to repeat posting list split steps from
210  * primary. Note that newitem from WAL record is 'orignewitem',
211  * not the final version of newitem that is actually inserted on
212  * page.
213  */
214  postingoff = *((uint16 *) datapos);
215  datapos += sizeof(uint16);
216  datalen -= sizeof(uint16);
217 
218  itemid = PageGetItemId(page, OffsetNumberPrev(xlrec->offnum));
219  oposting = (IndexTuple) PageGetItem(page, itemid);
220 
221  /* Use mutable, aligned newitem copy in _bt_swap_posting() */
222  Assert(isleaf && postingoff > 0);
223  newitem = CopyIndexTuple((IndexTuple) datapos);
224  nposting = _bt_swap_posting(newitem, oposting, postingoff);
225 
226  /* Replace existing posting list with post-split version */
227  memcpy(oposting, nposting, MAXALIGN(IndexTupleSize(nposting)));
228 
229  /* Insert "final" new item (not orignewitem from WAL stream) */
230  Assert(IndexTupleSize(newitem) == datalen);
231  if (PageAddItem(page, (Item) newitem, datalen, xlrec->offnum,
232  false, false) == InvalidOffsetNumber)
233  elog(PANIC, "failed to add posting split new item");
234  }
235 
236  PageSetLSN(page, lsn);
237  MarkBufferDirty(buffer);
238  }
239  if (BufferIsValid(buffer))
240  UnlockReleaseBuffer(buffer);
241 
242  /*
243  * Note: in normal operation, we'd update the metapage while still holding
244  * lock on the page we inserted into. But during replay it's not
245  * necessary to hold that lock, since no other index updates can be
246  * happening concurrently, and readers will cope fine with following an
247  * obsolete link from the metapage.
248  */
249  if (ismeta)
250  _bt_restore_meta(record, 2);
251 }
252 
253 static void
254 btree_xlog_split(bool newitemonleft, XLogReaderState *record)
255 {
256  XLogRecPtr lsn = record->EndRecPtr;
257  xl_btree_split *xlrec = (xl_btree_split *) XLogRecGetData(record);
258  bool isleaf = (xlrec->level == 0);
259  Buffer lbuf;
260  Buffer rbuf;
261  Page rpage;
262  BTPageOpaque ropaque;
263  char *datapos;
264  Size datalen;
265  BlockNumber leftsib;
266  BlockNumber rightsib;
267  BlockNumber rnext;
268 
269  XLogRecGetBlockTag(record, 0, NULL, NULL, &leftsib);
270  XLogRecGetBlockTag(record, 1, NULL, NULL, &rightsib);
271  if (!XLogRecGetBlockTag(record, 2, NULL, NULL, &rnext))
272  rnext = P_NONE;
273 
274  /*
275  * Clear the incomplete split flag on the left sibling of the child page
276  * this is a downlink for. (Like in btree_xlog_insert, this can be done
277  * before locking the other pages)
278  */
279  if (!isleaf)
280  _bt_clear_incomplete_split(record, 3);
281 
282  /* Reconstruct right (new) sibling page from scratch */
283  rbuf = XLogInitBufferForRedo(record, 1);
284  datapos = XLogRecGetBlockData(record, 1, &datalen);
285  rpage = (Page) BufferGetPage(rbuf);
286 
287  _bt_pageinit(rpage, BufferGetPageSize(rbuf));
288  ropaque = (BTPageOpaque) PageGetSpecialPointer(rpage);
289 
290  ropaque->btpo_prev = leftsib;
291  ropaque->btpo_next = rnext;
292  ropaque->btpo.level = xlrec->level;
293  ropaque->btpo_flags = isleaf ? BTP_LEAF : 0;
294  ropaque->btpo_cycleid = 0;
295 
296  _bt_restore_page(rpage, datapos, datalen);
297 
298  PageSetLSN(rpage, lsn);
299  MarkBufferDirty(rbuf);
300 
301  /* Now reconstruct left (original) sibling page */
302  if (XLogReadBufferForRedo(record, 0, &lbuf) == BLK_NEEDS_REDO)
303  {
304  /*
305  * To retain the same physical order of the tuples that they had, we
306  * initialize a temporary empty page for the left page and add all the
307  * items to that in item number order. This mirrors how _bt_split()
308  * works. Retaining the same physical order makes WAL consistency
309  * checking possible. See also _bt_restore_page(), which does the
310  * same for the right page.
311  */
312  Page lpage = (Page) BufferGetPage(lbuf);
314  OffsetNumber off;
315  IndexTuple newitem = NULL,
316  left_hikey = NULL,
317  nposting = NULL;
318  Size newitemsz = 0,
319  left_hikeysz = 0;
320  Page newlpage;
321  OffsetNumber leftoff,
322  replacepostingoff = InvalidOffsetNumber;
323 
324  datapos = XLogRecGetBlockData(record, 0, &datalen);
325 
326  if (newitemonleft || xlrec->postingoff != 0)
327  {
328  newitem = (IndexTuple) datapos;
329  newitemsz = MAXALIGN(IndexTupleSize(newitem));
330  datapos += newitemsz;
331  datalen -= newitemsz;
332 
333  if (xlrec->postingoff != 0)
334  {
335  ItemId itemid;
336  IndexTuple oposting;
337 
338  /* Posting list must be at offset number before new item's */
339  replacepostingoff = OffsetNumberPrev(xlrec->newitemoff);
340 
341  /* Use mutable, aligned newitem copy in _bt_swap_posting() */
342  newitem = CopyIndexTuple(newitem);
343  itemid = PageGetItemId(lpage, replacepostingoff);
344  oposting = (IndexTuple) PageGetItem(lpage, itemid);
345  nposting = _bt_swap_posting(newitem, oposting,
346  xlrec->postingoff);
347  }
348  }
349 
350  /*
351  * Extract left hikey and its size. We assume that 16-bit alignment
352  * is enough to apply IndexTupleSize (since it's fetching from a
353  * uint16 field).
354  */
355  left_hikey = (IndexTuple) datapos;
356  left_hikeysz = MAXALIGN(IndexTupleSize(left_hikey));
357  datapos += left_hikeysz;
358  datalen -= left_hikeysz;
359 
360  Assert(datalen == 0);
361 
362  newlpage = PageGetTempPageCopySpecial(lpage);
363 
364  /* Set high key */
365  leftoff = P_HIKEY;
366  if (PageAddItem(newlpage, (Item) left_hikey, left_hikeysz,
367  P_HIKEY, false, false) == InvalidOffsetNumber)
368  elog(PANIC, "failed to add high key to left page after split");
369  leftoff = OffsetNumberNext(leftoff);
370 
371  for (off = P_FIRSTDATAKEY(lopaque); off < xlrec->firstrightoff; off++)
372  {
373  ItemId itemid;
374  Size itemsz;
375  IndexTuple item;
376 
377  /* Add replacement posting list when required */
378  if (off == replacepostingoff)
379  {
380  Assert(newitemonleft ||
381  xlrec->firstrightoff == xlrec->newitemoff);
382  if (PageAddItem(newlpage, (Item) nposting,
383  MAXALIGN(IndexTupleSize(nposting)), leftoff,
384  false, false) == InvalidOffsetNumber)
385  elog(ERROR, "failed to add new posting list item to left page after split");
386  leftoff = OffsetNumberNext(leftoff);
387  continue; /* don't insert oposting */
388  }
389 
390  /* add the new item if it was inserted on left page */
391  else if (newitemonleft && off == xlrec->newitemoff)
392  {
393  if (PageAddItem(newlpage, (Item) newitem, newitemsz, leftoff,
394  false, false) == InvalidOffsetNumber)
395  elog(ERROR, "failed to add new item to left page after split");
396  leftoff = OffsetNumberNext(leftoff);
397  }
398 
399  itemid = PageGetItemId(lpage, off);
400  itemsz = ItemIdGetLength(itemid);
401  item = (IndexTuple) PageGetItem(lpage, itemid);
402  if (PageAddItem(newlpage, (Item) item, itemsz, leftoff,
403  false, false) == InvalidOffsetNumber)
404  elog(ERROR, "failed to add old item to left page after split");
405  leftoff = OffsetNumberNext(leftoff);
406  }
407 
408  /* cope with possibility that newitem goes at the end */
409  if (newitemonleft && off == xlrec->newitemoff)
410  {
411  if (PageAddItem(newlpage, (Item) newitem, newitemsz, leftoff,
412  false, false) == InvalidOffsetNumber)
413  elog(ERROR, "failed to add new item to left page after split");
414  leftoff = OffsetNumberNext(leftoff);
415  }
416 
417  PageRestoreTempPage(newlpage, lpage);
418 
419  /* Fix opaque fields */
420  lopaque->btpo_flags = BTP_INCOMPLETE_SPLIT;
421  if (isleaf)
422  lopaque->btpo_flags |= BTP_LEAF;
423  lopaque->btpo_next = rightsib;
424  lopaque->btpo_cycleid = 0;
425 
426  PageSetLSN(lpage, lsn);
427  MarkBufferDirty(lbuf);
428  }
429 
430  /*
431  * We no longer need the buffers. They must be released together, so that
432  * readers cannot observe two inconsistent halves.
433  */
434  if (BufferIsValid(lbuf))
435  UnlockReleaseBuffer(lbuf);
436  UnlockReleaseBuffer(rbuf);
437 
438  /*
439  * Fix left-link of the page to the right of the new right sibling.
440  *
441  * Note: in normal operation, we do this while still holding lock on the
442  * two split pages. However, that's not necessary for correctness in WAL
443  * replay, because no other index update can be in progress, and readers
444  * will cope properly when following an obsolete left-link.
445  */
446  if (rnext != P_NONE)
447  {
448  Buffer buffer;
449 
450  if (XLogReadBufferForRedo(record, 2, &buffer) == BLK_NEEDS_REDO)
451  {
452  Page page = (Page) BufferGetPage(buffer);
454 
455  pageop->btpo_prev = rightsib;
456 
457  PageSetLSN(page, lsn);
458  MarkBufferDirty(buffer);
459  }
460  if (BufferIsValid(buffer))
461  UnlockReleaseBuffer(buffer);
462  }
463 }
464 
465 static void
467 {
468  XLogRecPtr lsn = record->EndRecPtr;
469  xl_btree_dedup *xlrec = (xl_btree_dedup *) XLogRecGetData(record);
470  Buffer buf;
471 
472  if (XLogReadBufferForRedo(record, 0, &buf) == BLK_NEEDS_REDO)
473  {
474  char *ptr = XLogRecGetBlockData(record, 0, NULL);
475  Page page = (Page) BufferGetPage(buf);
477  OffsetNumber offnum,
478  minoff,
479  maxoff;
482  Page newpage;
483 
484  state = (BTDedupState) palloc(sizeof(BTDedupStateData));
485  state->deduplicate = true; /* unused */
486  state->nmaxitems = 0; /* unused */
487  /* Conservatively use larger maxpostingsize than primary */
488  state->maxpostingsize = BTMaxItemSize(page);
489  state->base = NULL;
490  state->baseoff = InvalidOffsetNumber;
491  state->basetupsize = 0;
492  state->htids = palloc(state->maxpostingsize);
493  state->nhtids = 0;
494  state->nitems = 0;
495  state->phystupsize = 0;
496  state->nintervals = 0;
497 
498  minoff = P_FIRSTDATAKEY(opaque);
499  maxoff = PageGetMaxOffsetNumber(page);
500  newpage = PageGetTempPageCopySpecial(page);
501 
502  if (!P_RIGHTMOST(opaque))
503  {
504  ItemId itemid = PageGetItemId(page, P_HIKEY);
505  Size itemsz = ItemIdGetLength(itemid);
506  IndexTuple item = (IndexTuple) PageGetItem(page, itemid);
507 
508  if (PageAddItem(newpage, (Item) item, itemsz, P_HIKEY,
509  false, false) == InvalidOffsetNumber)
510  elog(ERROR, "deduplication failed to add highkey");
511  }
512 
513  intervals = (BTDedupInterval *) ptr;
514  for (offnum = minoff;
515  offnum <= maxoff;
516  offnum = OffsetNumberNext(offnum))
517  {
518  ItemId itemid = PageGetItemId(page, offnum);
519  IndexTuple itup = (IndexTuple) PageGetItem(page, itemid);
520 
521  if (offnum == minoff)
522  _bt_dedup_start_pending(state, itup, offnum);
523  else if (state->nintervals < xlrec->nintervals &&
524  state->baseoff == intervals[state->nintervals].baseoff &&
525  state->nitems < intervals[state->nintervals].nitems)
526  {
527  if (!_bt_dedup_save_htid(state, itup))
528  elog(ERROR, "deduplication failed to add heap tid to pending posting list");
529  }
530  else
531  {
532  _bt_dedup_finish_pending(newpage, state);
533  _bt_dedup_start_pending(state, itup, offnum);
534  }
535  }
536 
537  _bt_dedup_finish_pending(newpage, state);
538  Assert(state->nintervals == xlrec->nintervals);
539  Assert(memcmp(state->intervals, intervals,
540  state->nintervals * sizeof(BTDedupInterval)) == 0);
541 
542  if (P_HAS_GARBAGE(opaque))
543  {
544  BTPageOpaque nopaque = (BTPageOpaque) PageGetSpecialPointer(newpage);
545 
546  nopaque->btpo_flags &= ~BTP_HAS_GARBAGE;
547  }
548 
549  PageRestoreTempPage(newpage, page);
550  PageSetLSN(page, lsn);
551  MarkBufferDirty(buf);
552  }
553 
554  if (BufferIsValid(buf))
555  UnlockReleaseBuffer(buf);
556 }
557 
558 static void
560 {
561  XLogRecPtr lsn = record->EndRecPtr;
562  xl_btree_vacuum *xlrec = (xl_btree_vacuum *) XLogRecGetData(record);
563  Buffer buffer;
564  Page page;
565  BTPageOpaque opaque;
566 
567  /*
568  * We need to take a cleanup lock here, just like btvacuumpage(). However,
569  * it isn't necessary to exhaustively get a cleanup lock on every block in
570  * the index during recovery (just getting a cleanup lock on pages with
571  * items to kill suffices). See nbtree/README for details.
572  */
573  if (XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &buffer)
574  == BLK_NEEDS_REDO)
575  {
576  char *ptr = XLogRecGetBlockData(record, 0, NULL);
577 
578  page = (Page) BufferGetPage(buffer);
579 
580  if (xlrec->nupdated > 0)
581  {
582  OffsetNumber *updatedoffsets;
583  xl_btree_update *updates;
584 
585  updatedoffsets = (OffsetNumber *)
586  (ptr + xlrec->ndeleted * sizeof(OffsetNumber));
587  updates = (xl_btree_update *) ((char *) updatedoffsets +
588  xlrec->nupdated *
589  sizeof(OffsetNumber));
590 
591  for (int i = 0; i < xlrec->nupdated; i++)
592  {
593  BTVacuumPosting vacposting;
594  IndexTuple origtuple;
595  ItemId itemid;
596  Size itemsz;
597 
598  itemid = PageGetItemId(page, updatedoffsets[i]);
599  origtuple = (IndexTuple) PageGetItem(page, itemid);
600 
601  vacposting = palloc(offsetof(BTVacuumPostingData, deletetids) +
602  updates->ndeletedtids * sizeof(uint16));
603  vacposting->updatedoffset = updatedoffsets[i];
604  vacposting->itup = origtuple;
605  vacposting->ndeletedtids = updates->ndeletedtids;
606  memcpy(vacposting->deletetids,
607  (char *) updates + SizeOfBtreeUpdate,
608  updates->ndeletedtids * sizeof(uint16));
609 
610  _bt_update_posting(vacposting);
611 
612  /* Overwrite updated version of tuple */
613  itemsz = MAXALIGN(IndexTupleSize(vacposting->itup));
614  if (!PageIndexTupleOverwrite(page, updatedoffsets[i],
615  (Item) vacposting->itup, itemsz))
616  elog(PANIC, "failed to update partially dead item");
617 
618  pfree(vacposting->itup);
619  pfree(vacposting);
620 
621  /* advance to next xl_btree_update from array */
622  updates = (xl_btree_update *)
623  ((char *) updates + SizeOfBtreeUpdate +
624  updates->ndeletedtids * sizeof(uint16));
625  }
626  }
627 
628  if (xlrec->ndeleted > 0)
629  PageIndexMultiDelete(page, (OffsetNumber *) ptr, xlrec->ndeleted);
630 
631  /*
632  * Mark the page as not containing any LP_DEAD items --- see comments
633  * in _bt_delitems_vacuum().
634  */
635  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
636  opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
637 
638  PageSetLSN(page, lsn);
639  MarkBufferDirty(buffer);
640  }
641  if (BufferIsValid(buffer))
642  UnlockReleaseBuffer(buffer);
643 }
644 
645 static void
647 {
648  XLogRecPtr lsn = record->EndRecPtr;
649  xl_btree_delete *xlrec = (xl_btree_delete *) XLogRecGetData(record);
650  Buffer buffer;
651  Page page;
652  BTPageOpaque opaque;
653 
654  /*
655  * If we have any conflict processing to do, it must happen before we
656  * update the page
657  */
658  if (InHotStandby)
659  {
660  RelFileNode rnode;
661 
662  XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
663 
665  }
666 
667  /*
668  * We don't need to take a cleanup lock to apply these changes. See
669  * nbtree/README for details.
670  */
671  if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
672  {
673  char *ptr = XLogRecGetBlockData(record, 0, NULL);
674 
675  page = (Page) BufferGetPage(buffer);
676 
677  PageIndexMultiDelete(page, (OffsetNumber *) ptr, xlrec->ndeleted);
678 
679  /* Mark the page as not containing any LP_DEAD items */
680  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
681  opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
682 
683  PageSetLSN(page, lsn);
684  MarkBufferDirty(buffer);
685  }
686  if (BufferIsValid(buffer))
687  UnlockReleaseBuffer(buffer);
688 }
689 
690 static void
692 {
693  XLogRecPtr lsn = record->EndRecPtr;
695  Buffer buffer;
696  Page page;
697  BTPageOpaque pageop;
698  IndexTupleData trunctuple;
699 
700  /*
701  * In normal operation, we would lock all the pages this WAL record
702  * touches before changing any of them. In WAL replay, it should be okay
703  * to lock just one page at a time, since no concurrent index updates can
704  * be happening, and readers should not care whether they arrive at the
705  * target page or not (since it's surely empty).
706  */
707 
708  /* to-be-deleted subtree's parent page */
709  if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
710  {
711  OffsetNumber poffset;
712  ItemId itemid;
713  IndexTuple itup;
714  OffsetNumber nextoffset;
715  BlockNumber rightsib;
716 
717  page = (Page) BufferGetPage(buffer);
718  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
719 
720  poffset = xlrec->poffset;
721 
722  nextoffset = OffsetNumberNext(poffset);
723  itemid = PageGetItemId(page, nextoffset);
724  itup = (IndexTuple) PageGetItem(page, itemid);
725  rightsib = BTreeTupleGetDownLink(itup);
726 
727  itemid = PageGetItemId(page, poffset);
728  itup = (IndexTuple) PageGetItem(page, itemid);
729  BTreeTupleSetDownLink(itup, rightsib);
730  nextoffset = OffsetNumberNext(poffset);
731  PageIndexTupleDelete(page, nextoffset);
732 
733  PageSetLSN(page, lsn);
734  MarkBufferDirty(buffer);
735  }
736  if (BufferIsValid(buffer))
737  UnlockReleaseBuffer(buffer);
738 
739  /* Rewrite the leaf page as a halfdead page */
740  buffer = XLogInitBufferForRedo(record, 0);
741  page = (Page) BufferGetPage(buffer);
742 
743  _bt_pageinit(page, BufferGetPageSize(buffer));
744  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
745 
746  pageop->btpo_prev = xlrec->leftblk;
747  pageop->btpo_next = xlrec->rightblk;
748  pageop->btpo.level = 0;
749  pageop->btpo_flags = BTP_HALF_DEAD | BTP_LEAF;
750  pageop->btpo_cycleid = 0;
751 
752  /*
753  * Construct a dummy high key item that points to top parent page (value
754  * is InvalidBlockNumber when the top parent page is the leaf page itself)
755  */
756  MemSet(&trunctuple, 0, sizeof(IndexTupleData));
757  trunctuple.t_info = sizeof(IndexTupleData);
758  BTreeTupleSetTopParent(&trunctuple, xlrec->topparent);
759 
760  if (PageAddItem(page, (Item) &trunctuple, sizeof(IndexTupleData), P_HIKEY,
761  false, false) == InvalidOffsetNumber)
762  elog(ERROR, "could not add dummy high key to half-dead page");
763 
764  PageSetLSN(page, lsn);
765  MarkBufferDirty(buffer);
766  UnlockReleaseBuffer(buffer);
767 }
768 
769 
770 static void
772 {
773  XLogRecPtr lsn = record->EndRecPtr;
775  BlockNumber leftsib;
776  BlockNumber rightsib;
777  Buffer leftbuf;
778  Buffer target;
779  Buffer rightbuf;
780  Page page;
781  BTPageOpaque pageop;
782 
783  leftsib = xlrec->leftsib;
784  rightsib = xlrec->rightsib;
785 
786  /*
787  * In normal operation, we would lock all the pages this WAL record
788  * touches before changing any of them. In WAL replay, we at least lock
789  * the pages in the same standard left-to-right order (leftsib, target,
790  * rightsib), and don't release the sibling locks until the target is
791  * marked deleted.
792  *
793  * btree_xlog_split() can get away with fixing its right sibling page's
794  * left link last of all, after dropping all other locks. We prefer to
795  * avoid dropping locks on same-level pages early compared to normal
796  * operation. This keeps things simple for backwards scans. See
797  * nbtree/README.
798  */
799 
800  /* Fix right-link of left sibling, if any */
801  if (leftsib != P_NONE)
802  {
803  if (XLogReadBufferForRedo(record, 1, &leftbuf) == BLK_NEEDS_REDO)
804  {
805  page = (Page) BufferGetPage(leftbuf);
806  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
807  pageop->btpo_next = rightsib;
808 
809  PageSetLSN(page, lsn);
810  MarkBufferDirty(leftbuf);
811  }
812  }
813  else
814  leftbuf = InvalidBuffer;
815 
816  /* Rewrite target page as empty deleted page */
817  target = XLogInitBufferForRedo(record, 0);
818  page = (Page) BufferGetPage(target);
819 
820  _bt_pageinit(page, BufferGetPageSize(target));
821  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
822 
823  pageop->btpo_prev = leftsib;
824  pageop->btpo_next = rightsib;
825  pageop->btpo.xact = xlrec->btpo_xact;
826  pageop->btpo_flags = BTP_DELETED;
827  pageop->btpo_cycleid = 0;
828 
829  PageSetLSN(page, lsn);
830  MarkBufferDirty(target);
831 
832  /* Fix left-link of right sibling */
833  if (XLogReadBufferForRedo(record, 2, &rightbuf) == BLK_NEEDS_REDO)
834  {
835  page = (Page) BufferGetPage(rightbuf);
836  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
837  pageop->btpo_prev = leftsib;
838 
839  PageSetLSN(page, lsn);
840  MarkBufferDirty(rightbuf);
841  }
842 
843  /* Release siblings */
844  if (BufferIsValid(leftbuf))
845  UnlockReleaseBuffer(leftbuf);
846  if (BufferIsValid(rightbuf))
847  UnlockReleaseBuffer(rightbuf);
848 
849  /* Release target */
850  UnlockReleaseBuffer(target);
851 
852  /*
853  * If we deleted a parent of the targeted leaf page, instead of the leaf
854  * itself, update the leaf to point to the next remaining child in the
855  * to-be-deleted subtree
856  */
857  if (XLogRecHasBlockRef(record, 3))
858  {
859  /*
860  * There is no real data on the page, so we just re-create it from
861  * scratch using the information from the WAL record.
862  *
863  * Note that we don't end up here when the target page is also the
864  * leafbuf page. There is no need to add a dummy hikey item with a
865  * top parent link when deleting leafbuf because it's the last page
866  * we'll delete in the subtree undergoing deletion.
867  */
868  Buffer leafbuf;
869  IndexTupleData trunctuple;
870 
871  leafbuf = XLogInitBufferForRedo(record, 3);
872  page = (Page) BufferGetPage(leafbuf);
873 
874  _bt_pageinit(page, BufferGetPageSize(leafbuf));
875  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
876 
877  pageop->btpo_flags = BTP_HALF_DEAD | BTP_LEAF;
878  pageop->btpo_prev = xlrec->leafleftsib;
879  pageop->btpo_next = xlrec->leafrightsib;
880  pageop->btpo.level = 0;
881  pageop->btpo_cycleid = 0;
882 
883  /* Add a dummy hikey item */
884  MemSet(&trunctuple, 0, sizeof(IndexTupleData));
885  trunctuple.t_info = sizeof(IndexTupleData);
886  BTreeTupleSetTopParent(&trunctuple, xlrec->topparent);
887 
888  if (PageAddItem(page, (Item) &trunctuple, sizeof(IndexTupleData), P_HIKEY,
889  false, false) == InvalidOffsetNumber)
890  elog(ERROR, "could not add dummy high key to half-dead page");
891 
892  PageSetLSN(page, lsn);
893  MarkBufferDirty(leafbuf);
894  UnlockReleaseBuffer(leafbuf);
895  }
896 
897  /* Update metapage if needed */
898  if (info == XLOG_BTREE_UNLINK_PAGE_META)
899  _bt_restore_meta(record, 4);
900 }
901 
902 static void
904 {
905  XLogRecPtr lsn = record->EndRecPtr;
906  xl_btree_newroot *xlrec = (xl_btree_newroot *) XLogRecGetData(record);
907  Buffer buffer;
908  Page page;
909  BTPageOpaque pageop;
910  char *ptr;
911  Size len;
912 
913  buffer = XLogInitBufferForRedo(record, 0);
914  page = (Page) BufferGetPage(buffer);
915 
916  _bt_pageinit(page, BufferGetPageSize(buffer));
917  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
918 
919  pageop->btpo_flags = BTP_ROOT;
920  pageop->btpo_prev = pageop->btpo_next = P_NONE;
921  pageop->btpo.level = xlrec->level;
922  if (xlrec->level == 0)
923  pageop->btpo_flags |= BTP_LEAF;
924  pageop->btpo_cycleid = 0;
925 
926  if (xlrec->level > 0)
927  {
928  ptr = XLogRecGetBlockData(record, 0, &len);
929  _bt_restore_page(page, ptr, len);
930 
931  /* Clear the incomplete-split flag in left child */
932  _bt_clear_incomplete_split(record, 1);
933  }
934 
935  PageSetLSN(page, lsn);
936  MarkBufferDirty(buffer);
937  UnlockReleaseBuffer(buffer);
938 
939  _bt_restore_meta(record, 2);
940 }
941 
942 static void
944 {
946 
947  /*
948  * Btree reuse_page records exist to provide a conflict point when we
949  * reuse pages in the index via the FSM. That's all they do though.
950  *
951  * latestRemovedXid was the page's btpo.xact. The btpo.xact <
952  * RecentGlobalXmin test in _bt_page_recyclable() conceptually mirrors the
953  * pgxact->xmin > limitXmin test in GetConflictingVirtualXIDs().
954  * Consequently, one XID value achieves the same exclusion effect on
955  * primary and standby.
956  */
957  if (InHotStandby)
958  {
960  xlrec->node);
961  }
962 }
963 
964 void
966 {
967  uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
968  MemoryContext oldCtx;
969 
970  oldCtx = MemoryContextSwitchTo(opCtx);
971  switch (info)
972  {
974  btree_xlog_insert(true, false, false, record);
975  break;
977  btree_xlog_insert(false, false, false, record);
978  break;
980  btree_xlog_insert(false, true, false, record);
981  break;
982  case XLOG_BTREE_SPLIT_L:
983  btree_xlog_split(true, record);
984  break;
985  case XLOG_BTREE_SPLIT_R:
986  btree_xlog_split(false, record);
987  break;
989  btree_xlog_insert(true, false, true, record);
990  break;
991  case XLOG_BTREE_DEDUP:
992  btree_xlog_dedup(record);
993  break;
994  case XLOG_BTREE_VACUUM:
995  btree_xlog_vacuum(record);
996  break;
997  case XLOG_BTREE_DELETE:
998  btree_xlog_delete(record);
999  break;
1001  btree_xlog_mark_page_halfdead(info, record);
1002  break;
1005  btree_xlog_unlink_page(info, record);
1006  break;
1007  case XLOG_BTREE_NEWROOT:
1008  btree_xlog_newroot(record);
1009  break;
1010  case XLOG_BTREE_REUSE_PAGE:
1011  btree_xlog_reuse_page(record);
1012  break;
1014  _bt_restore_meta(record, 0);
1015  break;
1016  default:
1017  elog(PANIC, "btree_redo: unknown op code %u", info);
1018  }
1019  MemoryContextSwitchTo(oldCtx);
1020  MemoryContextReset(opCtx);
1021 }
1022 
1023 void
1025 {
1027  "Btree recovery temporary context",
1029 }
1030 
1031 void
1033 {
1034  MemoryContextDelete(opCtx);
1035  opCtx = NULL;
1036 }
1037 
1038 /*
1039  * Mask a btree page before performing consistency checks on it.
1040  */
1041 void
1042 btree_mask(char *pagedata, BlockNumber blkno)
1043 {
1044  Page page = (Page) pagedata;
1045  BTPageOpaque maskopaq;
1046 
1048 
1049  mask_page_hint_bits(page);
1050  mask_unused_space(page);
1051 
1052  maskopaq = (BTPageOpaque) PageGetSpecialPointer(page);
1053 
1054  if (P_ISDELETED(maskopaq))
1055  {
1056  /*
1057  * Mask page content on a DELETED page since it will be re-initialized
1058  * during replay. See btree_xlog_unlink_page() for details.
1059  */
1060  mask_page_content(page);
1061  }
1062  else if (P_ISLEAF(maskopaq))
1063  {
1064  /*
1065  * In btree leaf pages, it is possible to modify the LP_FLAGS without
1066  * emitting any WAL record. Hence, mask the line pointer flags. See
1067  * _bt_killitems(), _bt_check_unique() for details.
1068  */
1069  mask_lp_flags(page);
1070  }
1071 
1072  /*
1073  * BTP_HAS_GARBAGE is just an un-logged hint bit. So, mask it. See
1074  * _bt_killitems(), _bt_check_unique() for details.
1075  */
1076  maskopaq->btpo_flags &= ~BTP_HAS_GARBAGE;
1077 
1078  /*
1079  * During replay of a btree page split, we don't set the BTP_SPLIT_END
1080  * flag of the right sibling and initialize the cycle_id to 0 for the same
1081  * page. See btree_xlog_split() for details.
1082  */
1083  maskopaq->btpo_flags &= ~BTP_SPLIT_END;
1084  maskopaq->btpo_cycleid = 0;
1085 }
char * intervals[]
bool allequalimage
Definition: nbtxlog.h:57
TransactionId latestRemovedXid
Definition: nbtxlog.h:189
IndexTuple base
Definition: nbtree.h:746
uint16 ndeletedtids
Definition: nbtree.h:782
#define BTP_ROOT
Definition: nbtree.h:73
#define BTP_SPLIT_END
Definition: nbtree.h:77
BlockNumber btpo_next
Definition: nbtree.h:59
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:211
#define AllocSetContextCreate
Definition: memutils.h:170
void _bt_update_posting(BTVacuumPosting vacposting)
Definition: nbtdedup.c:676
void PageRestoreTempPage(Page tempPage, Page oldPage)
Definition: bufpage.c:403
uint16 nintervals
Definition: nbtxlog.h:172
OffsetNumber baseoff
Definition: nbtree.h:747
uint32 btm_version
Definition: nbtree.h:101
#define SizeOfBtreeUpdate
Definition: nbtxlog.h:225
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:719
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1469
static void btree_xlog_vacuum(XLogReaderState *record)
Definition: nbtxlog.c:559
OffsetNumber updatedoffset
Definition: nbtree.h:779
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:244
uint32 btm_magic
Definition: nbtree.h:100
#define BTP_LEAF
Definition: nbtree.h:72
#define BTP_HALF_DEAD
Definition: nbtree.h:76
BlockNumber root
Definition: nbtxlog.h:51
IndexTuple itup
Definition: nbtree.h:778
union BTPageOpaqueData::@45 btpo
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
unsigned char uint8
Definition: c.h:372
Pointer Item
Definition: item.h:17
#define P_NONE
Definition: nbtree.h:206
static BlockNumber BTreeTupleGetDownLink(IndexTuple pivot)
Definition: nbtree.h:424
void mask_page_hint_bits(Page page)
Definition: bufmask.c:46
#define InvalidBuffer
Definition: buf.h:25
#define XLOG_BTREE_INSERT_META
Definition: nbtxlog.h:28
#define P_HAS_GARBAGE(opaque)
Definition: nbtree.h:220
RelFileNode node
Definition: nbtxlog.h:206
#define XLogRecHasBlockRef(decoder, block_id)
Definition: xlogreader.h:313
#define InHotStandby
Definition: xlog.h:74
uint32 level
Definition: nbtxlog.h:319
#define BTP_INCOMPLETE_SPLIT
Definition: nbtree.h:79
#define MemSet(start, val, len)
Definition: c.h:978
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:416
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:136
uint32 BlockNumber
Definition: block.h:31
void _bt_dedup_start_pending(BTDedupState state, IndexTuple base, OffsetNumber baseoff)
Definition: nbtdedup.c:324
uint16 nupdated
Definition: nbtxlog.h:243
#define P_INCOMPLETE_SPLIT(opaque)
Definition: nbtree.h:221
#define BTP_DELETED
Definition: nbtree.h:74
void btree_redo(XLogReaderState *record)
Definition: nbtxlog.c:965
static void btree_xlog_delete(XLogReaderState *record)
Definition: nbtxlog.c:646
#define PANIC
Definition: elog.h:53
IndexTuple _bt_swap_posting(IndexTuple newitem, IndexTuple oposting, int postingoff)
Definition: nbtdedup.c:774
Size _bt_dedup_finish_pending(Page newpage, BTDedupState state)
Definition: nbtdedup.c:446
#define PageGetMaxOffsetNumber(page)
Definition: bufpage.h:357
TransactionId xact
Definition: nbtree.h:63
#define BTP_META
Definition: nbtree.h:75
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:69
uint32 ndeleted
Definition: nbtxlog.h:190
void mask_unused_space(Page page)
Definition: bufmask.c:71
XLogRecPtr EndRecPtr
Definition: xlogreader.h:176
uint16 OffsetNumber
Definition: off.h:24
Page PageGetTempPageCopySpecial(Page page)
Definition: bufpage.c:381
void mask_page_content(Page page)
Definition: bufmask.c:119
ItemPointer htids
Definition: nbtree.h:751
BlockNumber btm_fastroot
Definition: nbtree.h:104
#define XLOG_BTREE_NEWROOT
Definition: nbtxlog.h:36
unsigned short uint16
Definition: c.h:373
void pfree(void *pointer)
Definition: mcxt.c:1056
#define ItemIdGetLength(itemId)
Definition: itemid.h:59
#define BTREE_MAGIC
Definition: nbtree.h:142
static void btree_xlog_newroot(XLogReaderState *record)
Definition: nbtxlog.c:903
static void _bt_restore_meta(XLogReaderState *record, uint8 block_id)
Definition: nbtxlog.c:85
#define XLogRecGetData(decoder)
Definition: xlogreader.h:310
Size phystupsize
Definition: nbtree.h:754
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3541
#define ERROR
Definition: elog.h:43
static void btree_xlog_reuse_page(XLogReaderState *record)
Definition: nbtxlog.c:943
float8 last_cleanup_num_heap_tuples
Definition: nbtxlog.h:56
Buffer XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id)
Definition: xlogutils.c:306
BTDedupInterval intervals[MaxIndexTuplesPerPage]
Definition: nbtree.h:763
#define XLOG_BTREE_INSERT_LEAF
Definition: nbtxlog.h:26
OffsetNumber newitemoff
Definition: nbtxlog.h:157
BTCycleId btpo_cycleid
Definition: nbtree.h:66
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:192
TransactionId oldest_btpo_xact
Definition: nbtxlog.h:55
#define BTPageGetMeta(p)
Definition: nbtree.h:114
bool btm_allequalimage
Definition: nbtree.h:111
bool PageIndexTupleOverwrite(Page page, OffsetNumber offnum, Item newtup, Size newsize)
Definition: bufpage.c:1060
BlockNumber btpo_prev
Definition: nbtree.h:58
IndexTuple CopyIndexTuple(IndexTuple source)
Definition: indextuple.c:510
static void _bt_clear_incomplete_split(XLogReaderState *record, uint8 block_id)
Definition: nbtxlog.c:142
static char * buf
Definition: pg_test_fsync.c:67
bool _bt_dedup_save_htid(BTDedupState state, IndexTuple itup)
Definition: nbtdedup.c:375
IndexTupleData * IndexTuple
Definition: itup.h:53
#define XLOG_BTREE_VACUUM
Definition: nbtxlog.h:38
#define XLOG_BTREE_UNLINK_PAGE
Definition: nbtxlog.h:34
static void btree_xlog_dedup(XLogReaderState *record)
Definition: nbtxlog.c:466
MemoryContext CurrentMemoryContext
Definition: mcxt.c:38
static void BTreeTupleSetDownLink(IndexTuple pivot, BlockNumber blkno)
Definition: nbtree.h:430
#define BTREE_NOVAC_VERSION
Definition: nbtree.h:145
#define BufferGetPage(buffer)
Definition: bufmgr.h:169
static void btree_xlog_insert(bool isleaf, bool ismeta, bool posting, XLogReaderState *record)
Definition: nbtxlog.c:163
#define XLogRecGetInfo(decoder)
Definition: xlogreader.h:305
#define BTREE_METAPAGE
Definition: nbtree.h:141
#define P_ISDELETED(opaque)
Definition: nbtree.h:216
uint32 version
Definition: nbtxlog.h:50
#define XLOG_BTREE_DELETE
Definition: nbtxlog.h:33
uint16 ndeleted
Definition: nbtxlog.h:242
#define XLOG_BTREE_DEDUP
Definition: nbtxlog.h:32
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:235
uint32 btm_fastlevel
Definition: nbtree.h:105
uint32 level
Definition: nbtree.h:62
bool XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id, RelFileNode *rnode, ForkNumber *forknum, BlockNumber *blknum)
Definition: xlogreader.c:1497
char * XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len)
Definition: xlogreader.c:1521
#define XLOG_BTREE_REUSE_PAGE
Definition: nbtxlog.h:40
void mask_page_lsn_and_checksum(Page page)
Definition: bufmask.c:31
uint32 level
Definition: nbtxlog.h:155
#define XLOG_BTREE_MARK_PAGE_HALFDEAD
Definition: nbtxlog.h:37
uint16 deletetids[FLEXIBLE_ARRAY_MEMBER]
Definition: nbtree.h:783
OffsetNumber offnum
Definition: nbtxlog.h:81
struct IndexTupleData IndexTupleData
static void _bt_restore_page(Page page, char *from, int len)
Definition: nbtxlog.c:38
#define BufferGetPageSize(buffer)
Definition: bufmgr.h:156
BlockNumber btm_root
Definition: nbtree.h:102
#define InvalidOffsetNumber
Definition: off.h:26
#define XLOG_BTREE_SPLIT_R
Definition: nbtxlog.h:30
void btree_xlog_cleanup(void)
Definition: nbtxlog.c:1032
XLogRedoAction XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id, Buffer *buf)
Definition: xlogutils.c:294
static MemoryContext opCtx
Definition: nbtxlog.c:27
uint16 nitems
Definition: nbtree.h:717
PageHeaderData * PageHeader
Definition: bufpage.h:166
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:745
#define XLR_INFO_MASK
Definition: xlogrecord.h:62
Definition: regguts.h:298
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:828
#define XLOG_BTREE_INSERT_POST
Definition: nbtxlog.h:31
#define OffsetNumberNext(offsetNumber)
Definition: off.h:52
size_t Size
Definition: c.h:473
#define PageGetSpecialPointer(page)
Definition: bufpage.h:326
#define OffsetNumberPrev(offsetNumber)
Definition: off.h:54
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:109
#define MAXALIGN(LEN)
Definition: c.h:698
#define BufferIsValid(bufnum)
Definition: bufmgr.h:123
static void btree_xlog_unlink_page(uint8 info, XLogReaderState *record)
Definition: nbtxlog.c:771
Size basetupsize
Definition: nbtree.h:748
#define XLOG_BTREE_INSERT_UPPER
Definition: nbtxlog.h:27
Size maxpostingsize
Definition: nbtree.h:743
#define BTMaxItemSize(page)
Definition: nbtree.h:157
#define P_HIKEY
Definition: nbtree.h:242
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2668
#define MaxIndexTuplesPerPage
Definition: itup.h:145
void * palloc(Size size)
Definition: mcxt.c:949
static void btree_xlog_mark_page_halfdead(uint8 info, XLogReaderState *record)
Definition: nbtxlog.c:691
bool deduplicate
Definition: nbtree.h:741
XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record, uint8 block_id, ReadBufferMode mode, bool get_cleanup_lock, Buffer *buf)
Definition: xlogutils.c:331
uint32 fastlevel
Definition: nbtxlog.h:54
uint32 btm_level
Definition: nbtree.h:103
#define elog(elevel,...)
Definition: elog.h:214
uint32 level
Definition: nbtxlog.h:52
int i
void _bt_pageinit(Page page, Size size)
Definition: nbtpage.c:1066
void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid, RelFileNode node)
Definition: standby.c:304
#define XLOG_BTREE_SPLIT_L
Definition: nbtxlog.h:29
BlockNumber fastroot
Definition: nbtxlog.h:53
#define XLOG_BTREE_UNLINK_PAGE_META
Definition: nbtxlog.h:35
TransactionId latestRemovedXid
Definition: nbtxlog.h:208
TransactionId btm_oldest_btpo_xact
Definition: nbtree.h:107
unsigned short t_info
Definition: itup.h:49
BTDedupStateData * BTDedupState
Definition: nbtree.h:766
uint16 postingoff
Definition: nbtxlog.h:158
static void btree_xlog_split(bool newitemonleft, XLogReaderState *record)
Definition: nbtxlog.c:254
OffsetNumber baseoff
Definition: nbtree.h:716
uint16 btpo_flags
Definition: nbtree.h:65
OffsetNumber firstrightoff
Definition: nbtxlog.h:156
#define PageSetLSN(page, lsn)
Definition: bufpage.h:368
int Buffer
Definition: buf.h:23
#define P_RIGHTMOST(opaque)
Definition: nbtree.h:213
static void BTreeTupleSetTopParent(IndexTuple leafhikey, BlockNumber blkno)
Definition: nbtree.h:494
void btree_xlog_startup(void)
Definition: nbtxlog.c:1024
#define XLOG_BTREE_META_CLEANUP
Definition: nbtxlog.h:42
void mask_lp_flags(Page page)
Definition: bufmask.c:95
#define offsetof(type, field)
Definition: c.h:668
void btree_mask(char *pagedata, BlockNumber blkno)
Definition: nbtxlog.c:1042
#define BTP_HAS_GARBAGE
Definition: nbtree.h:78
uint16 ndeletedtids
Definition: nbtxlog.h:220
#define PageGetItem(page, itemId)
Definition: bufpage.h:340
Pointer Page
Definition: bufpage.h:78
#define IndexTupleSize(itup)
Definition: itup.h:71
#define P_ISLEAF(opaque)
Definition: nbtree.h:214