1 /*-------------------------------------------------------------------------
4 * Externally visible index creation/insertion routines
6 * All the actual insertion logic is in spgdoinsert.c.
8 * Portions Copyright (c) 1996-2016, 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/xlog.h"
22 #include "access/xloginsert.h"
23 #include "catalog/index.h"
24 #include "miscadmin.h"
25 #include "storage/bufmgr.h"
26 #include "storage/smgr.h"
27 #include "utils/memutils.h"
28 #include "utils/rel.h"
33 SpGistState spgstate; /* SPGiST's working state */
34 MemoryContext tmpCtx; /* per-tuple temporary context */
38 /* Callback to process one heap tuple during IndexBuildHeapScan */
40 spgistBuildCallback(Relation index, HeapTuple htup, Datum *values,
41 bool *isnull, bool tupleIsAlive, void *state)
43 SpGistBuildState *buildstate = (SpGistBuildState *) state;
46 /* Work in temp context, and reset it after each tuple */
47 oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
50 * Even though no concurrent insertions can be happening, we still might
51 * get a buffer-locking failure due to bgwriter or checkpointer taking a
52 * lock on some buffer. So we need to be willing to retry. We can flush
53 * any temp data when retrying.
55 while (!spgdoinsert(index, &buildstate->spgstate, &htup->t_self,
58 MemoryContextReset(buildstate->tmpCtx);
61 MemoryContextSwitchTo(oldCtx);
62 MemoryContextReset(buildstate->tmpCtx);
66 * Build an SP-GiST index.
69 spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
71 IndexBuildResult *result;
73 SpGistBuildState buildstate;
78 if (RelationGetNumberOfBlocks(index) != 0)
79 elog(ERROR, "index \"%s\" already contains data",
80 RelationGetRelationName(index));
83 * Initialize the meta page and root pages
85 metabuffer = SpGistNewBuffer(index);
86 rootbuffer = SpGistNewBuffer(index);
87 nullbuffer = SpGistNewBuffer(index);
89 Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO);
90 Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
91 Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
95 SpGistInitMetapage(BufferGetPage(metabuffer, NULL, NULL,
96 BGP_NO_SNAPSHOT_TEST));
97 MarkBufferDirty(metabuffer);
98 SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
99 MarkBufferDirty(rootbuffer);
100 SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
101 MarkBufferDirty(nullbuffer);
103 if (RelationNeedsWAL(index))
110 * Replay will re-initialize the pages, so don't take full pages
111 * images. No other data to log.
113 XLogRegisterBuffer(0, metabuffer, REGBUF_WILL_INIT);
114 XLogRegisterBuffer(1, rootbuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
115 XLogRegisterBuffer(2, nullbuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
117 recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_CREATE_INDEX);
119 PageSetLSN(BufferGetPage(metabuffer, NULL, NULL,
120 BGP_NO_SNAPSHOT_TEST), recptr);
121 PageSetLSN(BufferGetPage(rootbuffer, NULL, NULL,
122 BGP_NO_SNAPSHOT_TEST), recptr);
123 PageSetLSN(BufferGetPage(nullbuffer, NULL, NULL,
124 BGP_NO_SNAPSHOT_TEST), recptr);
129 UnlockReleaseBuffer(metabuffer);
130 UnlockReleaseBuffer(rootbuffer);
131 UnlockReleaseBuffer(nullbuffer);
134 * Now insert all the heap data into the index
136 initSpGistState(&buildstate.spgstate, index);
137 buildstate.spgstate.isBuild = true;
139 buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
140 "SP-GiST build temporary context",
141 ALLOCSET_DEFAULT_MINSIZE,
142 ALLOCSET_DEFAULT_INITSIZE,
143 ALLOCSET_DEFAULT_MAXSIZE);
145 reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
146 spgistBuildCallback, (void *) &buildstate);
148 MemoryContextDelete(buildstate.tmpCtx);
150 SpGistUpdateMetaPage(index);
152 result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
153 result->heap_tuples = result->index_tuples = reltuples;
159 * Build an empty SPGiST index in the initialization fork
162 spgbuildempty(Relation index)
166 /* Construct metapage. */
167 page = (Page) palloc(BLCKSZ);
168 SpGistInitMetapage(page);
170 /* Write the page. If archiving/streaming, XLOG it. */
171 PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
172 smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
173 (char *) page, true);
175 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
176 SPGIST_METAPAGE_BLKNO, page, false);
178 /* Likewise for the root page. */
179 SpGistInitPage(page, SPGIST_LEAF);
181 PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
182 smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO,
183 (char *) page, true);
185 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
186 SPGIST_ROOT_BLKNO, page, true);
188 /* Likewise for the null-tuples root page. */
189 SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
191 PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
192 smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO,
193 (char *) page, true);
195 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
196 SPGIST_NULL_BLKNO, page, true);
199 * An immediate sync is required even if we xlog'd the pages, because the
200 * writes did not go through shared buffers and therefore a concurrent
201 * checkpoint may have moved the redo pointer past our xlog record.
203 smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
207 * Insert one new tuple into an SPGiST index.
210 spginsert(Relation index, Datum *values, bool *isnull,
211 ItemPointer ht_ctid, Relation heapRel,
212 IndexUniqueCheck checkUnique)
214 SpGistState spgstate;
215 MemoryContext oldCtx;
216 MemoryContext insertCtx;
218 insertCtx = AllocSetContextCreate(CurrentMemoryContext,
219 "SP-GiST insert temporary context",
220 ALLOCSET_DEFAULT_MINSIZE,
221 ALLOCSET_DEFAULT_INITSIZE,
222 ALLOCSET_DEFAULT_MAXSIZE);
223 oldCtx = MemoryContextSwitchTo(insertCtx);
225 initSpGistState(&spgstate, index);
228 * We might have to repeat spgdoinsert() multiple times, if conflicts
229 * occur with concurrent insertions. If so, reset the insertCtx each time
230 * to avoid cumulative memory consumption. That means we also have to
231 * redo initSpGistState(), but it's cheap enough not to matter.
233 while (!spgdoinsert(index, &spgstate, ht_ctid, *values, *isnull))
235 MemoryContextReset(insertCtx);
236 initSpGistState(&spgstate, index);
239 SpGistUpdateMetaPage(index);
241 MemoryContextSwitchTo(oldCtx);
242 MemoryContextDelete(insertCtx);
244 /* return false since we've not done any unique check */