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

Generated by: LCOV version 1.13