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

Generated by: LCOV version 1.14