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

Generated by: LCOV version 1.13