1 /*-------------------------------------------------------------------------
4 * Externally visible index creation/insertion routines
6 * All the actual insertion logic is in spgdoinsert.c.
8 * Portions Copyright (c) 1996-2014, 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/heapam_xlog.h"
21 #include "access/spgist_private.h"
22 #include "catalog/index.h"
23 #include "miscadmin.h"
24 #include "storage/bufmgr.h"
25 #include "storage/smgr.h"
26 #include "utils/memutils.h"
27 #include "utils/rel.h"
32 SpGistState spgstate; /* SPGiST's working state */
33 MemoryContext tmpCtx; /* per-tuple temporary context */
37 /* Callback to process one heap tuple during IndexBuildHeapScan */
39 spgistBuildCallback(Relation index, HeapTuple htup, Datum *values,
40 bool *isnull, bool tupleIsAlive, void *state)
42 SpGistBuildState *buildstate = (SpGistBuildState *) state;
45 /* Work in temp context, and reset it after each tuple */
46 oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
49 * Even though no concurrent insertions can be happening, we still might
50 * get a buffer-locking failure due to bgwriter or checkpointer taking a
51 * lock on some buffer. So we need to be willing to retry. We can flush
52 * any temp data when retrying.
54 while (!spgdoinsert(index, &buildstate->spgstate, &htup->t_self,
57 MemoryContextReset(buildstate->tmpCtx);
60 MemoryContextSwitchTo(oldCtx);
61 MemoryContextReset(buildstate->tmpCtx);
65 * Build an SP-GiST index.
68 spgbuild(PG_FUNCTION_ARGS)
70 Relation heap = (Relation) PG_GETARG_POINTER(0);
71 Relation index = (Relation) PG_GETARG_POINTER(1);
72 IndexInfo *indexInfo = (IndexInfo *) PG_GETARG_POINTER(2);
73 IndexBuildResult *result;
75 SpGistBuildState buildstate;
80 if (RelationGetNumberOfBlocks(index) != 0)
81 elog(ERROR, "index \"%s\" already contains data",
82 RelationGetRelationName(index));
85 * Initialize the meta page and root pages
87 metabuffer = SpGistNewBuffer(index);
88 rootbuffer = SpGistNewBuffer(index);
89 nullbuffer = SpGistNewBuffer(index);
91 Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO);
92 Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
93 Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
97 SpGistInitMetapage(BufferGetPage(metabuffer));
98 MarkBufferDirty(metabuffer);
99 SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
100 MarkBufferDirty(rootbuffer);
101 SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
102 MarkBufferDirty(nullbuffer);
104 if (RelationNeedsWAL(index))
109 /* WAL data is just the relfilenode */
110 rdata.data = (char *) &(index->rd_node);
111 rdata.len = sizeof(RelFileNode);
112 rdata.buffer = InvalidBuffer;
115 recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_CREATE_INDEX, &rdata);
117 PageSetLSN(BufferGetPage(metabuffer), recptr);
118 PageSetLSN(BufferGetPage(rootbuffer), recptr);
119 PageSetLSN(BufferGetPage(nullbuffer), recptr);
124 UnlockReleaseBuffer(metabuffer);
125 UnlockReleaseBuffer(rootbuffer);
126 UnlockReleaseBuffer(nullbuffer);
129 * Now insert all the heap data into the index
131 initSpGistState(&buildstate.spgstate, index);
132 buildstate.spgstate.isBuild = true;
134 buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
135 "SP-GiST build temporary context",
136 ALLOCSET_DEFAULT_MINSIZE,
137 ALLOCSET_DEFAULT_INITSIZE,
138 ALLOCSET_DEFAULT_MAXSIZE);
140 reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
141 spgistBuildCallback, (void *) &buildstate);
143 MemoryContextDelete(buildstate.tmpCtx);
145 SpGistUpdateMetaPage(index);
147 result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
148 result->heap_tuples = result->index_tuples = reltuples;
150 PG_RETURN_POINTER(result);
154 * Build an empty SPGiST index in the initialization fork
157 spgbuildempty(PG_FUNCTION_ARGS)
159 Relation index = (Relation) PG_GETARG_POINTER(0);
162 /* Construct metapage. */
163 page = (Page) palloc(BLCKSZ);
164 SpGistInitMetapage(page);
166 /* Write the page. If archiving/streaming, XLOG it. */
167 PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
168 smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
169 (char *) page, true);
171 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
172 SPGIST_METAPAGE_BLKNO, page, false);
174 /* Likewise for the root page. */
175 SpGistInitPage(page, SPGIST_LEAF);
177 PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
178 smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO,
179 (char *) page, true);
181 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
182 SPGIST_ROOT_BLKNO, page, true);
184 /* Likewise for the null-tuples root page. */
185 SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
187 PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
188 smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO,
189 (char *) page, true);
191 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
192 SPGIST_NULL_BLKNO, page, true);
195 * An immediate sync is required even if we xlog'd the pages, because the
196 * writes did not go through shared buffers and therefore a concurrent
197 * checkpoint may have moved the redo pointer past our xlog record.
199 smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
205 * Insert one new tuple into an SPGiST index.
208 spginsert(PG_FUNCTION_ARGS)
210 Relation index = (Relation) PG_GETARG_POINTER(0);
211 Datum *values = (Datum *) PG_GETARG_POINTER(1);
212 bool *isnull = (bool *) PG_GETARG_POINTER(2);
213 ItemPointer ht_ctid = (ItemPointer) PG_GETARG_POINTER(3);
216 Relation heapRel = (Relation) PG_GETARG_POINTER(4);
217 IndexUniqueCheck checkUnique = (IndexUniqueCheck) PG_GETARG_INT32(5);
219 SpGistState spgstate;
220 MemoryContext oldCtx;
221 MemoryContext insertCtx;
223 insertCtx = AllocSetContextCreate(CurrentMemoryContext,
224 "SP-GiST insert temporary context",
225 ALLOCSET_DEFAULT_MINSIZE,
226 ALLOCSET_DEFAULT_INITSIZE,
227 ALLOCSET_DEFAULT_MAXSIZE);
228 oldCtx = MemoryContextSwitchTo(insertCtx);
230 initSpGistState(&spgstate, index);
233 * We might have to repeat spgdoinsert() multiple times, if conflicts
234 * occur with concurrent insertions. If so, reset the insertCtx each time
235 * to avoid cumulative memory consumption. That means we also have to
236 * redo initSpGistState(), but it's cheap enough not to matter.
238 while (!spgdoinsert(index, &spgstate, ht_ctid, *values, *isnull))
240 MemoryContextReset(insertCtx);
241 initSpGistState(&spgstate, index);
244 SpGistUpdateMetaPage(index);
246 MemoryContextSwitchTo(oldCtx);
247 MemoryContextDelete(insertCtx);
249 /* return false since we've not done any unique check */
250 PG_RETURN_BOOL(false);