LCOV - code coverage report
Current view: top level - contrib/bloom - blinsert.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 93.2 % 117 109
Test Date: 2026-07-21 22:15:42 Functions: 100.0 % 7 7
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 55.3 % 38 21

             Branch data     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                 :          95 : 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                 :          36 : flushCachedPage(Relation index, BloomBuildState *buildstate)
      49                 :             : {
      50                 :             :     Page        page;
      51                 :          36 :     Buffer      buffer = BloomNewBuffer(index);
      52                 :             :     GenericXLogState *state;
      53                 :             : 
      54                 :          36 :     state = GenericXLogStart(index);
      55                 :          36 :     page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
      56                 :          36 :     memcpy(page, buildstate->data.data, BLCKSZ);
      57                 :          36 :     GenericXLogFinish(state);
      58                 :          36 :     UnlockReleaseBuffer(buffer);
      59                 :          36 : }
      60                 :             : 
      61                 :             : /*
      62                 :             :  * (Re)initialize cached page in BloomBuildState.
      63                 :             :  */
      64                 :             : static void
      65                 :          36 : initCachedPage(BloomBuildState *buildstate)
      66                 :             : {
      67                 :          36 :     BloomInitPage(buildstate->data.data, 0);
      68                 :          36 :     buildstate->count = 0;
      69                 :          36 : }
      70                 :             : 
      71                 :             : /*
      72                 :             :  * Per-tuple callback for table_index_build_scan.
      73                 :             :  */
      74                 :             : static void
      75                 :       18750 : bloomBuildCallback(Relation index, ItemPointer tid, Datum *values,
      76                 :             :                    bool *isnull, bool tupleIsAlive, void *state)
      77                 :             : {
      78                 :       18750 :     BloomBuildState *buildstate = (BloomBuildState *) state;
      79                 :             :     MemoryContext oldCtx;
      80                 :             :     BloomTuple *itup;
      81                 :             : 
      82                 :       18750 :     oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
      83                 :             : 
      84                 :       18750 :     itup = BloomFormTuple(&buildstate->blstate, tid, values, isnull);
      85                 :             : 
      86                 :             :     /* Try to add next item to cached page */
      87         [ +  + ]:       18750 :     if (BloomPageAddItem(&buildstate->blstate, buildstate->data.data, itup))
      88                 :             :     {
      89                 :             :         /* Next item was added successfully */
      90                 :       18719 :         buildstate->count++;
      91                 :             :     }
      92                 :             :     else
      93                 :             :     {
      94                 :             :         /* Cached page is full, flush it out and make a new one */
      95                 :          31 :         flushCachedPage(index, buildstate);
      96                 :             : 
      97         [ -  + ]:          31 :         CHECK_FOR_INTERRUPTS();
      98                 :             : 
      99                 :          31 :         initCachedPage(buildstate);
     100                 :             : 
     101         [ -  + ]:          31 :         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                 :             :         }
     106                 :             : 
     107                 :             :         /* Next item was added successfully */
     108                 :          31 :         buildstate->count++;
     109                 :             :     }
     110                 :             : 
     111                 :             :     /* Update total tuple count */
     112                 :       18750 :     buildstate->indtuples += 1;
     113                 :             : 
     114                 :       18750 :     MemoryContextSwitchTo(oldCtx);
     115                 :       18750 :     MemoryContextReset(buildstate->tmpCtx);
     116                 :       18750 : }
     117                 :             : 
     118                 :             : /*
     119                 :             :  * Build a new bloom index.
     120                 :             :  */
     121                 :             : IndexBuildResult *
     122                 :           5 : blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
     123                 :             : {
     124                 :             :     IndexBuildResult *result;
     125                 :             :     double      reltuples;
     126                 :             :     BloomBuildState buildstate;
     127                 :             : 
     128         [ -  + ]:           5 :     if (RelationGetNumberOfBlocks(index) != 0)
     129         [ #  # ]:           0 :         elog(ERROR, "index \"%s\" already contains data",
     130                 :             :              RelationGetRelationName(index));
     131                 :             : 
     132                 :             :     /* Initialize the meta page */
     133                 :           5 :     BloomInitMetapage(index, MAIN_FORKNUM);
     134                 :             : 
     135                 :             :     /* Initialize the bloom build state */
     136                 :           5 :     memset(&buildstate, 0, sizeof(buildstate));
     137                 :           5 :     initBloomState(&buildstate.blstate, index);
     138                 :           5 :     buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
     139                 :             :                                               "Bloom build temporary context",
     140                 :             :                                               ALLOCSET_DEFAULT_SIZES);
     141                 :           5 :     initCachedPage(&buildstate);
     142                 :             : 
     143                 :             :     /* Do the heap scan */
     144                 :           5 :     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         [ +  - ]:           5 :     if (buildstate.count > 0)
     150                 :           5 :         flushCachedPage(index, &buildstate);
     151                 :             : 
     152                 :           5 :     MemoryContextDelete(buildstate.tmpCtx);
     153                 :             : 
     154                 :           5 :     result = palloc_object(IndexBuildResult);
     155                 :           5 :     result->heap_tuples = reltuples;
     156                 :           5 :     result->index_tuples = buildstate.indtuples;
     157                 :             : 
     158                 :           5 :     return result;
     159                 :             : }
     160                 :             : 
     161                 :             : /*
     162                 :             :  * Build an empty bloom index in the initialization fork.
     163                 :             :  */
     164                 :             : void
     165                 :           1 : blbuildempty(Relation index)
     166                 :             : {
     167                 :             :     /* Initialize the meta page */
     168                 :           1 :     BloomInitMetapage(index, INIT_FORKNUM);
     169                 :           1 : }
     170                 :             : 
     171                 :             : /*
     172                 :             :  * Insert new tuple to the bloom index.
     173                 :             :  */
     174                 :             : bool
     175                 :      104000 : blinsert(Relation index, Datum *values, bool *isnull,
     176                 :             :          ItemPointer ht_ctid, Relation heapRel,
     177                 :             :          IndexUniqueCheck checkUnique,
     178                 :             :          bool indexUnchanged,
     179                 :             :          IndexInfo *indexInfo)
     180                 :             : {
     181                 :             :     BloomState  blstate;
     182                 :             :     BloomTuple *itup;
     183                 :             :     MemoryContext oldCtx;
     184                 :             :     MemoryContext insertCtx;
     185                 :             :     BloomMetaPageData *metaData;
     186                 :             :     Buffer      buffer,
     187                 :             :                 metaBuffer;
     188                 :             :     Page        page,
     189                 :             :                 metaPage;
     190                 :      104000 :     BlockNumber blkno = InvalidBlockNumber;
     191                 :             :     OffsetNumber nStart;
     192                 :             :     GenericXLogState *state;
     193                 :             : 
     194                 :      104000 :     insertCtx = AllocSetContextCreate(CurrentMemoryContext,
     195                 :             :                                       "Bloom insert temporary context",
     196                 :             :                                       ALLOCSET_DEFAULT_SIZES);
     197                 :             : 
     198                 :      104000 :     oldCtx = MemoryContextSwitchTo(insertCtx);
     199                 :             : 
     200                 :      104000 :     initBloomState(&blstate, index);
     201                 :      104000 :     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                 :      104000 :     metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
     208                 :      104000 :     LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
     209                 :      104000 :     metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
     210                 :             : 
     211         [ +  + ]:      104000 :     if (metaData->nEnd > metaData->nStart)
     212                 :             :     {
     213                 :      103999 :         blkno = metaData->notFullPage[metaData->nStart];
     214                 :             :         Assert(blkno != InvalidBlockNumber);
     215                 :             : 
     216                 :             :         /* Don't hold metabuffer lock while doing insert */
     217                 :      103999 :         LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
     218                 :             : 
     219                 :      103999 :         buffer = ReadBuffer(index, blkno);
     220                 :      103999 :         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
     221                 :             : 
     222                 :      103999 :         state = GenericXLogStart(index);
     223                 :      103999 :         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   [ +  -  -  + ]:      103999 :         if (PageIsNew(page) || BloomPageIsDeleted(page))
     230                 :           0 :             BloomInitPage(page, 0);
     231                 :             : 
     232         [ +  + ]:      103999 :         if (BloomPageAddItem(&blstate, page, itup))
     233                 :             :         {
     234                 :             :             /* Success!  Apply the change, clean up, and exit */
     235                 :      103242 :             GenericXLogFinish(state);
     236                 :      103242 :             UnlockReleaseBuffer(buffer);
     237                 :      103242 :             ReleaseBuffer(metaBuffer);
     238                 :      103242 :             MemoryContextSwitchTo(oldCtx);
     239                 :      103242 :             MemoryContextDelete(insertCtx);
     240                 :      103242 :             return false;
     241                 :             :         }
     242                 :             : 
     243                 :             :         /* Didn't fit, must try other pages */
     244                 :         757 :         GenericXLogAbort(state);
     245                 :         757 :         UnlockReleaseBuffer(buffer);
     246                 :             :     }
     247                 :             :     else
     248                 :             :     {
     249                 :             :         /* No entries in notFullPage */
     250                 :           1 :         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                 :         758 :     LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
     258                 :             : 
     259                 :             :     /* nStart might have changed while we didn't have lock */
     260                 :         758 :     nStart = metaData->nStart;
     261                 :             : 
     262                 :             :     /* Skip first page if we already tried it above */
     263         [ +  + ]:         758 :     if (nStart < metaData->nEnd &&
     264         [ +  - ]:         757 :         blkno == metaData->notFullPage[nStart])
     265                 :         757 :         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                 :             :     for (;;)
     273                 :             :     {
     274                 :         758 :         state = GenericXLogStart(index);
     275                 :             : 
     276                 :             :         /* get modifiable copy of metapage */
     277                 :         758 :         metaPage = GenericXLogRegisterBuffer(state, metaBuffer, 0);
     278                 :         758 :         metaData = BloomPageGetMeta(metaPage);
     279                 :             : 
     280         [ +  + ]:         758 :         if (nStart >= metaData->nEnd)
     281                 :         113 :             break;              /* no more entries in notFullPage array */
     282                 :             : 
     283                 :         645 :         blkno = metaData->notFullPage[nStart];
     284                 :             :         Assert(blkno != InvalidBlockNumber);
     285                 :             : 
     286                 :         645 :         buffer = ReadBuffer(index, blkno);
     287                 :         645 :         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
     288                 :         645 :         page = GenericXLogRegisterBuffer(state, buffer, 0);
     289                 :             : 
     290                 :             :         /* Basically same logic as above */
     291   [ +  -  -  + ]:         645 :         if (PageIsNew(page) || BloomPageIsDeleted(page))
     292                 :           0 :             BloomInitPage(page, 0);
     293                 :             : 
     294         [ +  - ]:         645 :         if (BloomPageAddItem(&blstate, page, itup))
     295                 :             :         {
     296                 :             :             /* Success!  Apply the changes, clean up, and exit */
     297                 :         645 :             metaData->nStart = nStart;
     298                 :         645 :             GenericXLogFinish(state);
     299                 :         645 :             UnlockReleaseBuffer(buffer);
     300                 :         645 :             UnlockReleaseBuffer(metaBuffer);
     301                 :         645 :             MemoryContextSwitchTo(oldCtx);
     302                 :         645 :             MemoryContextDelete(insertCtx);
     303                 :         645 :             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                 :         113 :     buffer = BloomNewBuffer(index);
     317                 :             : 
     318                 :         113 :     page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
     319                 :         113 :     BloomInitPage(page, 0);
     320                 :             : 
     321         [ -  + ]:         113 :     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                 :             :     }
     326                 :             : 
     327                 :             :     /* Reset notFullPage array to contain just this new page */
     328                 :         113 :     metaData->nStart = 0;
     329                 :         113 :     metaData->nEnd = 1;
     330                 :         113 :     metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
     331                 :             : 
     332                 :             :     /* Apply the changes, clean up, and exit */
     333                 :         113 :     GenericXLogFinish(state);
     334                 :             : 
     335                 :         113 :     UnlockReleaseBuffer(buffer);
     336                 :         113 :     UnlockReleaseBuffer(metaBuffer);
     337                 :             : 
     338                 :         113 :     MemoryContextSwitchTo(oldCtx);
     339                 :         113 :     MemoryContextDelete(insertCtx);
     340                 :             : 
     341                 :         113 :     return false;
     342                 :             : }
        

Generated by: LCOV version 2.0-1