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