Age Owner TLA 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-2023, 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
1248 andres 43 CBC 281361 : spgistBuildCallback(Relation index, ItemPointer tid, Datum *values,
44 : bool *isnull, bool tupleIsAlive, void *state)
45 : {
4131 tgl 46 281361 : SpGistBuildState *buildstate = (SpGistBuildState *) state;
47 : MemoryContext oldCtx;
48 :
49 : /* Work in temp context, and reset it after each tuple */
4046 50 281361 : 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 : */
1248 andres 58 281363 : while (!spgdoinsert(index, &buildstate->spgstate, tid,
59 : values, isnull))
60 : {
3445 tgl 61 2 : MemoryContextReset(buildstate->tmpCtx);
62 : }
63 :
64 : /* Update total tuple count */
1844 65 281361 : buildstate->indtuples += 1;
66 :
4046 67 281361 : MemoryContextSwitchTo(oldCtx);
68 281361 : MemoryContextReset(buildstate->tmpCtx);
4131 69 281361 : }
70 :
71 : /*
72 : * Build an SP-GiST index.
73 : */
74 : IndexBuildResult *
2639 75 100 : 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 :
4131 84 100 : if (RelationGetNumberOfBlocks(index) != 0)
4131 tgl 85 UBC 0 : elog(ERROR, "index \"%s\" already contains data",
86 : RelationGetRelationName(index));
87 :
88 : /*
89 : * Initialize the meta page and root pages
90 : */
4131 tgl 91 CBC 100 : metabuffer = SpGistNewBuffer(index);
92 100 : rootbuffer = SpGistNewBuffer(index);
4046 93 100 : nullbuffer = SpGistNewBuffer(index);
94 :
4131 95 100 : Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO);
4046 96 100 : Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
97 100 : Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
98 :
4131 99 100 : START_CRIT_SECTION();
100 :
2545 kgrittn 101 100 : SpGistInitMetapage(BufferGetPage(metabuffer));
4131 tgl 102 100 : MarkBufferDirty(metabuffer);
103 100 : SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
104 100 : MarkBufferDirty(rootbuffer);
4046 105 100 : SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
106 100 : MarkBufferDirty(nullbuffer);
107 :
108 :
4131 109 100 : END_CRIT_SECTION();
110 :
111 100 : UnlockReleaseBuffer(metabuffer);
112 100 : UnlockReleaseBuffer(rootbuffer);
4046 113 100 : UnlockReleaseBuffer(nullbuffer);
114 :
115 : /*
116 : * Now insert all the heap data into the index
117 : */
4131 118 100 : initSpGistState(&buildstate.spgstate, index);
119 100 : buildstate.spgstate.isBuild = true;
1844 120 100 : buildstate.indtuples = 0;
121 :
4131 122 100 : buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
123 : "SP-GiST build temporary context",
124 : ALLOCSET_DEFAULT_SIZES);
125 :
1468 alvherre 126 100 : reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
127 : spgistBuildCallback, (void *) &buildstate,
128 : NULL);
129 :
4131 tgl 130 100 : MemoryContextDelete(buildstate.tmpCtx);
131 :
132 100 : 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 : */
1467 heikki.linnakangas 138 100 : if (RelationNeedsWAL(index))
139 : {
140 34 : log_newpage_range(index, MAIN_FORKNUM,
141 : 0, RelationGetNumberOfBlocks(index),
142 : true);
143 : }
144 :
4131 tgl 145 100 : result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
1844 146 100 : result->heap_tuples = reltuples;
147 100 : result->index_tuples = buildstate.indtuples;
148 :
2639 149 100 : return result;
150 : }
151 :
152 : /*
153 : * Build an empty SPGiST index in the initialization fork
154 : */
155 : void
156 4 : spgbuildempty(Relation index)
157 : {
158 : Page page;
159 :
160 : /* Construct metapage. */
1 tmunro 161 GNC 4 : page = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
4131 tgl 162 CBC 4 : 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 : */
3670 simon 171 4 : PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
636 tgl 172 4 : smgrwrite(RelationGetSmgr(index), INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
173 : page, true);
277 rhaas 174 GNC 4 : log_newpage(&(RelationGetSmgr(index))->smgr_rlocator.locator, INIT_FORKNUM,
175 : SPGIST_METAPAGE_BLKNO, page, true);
176 :
177 : /* Likewise for the root page. */
4131 tgl 178 CBC 4 : SpGistInitPage(page, SPGIST_LEAF);
179 :
3670 simon 180 4 : PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
636 tgl 181 4 : smgrwrite(RelationGetSmgr(index), INIT_FORKNUM, SPGIST_ROOT_BLKNO,
182 : page, true);
277 rhaas 183 GNC 4 : log_newpage(&(RelationGetSmgr(index))->smgr_rlocator.locator, INIT_FORKNUM,
184 : SPGIST_ROOT_BLKNO, page, true);
185 :
186 : /* Likewise for the null-tuples root page. */
4046 tgl 187 CBC 4 : SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
188 :
3670 simon 189 4 : PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
636 tgl 190 4 : smgrwrite(RelationGetSmgr(index), INIT_FORKNUM, SPGIST_NULL_BLKNO,
191 : page, true);
277 rhaas 192 GNC 4 : log_newpage(&(RelationGetSmgr(index))->smgr_rlocator.locator, 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 : */
636 tgl 200 CBC 4 : smgrimmedsync(RelationGetSmgr(index), INIT_FORKNUM);
4131 201 4 : }
202 :
203 : /*
204 : * Insert one new tuple into an SPGiST index.
205 : */
206 : bool
2639 207 121225 : 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 :
4131 217 121225 : insertCtx = AllocSetContextCreate(CurrentMemoryContext,
218 : "SP-GiST insert temporary context",
219 : ALLOCSET_DEFAULT_SIZES);
220 121225 : oldCtx = MemoryContextSwitchTo(insertCtx);
221 :
222 121225 : 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 : */
734 230 121821 : while (!spgdoinsert(index, &spgstate, ht_ctid, values, isnull))
231 : {
3586 232 596 : MemoryContextReset(insertCtx);
233 596 : initSpGistState(&spgstate, index);
234 : }
235 :
4131 236 121223 : SpGistUpdateMetaPage(index);
237 :
238 121223 : MemoryContextSwitchTo(oldCtx);
239 121223 : MemoryContextDelete(insertCtx);
240 :
241 : /* return false since we've not done any unique check */
2639 242 121223 : return false;
243 : }
|