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-2017, 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/xlog.h"
23 : #include "access/xloginsert.h"
24 : #include "catalog/index.h"
25 : #include "miscadmin.h"
26 : #include "storage/bufmgr.h"
27 : #include "storage/smgr.h"
28 : #include "utils/memutils.h"
29 : #include "utils/rel.h"
30 :
31 :
32 : typedef struct
33 : {
34 : SpGistState spgstate; /* SPGiST's working state */
35 : MemoryContext tmpCtx; /* per-tuple temporary context */
36 : } SpGistBuildState;
37 :
38 :
39 : /* Callback to process one heap tuple during IndexBuildHeapScan */
40 : static void
41 45529 : spgistBuildCallback(Relation index, HeapTuple htup, Datum *values,
42 : bool *isnull, bool tupleIsAlive, void *state)
43 : {
44 45529 : SpGistBuildState *buildstate = (SpGistBuildState *) state;
45 : MemoryContext oldCtx;
46 :
47 : /* Work in temp context, and reset it after each tuple */
48 45529 : 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 91058 : while (!spgdoinsert(index, &buildstate->spgstate, &htup->t_self,
57 45529 : *values, *isnull))
58 : {
59 0 : MemoryContextReset(buildstate->tmpCtx);
60 : }
61 :
62 45529 : MemoryContextSwitchTo(oldCtx);
63 45529 : MemoryContextReset(buildstate->tmpCtx);
64 45529 : }
65 :
66 : /*
67 : * Build an SP-GiST index.
68 : */
69 : IndexBuildResult *
70 10 : spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
71 : {
72 : IndexBuildResult *result;
73 : double reltuples;
74 : SpGistBuildState buildstate;
75 : Buffer metabuffer,
76 : rootbuffer,
77 : nullbuffer;
78 :
79 10 : if (RelationGetNumberOfBlocks(index) != 0)
80 0 : elog(ERROR, "index \"%s\" already contains data",
81 : RelationGetRelationName(index));
82 :
83 : /*
84 : * Initialize the meta page and root pages
85 : */
86 10 : metabuffer = SpGistNewBuffer(index);
87 10 : rootbuffer = SpGistNewBuffer(index);
88 10 : nullbuffer = SpGistNewBuffer(index);
89 :
90 10 : Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO);
91 10 : Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
92 10 : Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
93 :
94 10 : START_CRIT_SECTION();
95 :
96 10 : SpGistInitMetapage(BufferGetPage(metabuffer));
97 10 : MarkBufferDirty(metabuffer);
98 10 : SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
99 10 : MarkBufferDirty(rootbuffer);
100 10 : SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
101 10 : MarkBufferDirty(nullbuffer);
102 :
103 10 : if (RelationNeedsWAL(index))
104 : {
105 : XLogRecPtr recptr;
106 :
107 9 : XLogBeginInsert();
108 :
109 : /*
110 : * Replay will re-initialize the pages, so don't take full pages
111 : * images. No other data to log.
112 : */
113 9 : XLogRegisterBuffer(0, metabuffer, REGBUF_WILL_INIT);
114 9 : XLogRegisterBuffer(1, rootbuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
115 9 : XLogRegisterBuffer(2, nullbuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
116 :
117 9 : recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_CREATE_INDEX);
118 :
119 9 : PageSetLSN(BufferGetPage(metabuffer), recptr);
120 9 : PageSetLSN(BufferGetPage(rootbuffer), recptr);
121 9 : PageSetLSN(BufferGetPage(nullbuffer), recptr);
122 : }
123 :
124 10 : END_CRIT_SECTION();
125 :
126 10 : UnlockReleaseBuffer(metabuffer);
127 10 : UnlockReleaseBuffer(rootbuffer);
128 10 : UnlockReleaseBuffer(nullbuffer);
129 :
130 : /*
131 : * Now insert all the heap data into the index
132 : */
133 10 : initSpGistState(&buildstate.spgstate, index);
134 10 : buildstate.spgstate.isBuild = true;
135 :
136 10 : buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
137 : "SP-GiST build temporary context",
138 : ALLOCSET_DEFAULT_SIZES);
139 :
140 10 : reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
141 : spgistBuildCallback, (void *) &buildstate);
142 :
143 10 : MemoryContextDelete(buildstate.tmpCtx);
144 :
145 10 : SpGistUpdateMetaPage(index);
146 :
147 10 : result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
148 10 : result->heap_tuples = result->index_tuples = reltuples;
149 :
150 10 : return result;
151 : }
152 :
153 : /*
154 : * Build an empty SPGiST index in the initialization fork
155 : */
156 : void
157 0 : spgbuildempty(Relation index)
158 : {
159 : Page page;
160 :
161 : /* Construct metapage. */
162 0 : page = (Page) palloc(BLCKSZ);
163 0 : SpGistInitMetapage(page);
164 :
165 : /*
166 : * Write the page and log it unconditionally. This is important
167 : * particularly for indexes created on tablespaces and databases whose
168 : * creation happened after the last redo pointer as recovery removes any
169 : * of their existing content when the corresponding create records are
170 : * replayed.
171 : */
172 0 : PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
173 0 : smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
174 : (char *) page, true);
175 0 : log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
176 : SPGIST_METAPAGE_BLKNO, page, false);
177 :
178 : /* Likewise for the root page. */
179 0 : SpGistInitPage(page, SPGIST_LEAF);
180 :
181 0 : PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
182 0 : smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO,
183 : (char *) page, true);
184 0 : log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
185 : SPGIST_ROOT_BLKNO, page, true);
186 :
187 : /* Likewise for the null-tuples root page. */
188 0 : SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
189 :
190 0 : PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
191 0 : smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO,
192 : (char *) page, true);
193 0 : log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
194 : SPGIST_NULL_BLKNO, page, true);
195 :
196 : /*
197 : * An immediate sync is required even if we xlog'd the pages, because the
198 : * writes did not go through shared buffers and therefore a concurrent
199 : * checkpoint may have moved the redo pointer past our xlog record.
200 : */
201 0 : smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
202 0 : }
203 :
204 : /*
205 : * Insert one new tuple into an SPGiST index.
206 : */
207 : bool
208 37316 : spginsert(Relation index, Datum *values, bool *isnull,
209 : ItemPointer ht_ctid, Relation heapRel,
210 : IndexUniqueCheck checkUnique,
211 : IndexInfo *indexInfo)
212 : {
213 : SpGistState spgstate;
214 : MemoryContext oldCtx;
215 : MemoryContext insertCtx;
216 :
217 37316 : insertCtx = AllocSetContextCreate(CurrentMemoryContext,
218 : "SP-GiST insert temporary context",
219 : ALLOCSET_DEFAULT_SIZES);
220 37316 : oldCtx = MemoryContextSwitchTo(insertCtx);
221 :
222 37316 : 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 74632 : while (!spgdoinsert(index, &spgstate, ht_ctid, *values, *isnull))
231 : {
232 0 : MemoryContextReset(insertCtx);
233 0 : initSpGistState(&spgstate, index);
234 : }
235 :
236 37316 : SpGistUpdateMetaPage(index);
237 :
238 37316 : MemoryContextSwitchTo(oldCtx);
239 37316 : MemoryContextDelete(insertCtx);
240 :
241 : /* return false since we've not done any unique check */
242 37316 : return false;
243 : }
|