LCOV - code coverage report
Current view: top level - src/backend/access/gist - gistxlog.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 76.2 % 256 195
Test Date: 2026-03-01 18:15:11 Functions: 70.6 % 17 12
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * gistxlog.c
       4              :  *    WAL replay logic for GiST.
       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/gist/gistxlog.c
      12              :  *-------------------------------------------------------------------------
      13              :  */
      14              : #include "postgres.h"
      15              : 
      16              : #include "access/bufmask.h"
      17              : #include "access/gist_private.h"
      18              : #include "access/gistxlog.h"
      19              : #include "access/transam.h"
      20              : #include "access/xloginsert.h"
      21              : #include "access/xlogutils.h"
      22              : #include "storage/standby.h"
      23              : #include "utils/memutils.h"
      24              : #include "utils/rel.h"
      25              : 
      26              : static MemoryContext opCtx;     /* working memory for operations */
      27              : 
      28              : /*
      29              :  * Replay the clearing of F_FOLLOW_RIGHT flag on a child page.
      30              :  *
      31              :  * Even if the WAL record includes a full-page image, we have to update the
      32              :  * follow-right flag, because that change is not included in the full-page
      33              :  * image.  To be sure that the intermediate state with the wrong flag value is
      34              :  * not visible to concurrent Hot Standby queries, this function handles
      35              :  * restoring the full-page image as well as updating the flag.  (Note that
      36              :  * we never need to do anything else to the child page in the current WAL
      37              :  * action.)
      38              :  */
      39              : static void
      40          450 : gistRedoClearFollowRight(XLogReaderState *record, uint8 block_id)
      41              : {
      42          450 :     XLogRecPtr  lsn = record->EndRecPtr;
      43              :     Buffer      buffer;
      44              :     Page        page;
      45              :     XLogRedoAction action;
      46              : 
      47              :     /*
      48              :      * Note that we still update the page even if it was restored from a full
      49              :      * page image, because the updated NSN is not included in the image.
      50              :      */
      51          450 :     action = XLogReadBufferForRedo(record, block_id, &buffer);
      52          450 :     if (action == BLK_NEEDS_REDO || action == BLK_RESTORED)
      53              :     {
      54          450 :         page = BufferGetPage(buffer);
      55              : 
      56          450 :         GistPageSetNSN(page, lsn);
      57          450 :         GistClearFollowRight(page);
      58              : 
      59          450 :         PageSetLSN(page, lsn);
      60          450 :         MarkBufferDirty(buffer);
      61              :     }
      62          450 :     if (BufferIsValid(buffer))
      63          450 :         UnlockReleaseBuffer(buffer);
      64          450 : }
      65              : 
      66              : /*
      67              :  * redo any page update (except page split)
      68              :  */
      69              : static void
      70        57532 : gistRedoPageUpdateRecord(XLogReaderState *record)
      71              : {
      72        57532 :     XLogRecPtr  lsn = record->EndRecPtr;
      73        57532 :     gistxlogPageUpdate *xldata = (gistxlogPageUpdate *) XLogRecGetData(record);
      74              :     Buffer      buffer;
      75              :     Page        page;
      76              : 
      77        57532 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
      78              :     {
      79              :         char       *begin;
      80              :         char       *data;
      81              :         Size        datalen;
      82        57171 :         int         ninserted PG_USED_FOR_ASSERTS_ONLY = 0;
      83              : 
      84        57171 :         data = begin = XLogRecGetBlockData(record, 0, &datalen);
      85              : 
      86        57171 :         page = BufferGetPage(buffer);
      87              : 
      88        57171 :         if (xldata->ntodelete == 1 && xldata->ntoinsert == 1)
      89        20876 :         {
      90              :             /*
      91              :              * When replacing one tuple with one other tuple, we must use
      92              :              * PageIndexTupleOverwrite for consistency with gistplacetopage.
      93              :              */
      94        20876 :             OffsetNumber offnum = *((OffsetNumber *) data);
      95              :             IndexTuple  itup;
      96              :             Size        itupsize;
      97              : 
      98        20876 :             data += sizeof(OffsetNumber);
      99        20876 :             itup = (IndexTuple) data;
     100        20876 :             itupsize = IndexTupleSize(itup);
     101        20876 :             if (!PageIndexTupleOverwrite(page, offnum, itup, itupsize))
     102            0 :                 elog(ERROR, "failed to add item to GiST index page, size %zu bytes", itupsize);
     103        20876 :             data += itupsize;
     104              :             /* should be nothing left after consuming 1 tuple */
     105              :             Assert(data - begin == datalen);
     106              :             /* update insertion count for assert check below */
     107        20876 :             ninserted++;
     108              :         }
     109        36295 :         else if (xldata->ntodelete > 0)
     110              :         {
     111              :             /* Otherwise, delete old tuples if any */
     112          465 :             OffsetNumber *todelete = (OffsetNumber *) data;
     113              : 
     114          465 :             data += sizeof(OffsetNumber) * xldata->ntodelete;
     115              : 
     116          465 :             PageIndexMultiDelete(page, todelete, xldata->ntodelete);
     117          465 :             if (GistPageIsLeaf(page))
     118           19 :                 GistMarkTuplesDeleted(page);
     119              :         }
     120              : 
     121              :         /* Add new tuples if any */
     122        57171 :         if (data - begin < datalen)
     123              :         {
     124        36276 :             OffsetNumber off = (PageIsEmpty(page)) ? FirstOffsetNumber :
     125        36212 :                 OffsetNumberNext(PageGetMaxOffsetNumber(page));
     126              : 
     127        72998 :             while (data - begin < datalen)
     128              :             {
     129        36722 :                 IndexTuple  itup = (IndexTuple) data;
     130        36722 :                 Size        sz = IndexTupleSize(itup);
     131              :                 OffsetNumber l;
     132              : 
     133        36722 :                 data += sz;
     134              : 
     135        36722 :                 l = PageAddItem(page, itup, sz, off, false, false);
     136        36722 :                 if (l == InvalidOffsetNumber)
     137            0 :                     elog(ERROR, "failed to add item to GiST index page, size %zu bytes", sz);
     138        36722 :                 off++;
     139        36722 :                 ninserted++;
     140              :             }
     141              :         }
     142              : 
     143              :         /* Check that XLOG record contained expected number of tuples */
     144              :         Assert(ninserted == xldata->ntoinsert);
     145              : 
     146        57171 :         PageSetLSN(page, lsn);
     147        57171 :         MarkBufferDirty(buffer);
     148              :     }
     149              : 
     150              :     /*
     151              :      * Fix follow-right data on left child page
     152              :      *
     153              :      * This must be done while still holding the lock on the target page. Note
     154              :      * that even if the target page no longer exists, we still attempt to
     155              :      * replay the change on the child page.
     156              :      */
     157        57532 :     if (XLogRecHasBlockRef(record, 1))
     158          448 :         gistRedoClearFollowRight(record, 1);
     159              : 
     160        57532 :     if (BufferIsValid(buffer))
     161        57532 :         UnlockReleaseBuffer(buffer);
     162        57532 : }
     163              : 
     164              : 
     165              : /*
     166              :  * redo delete on gist index page to remove tuples marked as DEAD during index
     167              :  * tuple insertion
     168              :  */
     169              : static void
     170            0 : gistRedoDeleteRecord(XLogReaderState *record)
     171              : {
     172            0 :     XLogRecPtr  lsn = record->EndRecPtr;
     173            0 :     gistxlogDelete *xldata = (gistxlogDelete *) XLogRecGetData(record);
     174              :     Buffer      buffer;
     175              :     Page        page;
     176            0 :     OffsetNumber *toDelete = xldata->offsets;
     177              : 
     178              :     /*
     179              :      * If we have any conflict processing to do, it must happen before we
     180              :      * update the page.
     181              :      *
     182              :      * GiST delete records can conflict with standby queries.  You might think
     183              :      * that vacuum records would conflict as well, but we've handled that
     184              :      * already.  XLOG_HEAP2_PRUNE_VACUUM_SCAN records provide the highest xid
     185              :      * cleaned by the vacuum of the heap and so we can resolve any conflicts
     186              :      * just once when that arrives.  After that we know that no conflicts
     187              :      * exist from individual gist vacuum records on that index.
     188              :      */
     189            0 :     if (InHotStandby)
     190              :     {
     191              :         RelFileLocator rlocator;
     192              : 
     193            0 :         XLogRecGetBlockTag(record, 0, &rlocator, NULL, NULL);
     194              : 
     195            0 :         ResolveRecoveryConflictWithSnapshot(xldata->snapshotConflictHorizon,
     196            0 :                                             xldata->isCatalogRel,
     197              :                                             rlocator);
     198              :     }
     199              : 
     200            0 :     if (XLogReadBufferForRedo(record, 0, &buffer) == BLK_NEEDS_REDO)
     201              :     {
     202            0 :         page = BufferGetPage(buffer);
     203              : 
     204            0 :         PageIndexMultiDelete(page, toDelete, xldata->ntodelete);
     205              : 
     206            0 :         GistClearPageHasGarbage(page);
     207            0 :         GistMarkTuplesDeleted(page);
     208              : 
     209            0 :         PageSetLSN(page, lsn);
     210            0 :         MarkBufferDirty(buffer);
     211              :     }
     212              : 
     213            0 :     if (BufferIsValid(buffer))
     214            0 :         UnlockReleaseBuffer(buffer);
     215            0 : }
     216              : 
     217              : /*
     218              :  * Returns an array of index pointers.
     219              :  */
     220              : static IndexTuple *
     221          915 : decodePageSplitRecord(char *begin, int len, int *n)
     222              : {
     223              :     char       *ptr;
     224          915 :     int         i = 0;
     225              :     IndexTuple *tuples;
     226              : 
     227              :     /* extract the number of tuples */
     228          915 :     memcpy(n, begin, sizeof(int));
     229          915 :     ptr = begin + sizeof(int);
     230              : 
     231          915 :     tuples = palloc(*n * sizeof(IndexTuple));
     232              : 
     233        79647 :     for (i = 0; i < *n; i++)
     234              :     {
     235              :         Assert(ptr - begin < len);
     236        78732 :         tuples[i] = (IndexTuple) ptr;
     237        78732 :         ptr += IndexTupleSize((IndexTuple) ptr);
     238              :     }
     239              :     Assert(ptr - begin == len);
     240              : 
     241          915 :     return tuples;
     242              : }
     243              : 
     244              : static void
     245          455 : gistRedoPageSplitRecord(XLogReaderState *record)
     246              : {
     247          455 :     XLogRecPtr  lsn = record->EndRecPtr;
     248          455 :     gistxlogPageSplit *xldata = (gistxlogPageSplit *) XLogRecGetData(record);
     249          455 :     Buffer      firstbuffer = InvalidBuffer;
     250              :     Buffer      buffer;
     251              :     Page        page;
     252              :     int         i;
     253          455 :     bool        isrootsplit = false;
     254              : 
     255              :     /*
     256              :      * We must hold lock on the first-listed page throughout the action,
     257              :      * including while updating the left child page (if any).  We can unlock
     258              :      * remaining pages in the list as soon as they've been written, because
     259              :      * there is no path for concurrent queries to reach those pages without
     260              :      * first visiting the first-listed page.
     261              :      */
     262              : 
     263              :     /* loop around all pages */
     264         1370 :     for (i = 0; i < xldata->npage; i++)
     265              :     {
     266              :         int         flags;
     267              :         char       *data;
     268              :         Size        datalen;
     269              :         int         num;
     270              :         BlockNumber blkno;
     271              :         IndexTuple *tuples;
     272              : 
     273          915 :         XLogRecGetBlockTag(record, i + 1, NULL, NULL, &blkno);
     274          915 :         if (blkno == GIST_ROOT_BLKNO)
     275              :         {
     276              :             Assert(i == 0);
     277            5 :             isrootsplit = true;
     278              :         }
     279              : 
     280          915 :         buffer = XLogInitBufferForRedo(record, i + 1);
     281          915 :         page = BufferGetPage(buffer);
     282          915 :         data = XLogRecGetBlockData(record, i + 1, &datalen);
     283              : 
     284          915 :         tuples = decodePageSplitRecord(data, datalen, &num);
     285              : 
     286              :         /* ok, clear buffer */
     287          915 :         if (xldata->origleaf && blkno != GIST_ROOT_BLKNO)
     288          906 :             flags = F_LEAF;
     289              :         else
     290            9 :             flags = 0;
     291          915 :         GISTInitBuffer(buffer, flags);
     292              : 
     293              :         /* and fill it */
     294          915 :         gistfillbuffer(page, tuples, num, FirstOffsetNumber);
     295              : 
     296          915 :         if (blkno == GIST_ROOT_BLKNO)
     297              :         {
     298            5 :             GistPageGetOpaque(page)->rightlink = InvalidBlockNumber;
     299            5 :             GistPageSetNSN(page, xldata->orignsn);
     300            5 :             GistClearFollowRight(page);
     301              :         }
     302              :         else
     303              :         {
     304          910 :             if (i < xldata->npage - 1)
     305              :             {
     306              :                 BlockNumber nextblkno;
     307              : 
     308          455 :                 XLogRecGetBlockTag(record, i + 2, NULL, NULL, &nextblkno);
     309          455 :                 GistPageGetOpaque(page)->rightlink = nextblkno;
     310              :             }
     311              :             else
     312          455 :                 GistPageGetOpaque(page)->rightlink = xldata->origrlink;
     313          910 :             GistPageSetNSN(page, xldata->orignsn);
     314          910 :             if (i < xldata->npage - 1 && !isrootsplit &&
     315          450 :                 xldata->markfollowright)
     316          450 :                 GistMarkFollowRight(page);
     317              :             else
     318          460 :                 GistClearFollowRight(page);
     319              :         }
     320              : 
     321          915 :         PageSetLSN(page, lsn);
     322          915 :         MarkBufferDirty(buffer);
     323              : 
     324          915 :         if (i == 0)
     325          455 :             firstbuffer = buffer;
     326              :         else
     327          460 :             UnlockReleaseBuffer(buffer);
     328              :     }
     329              : 
     330              :     /* Fix follow-right data on left child page, if any */
     331          455 :     if (XLogRecHasBlockRef(record, 0))
     332            2 :         gistRedoClearFollowRight(record, 0);
     333              : 
     334              :     /* Finally, release lock on the first page */
     335          455 :     UnlockReleaseBuffer(firstbuffer);
     336          455 : }
     337              : 
     338              : /* redo page deletion */
     339              : static void
     340           81 : gistRedoPageDelete(XLogReaderState *record)
     341              : {
     342           81 :     XLogRecPtr  lsn = record->EndRecPtr;
     343           81 :     gistxlogPageDelete *xldata = (gistxlogPageDelete *) XLogRecGetData(record);
     344              :     Buffer      parentBuffer;
     345              :     Buffer      leafBuffer;
     346              : 
     347           81 :     if (XLogReadBufferForRedo(record, 0, &leafBuffer) == BLK_NEEDS_REDO)
     348              :     {
     349           81 :         Page        page = BufferGetPage(leafBuffer);
     350              : 
     351           81 :         GistPageSetDeleted(page, xldata->deleteXid);
     352              : 
     353           81 :         PageSetLSN(page, lsn);
     354           81 :         MarkBufferDirty(leafBuffer);
     355              :     }
     356              : 
     357           81 :     if (XLogReadBufferForRedo(record, 1, &parentBuffer) == BLK_NEEDS_REDO)
     358              :     {
     359           80 :         Page        page = BufferGetPage(parentBuffer);
     360              : 
     361           80 :         PageIndexTupleDelete(page, xldata->downlinkOffset);
     362              : 
     363           80 :         PageSetLSN(page, lsn);
     364           80 :         MarkBufferDirty(parentBuffer);
     365              :     }
     366              : 
     367           81 :     if (BufferIsValid(parentBuffer))
     368           81 :         UnlockReleaseBuffer(parentBuffer);
     369           81 :     if (BufferIsValid(leafBuffer))
     370           81 :         UnlockReleaseBuffer(leafBuffer);
     371           81 : }
     372              : 
     373              : static void
     374            0 : gistRedoPageReuse(XLogReaderState *record)
     375              : {
     376            0 :     gistxlogPageReuse *xlrec = (gistxlogPageReuse *) XLogRecGetData(record);
     377              : 
     378              :     /*
     379              :      * PAGE_REUSE records exist to provide a conflict point when we reuse
     380              :      * pages in the index via the FSM.  That's all they do though.
     381              :      *
     382              :      * snapshotConflictHorizon was the page's deleteXid.  The
     383              :      * GlobalVisCheckRemovableFullXid(deleteXid) test in gistPageRecyclable()
     384              :      * conceptually mirrors the PGPROC->xmin > limitXmin test in
     385              :      * GetConflictingVirtualXIDs().  Consequently, one XID value achieves the
     386              :      * same exclusion effect on primary and standby.
     387              :      */
     388            0 :     if (InHotStandby)
     389            0 :         ResolveRecoveryConflictWithSnapshotFullXid(xlrec->snapshotConflictHorizon,
     390            0 :                                                    xlrec->isCatalogRel,
     391              :                                                    xlrec->locator);
     392            0 : }
     393              : 
     394              : void
     395        58068 : gist_redo(XLogReaderState *record)
     396              : {
     397        58068 :     uint8       info = XLogRecGetInfo(record) & ~XLR_INFO_MASK;
     398              :     MemoryContext oldCxt;
     399              : 
     400              :     /*
     401              :      * GiST indexes do not require any conflict processing. NB: If we ever
     402              :      * implement a similar optimization we have in b-tree, and remove killed
     403              :      * tuples outside VACUUM, we'll need to handle that here.
     404              :      */
     405              : 
     406        58068 :     oldCxt = MemoryContextSwitchTo(opCtx);
     407        58068 :     switch (info)
     408              :     {
     409        57532 :         case XLOG_GIST_PAGE_UPDATE:
     410        57532 :             gistRedoPageUpdateRecord(record);
     411        57532 :             break;
     412            0 :         case XLOG_GIST_DELETE:
     413            0 :             gistRedoDeleteRecord(record);
     414            0 :             break;
     415            0 :         case XLOG_GIST_PAGE_REUSE:
     416            0 :             gistRedoPageReuse(record);
     417            0 :             break;
     418          455 :         case XLOG_GIST_PAGE_SPLIT:
     419          455 :             gistRedoPageSplitRecord(record);
     420          455 :             break;
     421           81 :         case XLOG_GIST_PAGE_DELETE:
     422           81 :             gistRedoPageDelete(record);
     423           81 :             break;
     424            0 :         case XLOG_GIST_ASSIGN_LSN:
     425              :             /* nop. See gistGetFakeLSN(). */
     426            0 :             break;
     427            0 :         default:
     428            0 :             elog(PANIC, "gist_redo: unknown op code %u", info);
     429              :     }
     430              : 
     431        58068 :     MemoryContextSwitchTo(oldCxt);
     432        58068 :     MemoryContextReset(opCtx);
     433        58068 : }
     434              : 
     435              : void
     436          216 : gist_xlog_startup(void)
     437              : {
     438          216 :     opCtx = createTempGistContext();
     439          216 : }
     440              : 
     441              : void
     442          155 : gist_xlog_cleanup(void)
     443              : {
     444          155 :     MemoryContextDelete(opCtx);
     445          155 : }
     446              : 
     447              : /*
     448              :  * Mask a Gist page before running consistency checks on it.
     449              :  */
     450              : void
     451       117394 : gist_mask(char *pagedata, BlockNumber blkno)
     452              : {
     453       117394 :     Page        page = (Page) pagedata;
     454              : 
     455       117394 :     mask_page_lsn_and_checksum(page);
     456              : 
     457       117394 :     mask_page_hint_bits(page);
     458       117394 :     mask_unused_space(page);
     459              : 
     460              :     /*
     461              :      * NSN is nothing but a special purpose LSN. Hence, mask it for the same
     462              :      * reason as mask_page_lsn_and_checksum.
     463              :      */
     464       117394 :     GistPageSetNSN(page, (uint64) MASK_MARKER);
     465              : 
     466              :     /*
     467              :      * We update F_FOLLOW_RIGHT flag on the left child after writing WAL
     468              :      * record. Hence, mask this flag. See gistplacetopage() for details.
     469              :      */
     470       117394 :     GistMarkFollowRight(page);
     471              : 
     472       117394 :     if (GistPageIsLeaf(page))
     473              :     {
     474              :         /*
     475              :          * In gist leaf pages, it is possible to modify the LP_FLAGS without
     476              :          * emitting any WAL record. Hence, mask the line pointer flags. See
     477              :          * gistkillitems() for details.
     478              :          */
     479        74572 :         mask_lp_flags(page);
     480              :     }
     481              : 
     482              :     /*
     483              :      * During gist redo, we never mark a page as garbage. Hence, mask it to
     484              :      * ignore any differences.
     485              :      */
     486       117394 :     GistClearPageHasGarbage(page);
     487       117394 : }
     488              : 
     489              : /*
     490              :  * Write WAL record of a page split.
     491              :  */
     492              : XLogRecPtr
     493         1738 : gistXLogSplit(bool page_is_leaf,
     494              :               SplitPageLayout *dist,
     495              :               BlockNumber origrlink, GistNSN orignsn,
     496              :               Buffer leftchildbuf, bool markfollowright)
     497              : {
     498              :     gistxlogPageSplit xlrec;
     499              :     SplitPageLayout *ptr;
     500         1738 :     int         npage = 0;
     501              :     XLogRecPtr  recptr;
     502              :     int         i;
     503              : 
     504         5270 :     for (ptr = dist; ptr; ptr = ptr->next)
     505         3532 :         npage++;
     506              : 
     507         1738 :     xlrec.origrlink = origrlink;
     508         1738 :     xlrec.orignsn = orignsn;
     509         1738 :     xlrec.origleaf = page_is_leaf;
     510         1738 :     xlrec.npage = (uint16) npage;
     511         1738 :     xlrec.markfollowright = markfollowright;
     512              : 
     513         1738 :     XLogBeginInsert();
     514              : 
     515              :     /*
     516              :      * Include a full page image of the child buf. (only necessary if a
     517              :      * checkpoint happened since the child page was split)
     518              :      */
     519         1738 :     if (BufferIsValid(leftchildbuf))
     520            6 :         XLogRegisterBuffer(0, leftchildbuf, REGBUF_STANDARD);
     521              : 
     522              :     /*
     523              :      * NOTE: We register a lot of data. The caller must've called
     524              :      * XLogEnsureRecordSpace() to prepare for that. We cannot do it here,
     525              :      * because we're already in a critical section. If you change the number
     526              :      * of buffer or data registrations here, make sure you modify the
     527              :      * XLogEnsureRecordSpace() calls accordingly!
     528              :      */
     529         1738 :     XLogRegisterData(&xlrec, sizeof(gistxlogPageSplit));
     530              : 
     531         1738 :     i = 1;
     532         5270 :     for (ptr = dist; ptr; ptr = ptr->next)
     533              :     {
     534         3532 :         XLogRegisterBuffer(i, ptr->buffer, REGBUF_WILL_INIT);
     535         3532 :         XLogRegisterBufData(i, &(ptr->block.num), sizeof(int));
     536         3532 :         XLogRegisterBufData(i, ptr->list, ptr->lenlist);
     537         3532 :         i++;
     538              :     }
     539              : 
     540         1738 :     recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_SPLIT);
     541              : 
     542         1738 :     return recptr;
     543              : }
     544              : 
     545              : /*
     546              :  * Write XLOG record describing a page deletion. This also includes removal of
     547              :  * downlink from the parent page.
     548              :  */
     549              : XLogRecPtr
     550          162 : gistXLogPageDelete(Buffer buffer, FullTransactionId xid,
     551              :                    Buffer parentBuffer, OffsetNumber downlinkOffset)
     552              : {
     553              :     gistxlogPageDelete xlrec;
     554              :     XLogRecPtr  recptr;
     555              : 
     556          162 :     xlrec.deleteXid = xid;
     557          162 :     xlrec.downlinkOffset = downlinkOffset;
     558              : 
     559          162 :     XLogBeginInsert();
     560          162 :     XLogRegisterData(&xlrec, SizeOfGistxlogPageDelete);
     561              : 
     562          162 :     XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
     563          162 :     XLogRegisterBuffer(1, parentBuffer, REGBUF_STANDARD);
     564              : 
     565          162 :     recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_DELETE);
     566              : 
     567          162 :     return recptr;
     568              : }
     569              : 
     570              : /*
     571              :  * Write an empty XLOG record to assign a distinct LSN.
     572              :  */
     573              : XLogRecPtr
     574            0 : gistXLogAssignLSN(void)
     575              : {
     576            0 :     int         dummy = 0;
     577              : 
     578              :     /*
     579              :      * Records other than XLOG_SWITCH must have content. We use an integer 0
     580              :      * to follow the restriction.
     581              :      */
     582            0 :     XLogBeginInsert();
     583            0 :     XLogSetRecordFlags(XLOG_MARK_UNIMPORTANT);
     584            0 :     XLogRegisterData(&dummy, sizeof(dummy));
     585            0 :     return XLogInsert(RM_GIST_ID, XLOG_GIST_ASSIGN_LSN);
     586              : }
     587              : 
     588              : /*
     589              :  * Write XLOG record about reuse of a deleted page.
     590              :  */
     591              : void
     592            0 : gistXLogPageReuse(Relation rel, Relation heaprel,
     593              :                   BlockNumber blkno, FullTransactionId deleteXid)
     594              : {
     595              :     gistxlogPageReuse xlrec_reuse;
     596              : 
     597              :     /*
     598              :      * Note that we don't register the buffer with the record, because this
     599              :      * operation doesn't modify the page. This record only exists to provide a
     600              :      * conflict point for Hot Standby.
     601              :      */
     602              : 
     603              :     /* XLOG stuff */
     604            0 :     xlrec_reuse.isCatalogRel = RelationIsAccessibleInLogicalDecoding(heaprel);
     605            0 :     xlrec_reuse.locator = rel->rd_locator;
     606            0 :     xlrec_reuse.block = blkno;
     607            0 :     xlrec_reuse.snapshotConflictHorizon = deleteXid;
     608              : 
     609            0 :     XLogBeginInsert();
     610            0 :     XLogRegisterData(&xlrec_reuse, SizeOfGistxlogPageReuse);
     611              : 
     612            0 :     XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_REUSE);
     613            0 : }
     614              : 
     615              : /*
     616              :  * Write XLOG record describing a page update. The update can include any
     617              :  * number of deletions and/or insertions of tuples on a single index page.
     618              :  *
     619              :  * If this update inserts a downlink for a split page, also record that
     620              :  * the F_FOLLOW_RIGHT flag on the child page is cleared and NSN set.
     621              :  *
     622              :  * Note that both the todelete array and the tuples are marked as belonging
     623              :  * to the target buffer; they need not be stored in XLOG if XLogInsert decides
     624              :  * to log the whole buffer contents instead.
     625              :  */
     626              : XLogRecPtr
     627       247763 : gistXLogUpdate(Buffer buffer,
     628              :                OffsetNumber *todelete, int ntodelete,
     629              :                IndexTuple *itup, int ituplen,
     630              :                Buffer leftchildbuf)
     631              : {
     632              :     gistxlogPageUpdate xlrec;
     633              :     int         i;
     634              :     XLogRecPtr  recptr;
     635              : 
     636       247763 :     xlrec.ntodelete = ntodelete;
     637       247763 :     xlrec.ntoinsert = ituplen;
     638              : 
     639       247763 :     XLogBeginInsert();
     640       247763 :     XLogRegisterData(&xlrec, sizeof(gistxlogPageUpdate));
     641              : 
     642       247763 :     XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
     643       247763 :     XLogRegisterBufData(0, todelete, sizeof(OffsetNumber) * ntodelete);
     644              : 
     645              :     /* new tuples */
     646       496794 :     for (i = 0; i < ituplen; i++)
     647       249031 :         XLogRegisterBufData(0, itup[i], IndexTupleSize(itup[i]));
     648              : 
     649              :     /*
     650              :      * Include a full page image of the child buf. (only necessary if a
     651              :      * checkpoint happened since the child page was split)
     652              :      */
     653       247763 :     if (BufferIsValid(leftchildbuf))
     654         1676 :         XLogRegisterBuffer(1, leftchildbuf, REGBUF_STANDARD);
     655              : 
     656       247763 :     recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_PAGE_UPDATE);
     657              : 
     658       247763 :     return recptr;
     659              : }
     660              : 
     661              : /*
     662              :  * Write XLOG record describing a delete of leaf index tuples marked as DEAD
     663              :  * during new tuple insertion.  One may think that this case is already covered
     664              :  * by gistXLogUpdate().  But deletion of index tuples might conflict with
     665              :  * standby queries and needs special handling.
     666              :  */
     667              : XLogRecPtr
     668            0 : gistXLogDelete(Buffer buffer, OffsetNumber *todelete, int ntodelete,
     669              :                TransactionId snapshotConflictHorizon, Relation heaprel)
     670              : {
     671              :     gistxlogDelete xlrec;
     672              :     XLogRecPtr  recptr;
     673              : 
     674            0 :     xlrec.isCatalogRel = RelationIsAccessibleInLogicalDecoding(heaprel);
     675            0 :     xlrec.snapshotConflictHorizon = snapshotConflictHorizon;
     676            0 :     xlrec.ntodelete = ntodelete;
     677              : 
     678            0 :     XLogBeginInsert();
     679            0 :     XLogRegisterData(&xlrec, SizeOfGistxlogDelete);
     680              : 
     681              :     /*
     682              :      * We need the target-offsets array whether or not we store the whole
     683              :      * buffer, to allow us to find the snapshotConflictHorizon on a standby
     684              :      * server.
     685              :      */
     686            0 :     XLogRegisterData(todelete, ntodelete * sizeof(OffsetNumber));
     687              : 
     688            0 :     XLogRegisterBuffer(0, buffer, REGBUF_STANDARD);
     689              : 
     690            0 :     recptr = XLogInsert(RM_GIST_ID, XLOG_GIST_DELETE);
     691              : 
     692            0 :     return recptr;
     693              : }
        

Generated by: LCOV version 2.0-1