LCOV - code coverage report
Current view: top level - contrib/bloom - blinsert.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 142 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 7 0
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * blinsert.c
       4              :  *              Bloom index build and insert functions.
       5              :  *
       6              :  * Copyright (c) 2016-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  * IDENTIFICATION
       9              :  *        contrib/bloom/blinsert.c
      10              :  *
      11              :  *-------------------------------------------------------------------------
      12              :  */
      13              : #include "postgres.h"
      14              : 
      15              : #include "access/genam.h"
      16              : #include "access/generic_xlog.h"
      17              : #include "access/tableam.h"
      18              : #include "bloom.h"
      19              : #include "miscadmin.h"
      20              : #include "nodes/execnodes.h"
      21              : #include "storage/bufmgr.h"
      22              : #include "utils/memutils.h"
      23              : #include "utils/rel.h"
      24              : 
      25            0 : PG_MODULE_MAGIC_EXT(
      26              :                                         .name = "bloom",
      27              :                                         .version = PG_VERSION
      28              : );
      29              : 
      30              : /*
      31              :  * State of bloom index build.  We accumulate one page data here before
      32              :  * flushing it to buffer manager.
      33              :  */
      34              : typedef struct
      35              : {
      36              :         BloomState      blstate;                /* bloom index state */
      37              :         int64           indtuples;              /* total number of tuples indexed */
      38              :         MemoryContext tmpCtx;           /* temporary memory context reset after each
      39              :                                                                  * tuple */
      40              :         PGAlignedBlock data;            /* cached page */
      41              :         int                     count;                  /* number of tuples in cached page */
      42              : } BloomBuildState;
      43              : 
      44              : /*
      45              :  * Flush page cached in BloomBuildState.
      46              :  */
      47              : static void
      48            0 : flushCachedPage(Relation index, BloomBuildState *buildstate)
      49              : {
      50            0 :         Page            page;
      51            0 :         Buffer          buffer = BloomNewBuffer(index);
      52            0 :         GenericXLogState *state;
      53              : 
      54            0 :         state = GenericXLogStart(index);
      55            0 :         page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
      56            0 :         memcpy(page, buildstate->data.data, BLCKSZ);
      57            0 :         GenericXLogFinish(state);
      58            0 :         UnlockReleaseBuffer(buffer);
      59            0 : }
      60              : 
      61              : /*
      62              :  * (Re)initialize cached page in BloomBuildState.
      63              :  */
      64              : static void
      65            0 : initCachedPage(BloomBuildState *buildstate)
      66              : {
      67            0 :         BloomInitPage(buildstate->data.data, 0);
      68            0 :         buildstate->count = 0;
      69            0 : }
      70              : 
      71              : /*
      72              :  * Per-tuple callback for table_index_build_scan.
      73              :  */
      74              : static void
      75            0 : bloomBuildCallback(Relation index, ItemPointer tid, Datum *values,
      76              :                                    bool *isnull, bool tupleIsAlive, void *state)
      77              : {
      78            0 :         BloomBuildState *buildstate = (BloomBuildState *) state;
      79            0 :         MemoryContext oldCtx;
      80            0 :         BloomTuple *itup;
      81              : 
      82            0 :         oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
      83              : 
      84            0 :         itup = BloomFormTuple(&buildstate->blstate, tid, values, isnull);
      85              : 
      86              :         /* Try to add next item to cached page */
      87            0 :         if (BloomPageAddItem(&buildstate->blstate, buildstate->data.data, itup))
      88              :         {
      89              :                 /* Next item was added successfully */
      90            0 :                 buildstate->count++;
      91            0 :         }
      92              :         else
      93              :         {
      94              :                 /* Cached page is full, flush it out and make a new one */
      95            0 :                 flushCachedPage(index, buildstate);
      96              : 
      97            0 :                 CHECK_FOR_INTERRUPTS();
      98              : 
      99            0 :                 initCachedPage(buildstate);
     100              : 
     101            0 :                 if (!BloomPageAddItem(&buildstate->blstate, buildstate->data.data, itup))
     102              :                 {
     103              :                         /* We shouldn't be here since we're inserting to the empty page */
     104            0 :                         elog(ERROR, "could not add new bloom tuple to empty page");
     105            0 :                 }
     106              : 
     107              :                 /* Next item was added successfully */
     108            0 :                 buildstate->count++;
     109              :         }
     110              : 
     111              :         /* Update total tuple count */
     112            0 :         buildstate->indtuples += 1;
     113              : 
     114            0 :         MemoryContextSwitchTo(oldCtx);
     115            0 :         MemoryContextReset(buildstate->tmpCtx);
     116            0 : }
     117              : 
     118              : /*
     119              :  * Build a new bloom index.
     120              :  */
     121              : IndexBuildResult *
     122            0 : blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
     123              : {
     124            0 :         IndexBuildResult *result;
     125            0 :         double          reltuples;
     126            0 :         BloomBuildState buildstate;
     127              : 
     128            0 :         if (RelationGetNumberOfBlocks(index) != 0)
     129            0 :                 elog(ERROR, "index \"%s\" already contains data",
     130              :                          RelationGetRelationName(index));
     131              : 
     132              :         /* Initialize the meta page */
     133            0 :         BloomInitMetapage(index, MAIN_FORKNUM);
     134              : 
     135              :         /* Initialize the bloom build state */
     136            0 :         memset(&buildstate, 0, sizeof(buildstate));
     137            0 :         initBloomState(&buildstate.blstate, index);
     138            0 :         buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
     139              :                                                                                           "Bloom build temporary context",
     140              :                                                                                           ALLOCSET_DEFAULT_SIZES);
     141            0 :         initCachedPage(&buildstate);
     142              : 
     143              :         /* Do the heap scan */
     144            0 :         reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
     145              :                                                                            bloomBuildCallback, &buildstate,
     146              :                                                                            NULL);
     147              : 
     148              :         /* Flush last page if needed (it will be, unless heap was empty) */
     149            0 :         if (buildstate.count > 0)
     150            0 :                 flushCachedPage(index, &buildstate);
     151              : 
     152            0 :         MemoryContextDelete(buildstate.tmpCtx);
     153              : 
     154            0 :         result = palloc_object(IndexBuildResult);
     155            0 :         result->heap_tuples = reltuples;
     156            0 :         result->index_tuples = buildstate.indtuples;
     157              : 
     158            0 :         return result;
     159            0 : }
     160              : 
     161              : /*
     162              :  * Build an empty bloom index in the initialization fork.
     163              :  */
     164              : void
     165            0 : blbuildempty(Relation index)
     166              : {
     167              :         /* Initialize the meta page */
     168            0 :         BloomInitMetapage(index, INIT_FORKNUM);
     169            0 : }
     170              : 
     171              : /*
     172              :  * Insert new tuple to the bloom index.
     173              :  */
     174              : bool
     175            0 : blinsert(Relation index, Datum *values, bool *isnull,
     176              :                  ItemPointer ht_ctid, Relation heapRel,
     177              :                  IndexUniqueCheck checkUnique,
     178              :                  bool indexUnchanged,
     179              :                  IndexInfo *indexInfo)
     180              : {
     181            0 :         BloomState      blstate;
     182            0 :         BloomTuple *itup;
     183            0 :         MemoryContext oldCtx;
     184            0 :         MemoryContext insertCtx;
     185            0 :         BloomMetaPageData *metaData;
     186            0 :         Buffer          buffer,
     187              :                                 metaBuffer;
     188            0 :         Page            page,
     189              :                                 metaPage;
     190            0 :         BlockNumber blkno = InvalidBlockNumber;
     191            0 :         OffsetNumber nStart;
     192            0 :         GenericXLogState *state;
     193              : 
     194            0 :         insertCtx = AllocSetContextCreate(CurrentMemoryContext,
     195              :                                                                           "Bloom insert temporary context",
     196              :                                                                           ALLOCSET_DEFAULT_SIZES);
     197              : 
     198            0 :         oldCtx = MemoryContextSwitchTo(insertCtx);
     199              : 
     200            0 :         initBloomState(&blstate, index);
     201            0 :         itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
     202              : 
     203              :         /*
     204              :          * At first, try to insert new tuple to the first page in notFullPage
     205              :          * array.  If successful, we don't need to modify the meta page.
     206              :          */
     207            0 :         metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
     208            0 :         LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
     209            0 :         metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
     210              : 
     211            0 :         if (metaData->nEnd > metaData->nStart)
     212              :         {
     213            0 :                 blkno = metaData->notFullPage[metaData->nStart];
     214            0 :                 Assert(blkno != InvalidBlockNumber);
     215              : 
     216              :                 /* Don't hold metabuffer lock while doing insert */
     217            0 :                 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
     218              : 
     219            0 :                 buffer = ReadBuffer(index, blkno);
     220            0 :                 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
     221              : 
     222            0 :                 state = GenericXLogStart(index);
     223            0 :                 page = GenericXLogRegisterBuffer(state, buffer, 0);
     224              : 
     225              :                 /*
     226              :                  * We might have found a page that was recently deleted by VACUUM.  If
     227              :                  * so, we can reuse it, but we must reinitialize it.
     228              :                  */
     229            0 :                 if (PageIsNew(page) || BloomPageIsDeleted(page))
     230            0 :                         BloomInitPage(page, 0);
     231              : 
     232            0 :                 if (BloomPageAddItem(&blstate, page, itup))
     233              :                 {
     234              :                         /* Success!  Apply the change, clean up, and exit */
     235            0 :                         GenericXLogFinish(state);
     236            0 :                         UnlockReleaseBuffer(buffer);
     237            0 :                         ReleaseBuffer(metaBuffer);
     238            0 :                         MemoryContextSwitchTo(oldCtx);
     239            0 :                         MemoryContextDelete(insertCtx);
     240            0 :                         return false;
     241              :                 }
     242              : 
     243              :                 /* Didn't fit, must try other pages */
     244            0 :                 GenericXLogAbort(state);
     245            0 :                 UnlockReleaseBuffer(buffer);
     246            0 :         }
     247              :         else
     248              :         {
     249              :                 /* No entries in notFullPage */
     250            0 :                 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
     251              :         }
     252              : 
     253              :         /*
     254              :          * Try other pages in notFullPage array.  We will have to change nStart in
     255              :          * metapage.  Thus, grab exclusive lock on metapage.
     256              :          */
     257            0 :         LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
     258              : 
     259              :         /* nStart might have changed while we didn't have lock */
     260            0 :         nStart = metaData->nStart;
     261              : 
     262              :         /* Skip first page if we already tried it above */
     263            0 :         if (nStart < metaData->nEnd &&
     264            0 :                 blkno == metaData->notFullPage[nStart])
     265            0 :                 nStart++;
     266              : 
     267              :         /*
     268              :          * This loop iterates for each page we try from the notFullPage array, and
     269              :          * will also initialize a GenericXLogState for the fallback case of having
     270              :          * to allocate a new page.
     271              :          */
     272            0 :         for (;;)
     273              :         {
     274            0 :                 state = GenericXLogStart(index);
     275              : 
     276              :                 /* get modifiable copy of metapage */
     277            0 :                 metaPage = GenericXLogRegisterBuffer(state, metaBuffer, 0);
     278            0 :                 metaData = BloomPageGetMeta(metaPage);
     279              : 
     280            0 :                 if (nStart >= metaData->nEnd)
     281            0 :                         break;                          /* no more entries in notFullPage array */
     282              : 
     283            0 :                 blkno = metaData->notFullPage[nStart];
     284            0 :                 Assert(blkno != InvalidBlockNumber);
     285              : 
     286            0 :                 buffer = ReadBuffer(index, blkno);
     287            0 :                 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
     288            0 :                 page = GenericXLogRegisterBuffer(state, buffer, 0);
     289              : 
     290              :                 /* Basically same logic as above */
     291            0 :                 if (PageIsNew(page) || BloomPageIsDeleted(page))
     292            0 :                         BloomInitPage(page, 0);
     293              : 
     294            0 :                 if (BloomPageAddItem(&blstate, page, itup))
     295              :                 {
     296              :                         /* Success!  Apply the changes, clean up, and exit */
     297            0 :                         metaData->nStart = nStart;
     298            0 :                         GenericXLogFinish(state);
     299            0 :                         UnlockReleaseBuffer(buffer);
     300            0 :                         UnlockReleaseBuffer(metaBuffer);
     301            0 :                         MemoryContextSwitchTo(oldCtx);
     302            0 :                         MemoryContextDelete(insertCtx);
     303            0 :                         return false;
     304              :                 }
     305              : 
     306              :                 /* Didn't fit, must try other pages */
     307            0 :                 GenericXLogAbort(state);
     308            0 :                 UnlockReleaseBuffer(buffer);
     309            0 :                 nStart++;
     310              :         }
     311              : 
     312              :         /*
     313              :          * Didn't find place to insert in notFullPage array.  Allocate new page.
     314              :          * (XXX is it good to do this while holding ex-lock on the metapage??)
     315              :          */
     316            0 :         buffer = BloomNewBuffer(index);
     317              : 
     318            0 :         page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
     319            0 :         BloomInitPage(page, 0);
     320              : 
     321            0 :         if (!BloomPageAddItem(&blstate, page, itup))
     322              :         {
     323              :                 /* We shouldn't be here since we're inserting to an empty page */
     324            0 :                 elog(ERROR, "could not add new bloom tuple to empty page");
     325            0 :         }
     326              : 
     327              :         /* Reset notFullPage array to contain just this new page */
     328            0 :         metaData->nStart = 0;
     329            0 :         metaData->nEnd = 1;
     330            0 :         metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
     331              : 
     332              :         /* Apply the changes, clean up, and exit */
     333            0 :         GenericXLogFinish(state);
     334              : 
     335            0 :         UnlockReleaseBuffer(buffer);
     336            0 :         UnlockReleaseBuffer(metaBuffer);
     337              : 
     338            0 :         MemoryContextSwitchTo(oldCtx);
     339            0 :         MemoryContextDelete(insertCtx);
     340              : 
     341            0 :         return false;
     342            0 : }
        

Generated by: LCOV version 2.3.2-1