1 /*-------------------------------------------------------------------------
4 * Externally visible index creation/insertion routines
6 * All the actual insertion logic is in spgdoinsert.c.
8 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
9 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/backend/access/spgist/spginsert.c
14 *-------------------------------------------------------------------------
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"
33 SpGistState spgstate
; /* SPGiST's working state */
34 int64 indtuples
; /* total number of tuples indexed */
35 MemoryContext tmpCtx
; /* per-tuple temporary context */
39 /* Callback to process one heap tuple during table_index_build_scan */
41 spgistBuildCallback(Relation index
, ItemPointer tid
, Datum
*values
,
42 bool *isnull
, bool tupleIsAlive
, void *state
)
44 SpGistBuildState
*buildstate
= (SpGistBuildState
*) state
;
47 /* Work in temp context, and reset it after each tuple */
48 oldCtx
= MemoryContextSwitchTo(buildstate
->tmpCtx
);
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.
56 while (!spgdoinsert(index
, &buildstate
->spgstate
, tid
,
59 MemoryContextReset(buildstate
->tmpCtx
);
62 /* Update total tuple count */
63 buildstate
->indtuples
+= 1;
65 MemoryContextSwitchTo(oldCtx
);
66 MemoryContextReset(buildstate
->tmpCtx
);
70 * Build an SP-GiST index.
73 spgbuild(Relation heap
, Relation index
, IndexInfo
*indexInfo
)
75 IndexBuildResult
*result
;
77 SpGistBuildState buildstate
;
82 if (RelationGetNumberOfBlocks(index
) != 0)
83 elog(ERROR
, "index \"%s\" already contains data",
84 RelationGetRelationName(index
));
87 * Initialize the meta page and root pages
89 metabuffer
= SpGistNewBuffer(index
);
90 rootbuffer
= SpGistNewBuffer(index
);
91 nullbuffer
= SpGistNewBuffer(index
);
93 Assert(BufferGetBlockNumber(metabuffer
) == SPGIST_METAPAGE_BLKNO
);
94 Assert(BufferGetBlockNumber(rootbuffer
) == SPGIST_ROOT_BLKNO
);
95 Assert(BufferGetBlockNumber(nullbuffer
) == SPGIST_NULL_BLKNO
);
99 SpGistInitMetapage(BufferGetPage(metabuffer
));
100 MarkBufferDirty(metabuffer
);
101 SpGistInitBuffer(rootbuffer
, SPGIST_LEAF
);
102 MarkBufferDirty(rootbuffer
);
103 SpGistInitBuffer(nullbuffer
, SPGIST_LEAF
| SPGIST_NULLS
);
104 MarkBufferDirty(nullbuffer
);
109 UnlockReleaseBuffer(metabuffer
);
110 UnlockReleaseBuffer(rootbuffer
);
111 UnlockReleaseBuffer(nullbuffer
);
114 * Now insert all the heap data into the index
116 initSpGistState(&buildstate
.spgstate
, index
);
117 buildstate
.spgstate
.isBuild
= true;
118 buildstate
.indtuples
= 0;
120 buildstate
.tmpCtx
= AllocSetContextCreate(CurrentMemoryContext
,
121 "SP-GiST build temporary context",
122 ALLOCSET_DEFAULT_SIZES
);
124 reltuples
= table_index_build_scan(heap
, index
, indexInfo
, true, true,
125 spgistBuildCallback
, (void *) &buildstate
,
128 MemoryContextDelete(buildstate
.tmpCtx
);
130 SpGistUpdateMetaPage(index
);
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.
136 if (RelationNeedsWAL(index
))
138 log_newpage_range(index
, MAIN_FORKNUM
,
139 0, RelationGetNumberOfBlocks(index
),
143 result
= (IndexBuildResult
*) palloc0(sizeof(IndexBuildResult
));
144 result
->heap_tuples
= reltuples
;
145 result
->index_tuples
= buildstate
.indtuples
;
151 * Build an empty SPGiST index in the initialization fork
154 spgbuildempty(Relation index
)
156 BulkWriteState
*bulkstate
;
159 bulkstate
= smgr_bulk_start_rel(index
, INIT_FORKNUM
);
161 /* Construct metapage. */
162 buf
= smgr_bulk_get_buf(bulkstate
);
163 SpGistInitMetapage((Page
) buf
);
164 smgr_bulk_write(bulkstate
, SPGIST_METAPAGE_BLKNO
, buf
, true);
166 /* Likewise for the root page. */
167 buf
= smgr_bulk_get_buf(bulkstate
);
168 SpGistInitPage((Page
) buf
, SPGIST_LEAF
);
169 smgr_bulk_write(bulkstate
, SPGIST_ROOT_BLKNO
, buf
, true);
171 /* Likewise for the null-tuples root page. */
172 buf
= smgr_bulk_get_buf(bulkstate
);
173 SpGistInitPage((Page
) buf
, SPGIST_LEAF
| SPGIST_NULLS
);
174 smgr_bulk_write(bulkstate
, SPGIST_NULL_BLKNO
, buf
, true);
176 smgr_bulk_finish(bulkstate
);
180 * Insert one new tuple into an SPGiST index.
183 spginsert(Relation index
, Datum
*values
, bool *isnull
,
184 ItemPointer ht_ctid
, Relation heapRel
,
185 IndexUniqueCheck checkUnique
,
187 IndexInfo
*indexInfo
)
189 SpGistState spgstate
;
190 MemoryContext oldCtx
;
191 MemoryContext insertCtx
;
193 insertCtx
= AllocSetContextCreate(CurrentMemoryContext
,
194 "SP-GiST insert temporary context",
195 ALLOCSET_DEFAULT_SIZES
);
196 oldCtx
= MemoryContextSwitchTo(insertCtx
);
198 initSpGistState(&spgstate
, index
);
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.
206 while (!spgdoinsert(index
, &spgstate
, ht_ctid
, values
, isnull
))
208 MemoryContextReset(insertCtx
);
209 initSpGistState(&spgstate
, index
);
212 SpGistUpdateMetaPage(index
);
214 MemoryContextSwitchTo(oldCtx
);
215 MemoryContextDelete(insertCtx
);
217 /* return false since we've not done any unique check */