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

Generated by: LCOV version 1.13