LCOV - code coverage report
Current view: top level - src/backend/access/spgist - spginsert.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 54 74 73.0 %
Date: 2017-09-29 15:12:54 Functions: 3 4 75.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * spginsert.c
       4             :  *    Externally visible index creation/insertion routines
       5             :  *
       6             :  * All the actual insertion logic is in spgdoinsert.c.
       7             :  *
       8             :  * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
       9             :  * Portions Copyright (c) 1994, Regents of the University of California
      10             :  *
      11             :  * IDENTIFICATION
      12             :  *          src/backend/access/spgist/spginsert.c
      13             :  *
      14             :  *-------------------------------------------------------------------------
      15             :  */
      16             : 
      17             : #include "postgres.h"
      18             : 
      19             : #include "access/genam.h"
      20             : #include "access/spgist_private.h"
      21             : #include "access/spgxlog.h"
      22             : #include "access/xlog.h"
      23             : #include "access/xloginsert.h"
      24             : #include "catalog/index.h"
      25             : #include "miscadmin.h"
      26             : #include "storage/bufmgr.h"
      27             : #include "storage/smgr.h"
      28             : #include "utils/memutils.h"
      29             : #include "utils/rel.h"
      30             : 
      31             : 
      32             : typedef struct
      33             : {
      34             :     SpGistState spgstate;       /* SPGiST's working state */
      35             :     MemoryContext tmpCtx;       /* per-tuple temporary context */
      36             : } SpGistBuildState;
      37             : 
      38             : 
      39             : /* Callback to process one heap tuple during IndexBuildHeapScan */
      40             : static void
      41       45529 : spgistBuildCallback(Relation index, HeapTuple htup, Datum *values,
      42             :                     bool *isnull, bool tupleIsAlive, void *state)
      43             : {
      44       45529 :     SpGistBuildState *buildstate = (SpGistBuildState *) state;
      45             :     MemoryContext oldCtx;
      46             : 
      47             :     /* Work in temp context, and reset it after each tuple */
      48       45529 :     oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
      49             : 
      50             :     /*
      51             :      * Even though no concurrent insertions can be happening, we still might
      52             :      * get a buffer-locking failure due to bgwriter or checkpointer taking a
      53             :      * lock on some buffer.  So we need to be willing to retry.  We can flush
      54             :      * any temp data when retrying.
      55             :      */
      56       91058 :     while (!spgdoinsert(index, &buildstate->spgstate, &htup->t_self,
      57       45529 :                         *values, *isnull))
      58             :     {
      59           0 :         MemoryContextReset(buildstate->tmpCtx);
      60             :     }
      61             : 
      62       45529 :     MemoryContextSwitchTo(oldCtx);
      63       45529 :     MemoryContextReset(buildstate->tmpCtx);
      64       45529 : }
      65             : 
      66             : /*
      67             :  * Build an SP-GiST index.
      68             :  */
      69             : IndexBuildResult *
      70          10 : spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
      71             : {
      72             :     IndexBuildResult *result;
      73             :     double      reltuples;
      74             :     SpGistBuildState buildstate;
      75             :     Buffer      metabuffer,
      76             :                 rootbuffer,
      77             :                 nullbuffer;
      78             : 
      79          10 :     if (RelationGetNumberOfBlocks(index) != 0)
      80           0 :         elog(ERROR, "index \"%s\" already contains data",
      81             :              RelationGetRelationName(index));
      82             : 
      83             :     /*
      84             :      * Initialize the meta page and root pages
      85             :      */
      86          10 :     metabuffer = SpGistNewBuffer(index);
      87          10 :     rootbuffer = SpGistNewBuffer(index);
      88          10 :     nullbuffer = SpGistNewBuffer(index);
      89             : 
      90          10 :     Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO);
      91          10 :     Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
      92          10 :     Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
      93             : 
      94          10 :     START_CRIT_SECTION();
      95             : 
      96          10 :     SpGistInitMetapage(BufferGetPage(metabuffer));
      97          10 :     MarkBufferDirty(metabuffer);
      98          10 :     SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
      99          10 :     MarkBufferDirty(rootbuffer);
     100          10 :     SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
     101          10 :     MarkBufferDirty(nullbuffer);
     102             : 
     103          10 :     if (RelationNeedsWAL(index))
     104             :     {
     105             :         XLogRecPtr  recptr;
     106             : 
     107           9 :         XLogBeginInsert();
     108             : 
     109             :         /*
     110             :          * Replay will re-initialize the pages, so don't take full pages
     111             :          * images.  No other data to log.
     112             :          */
     113           9 :         XLogRegisterBuffer(0, metabuffer, REGBUF_WILL_INIT);
     114           9 :         XLogRegisterBuffer(1, rootbuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
     115           9 :         XLogRegisterBuffer(2, nullbuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
     116             : 
     117           9 :         recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_CREATE_INDEX);
     118             : 
     119           9 :         PageSetLSN(BufferGetPage(metabuffer), recptr);
     120           9 :         PageSetLSN(BufferGetPage(rootbuffer), recptr);
     121           9 :         PageSetLSN(BufferGetPage(nullbuffer), recptr);
     122             :     }
     123             : 
     124          10 :     END_CRIT_SECTION();
     125             : 
     126          10 :     UnlockReleaseBuffer(metabuffer);
     127          10 :     UnlockReleaseBuffer(rootbuffer);
     128          10 :     UnlockReleaseBuffer(nullbuffer);
     129             : 
     130             :     /*
     131             :      * Now insert all the heap data into the index
     132             :      */
     133          10 :     initSpGistState(&buildstate.spgstate, index);
     134          10 :     buildstate.spgstate.isBuild = true;
     135             : 
     136          10 :     buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
     137             :                                               "SP-GiST build temporary context",
     138             :                                               ALLOCSET_DEFAULT_SIZES);
     139             : 
     140          10 :     reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
     141             :                                    spgistBuildCallback, (void *) &buildstate);
     142             : 
     143          10 :     MemoryContextDelete(buildstate.tmpCtx);
     144             : 
     145          10 :     SpGistUpdateMetaPage(index);
     146             : 
     147          10 :     result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
     148          10 :     result->heap_tuples = result->index_tuples = reltuples;
     149             : 
     150          10 :     return result;
     151             : }
     152             : 
     153             : /*
     154             :  * Build an empty SPGiST index in the initialization fork
     155             :  */
     156             : void
     157           0 : spgbuildempty(Relation index)
     158             : {
     159             :     Page        page;
     160             : 
     161             :     /* Construct metapage. */
     162           0 :     page = (Page) palloc(BLCKSZ);
     163           0 :     SpGistInitMetapage(page);
     164             : 
     165             :     /*
     166             :      * Write the page and log it unconditionally.  This is important
     167             :      * particularly for indexes created on tablespaces and databases whose
     168             :      * creation happened after the last redo pointer as recovery removes any
     169             :      * of their existing content when the corresponding create records are
     170             :      * replayed.
     171             :      */
     172           0 :     PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
     173           0 :     smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
     174             :               (char *) page, true);
     175           0 :     log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
     176             :                 SPGIST_METAPAGE_BLKNO, page, false);
     177             : 
     178             :     /* Likewise for the root page. */
     179           0 :     SpGistInitPage(page, SPGIST_LEAF);
     180             : 
     181           0 :     PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
     182           0 :     smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO,
     183             :               (char *) page, true);
     184           0 :     log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
     185             :                 SPGIST_ROOT_BLKNO, page, true);
     186             : 
     187             :     /* Likewise for the null-tuples root page. */
     188           0 :     SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
     189             : 
     190           0 :     PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
     191           0 :     smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO,
     192             :               (char *) page, true);
     193           0 :     log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
     194             :                 SPGIST_NULL_BLKNO, page, true);
     195             : 
     196             :     /*
     197             :      * An immediate sync is required even if we xlog'd the pages, because the
     198             :      * writes did not go through shared buffers and therefore a concurrent
     199             :      * checkpoint may have moved the redo pointer past our xlog record.
     200             :      */
     201           0 :     smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
     202           0 : }
     203             : 
     204             : /*
     205             :  * Insert one new tuple into an SPGiST index.
     206             :  */
     207             : bool
     208       37316 : spginsert(Relation index, Datum *values, bool *isnull,
     209             :           ItemPointer ht_ctid, Relation heapRel,
     210             :           IndexUniqueCheck checkUnique,
     211             :           IndexInfo *indexInfo)
     212             : {
     213             :     SpGistState spgstate;
     214             :     MemoryContext oldCtx;
     215             :     MemoryContext insertCtx;
     216             : 
     217       37316 :     insertCtx = AllocSetContextCreate(CurrentMemoryContext,
     218             :                                       "SP-GiST insert temporary context",
     219             :                                       ALLOCSET_DEFAULT_SIZES);
     220       37316 :     oldCtx = MemoryContextSwitchTo(insertCtx);
     221             : 
     222       37316 :     initSpGistState(&spgstate, index);
     223             : 
     224             :     /*
     225             :      * We might have to repeat spgdoinsert() multiple times, if conflicts
     226             :      * occur with concurrent insertions.  If so, reset the insertCtx each time
     227             :      * to avoid cumulative memory consumption.  That means we also have to
     228             :      * redo initSpGistState(), but it's cheap enough not to matter.
     229             :      */
     230       74632 :     while (!spgdoinsert(index, &spgstate, ht_ctid, *values, *isnull))
     231             :     {
     232           0 :         MemoryContextReset(insertCtx);
     233           0 :         initSpGistState(&spgstate, index);
     234             :     }
     235             : 
     236       37316 :     SpGistUpdateMetaPage(index);
     237             : 
     238       37316 :     MemoryContextSwitchTo(oldCtx);
     239       37316 :     MemoryContextDelete(insertCtx);
     240             : 
     241             :     /* return false since we've not done any unique check */
     242       37316 :     return false;
     243             : }

Generated by: LCOV version 1.11