LCOV - differential code coverage report
Current view: top level - contrib/bloom - blinsert.c (source / functions) Coverage Total Hit LBC UIC UBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 93.5 % 124 116 2 3 3 33 2 81 5 29 4
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 7 7 2 5
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * blinsert.c
       4                 :  *      Bloom index build and insert functions.
       5                 :  *
       6                 :  * Copyright (c) 2016-2023, 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 CBC          95 : 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             213 : flushCachedPage(Relation index, BloomBuildState *buildstate)
      48                 : {
      49                 :     Page        page;
      50             213 :     Buffer      buffer = BloomNewBuffer(index);
      51                 :     GenericXLogState *state;
      52                 : 
      53             213 :     state = GenericXLogStart(index);
      54             213 :     page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
      55             213 :     memcpy(page, buildstate->data.data, BLCKSZ);
      56             213 :     GenericXLogFinish(state);
      57             213 :     UnlockReleaseBuffer(buffer);
      58             213 : }
      59                 : 
      60                 : /*
      61                 :  * (Re)initialize cached page in BloomBuildState.
      62                 :  */
      63                 : static void
      64             213 : initCachedPage(BloomBuildState *buildstate)
      65                 : {
      66             213 :     BloomInitPage(buildstate->data.data, 0);
      67             213 :     buildstate->count = 0;
      68             213 : }
      69                 : 
      70                 : /*
      71                 :  * Per-tuple callback for table_index_build_scan.
      72                 :  */
      73                 : static void
      74          108758 : bloomBuildCallback(Relation index, ItemPointer tid, Datum *values,
      75                 :                    bool *isnull, bool tupleIsAlive, void *state)
      76                 : {
      77          108758 :     BloomBuildState *buildstate = (BloomBuildState *) state;
      78                 :     MemoryContext oldCtx;
      79                 :     BloomTuple *itup;
      80                 : 
      81          108758 :     oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
      82                 : 
      83          108758 :     itup = BloomFormTuple(&buildstate->blstate, tid, values, isnull);
      84                 : 
      85                 :     /* Try to add next item to cached page */
      86          108758 :     if (BloomPageAddItem(&buildstate->blstate, buildstate->data.data, itup))
      87                 :     {
      88                 :         /* Next item was added successfully */
      89          108550 :         buildstate->count++;
      90                 :     }
      91                 :     else
      92                 :     {
      93                 :         /* Cached page is full, flush it out and make a new one */
      94             208 :         flushCachedPage(index, buildstate);
      95                 : 
      96             208 :         CHECK_FOR_INTERRUPTS();
      97                 : 
      98             208 :         initCachedPage(buildstate);
      99                 : 
     100             208 :         if (!BloomPageAddItem(&buildstate->blstate, buildstate->data.data, itup))
     101                 :         {
     102                 :             /* We shouldn't be here since we're inserting to the empty page */
     103 UBC           0 :             elog(ERROR, "could not add new bloom tuple to empty page");
     104                 :         }
     105                 : 
     106                 :         /* Next item was added successfully */
     107 CBC         208 :         buildstate->count++;
     108                 :     }
     109                 : 
     110                 :     /* Update total tuple count */
     111          108758 :     buildstate->indtuples += 1;
     112                 : 
     113          108758 :     MemoryContextSwitchTo(oldCtx);
     114          108758 :     MemoryContextReset(buildstate->tmpCtx);
     115          108758 : }
     116                 : 
     117                 : /*
     118                 :  * Build a new bloom index.
     119                 :  */
     120                 : IndexBuildResult *
     121               5 : blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
     122                 : {
     123                 :     IndexBuildResult *result;
     124                 :     double      reltuples;
     125                 :     BloomBuildState buildstate;
     126                 : 
     127               5 :     if (RelationGetNumberOfBlocks(index) != 0)
     128 UBC           0 :         elog(ERROR, "index \"%s\" already contains data",
     129                 :              RelationGetRelationName(index));
     130                 : 
     131                 :     /* Initialize the meta page */
     132 CBC           5 :     BloomInitMetapage(index);
     133                 : 
     134                 :     /* Initialize the bloom build state */
     135               5 :     memset(&buildstate, 0, sizeof(buildstate));
     136               5 :     initBloomState(&buildstate.blstate, index);
     137               5 :     buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
     138                 :                                               "Bloom build temporary context",
     139                 :                                               ALLOCSET_DEFAULT_SIZES);
     140               5 :     initCachedPage(&buildstate);
     141                 : 
     142                 :     /* Do the heap scan */
     143               5 :     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               5 :     if (buildstate.count > 0)
     149               5 :         flushCachedPage(index, &buildstate);
     150                 : 
     151               5 :     MemoryContextDelete(buildstate.tmpCtx);
     152                 : 
     153               5 :     result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
     154               5 :     result->heap_tuples = reltuples;
     155               5 :     result->index_tuples = buildstate.indtuples;
     156                 : 
     157               5 :     return result;
     158                 : }
     159                 : 
     160                 : /*
     161                 :  * Build an empty bloom index in the initialization fork.
     162                 :  */
     163                 : void
     164               1 : blbuildempty(Relation index)
     165                 : {
     166                 :     Page        metapage;
     167                 : 
     168                 :     /* Construct metapage. */
     169 GNC           1 :     metapage = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
     170 CBC           1 :     BloomFillMetapage(index, metapage);
     171                 : 
     172                 :     /*
     173                 :      * Write the page and log it.  It might seem that an immediate sync would
     174                 :      * be sufficient to guarantee that the file exists on disk, but recovery
     175                 :      * itself might remove it while replaying, for example, an
     176                 :      * XLOG_DBASE_CREATE* or XLOG_TBLSPC_CREATE record.  Therefore, we need
     177                 :      * this even when wal_level=minimal.
     178                 :      */
     179               1 :     PageSetChecksumInplace(metapage, BLOOM_METAPAGE_BLKNO);
     180               1 :     smgrwrite(RelationGetSmgr(index), INIT_FORKNUM, BLOOM_METAPAGE_BLKNO,
     181                 :               metapage, true);
     182 GNC           1 :     log_newpage(&(RelationGetSmgr(index))->smgr_rlocator.locator, INIT_FORKNUM,
     183                 :                 BLOOM_METAPAGE_BLKNO, metapage, true);
     184                 : 
     185                 :     /*
     186                 :      * An immediate sync is required even if we xlog'd the page, because the
     187                 :      * write did not go through shared_buffers and therefore a concurrent
     188                 :      * checkpoint may have moved the redo pointer past our xlog record.
     189                 :      */
     190 CBC           1 :     smgrimmedsync(RelationGetSmgr(index), INIT_FORKNUM);
     191               1 : }
     192                 : 
     193                 : /*
     194                 :  * Insert new tuple to the bloom index.
     195                 :  */
     196                 : bool
     197          104000 : blinsert(Relation index, Datum *values, bool *isnull,
     198                 :          ItemPointer ht_ctid, Relation heapRel,
     199                 :          IndexUniqueCheck checkUnique,
     200                 :          bool indexUnchanged,
     201                 :          IndexInfo *indexInfo)
     202                 : {
     203                 :     BloomState  blstate;
     204                 :     BloomTuple *itup;
     205                 :     MemoryContext oldCtx;
     206                 :     MemoryContext insertCtx;
     207                 :     BloomMetaPageData *metaData;
     208                 :     Buffer      buffer,
     209                 :                 metaBuffer;
     210                 :     Page        page,
     211                 :                 metaPage;
     212          104000 :     BlockNumber blkno = InvalidBlockNumber;
     213                 :     OffsetNumber nStart;
     214                 :     GenericXLogState *state;
     215                 : 
     216          104000 :     insertCtx = AllocSetContextCreate(CurrentMemoryContext,
     217                 :                                       "Bloom insert temporary context",
     218                 :                                       ALLOCSET_DEFAULT_SIZES);
     219                 : 
     220          104000 :     oldCtx = MemoryContextSwitchTo(insertCtx);
     221                 : 
     222          104000 :     initBloomState(&blstate, index);
     223          104000 :     itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
     224                 : 
     225                 :     /*
     226                 :      * At first, try to insert new tuple to the first page in notFullPage
     227                 :      * array.  If successful, we don't need to modify the meta page.
     228                 :      */
     229          104000 :     metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
     230          104000 :     LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
     231          104000 :     metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
     232                 : 
     233          104000 :     if (metaData->nEnd > metaData->nStart)
     234                 :     {
     235 GIC      103999 :         blkno = metaData->notFullPage[metaData->nStart];
     236          103999 :         Assert(blkno != InvalidBlockNumber);
     237 ECB             : 
     238                 :         /* Don't hold metabuffer lock while doing insert */
     239 CBC      103999 :         LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
     240 ECB             : 
     241 GIC      103999 :         buffer = ReadBuffer(index, blkno);
     242 CBC      103999 :         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
     243 ECB             : 
     244 GIC      103999 :         state = GenericXLogStart(index);
     245          103999 :         page = GenericXLogRegisterBuffer(state, buffer, 0);
     246                 : 
     247                 :         /*
     248                 :          * We might have found a page that was recently deleted by VACUUM.  If
     249 ECB             :          * so, we can reuse it, but we must reinitialize it.
     250 EUB             :          */
     251 GIC      103999 :         if (PageIsNew(page) || BloomPageIsDeleted(page))
     252 LBC           0 :             BloomInitPage(page, 0);
     253                 : 
     254 GIC      103999 :         if (BloomPageAddItem(&blstate, page, itup))
     255 ECB             :         {
     256                 :             /* Success!  Apply the change, clean up, and exit */
     257 CBC      102712 :             GenericXLogFinish(state);
     258          102712 :             UnlockReleaseBuffer(buffer);
     259          102712 :             ReleaseBuffer(metaBuffer);
     260          102712 :             MemoryContextSwitchTo(oldCtx);
     261 GIC      102712 :             MemoryContextDelete(insertCtx);
     262          102712 :             return false;
     263                 :         }
     264 ECB             : 
     265                 :         /* Didn't fit, must try other pages */
     266 GIC        1287 :         GenericXLogAbort(state);
     267            1287 :         UnlockReleaseBuffer(buffer);
     268                 :     }
     269                 :     else
     270 ECB             :     {
     271                 :         /* No entries in notFullPage */
     272 GIC           1 :         LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
     273                 :     }
     274                 : 
     275                 :     /*
     276                 :      * Try other pages in notFullPage array.  We will have to change nStart in
     277 ECB             :      * metapage.  Thus, grab exclusive lock on metapage.
     278                 :      */
     279 GIC        1288 :     LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
     280 ECB             : 
     281                 :     /* nStart might have changed while we didn't have lock */
     282 GIC        1288 :     nStart = metaData->nStart;
     283 ECB             : 
     284                 :     /* Skip first page if we already tried it above */
     285 CBC        1288 :     if (nStart < metaData->nEnd &&
     286 GIC        1287 :         blkno == metaData->notFullPage[nStart])
     287            1287 :         nStart++;
     288                 : 
     289                 :     /*
     290                 :      * This loop iterates for each page we try from the notFullPage array, and
     291                 :      * will also initialize a GenericXLogState for the fallback case of having
     292                 :      * to allocate a new page.
     293                 :      */
     294 ECB             :     for (;;)
     295                 :     {
     296 GIC        1288 :         state = GenericXLogStart(index);
     297 ECB             : 
     298                 :         /* get modifiable copy of metapage */
     299 GIC        1288 :         metaPage = GenericXLogRegisterBuffer(state, metaBuffer, 0);
     300 CBC        1288 :         metaData = BloomPageGetMeta(metaPage);
     301 ECB             : 
     302 GIC        1288 :         if (nStart >= metaData->nEnd)
     303 CBC           5 :             break;              /* no more entries in notFullPage array */
     304 ECB             : 
     305 GIC        1283 :         blkno = metaData->notFullPage[nStart];
     306 CBC        1283 :         Assert(blkno != InvalidBlockNumber);
     307 ECB             : 
     308 CBC        1283 :         buffer = ReadBuffer(index, blkno);
     309 GIC        1283 :         LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
     310            1283 :         page = GenericXLogRegisterBuffer(state, buffer, 0);
     311 ECB             : 
     312 EUB             :         /* Basically same logic as above */
     313 GIC        1283 :         if (PageIsNew(page) || BloomPageIsDeleted(page))
     314 LBC           0 :             BloomInitPage(page, 0);
     315                 : 
     316 GIC        1283 :         if (BloomPageAddItem(&blstate, page, itup))
     317 ECB             :         {
     318                 :             /* Success!  Apply the changes, clean up, and exit */
     319 CBC        1283 :             metaData->nStart = nStart;
     320            1283 :             GenericXLogFinish(state);
     321            1283 :             UnlockReleaseBuffer(buffer);
     322            1283 :             UnlockReleaseBuffer(metaBuffer);
     323            1283 :             MemoryContextSwitchTo(oldCtx);
     324 GIC        1283 :             MemoryContextDelete(insertCtx);
     325            1283 :             return false;
     326                 :         }
     327 EUB             : 
     328                 :         /* Didn't fit, must try other pages */
     329 UBC           0 :         GenericXLogAbort(state);
     330 UIC           0 :         UnlockReleaseBuffer(buffer);
     331               0 :         nStart++;
     332                 :     }
     333                 : 
     334                 :     /*
     335                 :      * Didn't find place to insert in notFullPage array.  Allocate new page.
     336 ECB             :      * (XXX is it good to do this while holding ex-lock on the metapage??)
     337                 :      */
     338 CBC           5 :     buffer = BloomNewBuffer(index);
     339 ECB             : 
     340 GIC           5 :     page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
     341 CBC           5 :     BloomInitPage(page, 0);
     342                 : 
     343 GIC           5 :     if (!BloomPageAddItem(&blstate, page, itup))
     344 EUB             :     {
     345                 :         /* We shouldn't be here since we're inserting to an empty page */
     346 UIC           0 :         elog(ERROR, "could not add new bloom tuple to empty page");
     347                 :     }
     348 ECB             : 
     349                 :     /* Reset notFullPage array to contain just this new page */
     350 CBC           5 :     metaData->nStart = 0;
     351 GIC           5 :     metaData->nEnd = 1;
     352               5 :     metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
     353 ECB             : 
     354                 :     /* Apply the changes, clean up, and exit */
     355 CBC           5 :     GenericXLogFinish(state);
     356 ECB             : 
     357 GIC           5 :     UnlockReleaseBuffer(buffer);
     358 CBC           5 :     UnlockReleaseBuffer(metaBuffer);
     359 ECB             : 
     360 GIC           5 :     MemoryContextSwitchTo(oldCtx);
     361 CBC           5 :     MemoryContextDelete(insertCtx);
     362                 : 
     363 GIC           5 :     return false;
     364                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a