LCOV - code coverage report
Current view: top level - src/backend/access/brin - brin_revmap.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 92.0 % 201 185
Test Date: 2026-03-01 10:14:49 Functions: 100.0 % 11 11
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*
       2              :  * brin_revmap.c
       3              :  *      Range map for BRIN indexes
       4              :  *
       5              :  * The range map (revmap) is a translation structure for BRIN indexes: for each
       6              :  * page range there is one summary tuple, and its location is tracked by the
       7              :  * revmap.  Whenever a new tuple is inserted into a table that violates the
       8              :  * previously recorded summary values, a new tuple is inserted into the index
       9              :  * and the revmap is updated to point to it.
      10              :  *
      11              :  * The revmap is stored in the first pages of the index, immediately following
      12              :  * the metapage.  When the revmap needs to be expanded, all tuples on the
      13              :  * regular BRIN page at that block (if any) are moved out of the way.
      14              :  *
      15              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      16              :  * Portions Copyright (c) 1994, Regents of the University of California
      17              :  *
      18              :  * IDENTIFICATION
      19              :  *    src/backend/access/brin/brin_revmap.c
      20              :  */
      21              : #include "postgres.h"
      22              : 
      23              : #include "access/brin_page.h"
      24              : #include "access/brin_pageops.h"
      25              : #include "access/brin_revmap.h"
      26              : #include "access/brin_tuple.h"
      27              : #include "access/brin_xlog.h"
      28              : #include "access/rmgr.h"
      29              : #include "access/xloginsert.h"
      30              : #include "miscadmin.h"
      31              : #include "storage/bufmgr.h"
      32              : #include "utils/rel.h"
      33              : 
      34              : 
      35              : /*
      36              :  * In revmap pages, each item stores an ItemPointerData.  These defines let one
      37              :  * find the logical revmap page number and index number of the revmap item for
      38              :  * the given heap block number.
      39              :  */
      40              : #define HEAPBLK_TO_REVMAP_BLK(pagesPerRange, heapBlk) \
      41              :     ((heapBlk / pagesPerRange) / REVMAP_PAGE_MAXITEMS)
      42              : #define HEAPBLK_TO_REVMAP_INDEX(pagesPerRange, heapBlk) \
      43              :     ((heapBlk / pagesPerRange) % REVMAP_PAGE_MAXITEMS)
      44              : 
      45              : 
      46              : struct BrinRevmap
      47              : {
      48              :     Relation    rm_irel;
      49              :     BlockNumber rm_pagesPerRange;
      50              :     BlockNumber rm_lastRevmapPage;  /* cached from the metapage */
      51              :     Buffer      rm_metaBuf;
      52              :     Buffer      rm_currBuf;
      53              : };
      54              : 
      55              : /* typedef appears in brin_revmap.h */
      56              : 
      57              : 
      58              : static BlockNumber revmap_get_blkno(BrinRevmap *revmap,
      59              :                                     BlockNumber heapBlk);
      60              : static Buffer revmap_get_buffer(BrinRevmap *revmap, BlockNumber heapBlk);
      61              : static BlockNumber revmap_extend_and_get_blkno(BrinRevmap *revmap,
      62              :                                                BlockNumber heapBlk);
      63              : static void revmap_physical_extend(BrinRevmap *revmap);
      64              : 
      65              : /*
      66              :  * Initialize an access object for a range map.  This must be freed by
      67              :  * brinRevmapTerminate when caller is done with it.
      68              :  */
      69              : BrinRevmap *
      70         2377 : brinRevmapInitialize(Relation idxrel, BlockNumber *pagesPerRange)
      71              : {
      72              :     BrinRevmap *revmap;
      73              :     Buffer      meta;
      74              :     BrinMetaPageData *metadata;
      75              :     Page        page;
      76              : 
      77         2377 :     meta = ReadBuffer(idxrel, BRIN_METAPAGE_BLKNO);
      78         2377 :     LockBuffer(meta, BUFFER_LOCK_SHARE);
      79         2377 :     page = BufferGetPage(meta);
      80         2377 :     metadata = (BrinMetaPageData *) PageGetContents(page);
      81              : 
      82         2377 :     revmap = palloc_object(BrinRevmap);
      83         2377 :     revmap->rm_irel = idxrel;
      84         2377 :     revmap->rm_pagesPerRange = metadata->pagesPerRange;
      85         2377 :     revmap->rm_lastRevmapPage = metadata->lastRevmapPage;
      86         2377 :     revmap->rm_metaBuf = meta;
      87         2377 :     revmap->rm_currBuf = InvalidBuffer;
      88              : 
      89         2377 :     *pagesPerRange = metadata->pagesPerRange;
      90              : 
      91         2377 :     LockBuffer(meta, BUFFER_LOCK_UNLOCK);
      92              : 
      93         2377 :     return revmap;
      94              : }
      95              : 
      96              : /*
      97              :  * Release resources associated with a revmap access object.
      98              :  */
      99              : void
     100         2377 : brinRevmapTerminate(BrinRevmap *revmap)
     101              : {
     102         2377 :     ReleaseBuffer(revmap->rm_metaBuf);
     103         2377 :     if (revmap->rm_currBuf != InvalidBuffer)
     104         2353 :         ReleaseBuffer(revmap->rm_currBuf);
     105         2377 :     pfree(revmap);
     106         2377 : }
     107              : 
     108              : /*
     109              :  * Extend the revmap to cover the given heap block number.
     110              :  */
     111              : void
     112        16557 : brinRevmapExtend(BrinRevmap *revmap, BlockNumber heapBlk)
     113              : {
     114              :     BlockNumber mapBlk PG_USED_FOR_ASSERTS_ONLY;
     115              : 
     116        16557 :     mapBlk = revmap_extend_and_get_blkno(revmap, heapBlk);
     117              : 
     118              :     /* Ensure the buffer we got is in the expected range */
     119              :     Assert(mapBlk != InvalidBlockNumber &&
     120              :            mapBlk != BRIN_METAPAGE_BLKNO &&
     121              :            mapBlk <= revmap->rm_lastRevmapPage);
     122        16557 : }
     123              : 
     124              : /*
     125              :  * Prepare to insert an entry into the revmap; the revmap buffer in which the
     126              :  * entry is to reside is locked and returned.  Most callers should call
     127              :  * brinRevmapExtend beforehand, as this routine does not extend the revmap if
     128              :  * it's not long enough.
     129              :  *
     130              :  * The returned buffer is also recorded in the revmap struct; finishing that
     131              :  * releases the buffer, therefore the caller needn't do it explicitly.
     132              :  */
     133              : Buffer
     134         3171 : brinLockRevmapPageForUpdate(BrinRevmap *revmap, BlockNumber heapBlk)
     135              : {
     136              :     Buffer      rmBuf;
     137              : 
     138         3171 :     rmBuf = revmap_get_buffer(revmap, heapBlk);
     139         3171 :     LockBuffer(rmBuf, BUFFER_LOCK_EXCLUSIVE);
     140              : 
     141         3171 :     return rmBuf;
     142              : }
     143              : 
     144              : /*
     145              :  * In the given revmap buffer (locked appropriately by caller), which is used
     146              :  * in a BRIN index of pagesPerRange pages per range, set the element
     147              :  * corresponding to heap block number heapBlk to the given TID.
     148              :  *
     149              :  * Once the operation is complete, the caller must update the LSN on the
     150              :  * returned buffer.
     151              :  *
     152              :  * This is used both in regular operation and during WAL replay.
     153              :  */
     154              : void
     155         3592 : brinSetHeapBlockItemptr(Buffer buf, BlockNumber pagesPerRange,
     156              :                         BlockNumber heapBlk, ItemPointerData tid)
     157              : {
     158              :     RevmapContents *contents;
     159              :     ItemPointerData *iptr;
     160              :     Page        page;
     161              : 
     162              :     /* The correct page should already be pinned and locked */
     163         3592 :     page = BufferGetPage(buf);
     164         3592 :     contents = (RevmapContents *) PageGetContents(page);
     165         3592 :     iptr = (ItemPointerData *) contents->rm_tids;
     166         3592 :     iptr += HEAPBLK_TO_REVMAP_INDEX(pagesPerRange, heapBlk);
     167              : 
     168         3592 :     if (ItemPointerIsValid(&tid))
     169         3566 :         ItemPointerSet(iptr,
     170              :                        ItemPointerGetBlockNumber(&tid),
     171         3566 :                        ItemPointerGetOffsetNumber(&tid));
     172              :     else
     173           26 :         ItemPointerSetInvalid(iptr);
     174         3592 : }
     175              : 
     176              : /*
     177              :  * Fetch the BrinTuple for a given heap block.
     178              :  *
     179              :  * The buffer containing the tuple is locked, and returned in *buf.  The
     180              :  * returned tuple points to the shared buffer and must not be freed; if caller
     181              :  * wants to use it after releasing the buffer lock, it must create its own
     182              :  * palloc'ed copy.  As an optimization, the caller can pass a pinned buffer
     183              :  * *buf on entry, which will avoid a pin-unpin cycle when the next tuple is on
     184              :  * the same page as a previous one.
     185              :  *
     186              :  * If no tuple is found for the given heap range, returns NULL. In that case,
     187              :  * *buf might still be updated (and pin must be released by caller), but it's
     188              :  * not locked.
     189              :  *
     190              :  * The output tuple offset within the buffer is returned in *off, and its size
     191              :  * is returned in *size.
     192              :  */
     193              : BrinTuple *
     194       168445 : brinGetTupleForHeapBlock(BrinRevmap *revmap, BlockNumber heapBlk,
     195              :                          Buffer *buf, OffsetNumber *off, Size *size, int mode)
     196              : {
     197       168445 :     Relation    idxRel = revmap->rm_irel;
     198              :     BlockNumber mapBlk;
     199              :     RevmapContents *contents;
     200              :     ItemPointerData *iptr;
     201              :     BlockNumber blk;
     202              :     Page        page;
     203              :     ItemId      lp;
     204              :     BrinTuple  *tup;
     205              :     ItemPointerData previptr;
     206              : 
     207              :     /* normalize the heap block number to be the first page in the range */
     208       168445 :     heapBlk = (heapBlk / revmap->rm_pagesPerRange) * revmap->rm_pagesPerRange;
     209              : 
     210              :     /*
     211              :      * Compute the revmap page number we need.  If Invalid is returned (i.e.,
     212              :      * the revmap page hasn't been created yet), the requested page range is
     213              :      * not summarized.
     214              :      */
     215       168445 :     mapBlk = revmap_get_blkno(revmap, heapBlk);
     216       168445 :     if (mapBlk == InvalidBlockNumber)
     217              :     {
     218          941 :         *off = InvalidOffsetNumber;
     219          941 :         return NULL;
     220              :     }
     221              : 
     222       167504 :     ItemPointerSetInvalid(&previptr);
     223              :     for (;;)
     224              :     {
     225       167504 :         CHECK_FOR_INTERRUPTS();
     226              : 
     227       332873 :         if (revmap->rm_currBuf == InvalidBuffer ||
     228       165369 :             BufferGetBlockNumber(revmap->rm_currBuf) != mapBlk)
     229              :         {
     230         2135 :             if (revmap->rm_currBuf != InvalidBuffer)
     231            0 :                 ReleaseBuffer(revmap->rm_currBuf);
     232              : 
     233              :             Assert(mapBlk != InvalidBlockNumber);
     234         2135 :             revmap->rm_currBuf = ReadBuffer(revmap->rm_irel, mapBlk);
     235              :         }
     236              : 
     237       167504 :         LockBuffer(revmap->rm_currBuf, BUFFER_LOCK_SHARE);
     238              : 
     239              :         contents = (RevmapContents *)
     240       167504 :             PageGetContents(BufferGetPage(revmap->rm_currBuf));
     241       167504 :         iptr = contents->rm_tids;
     242       167504 :         iptr += HEAPBLK_TO_REVMAP_INDEX(revmap->rm_pagesPerRange, heapBlk);
     243              : 
     244       167504 :         if (!ItemPointerIsValid(iptr))
     245              :         {
     246        40522 :             LockBuffer(revmap->rm_currBuf, BUFFER_LOCK_UNLOCK);
     247        40522 :             return NULL;
     248              :         }
     249              : 
     250              :         /*
     251              :          * Check the TID we got in a previous iteration, if any, and save the
     252              :          * current TID we got from the revmap; if we loop, we can sanity-check
     253              :          * that the next one we get is different.  Otherwise we might be stuck
     254              :          * looping forever if the revmap is somehow badly broken.
     255              :          */
     256       126982 :         if (ItemPointerIsValid(&previptr) && ItemPointerEquals(&previptr, iptr))
     257            0 :             ereport(ERROR,
     258              :                     (errcode(ERRCODE_INDEX_CORRUPTED),
     259              :                      errmsg_internal("corrupted BRIN index: inconsistent range map")));
     260       126982 :         previptr = *iptr;
     261              : 
     262       126982 :         blk = ItemPointerGetBlockNumber(iptr);
     263       126982 :         *off = ItemPointerGetOffsetNumber(iptr);
     264              : 
     265       126982 :         LockBuffer(revmap->rm_currBuf, BUFFER_LOCK_UNLOCK);
     266              : 
     267              :         /* Ok, got a pointer to where the BrinTuple should be. Fetch it. */
     268       126982 :         if (!BufferIsValid(*buf) || BufferGetBlockNumber(*buf) != blk)
     269              :         {
     270        34342 :             if (BufferIsValid(*buf))
     271         8806 :                 ReleaseBuffer(*buf);
     272        34342 :             *buf = ReadBuffer(idxRel, blk);
     273              :         }
     274       126982 :         LockBuffer(*buf, mode);
     275       126982 :         page = BufferGetPage(*buf);
     276              : 
     277              :         /* If we land on a revmap page, start over */
     278       126982 :         if (BRIN_IS_REGULAR_PAGE(page))
     279              :         {
     280              :             /*
     281              :              * If the offset number is greater than what's in the page, it's
     282              :              * possible that the range was desummarized concurrently. Just
     283              :              * return NULL to handle that case.
     284              :              */
     285       126982 :             if (*off > PageGetMaxOffsetNumber(page))
     286              :             {
     287            0 :                 LockBuffer(*buf, BUFFER_LOCK_UNLOCK);
     288            0 :                 return NULL;
     289              :             }
     290              : 
     291       126982 :             lp = PageGetItemId(page, *off);
     292       126982 :             if (ItemIdIsUsed(lp))
     293              :             {
     294       126982 :                 tup = (BrinTuple *) PageGetItem(page, lp);
     295              : 
     296       126982 :                 if (tup->bt_blkno == heapBlk)
     297              :                 {
     298       126982 :                     if (size)
     299        94968 :                         *size = ItemIdGetLength(lp);
     300              :                     /* found it! */
     301       126982 :                     return tup;
     302              :                 }
     303              :             }
     304              :         }
     305              : 
     306              :         /*
     307              :          * No luck. Assume that the revmap was updated concurrently.
     308              :          */
     309            0 :         LockBuffer(*buf, BUFFER_LOCK_UNLOCK);
     310              :     }
     311              :     /* not reached, but keep compiler quiet */
     312              :     return NULL;
     313              : }
     314              : 
     315              : /*
     316              :  * Delete an index tuple, marking a page range as unsummarized.
     317              :  *
     318              :  * Index must be locked in ShareUpdateExclusiveLock mode.
     319              :  *
     320              :  * Return false if caller should retry.
     321              :  */
     322              : bool
     323           43 : brinRevmapDesummarizeRange(Relation idxrel, BlockNumber heapBlk)
     324              : {
     325              :     BrinRevmap *revmap;
     326              :     BlockNumber pagesPerRange;
     327              :     RevmapContents *contents;
     328              :     ItemPointerData *iptr;
     329              :     ItemPointerData invalidIptr;
     330              :     BlockNumber revmapBlk;
     331              :     Buffer      revmapBuf;
     332              :     Buffer      regBuf;
     333              :     Page        revmapPg;
     334              :     Page        regPg;
     335              :     OffsetNumber revmapOffset;
     336              :     OffsetNumber regOffset;
     337              :     ItemId      lp;
     338              : 
     339           43 :     revmap = brinRevmapInitialize(idxrel, &pagesPerRange);
     340              : 
     341           43 :     revmapBlk = revmap_get_blkno(revmap, heapBlk);
     342           43 :     if (!BlockNumberIsValid(revmapBlk))
     343              :     {
     344              :         /* revmap page doesn't exist: range not summarized, we're done */
     345            9 :         brinRevmapTerminate(revmap);
     346            9 :         return true;
     347              :     }
     348              : 
     349              :     /* Lock the revmap page, obtain the index tuple pointer from it */
     350           34 :     revmapBuf = brinLockRevmapPageForUpdate(revmap, heapBlk);
     351           34 :     revmapPg = BufferGetPage(revmapBuf);
     352           34 :     revmapOffset = HEAPBLK_TO_REVMAP_INDEX(revmap->rm_pagesPerRange, heapBlk);
     353              : 
     354           34 :     contents = (RevmapContents *) PageGetContents(revmapPg);
     355           34 :     iptr = contents->rm_tids;
     356           34 :     iptr += revmapOffset;
     357              : 
     358           34 :     if (!ItemPointerIsValid(iptr))
     359              :     {
     360              :         /* no index tuple: range not summarized, we're done */
     361           12 :         LockBuffer(revmapBuf, BUFFER_LOCK_UNLOCK);
     362           12 :         brinRevmapTerminate(revmap);
     363           12 :         return true;
     364              :     }
     365              : 
     366           22 :     regBuf = ReadBuffer(idxrel, ItemPointerGetBlockNumber(iptr));
     367           22 :     LockBuffer(regBuf, BUFFER_LOCK_EXCLUSIVE);
     368           22 :     regPg = BufferGetPage(regBuf);
     369              : 
     370              :     /* if this is no longer a regular page, tell caller to start over */
     371           22 :     if (!BRIN_IS_REGULAR_PAGE(regPg))
     372              :     {
     373            0 :         LockBuffer(revmapBuf, BUFFER_LOCK_UNLOCK);
     374            0 :         LockBuffer(regBuf, BUFFER_LOCK_UNLOCK);
     375            0 :         brinRevmapTerminate(revmap);
     376            0 :         return false;
     377              :     }
     378              : 
     379           22 :     regOffset = ItemPointerGetOffsetNumber(iptr);
     380           22 :     if (regOffset > PageGetMaxOffsetNumber(regPg))
     381            0 :         ereport(ERROR,
     382              :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     383              :                  errmsg("corrupted BRIN index: inconsistent range map")));
     384              : 
     385           22 :     lp = PageGetItemId(regPg, regOffset);
     386           22 :     if (!ItemIdIsUsed(lp))
     387            0 :         ereport(ERROR,
     388              :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     389              :                  errmsg("corrupted BRIN index: inconsistent range map")));
     390              : 
     391              :     /*
     392              :      * Placeholder tuples only appear during unfinished summarization, and we
     393              :      * hold ShareUpdateExclusiveLock, so this function cannot run concurrently
     394              :      * with that.  So any placeholder tuples that exist are leftovers from a
     395              :      * crashed or aborted summarization; remove them silently.
     396              :      */
     397              : 
     398           22 :     START_CRIT_SECTION();
     399              : 
     400           22 :     ItemPointerSetInvalid(&invalidIptr);
     401           22 :     brinSetHeapBlockItemptr(revmapBuf, revmap->rm_pagesPerRange, heapBlk,
     402              :                             invalidIptr);
     403           22 :     PageIndexTupleDeleteNoCompact(regPg, regOffset);
     404              :     /* XXX record free space in FSM? */
     405              : 
     406           22 :     MarkBufferDirty(regBuf);
     407           22 :     MarkBufferDirty(revmapBuf);
     408              : 
     409           22 :     if (RelationNeedsWAL(idxrel))
     410              :     {
     411              :         xl_brin_desummarize xlrec;
     412              :         XLogRecPtr  recptr;
     413              : 
     414           22 :         xlrec.pagesPerRange = revmap->rm_pagesPerRange;
     415           22 :         xlrec.heapBlk = heapBlk;
     416           22 :         xlrec.regOffset = regOffset;
     417              : 
     418           22 :         XLogBeginInsert();
     419           22 :         XLogRegisterData(&xlrec, SizeOfBrinDesummarize);
     420           22 :         XLogRegisterBuffer(0, revmapBuf, 0);
     421           22 :         XLogRegisterBuffer(1, regBuf, REGBUF_STANDARD);
     422           22 :         recptr = XLogInsert(RM_BRIN_ID, XLOG_BRIN_DESUMMARIZE);
     423           22 :         PageSetLSN(revmapPg, recptr);
     424           22 :         PageSetLSN(regPg, recptr);
     425              :     }
     426              : 
     427           22 :     END_CRIT_SECTION();
     428              : 
     429           22 :     UnlockReleaseBuffer(regBuf);
     430           22 :     LockBuffer(revmapBuf, BUFFER_LOCK_UNLOCK);
     431           22 :     brinRevmapTerminate(revmap);
     432              : 
     433           22 :     return true;
     434              : }
     435              : 
     436              : /*
     437              :  * Given a heap block number, find the corresponding physical revmap block
     438              :  * number and return it.  If the revmap page hasn't been allocated yet, return
     439              :  * InvalidBlockNumber.
     440              :  */
     441              : static BlockNumber
     442       171659 : revmap_get_blkno(BrinRevmap *revmap, BlockNumber heapBlk)
     443              : {
     444              :     BlockNumber targetblk;
     445              : 
     446              :     /* obtain revmap block number, skip 1 for metapage block */
     447       171659 :     targetblk = HEAPBLK_TO_REVMAP_BLK(revmap->rm_pagesPerRange, heapBlk) + 1;
     448              : 
     449              :     /* Normal case: the revmap page is already allocated */
     450       171659 :     if (targetblk <= revmap->rm_lastRevmapPage)
     451       170709 :         return targetblk;
     452              : 
     453          950 :     return InvalidBlockNumber;
     454              : }
     455              : 
     456              : /*
     457              :  * Obtain and return a buffer containing the revmap page for the given heap
     458              :  * page.  The revmap must have been previously extended to cover that page.
     459              :  * The returned buffer is also recorded in the revmap struct; finishing that
     460              :  * releases the buffer, therefore the caller needn't do it explicitly.
     461              :  */
     462              : static Buffer
     463         3171 : revmap_get_buffer(BrinRevmap *revmap, BlockNumber heapBlk)
     464              : {
     465              :     BlockNumber mapBlk;
     466              : 
     467              :     /* Translate the heap block number to physical index location. */
     468         3171 :     mapBlk = revmap_get_blkno(revmap, heapBlk);
     469              : 
     470         3171 :     if (mapBlk == InvalidBlockNumber)
     471            0 :         elog(ERROR, "revmap does not cover heap block %u", heapBlk);
     472              : 
     473              :     /* Ensure the buffer we got is in the expected range */
     474              :     Assert(mapBlk != BRIN_METAPAGE_BLKNO &&
     475              :            mapBlk <= revmap->rm_lastRevmapPage);
     476              : 
     477              :     /*
     478              :      * Obtain the buffer from which we need to read.  If we already have the
     479              :      * correct buffer in our access struct, use that; otherwise, release that,
     480              :      * (if valid) and read the one we need.
     481              :      */
     482         6124 :     if (revmap->rm_currBuf == InvalidBuffer ||
     483         2953 :         mapBlk != BufferGetBlockNumber(revmap->rm_currBuf))
     484              :     {
     485          219 :         if (revmap->rm_currBuf != InvalidBuffer)
     486            1 :             ReleaseBuffer(revmap->rm_currBuf);
     487              : 
     488          219 :         revmap->rm_currBuf = ReadBuffer(revmap->rm_irel, mapBlk);
     489              :     }
     490              : 
     491         3171 :     return revmap->rm_currBuf;
     492              : }
     493              : 
     494              : /*
     495              :  * Given a heap block number, find the corresponding physical revmap block
     496              :  * number and return it. If the revmap page hasn't been allocated yet, extend
     497              :  * the revmap until it is.
     498              :  */
     499              : static BlockNumber
     500        16557 : revmap_extend_and_get_blkno(BrinRevmap *revmap, BlockNumber heapBlk)
     501              : {
     502              :     BlockNumber targetblk;
     503              : 
     504              :     /* obtain revmap block number, skip 1 for metapage block */
     505        16557 :     targetblk = HEAPBLK_TO_REVMAP_BLK(revmap->rm_pagesPerRange, heapBlk) + 1;
     506              : 
     507              :     /* Extend the revmap, if necessary */
     508        16928 :     while (targetblk > revmap->rm_lastRevmapPage)
     509              :     {
     510          371 :         CHECK_FOR_INTERRUPTS();
     511          371 :         revmap_physical_extend(revmap);
     512              :     }
     513              : 
     514        16557 :     return targetblk;
     515              : }
     516              : 
     517              : /*
     518              :  * Try to extend the revmap by one page.  This might not happen for a number of
     519              :  * reasons; caller is expected to retry until the expected outcome is obtained.
     520              :  */
     521              : static void
     522          371 : revmap_physical_extend(BrinRevmap *revmap)
     523              : {
     524              :     Buffer      buf;
     525              :     Page        page;
     526              :     Page        metapage;
     527              :     BrinMetaPageData *metadata;
     528              :     BlockNumber mapBlk;
     529              :     BlockNumber nblocks;
     530          371 :     Relation    irel = revmap->rm_irel;
     531              : 
     532              :     /*
     533              :      * Lock the metapage. This locks out concurrent extensions of the revmap,
     534              :      * but note that we still need to grab the relation extension lock because
     535              :      * another backend can extend the index with regular BRIN pages.
     536              :      */
     537          371 :     LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_EXCLUSIVE);
     538          371 :     metapage = BufferGetPage(revmap->rm_metaBuf);
     539          371 :     metadata = (BrinMetaPageData *) PageGetContents(metapage);
     540              : 
     541              :     /*
     542              :      * Check that our cached lastRevmapPage value was up-to-date; if it
     543              :      * wasn't, update the cached copy and have caller start over.
     544              :      */
     545          371 :     if (metadata->lastRevmapPage != revmap->rm_lastRevmapPage)
     546              :     {
     547          185 :         revmap->rm_lastRevmapPage = metadata->lastRevmapPage;
     548          185 :         LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_UNLOCK);
     549          185 :         return;
     550              :     }
     551          186 :     mapBlk = metadata->lastRevmapPage + 1;
     552              : 
     553          186 :     nblocks = RelationGetNumberOfBlocks(irel);
     554          186 :     if (mapBlk < nblocks)
     555              :     {
     556            2 :         buf = ReadBuffer(irel, mapBlk);
     557            2 :         LockBuffer(buf, BUFFER_LOCK_EXCLUSIVE);
     558            2 :         page = BufferGetPage(buf);
     559              :     }
     560              :     else
     561              :     {
     562          184 :         buf = ExtendBufferedRel(BMR_REL(irel), MAIN_FORKNUM, NULL,
     563              :                                 EB_LOCK_FIRST);
     564          184 :         if (BufferGetBlockNumber(buf) != mapBlk)
     565              :         {
     566              :             /*
     567              :              * Very rare corner case: somebody extended the relation
     568              :              * concurrently after we read its length.  If this happens, give
     569              :              * up and have caller start over.  We will have to evacuate that
     570              :              * page from under whoever is using it.
     571              :              */
     572            0 :             LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_UNLOCK);
     573            0 :             UnlockReleaseBuffer(buf);
     574            0 :             return;
     575              :         }
     576          184 :         page = BufferGetPage(buf);
     577              :     }
     578              : 
     579              :     /* Check that it's a regular block (or an empty page) */
     580          186 :     if (!PageIsNew(page) && !BRIN_IS_REGULAR_PAGE(page))
     581            0 :         ereport(ERROR,
     582              :                 (errcode(ERRCODE_INDEX_CORRUPTED),
     583              :                  errmsg("unexpected page type 0x%04X in BRIN index \"%s\" block %u",
     584              :                         BrinPageType(page),
     585              :                         RelationGetRelationName(irel),
     586              :                         BufferGetBlockNumber(buf))));
     587              : 
     588              :     /* If the page is in use, evacuate it and restart */
     589          186 :     if (brin_start_evacuating_page(irel, buf))
     590              :     {
     591            1 :         LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_UNLOCK);
     592            1 :         brin_evacuate_page(irel, revmap->rm_pagesPerRange, revmap, buf);
     593              : 
     594              :         /* have caller start over */
     595            1 :         return;
     596              :     }
     597              : 
     598              :     /*
     599              :      * Ok, we have now locked the metapage and the target block. Re-initialize
     600              :      * the target block as a revmap page, and update the metapage.
     601              :      */
     602          185 :     START_CRIT_SECTION();
     603              : 
     604              :     /* the rm_tids array is initialized to all invalid by PageInit */
     605          185 :     brin_page_init(page, BRIN_PAGETYPE_REVMAP);
     606          185 :     MarkBufferDirty(buf);
     607              : 
     608          185 :     metadata->lastRevmapPage = mapBlk;
     609              : 
     610              :     /*
     611              :      * Set pd_lower just past the end of the metadata.  This is essential,
     612              :      * because without doing so, metadata will be lost if xlog.c compresses
     613              :      * the page.  (We must do this here because pre-v11 versions of PG did not
     614              :      * set the metapage's pd_lower correctly, so a pg_upgraded index might
     615              :      * contain the wrong value.)
     616              :      */
     617          185 :     ((PageHeader) metapage)->pd_lower =
     618          185 :         ((char *) metadata + sizeof(BrinMetaPageData)) - (char *) metapage;
     619              : 
     620          185 :     MarkBufferDirty(revmap->rm_metaBuf);
     621              : 
     622          185 :     if (RelationNeedsWAL(revmap->rm_irel))
     623              :     {
     624              :         xl_brin_revmap_extend xlrec;
     625              :         XLogRecPtr  recptr;
     626              : 
     627          128 :         xlrec.targetBlk = mapBlk;
     628              : 
     629          128 :         XLogBeginInsert();
     630          128 :         XLogRegisterData(&xlrec, SizeOfBrinRevmapExtend);
     631          128 :         XLogRegisterBuffer(0, revmap->rm_metaBuf, REGBUF_STANDARD);
     632              : 
     633          128 :         XLogRegisterBuffer(1, buf, REGBUF_WILL_INIT);
     634              : 
     635          128 :         recptr = XLogInsert(RM_BRIN_ID, XLOG_BRIN_REVMAP_EXTEND);
     636          128 :         PageSetLSN(metapage, recptr);
     637          128 :         PageSetLSN(page, recptr);
     638              :     }
     639              : 
     640          185 :     END_CRIT_SECTION();
     641              : 
     642          185 :     LockBuffer(revmap->rm_metaBuf, BUFFER_LOCK_UNLOCK);
     643              : 
     644          185 :     UnlockReleaseBuffer(buf);
     645              : }
        

Generated by: LCOV version 2.0-1