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 	int64		indtuples;		/* total number of tuples indexed */
36 	MemoryContext tmpCtx;		/* per-tuple temporary context */
37 } SpGistBuildState;
38 
39 
40 /* Callback to process one heap tuple during IndexBuildHeapScan */
41 static void
spgistBuildCallback(Relation index,HeapTuple htup,Datum * values,bool * isnull,bool tupleIsAlive,void * state)42 spgistBuildCallback(Relation index, HeapTuple htup, Datum *values,
43 					bool *isnull, bool tupleIsAlive, void *state)
44 {
45 	SpGistBuildState *buildstate = (SpGistBuildState *) state;
46 	MemoryContext oldCtx;
47 
48 	/* Work in temp context, and reset it after each tuple */
49 	oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
50 
51 	/*
52 	 * Even though no concurrent insertions can be happening, we still might
53 	 * get a buffer-locking failure due to bgwriter or checkpointer taking a
54 	 * lock on some buffer.  So we need to be willing to retry.  We can flush
55 	 * any temp data when retrying.
56 	 */
57 	while (!spgdoinsert(index, &buildstate->spgstate, &htup->t_self,
58 						*values, *isnull))
59 	{
60 		MemoryContextReset(buildstate->tmpCtx);
61 	}
62 
63 	/* Update total tuple count */
64 	buildstate->indtuples += 1;
65 
66 	MemoryContextSwitchTo(oldCtx);
67 	MemoryContextReset(buildstate->tmpCtx);
68 }
69 
70 /*
71  * Build an SP-GiST index.
72  */
73 IndexBuildResult *
spgbuild(Relation heap,Relation index,IndexInfo * indexInfo)74 spgbuild(Relation heap, Relation index, IndexInfo *indexInfo)
75 {
76 	IndexBuildResult *result;
77 	double		reltuples;
78 	SpGistBuildState buildstate;
79 	Buffer		metabuffer,
80 				rootbuffer,
81 				nullbuffer;
82 
83 	if (RelationGetNumberOfBlocks(index) != 0)
84 		elog(ERROR, "index \"%s\" already contains data",
85 			 RelationGetRelationName(index));
86 
87 	/*
88 	 * Initialize the meta page and root pages
89 	 */
90 	metabuffer = SpGistNewBuffer(index);
91 	rootbuffer = SpGistNewBuffer(index);
92 	nullbuffer = SpGistNewBuffer(index);
93 
94 	Assert(BufferGetBlockNumber(metabuffer) == SPGIST_METAPAGE_BLKNO);
95 	Assert(BufferGetBlockNumber(rootbuffer) == SPGIST_ROOT_BLKNO);
96 	Assert(BufferGetBlockNumber(nullbuffer) == SPGIST_NULL_BLKNO);
97 
98 	START_CRIT_SECTION();
99 
100 	SpGistInitMetapage(BufferGetPage(metabuffer));
101 	MarkBufferDirty(metabuffer);
102 	SpGistInitBuffer(rootbuffer, SPGIST_LEAF);
103 	MarkBufferDirty(rootbuffer);
104 	SpGistInitBuffer(nullbuffer, SPGIST_LEAF | SPGIST_NULLS);
105 	MarkBufferDirty(nullbuffer);
106 
107 	if (RelationNeedsWAL(index))
108 	{
109 		XLogRecPtr	recptr;
110 
111 		XLogBeginInsert();
112 
113 		/*
114 		 * Replay will re-initialize the pages, so don't take full pages
115 		 * images.  No other data to log.
116 		 */
117 		XLogRegisterBuffer(0, metabuffer, REGBUF_WILL_INIT);
118 		XLogRegisterBuffer(1, rootbuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
119 		XLogRegisterBuffer(2, nullbuffer, REGBUF_WILL_INIT | REGBUF_STANDARD);
120 
121 		recptr = XLogInsert(RM_SPGIST_ID, XLOG_SPGIST_CREATE_INDEX);
122 
123 		PageSetLSN(BufferGetPage(metabuffer), recptr);
124 		PageSetLSN(BufferGetPage(rootbuffer), recptr);
125 		PageSetLSN(BufferGetPage(nullbuffer), recptr);
126 	}
127 
128 	END_CRIT_SECTION();
129 
130 	UnlockReleaseBuffer(metabuffer);
131 	UnlockReleaseBuffer(rootbuffer);
132 	UnlockReleaseBuffer(nullbuffer);
133 
134 	/*
135 	 * Now insert all the heap data into the index
136 	 */
137 	initSpGistState(&buildstate.spgstate, index);
138 	buildstate.spgstate.isBuild = true;
139 	buildstate.indtuples = 0;
140 
141 	buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
142 											  "SP-GiST build temporary context",
143 											  ALLOCSET_DEFAULT_SIZES);
144 
145 	reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
146 								   spgistBuildCallback, (void *) &buildstate);
147 
148 	MemoryContextDelete(buildstate.tmpCtx);
149 
150 	SpGistUpdateMetaPage(index);
151 
152 	result = (IndexBuildResult *) palloc0(sizeof(IndexBuildResult));
153 	result->heap_tuples = reltuples;
154 	result->index_tuples = buildstate.indtuples;
155 
156 	return result;
157 }
158 
159 /*
160  * Build an empty SPGiST index in the initialization fork
161  */
162 void
spgbuildempty(Relation index)163 spgbuildempty(Relation index)
164 {
165 	Page		page;
166 
167 	/* Construct metapage. */
168 	page = (Page) palloc(BLCKSZ);
169 	SpGistInitMetapage(page);
170 
171 	/*
172 	 * Write the page and log it unconditionally.  This is important
173 	 * particularly for indexes created on tablespaces and databases whose
174 	 * creation happened after the last redo pointer as recovery removes any
175 	 * of their existing content when the corresponding create records are
176 	 * replayed.
177 	 */
178 	PageSetChecksumInplace(page, SPGIST_METAPAGE_BLKNO);
179 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_METAPAGE_BLKNO,
180 			  (char *) page, true);
181 	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
182 				SPGIST_METAPAGE_BLKNO, page, false);
183 
184 	/* Likewise for the root page. */
185 	SpGistInitPage(page, SPGIST_LEAF);
186 
187 	PageSetChecksumInplace(page, SPGIST_ROOT_BLKNO);
188 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_ROOT_BLKNO,
189 			  (char *) page, true);
190 	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
191 				SPGIST_ROOT_BLKNO, page, true);
192 
193 	/* Likewise for the null-tuples root page. */
194 	SpGistInitPage(page, SPGIST_LEAF | SPGIST_NULLS);
195 
196 	PageSetChecksumInplace(page, SPGIST_NULL_BLKNO);
197 	smgrwrite(index->rd_smgr, INIT_FORKNUM, SPGIST_NULL_BLKNO,
198 			  (char *) page, true);
199 	log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
200 				SPGIST_NULL_BLKNO, page, true);
201 
202 	/*
203 	 * An immediate sync is required even if we xlog'd the pages, because the
204 	 * writes did not go through shared buffers and therefore a concurrent
205 	 * checkpoint may have moved the redo pointer past our xlog record.
206 	 */
207 	smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
208 }
209 
210 /*
211  * Insert one new tuple into an SPGiST index.
212  */
213 bool
spginsert(Relation index,Datum * values,bool * isnull,ItemPointer ht_ctid,Relation heapRel,IndexUniqueCheck checkUnique,IndexInfo * indexInfo)214 spginsert(Relation index, Datum *values, bool *isnull,
215 		  ItemPointer ht_ctid, Relation heapRel,
216 		  IndexUniqueCheck checkUnique,
217 		  IndexInfo *indexInfo)
218 {
219 	SpGistState spgstate;
220 	MemoryContext oldCtx;
221 	MemoryContext insertCtx;
222 
223 	insertCtx = AllocSetContextCreate(CurrentMemoryContext,
224 									  "SP-GiST insert temporary context",
225 									  ALLOCSET_DEFAULT_SIZES);
226 	oldCtx = MemoryContextSwitchTo(insertCtx);
227 
228 	initSpGistState(&spgstate, index);
229 
230 	/*
231 	 * We might have to repeat spgdoinsert() multiple times, if conflicts
232 	 * occur with concurrent insertions.  If so, reset the insertCtx each time
233 	 * to avoid cumulative memory consumption.  That means we also have to
234 	 * redo initSpGistState(), but it's cheap enough not to matter.
235 	 */
236 	while (!spgdoinsert(index, &spgstate, ht_ctid, *values, *isnull))
237 	{
238 		MemoryContextReset(insertCtx);
239 		initSpGistState(&spgstate, index);
240 	}
241 
242 	SpGistUpdateMetaPage(index);
243 
244 	MemoryContextSwitchTo(oldCtx);
245 	MemoryContextDelete(insertCtx);
246 
247 	/* return false since we've not done any unique check */
248 	return false;
249 }
250