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

Generated by: LCOV version 1.14