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
43 CBC 281361 : spgistBuildCallback(Relation index, ItemPointer tid, Datum *values,
44 : bool *isnull, bool tupleIsAlive, void *state)
45 : {
46 281361 : SpGistBuildState *buildstate = (SpGistBuildState *) state;
47 : MemoryContext oldCtx;
48 :
49 : /* Work in temp context, and reset it after each tuple */
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 : */
58 281363 : while (!spgdoinsert(index, &buildstate->spgstate, tid,
59 : values, isnull))
60 : {
61 2 : MemoryContextReset(buildstate->tmpCtx);
62 : }
63 :
64 : /* Update total tuple count */
65 281361 : buildstate->indtuples += 1;
66 :
67 281361 : MemoryContextSwitchTo(oldCtx);
68 281361 : MemoryContextReset(buildstate->tmpCtx);
69 281361 : }
70 :
71 : /*
72 : * Build an SP-GiST index.
73 : */
74 : IndexBuildResult *
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 :
84 100 : if (RelationGetNumberOfBlocks(index) != 0)
85 UBC 0 : elog(ERROR, "index \"%s\" already contains data",
86 : RelationGetRelationName(index));
87 :
88 : /*
89 : * Initialize the meta page and root pages
90 : */
91 CBC 100 : metabuffer = SpGistNewBuffer(index);
92 100 : rootbuffer = SpGistNewBuffer(index);
93 100 : nullbuffer = SpGistNewBuffer(index);
94 :
95 100 : Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO);
96 100 : Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
97 100 : Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
98 :
99 100 : START_CRIT_SECTION();
100 :
101 100 : SpGistInitMetapage(BufferGetPage(metabuffer));
102 100 : MarkBufferDirty(metabuffer);
103 100 : SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
104 100 : MarkBufferDirty(rootbuffer);
105 100 : SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
106 100 : MarkBufferDirty(nullbuffer);
107 :
108 :
109 100 : END_CRIT_SECTION();
110 :
111 100 : UnlockReleaseBuffer(metabuffer);
112 100 : UnlockReleaseBuffer(rootbuffer);
113 100 : UnlockReleaseBuffer(nullbuffer);
114 :
115 : /*
116 : * Now insert all the heap data into the index
117 : */
118 100 : initSpGistState(&buildstate.spgstate, index);
119 100 : buildstate.spgstate.isBuild = true;
120 100 : buildstate.indtuples = 0;
121 :
122 100 : buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
123 : "SP-GiST build temporary context",
124 : ALLOCSET_DEFAULT_SIZES);
125 :
126 100 : reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
127 : spgistBuildCallback, (void *) &buildstate,
128 : NULL);
129 :
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 : */
138 100 : if (RelationNeedsWAL(index))
139 : {
140 34 : log_newpage_range(index, MAIN_FORKNUM,
141 : 0, RelationGetNumberOfBlocks(index),
142 : true);
143 : }
144 :
145 100 : result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
146 100 : result->heap_tuples = reltuples;
147 100 : result->index_tuples = buildstate.indtuples;
148 :
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. */
161 GNC 4 : page = (Page) palloc_aligned(BLCKSZ, PG_IO_ALIGN_SIZE, 0);
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 : */
171 4 : PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
172 4 : smgrwrite(RelationGetSmgr(index), INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
173 : page, true);
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. */
178 CBC 4 : SpGistInitPage(page, SPGIST_LEAF);
179 :
180 4 : PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
181 4 : smgrwrite(RelationGetSmgr(index), INIT_FORKNUM, SPGIST_ROOT_BLKNO,
182 : page, true);
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. */
187 CBC 4 : SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
188 :
189 4 : PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
190 4 : smgrwrite(RelationGetSmgr(index), INIT_FORKNUM, SPGIST_NULL_BLKNO,
191 : page, true);
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 : */
200 CBC 4 : smgrimmedsync(RelationGetSmgr(index), INIT_FORKNUM);
201 4 : }
202 :
203 : /*
204 : * Insert one new tuple into an SPGiST index.
205 : */
206 : bool
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 :
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 : */
230 121821 : while (!spgdoinsert(index, &spgstate, ht_ctid, values, isnull))
231 : {
232 596 : MemoryContextReset(insertCtx);
233 596 : initSpGistState(&spgstate, index);
234 : }
235 :
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 */
242 121223 : return false;
243 : }
|