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-2018, 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/heapam_xlog.h"
19 #include "access/nbtree.h"
20 #include "access/nbtxlog.h"
21 #include "access/transam.h"
22 #include "access/xlog.h"
23 #include "access/xlogutils.h"
24 #include "storage/procarray.h"
25 #include "miscadmin.h"
26 
27 /*
28  * _bt_restore_page -- re-enter all the index tuples on a page
29  *
30  * The page is freshly init'd, and *from (length len) is a copy of what
31  * had been its upper part (pd_upper to pd_special). We assume that the
32  * tuples had been added to the page in item-number order, and therefore
33  * the one with highest item number appears first (lowest on the page).
34  */
35 static void
36 _bt_restore_page(Page page, char *from, int len)
37 {
38  IndexTupleData itupdata;
39  Size itemsz;
40  char *end = from + len;
42  uint16 itemsizes[MaxIndexTuplesPerPage];
43  int i;
44  int nitems;
45 
46  /*
47  * To get the items back in the original order, we add them to the page in
48  * reverse. To figure out where one tuple ends and another begins, we
49  * have to scan them in forward order first.
50  */
51  i = 0;
52  while (from < end)
53  {
54  /*
55  * As we step through the items, 'from' won't always be properly
56  * aligned, so we need to use memcpy(). Further, we use Item (which
57  * is just a char*) here for our items array for the same reason;
58  * wouldn't want the compiler or anyone thinking that an item is
59  * aligned when it isn't.
60  */
61  memcpy(&itupdata, from, sizeof(IndexTupleData));
62  itemsz = IndexTupleSize(&itupdata);
63  itemsz = MAXALIGN(itemsz);
64 
65  items[i] = (Item) from;
66  itemsizes[i] = itemsz;
67  i++;
68 
69  from += itemsz;
70  }
71  nitems = i;
72 
73  for (i = nitems - 1; i >= 0; i--)
74  {
75  if (PageAddItem(page, items[i], itemsizes[i], nitems - i,
76  false, false) == InvalidOffsetNumber)
77  elog(PANIC, "_bt_restore_page: cannot add item to page");
78  from += itemsz;
79  }
80 }
81 
82 static void
84 {
85  XLogRecPtr lsn = record->EndRecPtr;
86  Buffer metabuf;
87  Page metapg;
88  BTMetaPageData *md;
89  BTPageOpaque pageop;
90  xl_btree_metadata *xlrec;
91  char *ptr;
92  Size len;
93 
94  metabuf = XLogInitBufferForRedo(record, block_id);
95  ptr = XLogRecGetBlockData(record, block_id, &len);
96 
97  Assert(len == sizeof(xl_btree_metadata));
99  xlrec = (xl_btree_metadata *) ptr;
100  metapg = BufferGetPage(metabuf);
101 
102  _bt_pageinit(metapg, BufferGetPageSize(metabuf));
103 
104  md = BTPageGetMeta(metapg);
105  md->btm_magic = BTREE_MAGIC;
107  md->btm_root = xlrec->root;
108  md->btm_level = xlrec->level;
109  md->btm_fastroot = xlrec->fastroot;
110  md->btm_fastlevel = xlrec->fastlevel;
113 
114  pageop = (BTPageOpaque) PageGetSpecialPointer(metapg);
115  pageop->btpo_flags = BTP_META;
116 
117  /*
118  * Set pd_lower just past the end of the metadata. This is essential,
119  * because without doing so, metadata will be lost if xlog.c compresses
120  * the page.
121  */
122  ((PageHeader) metapg)->pd_lower =
123  ((char *) md + sizeof(BTMetaPageData)) - (char *) metapg;
124 
125  PageSetLSN(metapg, lsn);
126  MarkBufferDirty(metabuf);
127  UnlockReleaseBuffer(metabuf);
128 }
129 
130 /*
131  * _bt_clear_incomplete_split -- clear INCOMPLETE_SPLIT flag on a page
132  *
133  * This is a common subroutine of the redo functions of all the WAL record
134  * types that can insert a downlink: insert, split, and newroot.
135  */
136 static void
138 {
139  XLogRecPtr lsn = record->EndRecPtr;
140  Buffer buf;
141 
142  if (XLogReadBufferForRedo(record, block_id, &buf) == BLK_NEEDS_REDO)
143  {
144  Page page = (Page) BufferGetPage(buf);
146 
147  Assert(P_INCOMPLETE_SPLIT(pageop));
148  pageop->btpo_flags &= ~BTP_INCOMPLETE_SPLIT;
149 
150  PageSetLSN(page, lsn);
151  MarkBufferDirty(buf);
152  }
153  if (BufferIsValid(buf))
154  UnlockReleaseBuffer(buf);
155 }
156 
157 static void
158 btree_xlog_insert(bool isleaf, bool ismeta, XLogReaderState *record)
159 {
160  XLogRecPtr lsn = record->EndRecPtr;
161  xl_btree_insert *xlrec = (xl_btree_insert *) XLogRecGetData(record);
162  Buffer buffer;
163  Page page;
164 
165  /*
166  * Insertion to an internal page finishes an incomplete split at the child
167  * level. Clear the incomplete-split flag in the child. Note: during
168  * normal operation, the child and parent pages are locked at the same
169  * time, so that clearing the flag and inserting the downlink appear
170  * atomic to other backends. We don't bother with that during replay,
171  * because readers don't care about the incomplete-split flag and there
172  * cannot be updates happening.
173  */
174  if (!isleaf)
175  _bt_clear_incomplete_split(record, 1);
176  if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
177  {
178  Size datalen;
179  char *datapos = XLogRecGetBlockData(record, 0, &datalen);
180 
181  page = BufferGetPage(buffer);
182 
183  if (PageAddItem(page, (Item) datapos, datalen, xlrec->offnum,
184  false, false) == InvalidOffsetNumber)
185  elog(PANIC, "btree_insert_redo: failed to add item");
186 
187  PageSetLSN(page, lsn);
188  MarkBufferDirty(buffer);
189  }
190  if (BufferIsValid(buffer))
191  UnlockReleaseBuffer(buffer);
192 
193  /*
194  * Note: in normal operation, we'd update the metapage while still holding
195  * lock on the page we inserted into. But during replay it's not
196  * necessary to hold that lock, since no other index updates can be
197  * happening concurrently, and readers will cope fine with following an
198  * obsolete link from the metapage.
199  */
200  if (ismeta)
201  _bt_restore_meta(record, 2);
202 }
203 
204 static void
205 btree_xlog_split(bool onleft, bool lhighkey, XLogReaderState *record)
206 {
207  XLogRecPtr lsn = record->EndRecPtr;
208  xl_btree_split *xlrec = (xl_btree_split *) XLogRecGetData(record);
209  bool isleaf = (xlrec->level == 0);
210  Buffer lbuf;
211  Buffer rbuf;
212  Page rpage;
213  BTPageOpaque ropaque;
214  char *datapos;
215  Size datalen;
216  IndexTuple left_hikey = NULL;
217  Size left_hikeysz = 0;
218  BlockNumber leftsib;
219  BlockNumber rightsib;
220  BlockNumber rnext;
221 
222  XLogRecGetBlockTag(record, 0, NULL, NULL, &leftsib);
223  XLogRecGetBlockTag(record, 1, NULL, NULL, &rightsib);
224  if (!XLogRecGetBlockTag(record, 2, NULL, NULL, &rnext))
225  rnext = P_NONE;
226 
227  /*
228  * Clear the incomplete split flag on the left sibling of the child page
229  * this is a downlink for. (Like in btree_xlog_insert, this can be done
230  * before locking the other pages)
231  */
232  if (!isleaf)
233  _bt_clear_incomplete_split(record, 3);
234 
235  /* Reconstruct right (new) sibling page from scratch */
236  rbuf = XLogInitBufferForRedo(record, 1);
237  datapos = XLogRecGetBlockData(record, 1, &datalen);
238  rpage = (Page) BufferGetPage(rbuf);
239 
240  _bt_pageinit(rpage, BufferGetPageSize(rbuf));
241  ropaque = (BTPageOpaque) PageGetSpecialPointer(rpage);
242 
243  ropaque->btpo_prev = leftsib;
244  ropaque->btpo_next = rnext;
245  ropaque->btpo.level = xlrec->level;
246  ropaque->btpo_flags = isleaf ? BTP_LEAF : 0;
247  ropaque->btpo_cycleid = 0;
248 
249  _bt_restore_page(rpage, datapos, datalen);
250 
251  /*
252  * When the high key isn't present is the wal record, then we assume it to
253  * be equal to the first key on the right page. It must be from the leaf
254  * level.
255  */
256  if (!lhighkey)
257  {
258  ItemId hiItemId = PageGetItemId(rpage, P_FIRSTDATAKEY(ropaque));
259 
260  Assert(isleaf);
261  left_hikey = (IndexTuple) PageGetItem(rpage, hiItemId);
262  left_hikeysz = ItemIdGetLength(hiItemId);
263  }
264 
265  PageSetLSN(rpage, lsn);
266  MarkBufferDirty(rbuf);
267 
268  /* don't release the buffer yet; we touch right page's first item below */
269 
270  /* Now reconstruct left (original) sibling page */
271  if (XLogReadBufferForRedo(record, 0, &lbuf) == BLK_NEEDS_REDO)
272  {
273  /*
274  * To retain the same physical order of the tuples that they had, we
275  * initialize a temporary empty page for the left page and add all the
276  * items to that in item number order. This mirrors how _bt_split()
277  * works. It's not strictly required to retain the same physical
278  * order, as long as the items are in the correct item number order,
279  * but it helps debugging. See also _bt_restore_page(), which does
280  * the same for the right page.
281  */
282  Page lpage = (Page) BufferGetPage(lbuf);
284  OffsetNumber off;
285  IndexTuple newitem = NULL;
286  Size newitemsz = 0;
287  Page newlpage;
288  OffsetNumber leftoff;
289 
290  datapos = XLogRecGetBlockData(record, 0, &datalen);
291 
292  if (onleft)
293  {
294  newitem = (IndexTuple) datapos;
295  newitemsz = MAXALIGN(IndexTupleSize(newitem));
296  datapos += newitemsz;
297  datalen -= newitemsz;
298  }
299 
300  /* Extract left hikey and its size (assuming 16-bit alignment) */
301  if (lhighkey)
302  {
303  left_hikey = (IndexTuple) datapos;
304  left_hikeysz = MAXALIGN(IndexTupleSize(left_hikey));
305  datapos += left_hikeysz;
306  datalen -= left_hikeysz;
307  }
308 
309  Assert(datalen == 0);
310 
311  newlpage = PageGetTempPageCopySpecial(lpage);
312 
313  /* Set high key */
314  leftoff = P_HIKEY;
315  if (PageAddItem(newlpage, (Item) left_hikey, left_hikeysz,
316  P_HIKEY, false, false) == InvalidOffsetNumber)
317  elog(PANIC, "failed to add high key to left page after split");
318  leftoff = OffsetNumberNext(leftoff);
319 
320  for (off = P_FIRSTDATAKEY(lopaque); off < xlrec->firstright; off++)
321  {
322  ItemId itemid;
323  Size itemsz;
324  IndexTuple item;
325 
326  /* add the new item if it was inserted on left page */
327  if (onleft && off == xlrec->newitemoff)
328  {
329  if (PageAddItem(newlpage, (Item) newitem, newitemsz, leftoff,
330  false, false) == InvalidOffsetNumber)
331  elog(ERROR, "failed to add new item to left page after split");
332  leftoff = OffsetNumberNext(leftoff);
333  }
334 
335  itemid = PageGetItemId(lpage, off);
336  itemsz = ItemIdGetLength(itemid);
337  item = (IndexTuple) PageGetItem(lpage, itemid);
338  if (PageAddItem(newlpage, (Item) item, itemsz, leftoff,
339  false, false) == InvalidOffsetNumber)
340  elog(ERROR, "failed to add old item to left page after split");
341  leftoff = OffsetNumberNext(leftoff);
342  }
343 
344  /* cope with possibility that newitem goes at the end */
345  if (onleft && off == xlrec->newitemoff)
346  {
347  if (PageAddItem(newlpage, (Item) newitem, newitemsz, leftoff,
348  false, false) == InvalidOffsetNumber)
349  elog(ERROR, "failed to add new item to left page after split");
350  leftoff = OffsetNumberNext(leftoff);
351  }
352 
353  PageRestoreTempPage(newlpage, lpage);
354 
355  /* Fix opaque fields */
356  lopaque->btpo_flags = BTP_INCOMPLETE_SPLIT;
357  if (isleaf)
358  lopaque->btpo_flags |= BTP_LEAF;
359  lopaque->btpo_next = rightsib;
360  lopaque->btpo_cycleid = 0;
361 
362  PageSetLSN(lpage, lsn);
363  MarkBufferDirty(lbuf);
364  }
365 
366  /* We no longer need the buffers */
367  if (BufferIsValid(lbuf))
368  UnlockReleaseBuffer(lbuf);
369  UnlockReleaseBuffer(rbuf);
370 
371  /*
372  * Fix left-link of the page to the right of the new right sibling.
373  *
374  * Note: in normal operation, we do this while still holding lock on the
375  * two split pages. However, that's not necessary for correctness in WAL
376  * replay, because no other index update can be in progress, and readers
377  * will cope properly when following an obsolete left-link.
378  */
379  if (rnext != P_NONE)
380  {
381  Buffer buffer;
382 
383  if (XLogReadBufferForRedo(record, 2, &buffer) == BLK_NEEDS_REDO)
384  {
385  Page page = (Page) BufferGetPage(buffer);
387 
388  pageop->btpo_prev = rightsib;
389 
390  PageSetLSN(page, lsn);
391  MarkBufferDirty(buffer);
392  }
393  if (BufferIsValid(buffer))
394  UnlockReleaseBuffer(buffer);
395  }
396 }
397 
398 static void
400 {
401  XLogRecPtr lsn = record->EndRecPtr;
402  Buffer buffer;
403  Page page;
404  BTPageOpaque opaque;
405 #ifdef UNUSED
406  xl_btree_vacuum *xlrec = (xl_btree_vacuum *) XLogRecGetData(record);
407 
408  /*
409  * This section of code is thought to be no longer needed, after analysis
410  * of the calling paths. It is retained to allow the code to be reinstated
411  * if a flaw is revealed in that thinking.
412  *
413  * If we are running non-MVCC scans using this index we need to do some
414  * additional work to ensure correctness, which is known as a "pin scan"
415  * described in more detail in next paragraphs. We used to do the extra
416  * work in all cases, whereas we now avoid that work in most cases. If
417  * lastBlockVacuumed is set to InvalidBlockNumber then we skip the
418  * additional work required for the pin scan.
419  *
420  * Avoiding this extra work is important since it requires us to touch
421  * every page in the index, so is an O(N) operation. Worse, it is an
422  * operation performed in the foreground during redo, so it delays
423  * replication directly.
424  *
425  * If queries might be active then we need to ensure every leaf page is
426  * unpinned between the lastBlockVacuumed and the current block, if there
427  * are any. This prevents replay of the VACUUM from reaching the stage of
428  * removing heap tuples while there could still be indexscans "in flight"
429  * to those particular tuples for those scans which could be confused by
430  * finding new tuples at the old TID locations (see nbtree/README).
431  *
432  * It might be worth checking if there are actually any backends running;
433  * if not, we could just skip this.
434  *
435  * Since VACUUM can visit leaf pages out-of-order, it might issue records
436  * with lastBlockVacuumed >= block; that's not an error, it just means
437  * nothing to do now.
438  *
439  * Note: since we touch all pages in the range, we will lock non-leaf
440  * pages, and also any empty (all-zero) pages that may be in the index. It
441  * doesn't seem worth the complexity to avoid that. But it's important
442  * that HotStandbyActiveInReplay() will not return true if the database
443  * isn't yet consistent; so we need not fear reading still-corrupt blocks
444  * here during crash recovery.
445  */
447  {
448  RelFileNode thisrnode;
449  BlockNumber thisblkno;
450  BlockNumber blkno;
451 
452  XLogRecGetBlockTag(record, 0, &thisrnode, NULL, &thisblkno);
453 
454  for (blkno = xlrec->lastBlockVacuumed + 1; blkno < thisblkno; blkno++)
455  {
456  /*
457  * We use RBM_NORMAL_NO_LOG mode because it's not an error
458  * condition to see all-zero pages. The original btvacuumpage
459  * scan would have skipped over all-zero pages, noting them in FSM
460  * but not bothering to initialize them just yet; so we mustn't
461  * throw an error here. (We could skip acquiring the cleanup lock
462  * if PageIsNew, but it's probably not worth the cycles to test.)
463  *
464  * XXX we don't actually need to read the block, we just need to
465  * confirm it is unpinned. If we had a special call into the
466  * buffer manager we could optimise this so that if the block is
467  * not in shared_buffers we confirm it as unpinned. Optimizing
468  * this is now moot, since in most cases we avoid the scan.
469  */
470  buffer = XLogReadBufferExtended(thisrnode, MAIN_FORKNUM, blkno,
472  if (BufferIsValid(buffer))
473  {
474  LockBufferForCleanup(buffer);
475  UnlockReleaseBuffer(buffer);
476  }
477  }
478  }
479 #endif
480 
481  /*
482  * Like in btvacuumpage(), we need to take a cleanup lock on every leaf
483  * page. See nbtree/README for details.
484  */
485  if (XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &buffer)
486  == BLK_NEEDS_REDO)
487  {
488  char *ptr;
489  Size len;
490 
491  ptr = XLogRecGetBlockData(record, 0, &len);
492 
493  page = (Page) BufferGetPage(buffer);
494 
495  if (len > 0)
496  {
497  OffsetNumber *unused;
498  OffsetNumber *unend;
499 
500  unused = (OffsetNumber *) ptr;
501  unend = (OffsetNumber *) ((char *) ptr + len);
502 
503  if ((unend - unused) > 0)
504  PageIndexMultiDelete(page, unused, unend - unused);
505  }
506 
507  /*
508  * Mark the page as not containing any LP_DEAD items --- see comments
509  * in _bt_delitems_vacuum().
510  */
511  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
512  opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
513 
514  PageSetLSN(page, lsn);
515  MarkBufferDirty(buffer);
516  }
517  if (BufferIsValid(buffer))
518  UnlockReleaseBuffer(buffer);
519 }
520 
521 /*
522  * Get the latestRemovedXid from the heap pages pointed at by the index
523  * tuples being deleted. This puts the work for calculating latestRemovedXid
524  * into the recovery path rather than the primary path.
525  *
526  * It's possible that this generates a fair amount of I/O, since an index
527  * block may have hundreds of tuples being deleted. Repeat accesses to the
528  * same heap blocks are common, though are not yet optimised.
529  *
530  * XXX optimise later with something like XLogPrefetchBuffer()
531  */
532 static TransactionId
534 {
535  xl_btree_delete *xlrec = (xl_btree_delete *) XLogRecGetData(record);
536  OffsetNumber *unused;
537  Buffer ibuffer,
538  hbuffer;
539  Page ipage,
540  hpage;
541  RelFileNode rnode;
542  BlockNumber blkno;
543  ItemId iitemid,
544  hitemid;
545  IndexTuple itup;
546  HeapTupleHeader htuphdr;
547  BlockNumber hblkno;
548  OffsetNumber hoffnum;
549  TransactionId latestRemovedXid = InvalidTransactionId;
550  int i;
551 
552  /*
553  * If there's nothing running on the standby we don't need to derive a
554  * full latestRemovedXid value, so use a fast path out of here. This
555  * returns InvalidTransactionId, and so will conflict with all HS
556  * transactions; but since we just worked out that that's zero people,
557  * it's OK.
558  *
559  * XXX There is a race condition here, which is that a new backend might
560  * start just after we look. If so, it cannot need to conflict, but this
561  * coding will result in throwing a conflict anyway.
562  */
563  if (CountDBBackends(InvalidOid) == 0)
564  return latestRemovedXid;
565 
566  /*
567  * In what follows, we have to examine the previous state of the index
568  * page, as well as the heap page(s) it points to. This is only valid if
569  * WAL replay has reached a consistent database state; which means that
570  * the preceding check is not just an optimization, but is *necessary*. We
571  * won't have let in any user sessions before we reach consistency.
572  */
573  if (!reachedConsistency)
574  elog(PANIC, "btree_xlog_delete_get_latestRemovedXid: cannot operate with inconsistent data");
575 
576  /*
577  * Get index page. If the DB is consistent, this should not fail, nor
578  * should any of the heap page fetches below. If one does, we return
579  * InvalidTransactionId to cancel all HS transactions. That's probably
580  * overkill, but it's safe, and certainly better than panicking here.
581  */
582  XLogRecGetBlockTag(record, 0, &rnode, NULL, &blkno);
583  ibuffer = XLogReadBufferExtended(rnode, MAIN_FORKNUM, blkno, RBM_NORMAL);
584  if (!BufferIsValid(ibuffer))
585  return InvalidTransactionId;
586  LockBuffer(ibuffer, BT_READ);
587  ipage = (Page) BufferGetPage(ibuffer);
588 
589  /*
590  * Loop through the deleted index items to obtain the TransactionId from
591  * the heap items they point to.
592  */
593  unused = (OffsetNumber *) ((char *) xlrec + SizeOfBtreeDelete);
594 
595  for (i = 0; i < xlrec->nitems; i++)
596  {
597  /*
598  * Identify the index tuple about to be deleted
599  */
600  iitemid = PageGetItemId(ipage, unused[i]);
601  itup = (IndexTuple) PageGetItem(ipage, iitemid);
602 
603  /*
604  * Locate the heap page that the index tuple points at
605  */
606  hblkno = ItemPointerGetBlockNumber(&(itup->t_tid));
607  hbuffer = XLogReadBufferExtended(xlrec->hnode, MAIN_FORKNUM, hblkno, RBM_NORMAL);
608  if (!BufferIsValid(hbuffer))
609  {
610  UnlockReleaseBuffer(ibuffer);
611  return InvalidTransactionId;
612  }
613  LockBuffer(hbuffer, BT_READ);
614  hpage = (Page) BufferGetPage(hbuffer);
615 
616  /*
617  * Look up the heap tuple header that the index tuple points at by
618  * using the heap node supplied with the xlrec. We can't use
619  * heap_fetch, since it uses ReadBuffer rather than XLogReadBuffer.
620  * Note that we are not looking at tuple data here, just headers.
621  */
622  hoffnum = ItemPointerGetOffsetNumber(&(itup->t_tid));
623  hitemid = PageGetItemId(hpage, hoffnum);
624 
625  /*
626  * Follow any redirections until we find something useful.
627  */
628  while (ItemIdIsRedirected(hitemid))
629  {
630  hoffnum = ItemIdGetRedirect(hitemid);
631  hitemid = PageGetItemId(hpage, hoffnum);
633  }
634 
635  /*
636  * If the heap item has storage, then read the header and use that to
637  * set latestRemovedXid.
638  *
639  * Some LP_DEAD items may not be accessible, so we ignore them.
640  */
641  if (ItemIdHasStorage(hitemid))
642  {
643  htuphdr = (HeapTupleHeader) PageGetItem(hpage, hitemid);
644 
645  HeapTupleHeaderAdvanceLatestRemovedXid(htuphdr, &latestRemovedXid);
646  }
647  else if (ItemIdIsDead(hitemid))
648  {
649  /*
650  * Conjecture: if hitemid is dead then it had xids before the xids
651  * marked on LP_NORMAL items. So we just ignore this item and move
652  * onto the next, for the purposes of calculating
653  * latestRemovedxids.
654  */
655  }
656  else
657  Assert(!ItemIdIsUsed(hitemid));
658 
659  UnlockReleaseBuffer(hbuffer);
660  }
661 
662  UnlockReleaseBuffer(ibuffer);
663 
664  /*
665  * If all heap tuples were LP_DEAD then we will be returning
666  * InvalidTransactionId here, which avoids conflicts. This matches
667  * existing logic which assumes that LP_DEAD tuples must already be older
668  * than the latestRemovedXid on the cleanup record that set them as
669  * LP_DEAD, hence must already have generated a conflict.
670  */
671  return latestRemovedXid;
672 }
673 
674 static void
676 {
677  XLogRecPtr lsn = record->EndRecPtr;
678  xl_btree_delete *xlrec = (xl_btree_delete *) XLogRecGetData(record);
679  Buffer buffer;
680  Page page;
681  BTPageOpaque opaque;
682 
683  /*
684  * If we have any conflict processing to do, it must happen before we
685  * update the page.
686  *
687  * Btree delete records can conflict with standby queries. You might
688  * think that vacuum records would conflict as well, but we've handled
689  * that already. XLOG_HEAP2_CLEANUP_INFO records provide the highest xid
690  * cleaned by the vacuum of the heap and so we can resolve any conflicts
691  * just once when that arrives. After that we know that no conflicts
692  * exist from individual btree vacuum records on that index.
693  */
694  if (InHotStandby)
695  {
696  TransactionId latestRemovedXid = btree_xlog_delete_get_latestRemovedXid(record);
697  RelFileNode rnode;
698 
699  XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
700 
701  ResolveRecoveryConflictWithSnapshot(latestRemovedXid, rnode);
702  }
703 
704  /*
705  * We don't need to take a cleanup lock to apply these changes. See
706  * nbtree/README for details.
707  */
708  if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
709  {
710  page = (Page) BufferGetPage(buffer);
711 
712  if (XLogRecGetDataLen(record) > SizeOfBtreeDelete)
713  {
714  OffsetNumber *unused;
715 
716  unused = (OffsetNumber *) ((char *) xlrec + SizeOfBtreeDelete);
717 
718  PageIndexMultiDelete(page, unused, xlrec->nitems);
719  }
720 
721  /*
722  * Mark the page as not containing any LP_DEAD items --- see comments
723  * in _bt_delitems_delete().
724  */
725  opaque = (BTPageOpaque) PageGetSpecialPointer(page);
726  opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
727 
728  PageSetLSN(page, lsn);
729  MarkBufferDirty(buffer);
730  }
731  if (BufferIsValid(buffer))
732  UnlockReleaseBuffer(buffer);
733 }
734 
735 static void
737 {
738  XLogRecPtr lsn = record->EndRecPtr;
740  Buffer buffer;
741  Page page;
742  BTPageOpaque pageop;
743  IndexTupleData trunctuple;
744 
745  /*
746  * In normal operation, we would lock all the pages this WAL record
747  * touches before changing any of them. In WAL replay, it should be okay
748  * to lock just one page at a time, since no concurrent index updates can
749  * be happening, and readers should not care whether they arrive at the
750  * target page or not (since it's surely empty).
751  */
752 
753  /* parent page */
754  if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
755  {
756  OffsetNumber poffset;
757  ItemId itemid;
758  IndexTuple itup;
759  OffsetNumber nextoffset;
760  BlockNumber rightsib;
761 
762  page = (Page) BufferGetPage(buffer);
763  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
764 
765  poffset = xlrec->poffset;
766 
767  nextoffset = OffsetNumberNext(poffset);
768  itemid = PageGetItemId(page, nextoffset);
769  itup = (IndexTuple) PageGetItem(page, itemid);
770  rightsib = BTreeInnerTupleGetDownLink(itup);
771 
772  itemid = PageGetItemId(page, poffset);
773  itup = (IndexTuple) PageGetItem(page, itemid);
774  BTreeInnerTupleSetDownLink(itup, rightsib);
775  nextoffset = OffsetNumberNext(poffset);
776  PageIndexTupleDelete(page, nextoffset);
777 
778  PageSetLSN(page, lsn);
779  MarkBufferDirty(buffer);
780  }
781  if (BufferIsValid(buffer))
782  UnlockReleaseBuffer(buffer);
783 
784  /* Rewrite the leaf page as a halfdead page */
785  buffer = XLogInitBufferForRedo(record, 0);
786  page = (Page) BufferGetPage(buffer);
787 
788  _bt_pageinit(page, BufferGetPageSize(buffer));
789  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
790 
791  pageop->btpo_prev = xlrec->leftblk;
792  pageop->btpo_next = xlrec->rightblk;
793  pageop->btpo.level = 0;
794  pageop->btpo_flags = BTP_HALF_DEAD | BTP_LEAF;
795  pageop->btpo_cycleid = 0;
796 
797  /*
798  * Construct a dummy hikey item that points to the next parent to be
799  * deleted (if any).
800  */
801  MemSet(&trunctuple, 0, sizeof(IndexTupleData));
802  trunctuple.t_info = sizeof(IndexTupleData);
803  BTreeTupleSetTopParent(&trunctuple, xlrec->topparent);
804 
805  if (PageAddItem(page, (Item) &trunctuple, sizeof(IndexTupleData), P_HIKEY,
806  false, false) == InvalidOffsetNumber)
807  elog(ERROR, "could not add dummy high key to half-dead page");
808 
809  PageSetLSN(page, lsn);
810  MarkBufferDirty(buffer);
811  UnlockReleaseBuffer(buffer);
812 }
813 
814 
815 static void
817 {
818  XLogRecPtr lsn = record->EndRecPtr;
820  BlockNumber leftsib;
821  BlockNumber rightsib;
822  Buffer buffer;
823  Page page;
824  BTPageOpaque pageop;
825 
826  leftsib = xlrec->leftsib;
827  rightsib = xlrec->rightsib;
828 
829  /*
830  * In normal operation, we would lock all the pages this WAL record
831  * touches before changing any of them. In WAL replay, it should be okay
832  * to lock just one page at a time, since no concurrent index updates can
833  * be happening, and readers should not care whether they arrive at the
834  * target page or not (since it's surely empty).
835  */
836 
837  /* Fix left-link of right sibling */
838  if (XLogReadBufferForRedo(record, 2, &buffer) == BLK_NEEDS_REDO)
839  {
840  page = (Page) BufferGetPage(buffer);
841  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
842  pageop->btpo_prev = leftsib;
843 
844  PageSetLSN(page, lsn);
845  MarkBufferDirty(buffer);
846  }
847  if (BufferIsValid(buffer))
848  UnlockReleaseBuffer(buffer);
849 
850  /* Fix right-link of left sibling, if any */
851  if (leftsib != P_NONE)
852  {
853  if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
854  {
855  page = (Page) BufferGetPage(buffer);
856  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
857  pageop->btpo_next = rightsib;
858 
859  PageSetLSN(page, lsn);
860  MarkBufferDirty(buffer);
861  }
862  if (BufferIsValid(buffer))
863  UnlockReleaseBuffer(buffer);
864  }
865 
866  /* Rewrite target page as empty deleted page */
867  buffer = XLogInitBufferForRedo(record, 0);
868  page = (Page) BufferGetPage(buffer);
869 
870  _bt_pageinit(page, BufferGetPageSize(buffer));
871  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
872 
873  pageop->btpo_prev = leftsib;
874  pageop->btpo_next = rightsib;
875  pageop->btpo.xact = xlrec->btpo_xact;
876  pageop->btpo_flags = BTP_DELETED;
877  pageop->btpo_cycleid = 0;
878 
879  PageSetLSN(page, lsn);
880  MarkBufferDirty(buffer);
881  UnlockReleaseBuffer(buffer);
882 
883  /*
884  * If we deleted a parent of the targeted leaf page, instead of the leaf
885  * itself, update the leaf to point to the next remaining child in the
886  * branch.
887  */
888  if (XLogRecHasBlockRef(record, 3))
889  {
890  /*
891  * There is no real data on the page, so we just re-create it from
892  * scratch using the information from the WAL record.
893  */
894  IndexTupleData trunctuple;
895 
896  buffer = XLogInitBufferForRedo(record, 3);
897  page = (Page) BufferGetPage(buffer);
898 
899  _bt_pageinit(page, BufferGetPageSize(buffer));
900  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
901 
902  pageop->btpo_flags = BTP_HALF_DEAD | BTP_LEAF;
903  pageop->btpo_prev = xlrec->leafleftsib;
904  pageop->btpo_next = xlrec->leafrightsib;
905  pageop->btpo.level = 0;
906  pageop->btpo_cycleid = 0;
907 
908  /* Add a dummy hikey item */
909  MemSet(&trunctuple, 0, sizeof(IndexTupleData));
910  trunctuple.t_info = sizeof(IndexTupleData);
911  BTreeTupleSetTopParent(&trunctuple, xlrec->topparent);
912 
913  if (PageAddItem(page, (Item) &trunctuple, sizeof(IndexTupleData), P_HIKEY,
914  false, false) == InvalidOffsetNumber)
915  elog(ERROR, "could not add dummy high key to half-dead page");
916 
917  PageSetLSN(page, lsn);
918  MarkBufferDirty(buffer);
919  UnlockReleaseBuffer(buffer);
920  }
921 
922  /* Update metapage if needed */
923  if (info == XLOG_BTREE_UNLINK_PAGE_META)
924  _bt_restore_meta(record, 4);
925 }
926 
927 static void
929 {
930  XLogRecPtr lsn = record->EndRecPtr;
931  xl_btree_newroot *xlrec = (xl_btree_newroot *) XLogRecGetData(record);
932  Buffer buffer;
933  Page page;
934  BTPageOpaque pageop;
935  char *ptr;
936  Size len;
937 
938  buffer = XLogInitBufferForRedo(record, 0);
939  page = (Page) BufferGetPage(buffer);
940 
941  _bt_pageinit(page, BufferGetPageSize(buffer));
942  pageop = (BTPageOpaque) PageGetSpecialPointer(page);
943 
944  pageop->btpo_flags = BTP_ROOT;
945  pageop->btpo_prev = pageop->btpo_next = P_NONE;
946  pageop->btpo.level = xlrec->level;
947  if (xlrec->level == 0)
948  pageop->btpo_flags |= BTP_LEAF;
949  pageop->btpo_cycleid = 0;
950 
951  if (xlrec->level > 0)
952  {
953  ptr = XLogRecGetBlockData(record, 0, &len);
954  _bt_restore_page(page, ptr, len);
955 
956  /* Clear the incomplete-split flag in left child */
957  _bt_clear_incomplete_split(record, 1);
958  }
959 
960  PageSetLSN(page, lsn);
961  MarkBufferDirty(buffer);
962  UnlockReleaseBuffer(buffer);
963 
964  _bt_restore_meta(record, 2);
965 }
966 
967 static void
969 {
971 
972  /*
973  * Btree reuse_page records exist to provide a conflict point when we
974  * reuse pages in the index via the FSM. That's all they do though.
975  *
976  * latestRemovedXid was the page's btpo.xact. The btpo.xact <
977  * RecentGlobalXmin test in _bt_page_recyclable() conceptually mirrors the
978  * pgxact->xmin > limitXmin test in GetConflictingVirtualXIDs().
979  * Consequently, one XID value achieves the same exclusion effect on
980  * master and standby.
981  */
982  if (InHotStandby)
983  {
985  xlrec->node);
986  }
987 }
988 
989 void
991 {
992  uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
993 
994  switch (info)
995  {
997  btree_xlog_insert(true, false, record);
998  break;
1000  btree_xlog_insert(false, false, record);
1001  break;
1003  btree_xlog_insert(false, true, record);
1004  break;
1005  case XLOG_BTREE_SPLIT_L:
1006  btree_xlog_split(true, false, record);
1007  break;
1009  btree_xlog_split(true, true, record);
1010  break;
1011  case XLOG_BTREE_SPLIT_R:
1012  btree_xlog_split(false, false, record);
1013  break;
1015  btree_xlog_split(false, true, record);
1016  break;
1017  case XLOG_BTREE_VACUUM:
1018  btree_xlog_vacuum(record);
1019  break;
1020  case XLOG_BTREE_DELETE:
1021  btree_xlog_delete(record);
1022  break;
1024  btree_xlog_mark_page_halfdead(info, record);
1025  break;
1028  btree_xlog_unlink_page(info, record);
1029  break;
1030  case XLOG_BTREE_NEWROOT:
1031  btree_xlog_newroot(record);
1032  break;
1033  case XLOG_BTREE_REUSE_PAGE:
1034  btree_xlog_reuse_page(record);
1035  break;
1037  _bt_restore_meta(record, 0);
1038  break;
1039  default:
1040  elog(PANIC, "btree_redo: unknown op code %u", info);
1041  }
1042 }
1043 
1044 /*
1045  * Mask a btree page before performing consistency checks on it.
1046  */
1047 void
1048 btree_mask(char *pagedata, BlockNumber blkno)
1049 {
1050  Page page = (Page) pagedata;
1051  BTPageOpaque maskopaq;
1052 
1054 
1055  mask_page_hint_bits(page);
1056  mask_unused_space(page);
1057 
1058  maskopaq = (BTPageOpaque) PageGetSpecialPointer(page);
1059 
1060  if (P_ISDELETED(maskopaq))
1061  {
1062  /*
1063  * Mask page content on a DELETED page since it will be re-initialized
1064  * during replay. See btree_xlog_unlink_page() for details.
1065  */
1066  mask_page_content(page);
1067  }
1068  else if (P_ISLEAF(maskopaq))
1069  {
1070  /*
1071  * In btree leaf pages, it is possible to modify the LP_FLAGS without
1072  * emitting any WAL record. Hence, mask the line pointer flags. See
1073  * _bt_killitems(), _bt_check_unique() for details.
1074  */
1075  mask_lp_flags(page);
1076  }
1077 
1078  /*
1079  * BTP_HAS_GARBAGE is just an un-logged hint bit. So, mask it. See
1080  * _bt_killitems(), _bt_check_unique() for details.
1081  */
1082  maskopaq->btpo_flags &= ~BTP_HAS_GARBAGE;
1083 
1084  /*
1085  * During replay of a btree page split, we don't set the BTP_SPLIT_END
1086  * flag of the right sibling and initialize the cycle_id to 0 for the same
1087  * page. See btree_xlog_split() for details.
1088  */
1089  maskopaq->btpo_flags &= ~BTP_SPLIT_END;
1090  maskopaq->btpo_cycleid = 0;
1091 }
BlockNumber lastBlockVacuumed
Definition: nbtxlog.h:172
void HeapTupleHeaderAdvanceLatestRemovedXid(HeapTupleHeader tuple, TransactionId *latestRemovedXid)
Definition: heapam.c:7557
#define BTP_ROOT
Definition: nbtree.h:72
void LockBufferForCleanup(Buffer buffer)
Definition: bufmgr.c:3603
#define BTP_SPLIT_END
Definition: nbtree.h:76
BlockNumber btpo_next
Definition: nbtree.h:58
int CountDBBackends(Oid databaseid)
Definition: procarray.c:2747
void PageRestoreTempPage(Page tempPage, Page oldPage)
Definition: bufpage.c:407
#define ItemIdIsRedirected(itemId)
Definition: itemid.h:105
#define BTreeInnerTupleGetDownLink(itup)
Definition: nbtree.h:224
uint32 TransactionId
Definition: c.h:474
uint32 btm_version
Definition: nbtree.h:100
void PageIndexTupleDelete(Page page, OffsetNumber offnum)
Definition: bufpage.c:723
void MarkBufferDirty(Buffer buffer)
Definition: bufmgr.c:1450
HeapTupleHeaderData * HeapTupleHeader
Definition: htup.h:23
#define BTREE_VERSION
Definition: nbtree.h:117
static void btree_xlog_vacuum(XLogReaderState *record)
Definition: nbtxlog.c:399
#define ItemIdGetRedirect(itemId)
Definition: itemid.h:77
#define P_FIRSTDATAKEY(opaque)
Definition: nbtree.h:187
uint32 btm_magic
Definition: nbtree.h:99
#define BTP_LEAF
Definition: nbtree.h:71
RelFileNode hnode
Definition: nbtxlog.h:126
ItemPointerData t_tid
Definition: itup.h:37
#define BTP_HALF_DEAD
Definition: nbtree.h:75
union BTPageOpaqueData::@46 btpo
BlockNumber root
Definition: nbtxlog.h:50
#define ItemIdIsUsed(itemId)
Definition: itemid.h:91
Buffer XLogReadBufferExtended(RelFileNode rnode, ForkNumber forknum, BlockNumber blkno, ReadBufferMode mode)
Definition: xlogutils.c:437
unsigned char uint8
Definition: c.h:323
Pointer Item
Definition: item.h:17
#define P_NONE
Definition: nbtree.h:150
void mask_page_hint_bits(Page page)
Definition: bufmask.c:46
#define XLOG_BTREE_INSERT_META
Definition: nbtxlog.h:28
RelFileNode node
Definition: nbtxlog.h:140
#define XLogRecHasBlockRef(decoder, block_id)
Definition: xlogreader.h:233
#define InHotStandby
Definition: xlog.h:74
uint32 level
Definition: nbtxlog.h:246
#define BTP_INCOMPLETE_SPLIT
Definition: nbtree.h:78
#define MemSet(start, val, len)
Definition: c.h:908
#define PageAddItem(page, item, size, offsetNumber, overwrite, is_heap)
Definition: bufpage.h:412
uint32 BlockNumber
Definition: block.h:31
#define P_INCOMPLETE_SPLIT(opaque)
Definition: nbtree.h:165
#define BTP_DELETED
Definition: nbtree.h:73
void btree_redo(XLogReaderState *record)
Definition: nbtxlog.c:990
static void btree_xlog_delete(XLogReaderState *record)
Definition: nbtxlog.c:675
#define ItemIdIsDead(itemId)
Definition: itemid.h:112
#define PANIC
Definition: elog.h:53
bool HotStandbyActiveInReplay(void)
Definition: xlog.c:8030
TransactionId xact
Definition: nbtree.h:62
#define BTP_META
Definition: nbtree.h:74
BTPageOpaqueData * BTPageOpaque
Definition: nbtree.h:68
void mask_unused_space(Page page)
Definition: bufmask.c:71
XLogRecPtr EndRecPtr
Definition: xlogreader.h:120
uint16 OffsetNumber
Definition: off.h:24
Page PageGetTempPageCopySpecial(Page page)
Definition: bufpage.c:385
void mask_page_content(Page page)
Definition: bufmask.c:119
#define BT_READ
Definition: nbtree.h:300
BlockNumber btm_fastroot
Definition: nbtree.h:103
#define XLOG_BTREE_NEWROOT
Definition: nbtxlog.h:36
unsigned short uint16
Definition: c.h:324
#define ItemIdGetLength(itemId)
Definition: itemid.h:58
#define BTREE_MAGIC
Definition: nbtree.h:116
static void btree_xlog_newroot(XLogReaderState *record)
Definition: nbtxlog.c:928
static void _bt_restore_meta(XLogReaderState *record, uint8 block_id)
Definition: nbtxlog.c:83
#define XLogRecGetData(decoder)
Definition: xlogreader.h:230
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:3332
#define ERROR
Definition: elog.h:43
#define XLogRecGetDataLen(decoder)
Definition: xlogreader.h:231
static void btree_xlog_reuse_page(XLogReaderState *record)
Definition: nbtxlog.c:968
float8 last_cleanup_num_heap_tuples
Definition: nbtxlog.h:55
Buffer XLogInitBufferForRedo(XLogReaderState *record, uint8 block_id)
Definition: xlogutils.c:301
#define XLOG_BTREE_INSERT_LEAF
Definition: nbtxlog.h:26
OffsetNumber newitemoff
Definition: nbtxlog.h:112
#define BTreeTupleSetTopParent(itup, blkno)
Definition: nbtree.h:237
BTCycleId btpo_cycleid
Definition: nbtree.h:65
TransactionId oldest_btpo_xact
Definition: nbtxlog.h:54
#define BTPageGetMeta(p)
Definition: nbtree.h:112
#define XLOG_BTREE_SPLIT_L_HIGHKEY
Definition: nbtxlog.h:31
BlockNumber btpo_prev
Definition: nbtree.h:57
static void _bt_clear_incomplete_split(XLogReaderState *record, uint8 block_id)
Definition: nbtxlog.c:137
static char * buf
Definition: pg_test_fsync.c:67
IndexTupleData * IndexTuple
Definition: itup.h:53
#define XLOG_BTREE_VACUUM
Definition: nbtxlog.h:38
#define InvalidTransactionId
Definition: transam.h:31
#define XLOG_BTREE_UNLINK_PAGE
Definition: nbtxlog.h:34
#define BufferGetPage(buffer)
Definition: bufmgr.h:160
#define XLogRecGetInfo(decoder)
Definition: xlogreader.h:226
#define BTREE_METAPAGE
Definition: nbtree.h:115
#define P_ISDELETED(opaque)
Definition: nbtree.h:160
#define XLOG_BTREE_DELETE
Definition: nbtxlog.h:33
#define PageGetItemId(page, offsetNumber)
Definition: bufpage.h:231
uint32 btm_fastlevel
Definition: nbtree.h:104
uint32 level
Definition: nbtree.h:61
bool XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id, RelFileNode *rnode, ForkNumber *forknum, BlockNumber *blknum)
Definition: xlogreader.c:1358
char * XLogRecGetBlockData(XLogReaderState *record, uint8 block_id, Size *len)
Definition: xlogreader.c:1382
#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:110
#define XLOG_BTREE_MARK_PAGE_HALFDEAD
Definition: nbtxlog.h:37
OffsetNumber offnum
Definition: nbtxlog.h:70
struct IndexTupleData IndexTupleData
static void _bt_restore_page(Page page, char *from, int len)
Definition: nbtxlog.c:36
#define BufferGetPageSize(buffer)
Definition: bufmgr.h:147
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:3546
BlockNumber btm_root
Definition: nbtree.h:101
static void btree_xlog_split(bool onleft, bool lhighkey, XLogReaderState *record)
Definition: nbtxlog.c:205
#define InvalidOffsetNumber
Definition: off.h:26
#define InvalidOid
Definition: postgres_ext.h:36
#define XLOG_BTREE_SPLIT_R
Definition: nbtxlog.h:30
#define ItemIdHasStorage(itemId)
Definition: itemid.h:119
#define BlockNumberIsValid(blockNumber)
Definition: block.h:70
XLogRedoAction XLogReadBufferForRedo(XLogReaderState *record, uint8 block_id, Buffer *buf)
Definition: xlogutils.c:289
bool reachedConsistency
Definition: xlog.c:834
PageHeaderData * PageHeader
Definition: bufpage.h:162
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define Assert(condition)
Definition: c.h:699
#define XLR_INFO_MASK
Definition: xlogrecord.h:62
OffsetNumber firstright
Definition: nbtxlog.h:111
WalTimeSample buffer[LAG_TRACKER_BUFFER_SIZE]
Definition: walsender.c:215
void PageIndexMultiDelete(Page page, OffsetNumber *itemnos, int nitems)
Definition: bufpage.c:832
#define OffsetNumberNext(offsetNumber)
Definition: off.h:53
size_t Size
Definition: c.h:433
#define PageGetSpecialPointer(page)
Definition: bufpage.h:322
#define XLOG_BTREE_SPLIT_R_HIGHKEY
Definition: nbtxlog.h:32
float8 btm_last_cleanup_num_heap_tuples
Definition: nbtree.h:108
#define MAXALIGN(LEN)
Definition: c.h:652
#define BufferIsValid(bufnum)
Definition: bufmgr.h:114
static void btree_xlog_unlink_page(uint8 info, XLogReaderState *record)
Definition: nbtxlog.c:816
#define ItemPointerGetOffsetNumber(pointer)
Definition: itemptr.h:117
#define XLOG_BTREE_INSERT_UPPER
Definition: nbtxlog.h:27
#define BTreeInnerTupleSetDownLink(itup, blkno)
Definition: nbtree.h:226
#define P_HIKEY
Definition: nbtree.h:185
static TransactionId btree_xlog_delete_get_latestRemovedXid(XLogReaderState *record)
Definition: nbtxlog.c:533
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2605
#define MaxIndexTuplesPerPage
Definition: itup.h:145
static void btree_xlog_mark_page_halfdead(uint8 info, XLogReaderState *record)
Definition: nbtxlog.c:736
XLogRedoAction XLogReadBufferForRedoExtended(XLogReaderState *record, uint8 block_id, ReadBufferMode mode, bool get_cleanup_lock, Buffer *buf)
Definition: xlogutils.c:326
uint32 fastlevel
Definition: nbtxlog.h:53
uint32 btm_level
Definition: nbtree.h:102
uint32 level
Definition: nbtxlog.h:51
int i
void _bt_pageinit(Page page, Size size)
Definition: nbtpage.c:891
#define SizeOfBtreeDelete
Definition: nbtxlog.h:133
void ResolveRecoveryConflictWithSnapshot(TransactionId latestRemovedXid, RelFileNode node)
Definition: standby.c:267
#define XLOG_BTREE_SPLIT_L
Definition: nbtxlog.h:29
BlockNumber fastroot
Definition: nbtxlog.h:52
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:98
#define XLOG_BTREE_UNLINK_PAGE_META
Definition: nbtxlog.h:35
TransactionId latestRemovedXid
Definition: nbtxlog.h:142
#define elog
Definition: elog.h:219
#define ItemPointerGetBlockNumber(pointer)
Definition: itemptr.h:98
TransactionId btm_oldest_btpo_xact
Definition: nbtree.h:106
unsigned short t_info
Definition: itup.h:49
uint16 btpo_flags
Definition: nbtree.h:64
#define PageSetLSN(page, lsn)
Definition: bufpage.h:364
int Buffer
Definition: buf.h:23
#define XLOG_BTREE_META_CLEANUP
Definition: nbtxlog.h:42
void mask_lp_flags(Page page)
Definition: bufmask.c:95
void btree_mask(char *pagedata, BlockNumber blkno)
Definition: nbtxlog.c:1048
#define BTP_HAS_GARBAGE
Definition: nbtree.h:77
#define PageGetItem(page, itemId)
Definition: bufpage.h:336
Pointer Page
Definition: bufpage.h:74
#define IndexTupleSize(itup)
Definition: itup.h:71
#define P_ISLEAF(opaque)
Definition: nbtree.h:158
static void btree_xlog_insert(bool isleaf, bool ismeta, XLogReaderState *record)
Definition: nbtxlog.c:158