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 : }
|