LCOV - code coverage report
Current view: top level - src/backend/access/spgist - spginsert.c (source / functions) Hit Total Coverage
Test: PostgreSQL 15devel Lines: 46 66 69.7 %
Date: 2021-12-09 04:09:06 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-2021, 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      377280 : spgistBuildCallback(Relation index, ItemPointer tid, Datum *values,
      44             :                     bool *isnull, bool tupleIsAlive, void *state)
      45             : {
      46      377280 :     SpGistBuildState *buildstate = (SpGistBuildState *) state;
      47             :     MemoryContext oldCtx;
      48             : 
      49             :     /* Work in temp context, and reset it after each tuple */
      50      377280 :     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      377280 :     while (!spgdoinsert(index, &buildstate->spgstate, tid,
      59             :                         values, isnull))
      60             :     {
      61           0 :         MemoryContextReset(buildstate->tmpCtx);
      62             :     }
      63             : 
      64             :     /* Update total tuple count */
      65      377280 :     buildstate->indtuples += 1;
      66             : 
      67      377280 :     MemoryContextSwitchTo(oldCtx);
      68      377280 :     MemoryContextReset(buildstate->tmpCtx);
      69      377280 : }
      70             : 
      71             : /*
      72             :  * Build an SP-GiST index.
      73             :  */
      74             : IndexBuildResult *
      75         156 : 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         156 :     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         156 :     metabuffer = SpGistNewBuffer(index);
      92         156 :     rootbuffer = SpGistNewBuffer(index);
      93         156 :     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         156 :     START_CRIT_SECTION();
     100             : 
     101         156 :     SpGistInitMetapage(BufferGetPage(metabuffer));
     102         156 :     MarkBufferDirty(metabuffer);
     103         156 :     SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
     104         156 :     MarkBufferDirty(rootbuffer);
     105         156 :     SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
     106         156 :     MarkBufferDirty(nullbuffer);
     107             : 
     108             : 
     109         156 :     END_CRIT_SECTION();
     110             : 
     111         156 :     UnlockReleaseBuffer(metabuffer);
     112         156 :     UnlockReleaseBuffer(rootbuffer);
     113         156 :     UnlockReleaseBuffer(nullbuffer);
     114             : 
     115             :     /*
     116             :      * Now insert all the heap data into the index
     117             :      */
     118         156 :     initSpGistState(&buildstate.spgstate, index);
     119         156 :     buildstate.spgstate.isBuild = true;
     120         156 :     buildstate.indtuples = 0;
     121             : 
     122         156 :     buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
     123             :                                               "SP-GiST build temporary context",
     124             :                                               ALLOCSET_DEFAULT_SIZES);
     125             : 
     126         156 :     reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
     127             :                                        spgistBuildCallback, (void *) &buildstate,
     128             :                                        NULL);
     129             : 
     130         156 :     MemoryContextDelete(buildstate.tmpCtx);
     131             : 
     132         156 :     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         156 :     if (RelationNeedsWAL(index))
     139             :     {
     140          88 :         log_newpage_range(index, MAIN_FORKNUM,
     141             :                           0, RelationGetNumberOfBlocks(index),
     142             :                           true);
     143             :     }
     144             : 
     145         156 :     result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
     146         156 :     result->heap_tuples = reltuples;
     147         156 :     result->index_tuples = buildstate.indtuples;
     148             : 
     149         156 :     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(RelationGetSmgr(index), INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
     173             :               (char *) page, true);
     174           0 :     log_newpage(&(RelationGetSmgr(index))->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(RelationGetSmgr(index), INIT_FORKNUM, SPGIST_ROOT_BLKNO,
     182             :               (char *) page, true);
     183           0 :     log_newpage(&(RelationGetSmgr(index))->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(RelationGetSmgr(index), INIT_FORKNUM, SPGIST_NULL_BLKNO,
     191             :               (char *) page, true);
     192           0 :     log_newpage(&(RelationGetSmgr(index))->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(RelationGetSmgr(index), INIT_FORKNUM);
     201           0 : }
     202             : 
     203             : /*
     204             :  * Insert one new tuple into an SPGiST index.
     205             :  */
     206             : bool
     207      163556 : spginsert(Relation index, Datum *values, bool *isnull,
     208             :           ItemPointer ht_ctid, Relation heapRel,
     209             :           IndexUniqueCheck checkUnique,
     210             :           bool indexUnchanged,
     211             :           IndexInfo *indexInfo)
     212             : {
     213             :     SpGistState spgstate;
     214             :     MemoryContext oldCtx;
     215             :     MemoryContext insertCtx;
     216             : 
     217      163556 :     insertCtx = AllocSetContextCreate(CurrentMemoryContext,
     218             :                                       "SP-GiST insert temporary context",
     219             :                                       ALLOCSET_DEFAULT_SIZES);
     220      163556 :     oldCtx = MemoryContextSwitchTo(insertCtx);
     221             : 
     222      163556 :     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      163556 :     while (!spgdoinsert(index, &spgstate, ht_ctid, values, isnull))
     231             :     {
     232           0 :         MemoryContextReset(insertCtx);
     233           0 :         initSpGistState(&spgstate, index);
     234             :     }
     235             : 
     236      163552 :     SpGistUpdateMetaPage(index);
     237             : 
     238      163552 :     MemoryContextSwitchTo(oldCtx);
     239      163552 :     MemoryContextDelete(insertCtx);
     240             : 
     241             :     /* return false since we've not done any unique check */
     242      163552 :     return false;
     243             : }

Generated by: LCOV version 1.14