LCOV - code coverage report
Current view: top level - src/backend/access/spgist - spginsert.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 62 63 98.4 %
Date: 2024-11-21 08:14:44 Functions: 4 4 100.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-2024, 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/tableam.h"
      22             : #include "access/xloginsert.h"
      23             : #include "miscadmin.h"
      24             : #include "nodes/execnodes.h"
      25             : #include "storage/bufmgr.h"
      26             : #include "storage/bulk_write.h"
      27             : #include "utils/memutils.h"
      28             : #include "utils/rel.h"
      29             : 
      30             : 
      31             : typedef struct
      32             : {
      33             :     SpGistState spgstate;       /* SPGiST's working state */
      34             :     int64       indtuples;      /* total number of tuples indexed */
      35             :     MemoryContext tmpCtx;       /* per-tuple temporary context */
      36             : } SpGistBuildState;
      37             : 
      38             : 
      39             : /* Callback to process one heap tuple during table_index_build_scan */
      40             : static void
      41      562842 : spgistBuildCallback(Relation index, ItemPointer tid, Datum *values,
      42             :                     bool *isnull, bool tupleIsAlive, void *state)
      43             : {
      44      562842 :     SpGistBuildState *buildstate = (SpGistBuildState *) state;
      45             :     MemoryContext oldCtx;
      46             : 
      47             :     /* Work in temp context, and reset it after each tuple */
      48      562842 :     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      562846 :     while (!spgdoinsert(index, &buildstate->spgstate, tid,
      57             :                         values, isnull))
      58             :     {
      59           4 :         MemoryContextReset(buildstate->tmpCtx);
      60             :     }
      61             : 
      62             :     /* Update total tuple count */
      63      562842 :     buildstate->indtuples += 1;
      64             : 
      65      562842 :     MemoryContextSwitchTo(oldCtx);
      66      562842 :     MemoryContextReset(buildstate->tmpCtx);
      67      562842 : }
      68             : 
      69             : /*
      70             :  * Build an SP-GiST index.
      71             :  */
      72             : IndexBuildResult *
      73         208 : spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
      74             : {
      75             :     IndexBuildResult *result;
      76             :     double      reltuples;
      77             :     SpGistBuildState buildstate;
      78             :     Buffer      metabuffer,
      79             :                 rootbuffer,
      80             :                 nullbuffer;
      81             : 
      82         208 :     if (RelationGetNumberOfBlocks(index) != 0)
      83           0 :         elog(ERROR, "index \"%s\" already contains data",
      84             :              RelationGetRelationName(index));
      85             : 
      86             :     /*
      87             :      * Initialize the meta page and root pages
      88             :      */
      89         208 :     metabuffer = SpGistNewBuffer(index);
      90         208 :     rootbuffer = SpGistNewBuffer(index);
      91         208 :     nullbuffer = SpGistNewBuffer(index);
      92             : 
      93             :     Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO);
      94             :     Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
      95             :     Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
      96             : 
      97         208 :     START_CRIT_SECTION();
      98             : 
      99         208 :     SpGistInitMetapage(BufferGetPage(metabuffer));
     100         208 :     MarkBufferDirty(metabuffer);
     101         208 :     SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
     102         208 :     MarkBufferDirty(rootbuffer);
     103         208 :     SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
     104         208 :     MarkBufferDirty(nullbuffer);
     105             : 
     106             : 
     107         208 :     END_CRIT_SECTION();
     108             : 
     109         208 :     UnlockReleaseBuffer(metabuffer);
     110         208 :     UnlockReleaseBuffer(rootbuffer);
     111         208 :     UnlockReleaseBuffer(nullbuffer);
     112             : 
     113             :     /*
     114             :      * Now insert all the heap data into the index
     115             :      */
     116         208 :     initSpGistState(&buildstate.spgstate, index);
     117         208 :     buildstate.spgstate.isBuild = true;
     118         208 :     buildstate.indtuples = 0;
     119             : 
     120         208 :     buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
     121             :                                               "SP-GiST build temporary context",
     122             :                                               ALLOCSET_DEFAULT_SIZES);
     123             : 
     124         208 :     reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
     125             :                                        spgistBuildCallback, (void *) &buildstate,
     126             :                                        NULL);
     127             : 
     128         208 :     MemoryContextDelete(buildstate.tmpCtx);
     129             : 
     130         208 :     SpGistUpdateMetaPage(index);
     131             : 
     132             :     /*
     133             :      * We didn't write WAL records as we built the index, so if WAL-logging is
     134             :      * required, write all pages to the WAL now.
     135             :      */
     136         208 :     if (RelationNeedsWAL(index))
     137             :     {
     138          74 :         log_newpage_range(index, MAIN_FORKNUM,
     139             :                           0, RelationGetNumberOfBlocks(index),
     140             :                           true);
     141             :     }
     142             : 
     143         208 :     result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
     144         208 :     result->heap_tuples = reltuples;
     145         208 :     result->index_tuples = buildstate.indtuples;
     146             : 
     147         208 :     return result;
     148             : }
     149             : 
     150             : /*
     151             :  * Build an empty SPGiST index in the initialization fork
     152             :  */
     153             : void
     154           8 : spgbuildempty(Relation index)
     155             : {
     156             :     BulkWriteState *bulkstate;
     157             :     BulkWriteBuffer buf;
     158             : 
     159           8 :     bulkstate = smgr_bulk_start_rel(index, INIT_FORKNUM);
     160             : 
     161             :     /* Construct metapage. */
     162           8 :     buf = smgr_bulk_get_buf(bulkstate);
     163           8 :     SpGistInitMetapage((Page) buf);
     164           8 :     smgr_bulk_write(bulkstate, SPGIST_METAPAGE_BLKNO, buf, true);
     165             : 
     166             :     /* Likewise for the root page. */
     167           8 :     buf = smgr_bulk_get_buf(bulkstate);
     168           8 :     SpGistInitPage((Page) buf, SPGIST_LEAF);
     169           8 :     smgr_bulk_write(bulkstate, SPGIST_ROOT_BLKNO, buf, true);
     170             : 
     171             :     /* Likewise for the null-tuples root page. */
     172           8 :     buf = smgr_bulk_get_buf(bulkstate);
     173           8 :     SpGistInitPage((Page) buf, SPGIST_LEAF | SPGIST_NULLS);
     174           8 :     smgr_bulk_write(bulkstate, SPGIST_NULL_BLKNO, buf, true);
     175             : 
     176           8 :     smgr_bulk_finish(bulkstate);
     177           8 : }
     178             : 
     179             : /*
     180             :  * Insert one new tuple into an SPGiST index.
     181             :  */
     182             : bool
     183      242592 : spginsert(Relation index, Datum *values, bool *isnull,
     184             :           ItemPointer ht_ctid, Relation heapRel,
     185             :           IndexUniqueCheck checkUnique,
     186             :           bool indexUnchanged,
     187             :           IndexInfo *indexInfo)
     188             : {
     189             :     SpGistState spgstate;
     190             :     MemoryContext oldCtx;
     191             :     MemoryContext insertCtx;
     192             : 
     193      242592 :     insertCtx = AllocSetContextCreate(CurrentMemoryContext,
     194             :                                       "SP-GiST insert temporary context",
     195             :                                       ALLOCSET_DEFAULT_SIZES);
     196      242592 :     oldCtx = MemoryContextSwitchTo(insertCtx);
     197             : 
     198      242592 :     initSpGistState(&spgstate, index);
     199             : 
     200             :     /*
     201             :      * We might have to repeat spgdoinsert() multiple times, if conflicts
     202             :      * occur with concurrent insertions.  If so, reset the insertCtx each time
     203             :      * to avoid cumulative memory consumption.  That means we also have to
     204             :      * redo initSpGistState(), but it's cheap enough not to matter.
     205             :      */
     206      242656 :     while (!spgdoinsert(index, &spgstate, ht_ctid, values, isnull))
     207             :     {
     208          64 :         MemoryContextReset(insertCtx);
     209          64 :         initSpGistState(&spgstate, index);
     210             :     }
     211             : 
     212      242588 :     SpGistUpdateMetaPage(index);
     213             : 
     214      242588 :     MemoryContextSwitchTo(oldCtx);
     215      242588 :     MemoryContextDelete(insertCtx);
     216             : 
     217             :     /* return false since we've not done any unique check */
     218      242588 :     return false;
     219             : }

Generated by: LCOV version 1.14