LCOV - code coverage report
Current view: top level - src/backend/access/spgist - spginsert.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 98.4 % 63 62
Test Date: 2026-03-01 14:14:54 Functions: 100.0 % 4 4
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-2026, 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       281508 : spgistBuildCallback(Relation index, ItemPointer tid, Datum *values,
      42              :                     bool *isnull, bool tupleIsAlive, void *state)
      43              : {
      44       281508 :     SpGistBuildState *buildstate = (SpGistBuildState *) state;
      45              :     MemoryContext oldCtx;
      46              : 
      47              :     /* Work in temp context, and reset it after each tuple */
      48       281508 :     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       282004 :     while (!spgdoinsert(index, &buildstate->spgstate, tid,
      57              :                         values, isnull))
      58              :     {
      59          496 :         MemoryContextReset(buildstate->tmpCtx);
      60              :     }
      61              : 
      62              :     /* Update total tuple count */
      63       281508 :     buildstate->indtuples += 1;
      64              : 
      65       281508 :     MemoryContextSwitchTo(oldCtx);
      66       281508 :     MemoryContextReset(buildstate->tmpCtx);
      67       281508 : }
      68              : 
      69              : /*
      70              :  * Build an SP-GiST index.
      71              :  */
      72              : IndexBuildResult *
      73          104 : 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          104 :     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          104 :     metabuffer = SpGistNewBuffer(index);
      90          104 :     rootbuffer = SpGistNewBuffer(index);
      91          104 :     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          104 :     START_CRIT_SECTION();
      98              : 
      99          104 :     SpGistInitMetapage(BufferGetPage(metabuffer));
     100          104 :     MarkBufferDirty(metabuffer);
     101          104 :     SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
     102          104 :     MarkBufferDirty(rootbuffer);
     103          104 :     SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
     104          104 :     MarkBufferDirty(nullbuffer);
     105              : 
     106              : 
     107          104 :     END_CRIT_SECTION();
     108              : 
     109          104 :     UnlockReleaseBuffer(metabuffer);
     110          104 :     UnlockReleaseBuffer(rootbuffer);
     111          104 :     UnlockReleaseBuffer(nullbuffer);
     112              : 
     113              :     /*
     114              :      * Now insert all the heap data into the index
     115              :      */
     116          104 :     initSpGistState(&buildstate.spgstate, index);
     117          104 :     buildstate.spgstate.isBuild = true;
     118          104 :     buildstate.indtuples = 0;
     119              : 
     120          104 :     buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
     121              :                                               "SP-GiST build temporary context",
     122              :                                               ALLOCSET_DEFAULT_SIZES);
     123              : 
     124          104 :     reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
     125              :                                        spgistBuildCallback, &buildstate,
     126              :                                        NULL);
     127              : 
     128          104 :     MemoryContextDelete(buildstate.tmpCtx);
     129              : 
     130          104 :     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          104 :     if (RelationNeedsWAL(index))
     137              :     {
     138           54 :         log_newpage_range(index, MAIN_FORKNUM,
     139              :                           0, RelationGetNumberOfBlocks(index),
     140              :                           true);
     141              :     }
     142              : 
     143          104 :     result = palloc0_object(IndexBuildResult);
     144          104 :     result->heap_tuples = reltuples;
     145          104 :     result->index_tuples = buildstate.indtuples;
     146              : 
     147          104 :     return result;
     148              : }
     149              : 
     150              : /*
     151              :  * Build an empty SPGiST index in the initialization fork
     152              :  */
     153              : void
     154            4 : spgbuildempty(Relation index)
     155              : {
     156              :     BulkWriteState *bulkstate;
     157              :     BulkWriteBuffer buf;
     158              : 
     159            4 :     bulkstate = smgr_bulk_start_rel(index, INIT_FORKNUM);
     160              : 
     161              :     /* Construct metapage. */
     162            4 :     buf = smgr_bulk_get_buf(bulkstate);
     163            4 :     SpGistInitMetapage((Page) buf);
     164            4 :     smgr_bulk_write(bulkstate, SPGIST_METAPAGE_BLKNO, buf, true);
     165              : 
     166              :     /* Likewise for the root page. */
     167            4 :     buf = smgr_bulk_get_buf(bulkstate);
     168            4 :     SpGistInitPage((Page) buf, SPGIST_LEAF);
     169            4 :     smgr_bulk_write(bulkstate, SPGIST_ROOT_BLKNO, buf, true);
     170              : 
     171              :     /* Likewise for the null-tuples root page. */
     172            4 :     buf = smgr_bulk_get_buf(bulkstate);
     173            4 :     SpGistInitPage((Page) buf, SPGIST_LEAF | SPGIST_NULLS);
     174            4 :     smgr_bulk_write(bulkstate, SPGIST_NULL_BLKNO, buf, true);
     175              : 
     176            4 :     smgr_bulk_finish(bulkstate);
     177            4 : }
     178              : 
     179              : /*
     180              :  * Insert one new tuple into an SPGiST index.
     181              :  */
     182              : bool
     183       121455 : 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       121455 :     insertCtx = AllocSetContextCreate(CurrentMemoryContext,
     194              :                                       "SP-GiST insert temporary context",
     195              :                                       ALLOCSET_DEFAULT_SIZES);
     196       121455 :     oldCtx = MemoryContextSwitchTo(insertCtx);
     197              : 
     198       121455 :     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       121773 :     while (!spgdoinsert(index, &spgstate, ht_ctid, values, isnull))
     207              :     {
     208          318 :         MemoryContextReset(insertCtx);
     209          318 :         initSpGistState(&spgstate, index);
     210              :     }
     211              : 
     212       121453 :     SpGistUpdateMetaPage(index);
     213              : 
     214       121453 :     MemoryContextSwitchTo(oldCtx);
     215       121453 :     MemoryContextDelete(insertCtx);
     216              : 
     217              :     /* return false since we've not done any unique check */
     218       121453 :     return false;
     219              : }
        

Generated by: LCOV version 2.0-1