LCOV - code coverage report
Current view: top level - src/backend/access/nbtree - nbtxlog.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13beta1 Lines: 250 476 52.5 %
Date: 2020-06-01 09:07:10 Functions: 10 16 62.5 %
Legend: Lines: hit not hit

          Line data    Source code
       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         372 : _bt_restore_page(Page page, char *from, int len)
      39             : {
      40             :     IndexTupleData itupdata;
      41             :     Size        itemsz;
      42         372 :     char       *end = from + len;
      43             :     Item        items[MaxIndexTuplesPerPage];
      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         372 :     i = 0;
      54       24548 :     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       24176 :         memcpy(&itupdata, from, sizeof(IndexTupleData));
      64       24176 :         itemsz = IndexTupleSize(&itupdata);
      65       24176 :         itemsz = MAXALIGN(itemsz);
      66             : 
      67       24176 :         items[i] = (Item) from;
      68       24176 :         itemsizes[i] = itemsz;
      69       24176 :         i++;
      70             : 
      71       24176 :         from += itemsz;
      72             :     }
      73         372 :     nitems = i;
      74             : 
      75       24548 :     for (i = nitems - 1; i >= 0; i--)
      76             :     {
      77       24176 :         if (PageAddItem(page, items[i], itemsizes[i], nitems - i,
      78             :                         false, false) == InvalidOffsetNumber)
      79           0 :             elog(PANIC, "_bt_restore_page: cannot add item to page");
      80       24176 :         from += itemsz;
      81             :     }
      82         372 : }
      83             : 
      84             : static void
      85          92 : _bt_restore_meta(XLogReaderState *record, uint8 block_id)
      86             : {
      87          92 :     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          92 :     metabuf = XLogInitBufferForRedo(record, block_id);
      97          92 :     ptr = XLogRecGetBlockData(record, block_id, &len);
      98             : 
      99             :     Assert(len == sizeof(xl_btree_metadata));
     100             :     Assert(BufferGetBlockNumber(metabuf) == BTREE_METAPAGE);
     101          92 :     xlrec = (xl_btree_metadata *) ptr;
     102          92 :     metapg = BufferGetPage(metabuf);
     103             : 
     104          92 :     _bt_pageinit(metapg, BufferGetPageSize(metabuf));
     105             : 
     106          92 :     md = BTPageGetMeta(metapg);
     107          92 :     md->btm_magic = BTREE_MAGIC;
     108          92 :     md->btm_version = xlrec->version;
     109          92 :     md->btm_root = xlrec->root;
     110          92 :     md->btm_level = xlrec->level;
     111          92 :     md->btm_fastroot = xlrec->fastroot;
     112          92 :     md->btm_fastlevel = xlrec->fastlevel;
     113             :     /* Cannot log BTREE_MIN_VERSION index metapage without upgrade */
     114             :     Assert(md->btm_version >= BTREE_NOVAC_VERSION);
     115          92 :     md->btm_oldest_btpo_xact = xlrec->oldest_btpo_xact;
     116          92 :     md->btm_last_cleanup_num_heap_tuples = xlrec->last_cleanup_num_heap_tuples;
     117          92 :     md->btm_allequalimage = xlrec->allequalimage;
     118             : 
     119          92 :     pageop = (BTPageOpaque) PageGetSpecialPointer(metapg);
     120          92 :     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          92 :     ((PageHeader) metapg)->pd_lower =
     128          92 :         ((char *) md + sizeof(BTMetaPageData)) - (char *) metapg;
     129             : 
     130          92 :     PageSetLSN(metapg, lsn);
     131          92 :     MarkBufferDirty(metabuf);
     132          92 :     UnlockReleaseBuffer(metabuf);
     133          92 : }
     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
     142         358 : _bt_clear_incomplete_split(XLogReaderState *record, uint8 block_id)
     143             : {
     144         358 :     XLogRecPtr  lsn = record->EndRecPtr;
     145             :     Buffer      buf;
     146             : 
     147         358 :     if (XLogReadBufferForRedo(record, block_id, &buf) == BLK_NEEDS_REDO)
     148             :     {
     149         358 :         Page        page = (Page) BufferGetPage(buf);
     150         358 :         BTPageOpaque pageop = (BTPageOpaque) PageGetSpecialPointer(page);
     151             : 
     152             :         Assert(P_INCOMPLETE_SPLIT(pageop));
     153         358 :         pageop->btpo_flags &= ~BTP_INCOMPLETE_SPLIT;
     154             : 
     155         358 :         PageSetLSN(page, lsn);
     156         358 :         MarkBufferDirty(buf);
     157             :     }
     158         358 :     if (BufferIsValid(buf))
     159         358 :         UnlockReleaseBuffer(buf);
     160         358 : }
     161             : 
     162             : static void
     163      132850 : btree_xlog_insert(bool isleaf, bool ismeta, bool posting,
     164             :                   XLogReaderState *record)
     165             : {
     166      132850 :     XLogRecPtr  lsn = record->EndRecPtr;
     167      132850 :     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      132850 :     if (!isleaf)
     181         344 :         _bt_clear_incomplete_split(record, 1);
     182      132850 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
     183             :     {
     184             :         Size        datalen;
     185      130464 :         char       *datapos = XLogRecGetBlockData(record, 0, &datalen);
     186             : 
     187      130464 :         page = BufferGetPage(buffer);
     188             : 
     189      130464 :         if (!posting)
     190             :         {
     191             :             /* Simple retail insertion */
     192      130464 :             if (PageAddItem(page, (Item) datapos, datalen, xlrec->offnum,
     193             :                             false, false) == InvalidOffsetNumber)
     194           0 :                 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           0 :             postingoff = *((uint16 *) datapos);
     215           0 :             datapos += sizeof(uint16);
     216           0 :             datalen -= sizeof(uint16);
     217             : 
     218           0 :             itemid = PageGetItemId(page, OffsetNumberPrev(xlrec->offnum));
     219           0 :             oposting = (IndexTuple) PageGetItem(page, itemid);
     220             : 
     221             :             /* Use mutable, aligned newitem copy in _bt_swap_posting() */
     222             :             Assert(isleaf && postingoff > 0);
     223           0 :             newitem = CopyIndexTuple((IndexTuple) datapos);
     224           0 :             nposting = _bt_swap_posting(newitem, oposting, postingoff);
     225             : 
     226             :             /* Replace existing posting list with post-split version */
     227           0 :             memcpy(oposting, nposting, MAXALIGN(IndexTupleSize(nposting)));
     228             : 
     229             :             /* Insert "final" new item (not orignewitem from WAL stream) */
     230             :             Assert(IndexTupleSize(newitem) == datalen);
     231           0 :             if (PageAddItem(page, (Item) newitem, datalen, xlrec->offnum,
     232             :                             false, false) == InvalidOffsetNumber)
     233           0 :                 elog(PANIC, "failed to add posting split new item");
     234             :         }
     235             : 
     236      130464 :         PageSetLSN(page, lsn);
     237      130464 :         MarkBufferDirty(buffer);
     238             :     }
     239      132850 :     if (BufferIsValid(buffer))
     240      132850 :         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      132850 :     if (ismeta)
     250           0 :         _bt_restore_meta(record, 2);
     251      132850 : }
     252             : 
     253             : static void
     254         358 : btree_xlog_split(bool newitemonleft, XLogReaderState *record)
     255             : {
     256         358 :     XLogRecPtr  lsn = record->EndRecPtr;
     257         358 :     xl_btree_split *xlrec = (xl_btree_split *) XLogRecGetData(record);
     258         358 :     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         358 :     XLogRecGetBlockTag(record, 0, NULL, NULL, &leftsib);
     270         358 :     XLogRecGetBlockTag(record, 1, NULL, NULL, &rightsib);
     271         358 :     if (!XLogRecGetBlockTag(record, 2, NULL, NULL, &rnext))
     272         296 :         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         358 :     if (!isleaf)
     280           0 :         _bt_clear_incomplete_split(record, 3);
     281             : 
     282             :     /* Reconstruct right (new) sibling page from scratch */
     283         358 :     rbuf = XLogInitBufferForRedo(record, 1);
     284         358 :     datapos = XLogRecGetBlockData(record, 1, &datalen);
     285         358 :     rpage = (Page) BufferGetPage(rbuf);
     286             : 
     287         358 :     _bt_pageinit(rpage, BufferGetPageSize(rbuf));
     288         358 :     ropaque = (BTPageOpaque) PageGetSpecialPointer(rpage);
     289             : 
     290         358 :     ropaque->btpo_prev = leftsib;
     291         358 :     ropaque->btpo_next = rnext;
     292         358 :     ropaque->btpo.level = xlrec->level;
     293         358 :     ropaque->btpo_flags = isleaf ? BTP_LEAF : 0;
     294         358 :     ropaque->btpo_cycleid = 0;
     295             : 
     296         358 :     _bt_restore_page(rpage, datapos, datalen);
     297             : 
     298         358 :     PageSetLSN(rpage, lsn);
     299         358 :     MarkBufferDirty(rbuf);
     300             : 
     301             :     /* Now reconstruct left (original) sibling page */
     302         358 :     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         352 :         Page        lpage = (Page) BufferGetPage(lbuf);
     313         352 :         BTPageOpaque lopaque = (BTPageOpaque) PageGetSpecialPointer(lpage);
     314             :         OffsetNumber off;
     315         352 :         IndexTuple  newitem = NULL,
     316         352 :                     left_hikey = NULL,
     317         352 :                     nposting = NULL;
     318         352 :         Size        newitemsz = 0,
     319         352 :                     left_hikeysz = 0;
     320             :         Page        newlpage;
     321             :         OffsetNumber leftoff,
     322         352 :                     replacepostingoff = InvalidOffsetNumber;
     323             : 
     324         352 :         datapos = XLogRecGetBlockData(record, 0, &datalen);
     325             : 
     326         352 :         if (newitemonleft || xlrec->postingoff != 0)
     327             :         {
     328          54 :             newitem = (IndexTuple) datapos;
     329          54 :             newitemsz = MAXALIGN(IndexTupleSize(newitem));
     330          54 :             datapos += newitemsz;
     331          54 :             datalen -= newitemsz;
     332             : 
     333          54 :             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           0 :                 replacepostingoff = OffsetNumberPrev(xlrec->newitemoff);
     340             : 
     341             :                 /* Use mutable, aligned newitem copy in _bt_swap_posting() */
     342           0 :                 newitem = CopyIndexTuple(newitem);
     343           0 :                 itemid = PageGetItemId(lpage, replacepostingoff);
     344           0 :                 oposting = (IndexTuple) PageGetItem(lpage, itemid);
     345           0 :                 nposting = _bt_swap_posting(newitem, oposting,
     346           0 :                                             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         352 :         left_hikey = (IndexTuple) datapos;
     356         352 :         left_hikeysz = MAXALIGN(IndexTupleSize(left_hikey));
     357         352 :         datapos += left_hikeysz;
     358         352 :         datalen -= left_hikeysz;
     359             : 
     360             :         Assert(datalen == 0);
     361             : 
     362         352 :         newlpage = PageGetTempPageCopySpecial(lpage);
     363             : 
     364             :         /* Set high key */
     365         352 :         leftoff = P_HIKEY;
     366         352 :         if (PageAddItem(newlpage, (Item) left_hikey, left_hikeysz,
     367             :                         P_HIKEY, false, false) == InvalidOffsetNumber)
     368           0 :             elog(PANIC, "failed to add high key to left page after split");
     369         352 :         leftoff = OffsetNumberNext(leftoff);
     370             : 
     371      115446 :         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      115094 :             if (off == replacepostingoff)
     379             :             {
     380             :                 Assert(newitemonleft ||
     381             :                        xlrec->firstrightoff == xlrec->newitemoff);
     382           0 :                 if (PageAddItem(newlpage, (Item) nposting,
     383             :                                 MAXALIGN(IndexTupleSize(nposting)), leftoff,
     384             :                                 false, false) == InvalidOffsetNumber)
     385           0 :                     elog(ERROR, "failed to add new posting list item to left page after split");
     386           0 :                 leftoff = OffsetNumberNext(leftoff);
     387           0 :                 continue;       /* don't insert oposting */
     388             :             }
     389             : 
     390             :             /* add the new item if it was inserted on left page */
     391      115094 :             else if (newitemonleft && off == xlrec->newitemoff)
     392             :             {
     393          54 :                 if (PageAddItem(newlpage, (Item) newitem, newitemsz, leftoff,
     394             :                                 false, false) == InvalidOffsetNumber)
     395           0 :                     elog(ERROR, "failed to add new item to left page after split");
     396          54 :                 leftoff = OffsetNumberNext(leftoff);
     397             :             }
     398             : 
     399      115094 :             itemid = PageGetItemId(lpage, off);
     400      115094 :             itemsz = ItemIdGetLength(itemid);
     401      115094 :             item = (IndexTuple) PageGetItem(lpage, itemid);
     402      115094 :             if (PageAddItem(newlpage, (Item) item, itemsz, leftoff,
     403             :                             false, false) == InvalidOffsetNumber)
     404           0 :                 elog(ERROR, "failed to add old item to left page after split");
     405      115094 :             leftoff = OffsetNumberNext(leftoff);
     406             :         }
     407             : 
     408             :         /* cope with possibility that newitem goes at the end */
     409         352 :         if (newitemonleft && off == xlrec->newitemoff)
     410             :         {
     411           0 :             if (PageAddItem(newlpage, (Item) newitem, newitemsz, leftoff,
     412             :                             false, false) == InvalidOffsetNumber)
     413           0 :                 elog(ERROR, "failed to add new item to left page after split");
     414           0 :             leftoff = OffsetNumberNext(leftoff);
     415             :         }
     416             : 
     417         352 :         PageRestoreTempPage(newlpage, lpage);
     418             : 
     419             :         /* Fix opaque fields */
     420         352 :         lopaque->btpo_flags = BTP_INCOMPLETE_SPLIT;
     421         352 :         if (isleaf)
     422         352 :             lopaque->btpo_flags |= BTP_LEAF;
     423         352 :         lopaque->btpo_next = rightsib;
     424         352 :         lopaque->btpo_cycleid = 0;
     425             : 
     426         352 :         PageSetLSN(lpage, lsn);
     427         352 :         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         358 :     if (BufferIsValid(lbuf))
     435         358 :         UnlockReleaseBuffer(lbuf);
     436         358 :     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         358 :     if (rnext != P_NONE)
     447             :     {
     448             :         Buffer      buffer;
     449             : 
     450          62 :         if (XLogReadBufferForRedo(record, 2, &buffer) == BLK_NEEDS_REDO)
     451             :         {
     452          54 :             Page        page = (Page) BufferGetPage(buffer);
     453          54 :             BTPageOpaque pageop = (BTPageOpaque) PageGetSpecialPointer(page);
     454             : 
     455          54 :             pageop->btpo_prev = rightsib;
     456             : 
     457          54 :             PageSetLSN(page, lsn);
     458          54 :             MarkBufferDirty(buffer);
     459             :         }
     460          62 :         if (BufferIsValid(buffer))
     461          62 :             UnlockReleaseBuffer(buffer);
     462             :     }
     463         358 : }
     464             : 
     465             : static void
     466         216 : btree_xlog_dedup(XLogReaderState *record)
     467             : {
     468         216 :     XLogRecPtr  lsn = record->EndRecPtr;
     469         216 :     xl_btree_dedup *xlrec = (xl_btree_dedup *) XLogRecGetData(record);
     470             :     Buffer      buf;
     471             : 
     472         216 :     if (XLogReadBufferForRedo(record, 0, &buf) == BLK_NEEDS_REDO)
     473             :     {
     474         216 :         char       *ptr = XLogRecGetBlockData(record, 0, NULL);
     475         216 :         Page        page = (Page) BufferGetPage(buf);
     476         216 :         BTPageOpaque opaque = (BTPageOpaque) PageGetSpecialPointer(page);
     477             :         OffsetNumber offnum,
     478             :                     minoff,
     479             :                     maxoff;
     480             :         BTDedupState state;
     481             :         BTDedupInterval *intervals;
     482             :         Page        newpage;
     483             : 
     484         216 :         state = (BTDedupState) palloc(sizeof(BTDedupStateData));
     485         216 :         state->deduplicate = true;   /* unused */
     486             :         /* Conservatively use larger maxpostingsize than primary */
     487         216 :         state->maxpostingsize = BTMaxItemSize(page);
     488         216 :         state->base = NULL;
     489         216 :         state->baseoff = InvalidOffsetNumber;
     490         216 :         state->basetupsize = 0;
     491         216 :         state->htids = palloc(state->maxpostingsize);
     492         216 :         state->nhtids = 0;
     493         216 :         state->nitems = 0;
     494         216 :         state->phystupsize = 0;
     495         216 :         state->nintervals = 0;
     496             : 
     497         216 :         minoff = P_FIRSTDATAKEY(opaque);
     498         216 :         maxoff = PageGetMaxOffsetNumber(page);
     499         216 :         newpage = PageGetTempPageCopySpecial(page);
     500             : 
     501         216 :         if (!P_RIGHTMOST(opaque))
     502             :         {
     503         216 :             ItemId      itemid = PageGetItemId(page, P_HIKEY);
     504         216 :             Size        itemsz = ItemIdGetLength(itemid);
     505         216 :             IndexTuple  item = (IndexTuple) PageGetItem(page, itemid);
     506             : 
     507         216 :             if (PageAddItem(newpage, (Item) item, itemsz, P_HIKEY,
     508             :                             false, false) == InvalidOffsetNumber)
     509           0 :                 elog(ERROR, "deduplication failed to add highkey");
     510             :         }
     511             : 
     512         216 :         intervals = (BTDedupInterval *) ptr;
     513       84132 :         for (offnum = minoff;
     514             :              offnum <= maxoff;
     515       83916 :              offnum = OffsetNumberNext(offnum))
     516             :         {
     517       83916 :             ItemId      itemid = PageGetItemId(page, offnum);
     518       83916 :             IndexTuple  itup = (IndexTuple) PageGetItem(page, itemid);
     519             : 
     520       83916 :             if (offnum == minoff)
     521         216 :                 _bt_dedup_start_pending(state, itup, offnum);
     522       83700 :             else if (state->nintervals < xlrec->nintervals &&
     523       31968 :                      state->baseoff == intervals[state->nintervals].baseoff &&
     524       27216 :                      state->nitems < intervals[state->nintervals].nitems)
     525             :             {
     526       13608 :                 if (!_bt_dedup_save_htid(state, itup))
     527           0 :                     elog(ERROR, "deduplication failed to add heap tid to pending posting list");
     528             :             }
     529             :             else
     530             :             {
     531       70092 :                 _bt_dedup_finish_pending(newpage, state);
     532       70092 :                 _bt_dedup_start_pending(state, itup, offnum);
     533             :             }
     534             :         }
     535             : 
     536         216 :         _bt_dedup_finish_pending(newpage, state);
     537             :         Assert(state->nintervals == xlrec->nintervals);
     538             :         Assert(memcmp(state->intervals, intervals,
     539             :                       state->nintervals * sizeof(BTDedupInterval)) == 0);
     540             : 
     541         216 :         if (P_HAS_GARBAGE(opaque))
     542             :         {
     543           0 :             BTPageOpaque nopaque = (BTPageOpaque) PageGetSpecialPointer(newpage);
     544             : 
     545           0 :             nopaque->btpo_flags &= ~BTP_HAS_GARBAGE;
     546             :         }
     547             : 
     548         216 :         PageRestoreTempPage(newpage, page);
     549         216 :         PageSetLSN(page, lsn);
     550         216 :         MarkBufferDirty(buf);
     551             :     }
     552             : 
     553         216 :     if (BufferIsValid(buf))
     554         216 :         UnlockReleaseBuffer(buf);
     555         216 : }
     556             : 
     557             : static void
     558           0 : btree_xlog_vacuum(XLogReaderState *record)
     559             : {
     560           0 :     XLogRecPtr  lsn = record->EndRecPtr;
     561           0 :     xl_btree_vacuum *xlrec = (xl_btree_vacuum *) XLogRecGetData(record);
     562             :     Buffer      buffer;
     563             :     Page        page;
     564             :     BTPageOpaque opaque;
     565             : 
     566             :     /*
     567             :      * We need to take a cleanup lock here, just like btvacuumpage(). However,
     568             :      * it isn't necessary to exhaustively get a cleanup lock on every block in
     569             :      * the index during recovery (just getting a cleanup lock on pages with
     570             :      * items to kill suffices).  See nbtree/README for details.
     571             :      */
     572           0 :     if (XLogReadBufferForRedoExtended(record, 0, RBM_NORMAL, true, &buffer)
     573             :         == BLK_NEEDS_REDO)
     574             :     {
     575           0 :         char       *ptr = XLogRecGetBlockData(record, 0, NULL);
     576             : 
     577           0 :         page = (Page) BufferGetPage(buffer);
     578             : 
     579           0 :         if (xlrec->nupdated > 0)
     580             :         {
     581             :             OffsetNumber *updatedoffsets;
     582             :             xl_btree_update *updates;
     583             : 
     584           0 :             updatedoffsets = (OffsetNumber *)
     585           0 :                 (ptr + xlrec->ndeleted * sizeof(OffsetNumber));
     586           0 :             updates = (xl_btree_update *) ((char *) updatedoffsets +
     587           0 :                                            xlrec->nupdated *
     588             :                                            sizeof(OffsetNumber));
     589             : 
     590           0 :             for (int i = 0; i < xlrec->nupdated; i++)
     591             :             {
     592             :                 BTVacuumPosting vacposting;
     593             :                 IndexTuple  origtuple;
     594             :                 ItemId      itemid;
     595             :                 Size        itemsz;
     596             : 
     597           0 :                 itemid = PageGetItemId(page, updatedoffsets[i]);
     598           0 :                 origtuple = (IndexTuple) PageGetItem(page, itemid);
     599             : 
     600           0 :                 vacposting = palloc(offsetof(BTVacuumPostingData, deletetids) +
     601           0 :                                     updates->ndeletedtids * sizeof(uint16));
     602           0 :                 vacposting->updatedoffset = updatedoffsets[i];
     603           0 :                 vacposting->itup = origtuple;
     604           0 :                 vacposting->ndeletedtids = updates->ndeletedtids;
     605           0 :                 memcpy(vacposting->deletetids,
     606             :                        (char *) updates + SizeOfBtreeUpdate,
     607           0 :                        updates->ndeletedtids * sizeof(uint16));
     608             : 
     609           0 :                 _bt_update_posting(vacposting);
     610             : 
     611             :                 /* Overwrite updated version of tuple */
     612           0 :                 itemsz = MAXALIGN(IndexTupleSize(vacposting->itup));
     613           0 :                 if (!PageIndexTupleOverwrite(page, updatedoffsets[i],
     614           0 :                                              (Item) vacposting->itup, itemsz))
     615           0 :                     elog(PANIC, "failed to update partially dead item");
     616             : 
     617           0 :                 pfree(vacposting->itup);
     618           0 :                 pfree(vacposting);
     619             : 
     620             :                 /* advance to next xl_btree_update from array */
     621           0 :                 updates = (xl_btree_update *)
     622           0 :                     ((char *) updates + SizeOfBtreeUpdate +
     623           0 :                      updates->ndeletedtids * sizeof(uint16));
     624             :             }
     625             :         }
     626             : 
     627           0 :         if (xlrec->ndeleted > 0)
     628           0 :             PageIndexMultiDelete(page, (OffsetNumber *) ptr, xlrec->ndeleted);
     629             : 
     630             :         /*
     631             :          * Mark the page as not containing any LP_DEAD items --- see comments
     632             :          * in _bt_delitems_vacuum().
     633             :          */
     634           0 :         opaque = (BTPageOpaque) PageGetSpecialPointer(page);
     635           0 :         opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
     636             : 
     637           0 :         PageSetLSN(page, lsn);
     638           0 :         MarkBufferDirty(buffer);
     639             :     }
     640           0 :     if (BufferIsValid(buffer))
     641           0 :         UnlockReleaseBuffer(buffer);
     642           0 : }
     643             : 
     644             : static void
     645           0 : btree_xlog_delete(XLogReaderState *record)
     646             : {
     647           0 :     XLogRecPtr  lsn = record->EndRecPtr;
     648           0 :     xl_btree_delete *xlrec = (xl_btree_delete *) XLogRecGetData(record);
     649             :     Buffer      buffer;
     650             :     Page        page;
     651             :     BTPageOpaque opaque;
     652             : 
     653             :     /*
     654             :      * If we have any conflict processing to do, it must happen before we
     655             :      * update the page
     656             :      */
     657           0 :     if (InHotStandby)
     658             :     {
     659             :         RelFileNode rnode;
     660             : 
     661           0 :         XLogRecGetBlockTag(record, 0, &rnode, NULL, NULL);
     662             : 
     663           0 :         ResolveRecoveryConflictWithSnapshot(xlrec->latestRemovedXid, rnode);
     664             :     }
     665             : 
     666             :     /*
     667             :      * We don't need to take a cleanup lock to apply these changes. See
     668             :      * nbtree/README for details.
     669             :      */
     670           0 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
     671             :     {
     672           0 :         char       *ptr = XLogRecGetBlockData(record, 0, NULL);
     673             : 
     674           0 :         page = (Page) BufferGetPage(buffer);
     675             : 
     676           0 :         PageIndexMultiDelete(page, (OffsetNumber *) ptr, xlrec->ndeleted);
     677             : 
     678             :         /* Mark the page as not containing any LP_DEAD items */
     679           0 :         opaque = (BTPageOpaque) PageGetSpecialPointer(page);
     680           0 :         opaque->btpo_flags &= ~BTP_HAS_GARBAGE;
     681             : 
     682           0 :         PageSetLSN(page, lsn);
     683           0 :         MarkBufferDirty(buffer);
     684             :     }
     685           0 :     if (BufferIsValid(buffer))
     686           0 :         UnlockReleaseBuffer(buffer);
     687           0 : }
     688             : 
     689             : static void
     690           0 : btree_xlog_mark_page_halfdead(uint8 info, XLogReaderState *record)
     691             : {
     692           0 :     XLogRecPtr  lsn = record->EndRecPtr;
     693           0 :     xl_btree_mark_page_halfdead *xlrec = (xl_btree_mark_page_halfdead *) XLogRecGetData(record);
     694             :     Buffer      buffer;
     695             :     Page        page;
     696             :     BTPageOpaque pageop;
     697             :     IndexTupleData trunctuple;
     698             : 
     699             :     /*
     700             :      * In normal operation, we would lock all the pages this WAL record
     701             :      * touches before changing any of them.  In WAL replay, it should be okay
     702             :      * to lock just one page at a time, since no concurrent index updates can
     703             :      * be happening, and readers should not care whether they arrive at the
     704             :      * target page or not (since it's surely empty).
     705             :      */
     706             : 
     707             :     /* to-be-deleted subtree's parent page */
     708           0 :     if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
     709             :     {
     710             :         OffsetNumber poffset;
     711             :         ItemId      itemid;
     712             :         IndexTuple  itup;
     713             :         OffsetNumber nextoffset;
     714             :         BlockNumber rightsib;
     715             : 
     716           0 :         page = (Page) BufferGetPage(buffer);
     717           0 :         pageop = (BTPageOpaque) PageGetSpecialPointer(page);
     718             : 
     719           0 :         poffset = xlrec->poffset;
     720             : 
     721           0 :         nextoffset = OffsetNumberNext(poffset);
     722           0 :         itemid = PageGetItemId(page, nextoffset);
     723           0 :         itup = (IndexTuple) PageGetItem(page, itemid);
     724           0 :         rightsib = BTreeTupleGetDownLink(itup);
     725             : 
     726           0 :         itemid = PageGetItemId(page, poffset);
     727           0 :         itup = (IndexTuple) PageGetItem(page, itemid);
     728           0 :         BTreeTupleSetDownLink(itup, rightsib);
     729           0 :         nextoffset = OffsetNumberNext(poffset);
     730           0 :         PageIndexTupleDelete(page, nextoffset);
     731             : 
     732           0 :         PageSetLSN(page, lsn);
     733           0 :         MarkBufferDirty(buffer);
     734             :     }
     735           0 :     if (BufferIsValid(buffer))
     736           0 :         UnlockReleaseBuffer(buffer);
     737             : 
     738             :     /* Rewrite the leaf page as a halfdead page */
     739           0 :     buffer = XLogInitBufferForRedo(record, 0);
     740           0 :     page = (Page) BufferGetPage(buffer);
     741             : 
     742           0 :     _bt_pageinit(page, BufferGetPageSize(buffer));
     743           0 :     pageop = (BTPageOpaque) PageGetSpecialPointer(page);
     744             : 
     745           0 :     pageop->btpo_prev = xlrec->leftblk;
     746           0 :     pageop->btpo_next = xlrec->rightblk;
     747           0 :     pageop->btpo.level = 0;
     748           0 :     pageop->btpo_flags = BTP_HALF_DEAD | BTP_LEAF;
     749           0 :     pageop->btpo_cycleid = 0;
     750             : 
     751             :     /*
     752             :      * Construct a dummy high key item that points to top parent page (value
     753             :      * is InvalidBlockNumber when the top parent page is the leaf page itself)
     754             :      */
     755           0 :     MemSet(&trunctuple, 0, sizeof(IndexTupleData));
     756           0 :     trunctuple.t_info = sizeof(IndexTupleData);
     757           0 :     BTreeTupleSetTopParent(&trunctuple, xlrec->topparent);
     758             : 
     759           0 :     if (PageAddItem(page, (Item) &trunctuple, sizeof(IndexTupleData), P_HIKEY,
     760             :                     false, false) == InvalidOffsetNumber)
     761           0 :         elog(ERROR, "could not add dummy high key to half-dead page");
     762             : 
     763           0 :     PageSetLSN(page, lsn);
     764           0 :     MarkBufferDirty(buffer);
     765           0 :     UnlockReleaseBuffer(buffer);
     766           0 : }
     767             : 
     768             : 
     769             : static void
     770           0 : btree_xlog_unlink_page(uint8 info, XLogReaderState *record)
     771             : {
     772           0 :     XLogRecPtr  lsn = record->EndRecPtr;
     773           0 :     xl_btree_unlink_page *xlrec = (xl_btree_unlink_page *) XLogRecGetData(record);
     774             :     BlockNumber leftsib;
     775             :     BlockNumber rightsib;
     776             :     Buffer      buffer;
     777             :     Page        page;
     778             :     BTPageOpaque pageop;
     779             : 
     780           0 :     leftsib = xlrec->leftsib;
     781           0 :     rightsib = xlrec->rightsib;
     782             : 
     783             :     /*
     784             :      * In normal operation, we would lock all the pages this WAL record
     785             :      * touches before changing any of them.  In WAL replay, it should be okay
     786             :      * to lock just one page at a time, since no concurrent index updates can
     787             :      * be happening, and readers should not care whether they arrive at the
     788             :      * target page or not (since it's surely empty).
     789             :      */
     790             : 
     791             :     /* Fix left-link of right sibling */
     792           0 :     if (XLogReadBufferForRedo(record, 2, &buffer) == BLK_NEEDS_REDO)
     793             :     {
     794           0 :         page = (Page) BufferGetPage(buffer);
     795           0 :         pageop = (BTPageOpaque) PageGetSpecialPointer(page);
     796           0 :         pageop->btpo_prev = leftsib;
     797             : 
     798           0 :         PageSetLSN(page, lsn);
     799           0 :         MarkBufferDirty(buffer);
     800             :     }
     801           0 :     if (BufferIsValid(buffer))
     802           0 :         UnlockReleaseBuffer(buffer);
     803             : 
     804             :     /* Fix right-link of left sibling, if any */
     805           0 :     if (leftsib != P_NONE)
     806             :     {
     807           0 :         if (XLogReadBufferForRedo(record, 1, &buffer) == BLK_NEEDS_REDO)
     808             :         {
     809           0 :             page = (Page) BufferGetPage(buffer);
     810           0 :             pageop = (BTPageOpaque) PageGetSpecialPointer(page);
     811           0 :             pageop->btpo_next = rightsib;
     812             : 
     813           0 :             PageSetLSN(page, lsn);
     814           0 :             MarkBufferDirty(buffer);
     815             :         }
     816           0 :         if (BufferIsValid(buffer))
     817           0 :             UnlockReleaseBuffer(buffer);
     818             :     }
     819             : 
     820             :     /* Rewrite target page as empty deleted page */
     821           0 :     buffer = XLogInitBufferForRedo(record, 0);
     822           0 :     page = (Page) BufferGetPage(buffer);
     823             : 
     824           0 :     _bt_pageinit(page, BufferGetPageSize(buffer));
     825           0 :     pageop = (BTPageOpaque) PageGetSpecialPointer(page);
     826             : 
     827           0 :     pageop->btpo_prev = leftsib;
     828           0 :     pageop->btpo_next = rightsib;
     829           0 :     pageop->btpo.xact = xlrec->btpo_xact;
     830           0 :     pageop->btpo_flags = BTP_DELETED;
     831           0 :     pageop->btpo_cycleid = 0;
     832             : 
     833           0 :     PageSetLSN(page, lsn);
     834           0 :     MarkBufferDirty(buffer);
     835           0 :     UnlockReleaseBuffer(buffer);
     836             : 
     837             :     /*
     838             :      * If we deleted a parent of the targeted leaf page, instead of the leaf
     839             :      * itself, update the leaf to point to the next remaining child in the
     840             :      * to-be-deleted subtree
     841             :      */
     842           0 :     if (XLogRecHasBlockRef(record, 3))
     843             :     {
     844             :         /*
     845             :          * There is no real data on the page, so we just re-create it from
     846             :          * scratch using the information from the WAL record.
     847             :          */
     848             :         IndexTupleData trunctuple;
     849             : 
     850           0 :         buffer = XLogInitBufferForRedo(record, 3);
     851           0 :         page = (Page) BufferGetPage(buffer);
     852             : 
     853           0 :         _bt_pageinit(page, BufferGetPageSize(buffer));
     854           0 :         pageop = (BTPageOpaque) PageGetSpecialPointer(page);
     855             : 
     856           0 :         pageop->btpo_flags = BTP_HALF_DEAD | BTP_LEAF;
     857           0 :         pageop->btpo_prev = xlrec->leafleftsib;
     858           0 :         pageop->btpo_next = xlrec->leafrightsib;
     859           0 :         pageop->btpo.level = 0;
     860           0 :         pageop->btpo_cycleid = 0;
     861             : 
     862             :         /* Add a dummy hikey item */
     863           0 :         MemSet(&trunctuple, 0, sizeof(IndexTupleData));
     864           0 :         trunctuple.t_info = sizeof(IndexTupleData);
     865           0 :         BTreeTupleSetTopParent(&trunctuple, xlrec->topparent);
     866             : 
     867           0 :         if (PageAddItem(page, (Item) &trunctuple, sizeof(IndexTupleData), P_HIKEY,
     868             :                         false, false) == InvalidOffsetNumber)
     869           0 :             elog(ERROR, "could not add dummy high key to half-dead page");
     870             : 
     871           0 :         PageSetLSN(page, lsn);
     872           0 :         MarkBufferDirty(buffer);
     873           0 :         UnlockReleaseBuffer(buffer);
     874             :     }
     875             : 
     876             :     /* Update metapage if needed */
     877           0 :     if (info == XLOG_BTREE_UNLINK_PAGE_META)
     878           0 :         _bt_restore_meta(record, 4);
     879           0 : }
     880             : 
     881             : static void
     882          78 : btree_xlog_newroot(XLogReaderState *record)
     883             : {
     884          78 :     XLogRecPtr  lsn = record->EndRecPtr;
     885          78 :     xl_btree_newroot *xlrec = (xl_btree_newroot *) XLogRecGetData(record);
     886             :     Buffer      buffer;
     887             :     Page        page;
     888             :     BTPageOpaque pageop;
     889             :     char       *ptr;
     890             :     Size        len;
     891             : 
     892          78 :     buffer = XLogInitBufferForRedo(record, 0);
     893          78 :     page = (Page) BufferGetPage(buffer);
     894             : 
     895          78 :     _bt_pageinit(page, BufferGetPageSize(buffer));
     896          78 :     pageop = (BTPageOpaque) PageGetSpecialPointer(page);
     897             : 
     898          78 :     pageop->btpo_flags = BTP_ROOT;
     899          78 :     pageop->btpo_prev = pageop->btpo_next = P_NONE;
     900          78 :     pageop->btpo.level = xlrec->level;
     901          78 :     if (xlrec->level == 0)
     902          64 :         pageop->btpo_flags |= BTP_LEAF;
     903          78 :     pageop->btpo_cycleid = 0;
     904             : 
     905          78 :     if (xlrec->level > 0)
     906             :     {
     907          14 :         ptr = XLogRecGetBlockData(record, 0, &len);
     908          14 :         _bt_restore_page(page, ptr, len);
     909             : 
     910             :         /* Clear the incomplete-split flag in left child */
     911          14 :         _bt_clear_incomplete_split(record, 1);
     912             :     }
     913             : 
     914          78 :     PageSetLSN(page, lsn);
     915          78 :     MarkBufferDirty(buffer);
     916          78 :     UnlockReleaseBuffer(buffer);
     917             : 
     918          78 :     _bt_restore_meta(record, 2);
     919          78 : }
     920             : 
     921             : static void
     922           0 : btree_xlog_reuse_page(XLogReaderState *record)
     923             : {
     924           0 :     xl_btree_reuse_page *xlrec = (xl_btree_reuse_page *) XLogRecGetData(record);
     925             : 
     926             :     /*
     927             :      * Btree reuse_page records exist to provide a conflict point when we
     928             :      * reuse pages in the index via the FSM.  That's all they do though.
     929             :      *
     930             :      * latestRemovedXid was the page's btpo.xact.  The btpo.xact <
     931             :      * RecentGlobalXmin test in _bt_page_recyclable() conceptually mirrors the
     932             :      * pgxact->xmin > limitXmin test in GetConflictingVirtualXIDs().
     933             :      * Consequently, one XID value achieves the same exclusion effect on
     934             :      * master and standby.
     935             :      */
     936           0 :     if (InHotStandby)
     937             :     {
     938           0 :         ResolveRecoveryConflictWithSnapshot(xlrec->latestRemovedXid,
     939             :                                             xlrec->node);
     940             :     }
     941           0 : }
     942             : 
     943             : void
     944      133516 : btree_redo(XLogReaderState *record)
     945             : {
     946      133516 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
     947             :     MemoryContext oldCtx;
     948             : 
     949      133516 :     oldCtx = MemoryContextSwitchTo(opCtx);
     950      133516 :     switch (info)
     951             :     {
     952      132506 :         case XLOG_BTREE_INSERT_LEAF:
     953      132506 :             btree_xlog_insert(true, false, false, record);
     954      132506 :             break;
     955         344 :         case XLOG_BTREE_INSERT_UPPER:
     956         344 :             btree_xlog_insert(false, false, false, record);
     957         344 :             break;
     958           0 :         case XLOG_BTREE_INSERT_META:
     959           0 :             btree_xlog_insert(false, true, false, record);
     960           0 :             break;
     961          54 :         case XLOG_BTREE_SPLIT_L:
     962          54 :             btree_xlog_split(true, record);
     963          54 :             break;
     964         304 :         case XLOG_BTREE_SPLIT_R:
     965         304 :             btree_xlog_split(false, record);
     966         304 :             break;
     967           0 :         case XLOG_BTREE_INSERT_POST:
     968           0 :             btree_xlog_insert(true, false, true, record);
     969           0 :             break;
     970         216 :         case XLOG_BTREE_DEDUP:
     971         216 :             btree_xlog_dedup(record);
     972         216 :             break;
     973           0 :         case XLOG_BTREE_VACUUM:
     974           0 :             btree_xlog_vacuum(record);
     975           0 :             break;
     976           0 :         case XLOG_BTREE_DELETE:
     977           0 :             btree_xlog_delete(record);
     978           0 :             break;
     979           0 :         case XLOG_BTREE_MARK_PAGE_HALFDEAD:
     980           0 :             btree_xlog_mark_page_halfdead(info, record);
     981           0 :             break;
     982           0 :         case XLOG_BTREE_UNLINK_PAGE:
     983             :         case XLOG_BTREE_UNLINK_PAGE_META:
     984           0 :             btree_xlog_unlink_page(info, record);
     985           0 :             break;
     986          78 :         case XLOG_BTREE_NEWROOT:
     987          78 :             btree_xlog_newroot(record);
     988          78 :             break;
     989           0 :         case XLOG_BTREE_REUSE_PAGE:
     990           0 :             btree_xlog_reuse_page(record);
     991           0 :             break;
     992          14 :         case XLOG_BTREE_META_CLEANUP:
     993          14 :             _bt_restore_meta(record, 0);
     994          14 :             break;
     995           0 :         default:
     996           0 :             elog(PANIC, "btree_redo: unknown op code %u", info);
     997             :     }
     998      133516 :     MemoryContextSwitchTo(oldCtx);
     999      133516 :     MemoryContextReset(opCtx);
    1000      133516 : }
    1001             : 
    1002             : void
    1003         196 : btree_xlog_startup(void)
    1004             : {
    1005         196 :     opCtx = AllocSetContextCreate(CurrentMemoryContext,
    1006             :                                   "Btree recovery temporary context",
    1007             :                                   ALLOCSET_DEFAULT_SIZES);
    1008         196 : }
    1009             : 
    1010             : void
    1011         156 : btree_xlog_cleanup(void)
    1012             : {
    1013         156 :     MemoryContextDelete(opCtx);
    1014         156 :     opCtx = NULL;
    1015         156 : }
    1016             : 
    1017             : /*
    1018             :  * Mask a btree page before performing consistency checks on it.
    1019             :  */
    1020             : void
    1021           0 : btree_mask(char *pagedata, BlockNumber blkno)
    1022             : {
    1023           0 :     Page        page = (Page) pagedata;
    1024             :     BTPageOpaque maskopaq;
    1025             : 
    1026           0 :     mask_page_lsn_and_checksum(page);
    1027             : 
    1028           0 :     mask_page_hint_bits(page);
    1029           0 :     mask_unused_space(page);
    1030             : 
    1031           0 :     maskopaq = (BTPageOpaque) PageGetSpecialPointer(page);
    1032             : 
    1033           0 :     if (P_ISDELETED(maskopaq))
    1034             :     {
    1035             :         /*
    1036             :          * Mask page content on a DELETED page since it will be re-initialized
    1037             :          * during replay. See btree_xlog_unlink_page() for details.
    1038             :          */
    1039           0 :         mask_page_content(page);
    1040             :     }
    1041           0 :     else if (P_ISLEAF(maskopaq))
    1042             :     {
    1043             :         /*
    1044             :          * In btree leaf pages, it is possible to modify the LP_FLAGS without
    1045             :          * emitting any WAL record. Hence, mask the line pointer flags. See
    1046             :          * _bt_killitems(), _bt_check_unique() for details.
    1047             :          */
    1048           0 :         mask_lp_flags(page);
    1049             :     }
    1050             : 
    1051             :     /*
    1052             :      * BTP_HAS_GARBAGE is just an un-logged hint bit. So, mask it. See
    1053             :      * _bt_killitems(), _bt_check_unique() for details.
    1054             :      */
    1055           0 :     maskopaq->btpo_flags &= ~BTP_HAS_GARBAGE;
    1056             : 
    1057             :     /*
    1058             :      * During replay of a btree page split, we don't set the BTP_SPLIT_END
    1059             :      * flag of the right sibling and initialize the cycle_id to 0 for the same
    1060             :      * page. See btree_xlog_split() for details.
    1061             :      */
    1062           0 :     maskopaq->btpo_flags &= ~BTP_SPLIT_END;
    1063           0 :     maskopaq->btpo_cycleid = 0;
    1064           0 : }

Generated by: LCOV version 1.13