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