LCOV - code coverage report
Current view: top level - src/backend/access/nbtree - nbtxlog.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 609 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 17 0
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 0.0 % 268 0

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

Generated by: LCOV version 2.3.2-1