1 /*-------------------------------------------------------------------------
4 * Bloom index build and insert functions.
6 * Copyright (c) 2016, PostgreSQL Global Development Group
9 * contrib/bloom/blinsert.c
11 *-------------------------------------------------------------------------
15 #include "access/genam.h"
16 #include "access/generic_xlog.h"
17 #include "catalog/index.h"
18 #include "miscadmin.h"
19 #include "storage/bufmgr.h"
20 #include "storage/indexfsm.h"
21 #include "utils/memutils.h"
22 #include "utils/rel.h"
29 * State of bloom index build. We accumulate one page data here before
30 * flushing it to buffer manager.
34 BloomState blstate; /* bloom index state */
35 MemoryContext tmpCtx; /* temporary memory context reset after
37 char data[BLCKSZ]; /* cached page */
38 int64 count; /* number of tuples in cached page */
42 * Flush page cached in BloomBuildState.
45 flushCachedPage(Relation index, BloomBuildState *buildstate)
48 Buffer buffer = BloomNewBuffer(index);
49 GenericXLogState *state;
51 state = GenericXLogStart(index);
52 page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
53 memcpy(page, buildstate->data, BLCKSZ);
54 GenericXLogFinish(state);
55 UnlockReleaseBuffer(buffer);
59 * (Re)initialize cached page in BloomBuildState.
62 initCachedPage(BloomBuildState *buildstate)
64 memset(buildstate->data, 0, BLCKSZ);
65 BloomInitPage(buildstate->data, 0);
66 buildstate->count = 0;
70 * Per-tuple callback from IndexBuildHeapScan.
73 bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
74 bool *isnull, bool tupleIsAlive, void *state)
76 BloomBuildState *buildstate = (BloomBuildState *) state;
80 oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
82 itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
84 /* Try to add next item to cached page */
85 if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup))
87 /* Next item was added successfully */
92 /* Cached page is full, flush it out and make a new one */
93 flushCachedPage(index, buildstate);
95 CHECK_FOR_INTERRUPTS();
97 initCachedPage(buildstate);
99 if (!BloomPageAddItem(&buildstate->blstate, buildstate->data, itup))
101 /* We shouldn't be here since we're inserting to the empty page */
102 elog(ERROR, "could not add new bloom tuple to empty page");
106 MemoryContextSwitchTo(oldCtx);
107 MemoryContextReset(buildstate->tmpCtx);
111 * Build a new bloom index.
114 blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
116 IndexBuildResult *result;
118 BloomBuildState buildstate;
120 if (RelationGetNumberOfBlocks(index) != 0)
121 elog(ERROR, "index \"%s\" already contains data",
122 RelationGetRelationName(index));
124 /* Initialize the meta page */
125 BloomInitMetapage(index);
127 /* Initialize the bloom build state */
128 memset(&buildstate, 0, sizeof(buildstate));
129 initBloomState(&buildstate.blstate, index);
130 buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
131 "Bloom build temporary context",
132 ALLOCSET_DEFAULT_MINSIZE,
133 ALLOCSET_DEFAULT_INITSIZE,
134 ALLOCSET_DEFAULT_MAXSIZE);
135 initCachedPage(&buildstate);
137 /* Do the heap scan */
138 reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
139 bloomBuildCallback, (void *) &buildstate);
142 * There are could be some items in cached page. Flush this page
145 if (buildstate.count > 0)
146 flushCachedPage(index, &buildstate);
148 MemoryContextDelete(buildstate.tmpCtx);
150 result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
151 result->heap_tuples = result->index_tuples = reltuples;
157 * Build an empty bloom index in the initialization fork.
160 blbuildempty(Relation index)
162 if (RelationGetNumberOfBlocks(index) != 0)
163 elog(ERROR, "index \"%s\" already contains data",
164 RelationGetRelationName(index));
166 /* Initialize the meta page */
167 BloomInitMetapage(index);
171 * Insert new tuple to the bloom index.
174 blinsert(Relation index, Datum *values, bool *isnull,
175 ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
179 MemoryContext oldCtx;
180 MemoryContext insertCtx;
181 BloomMetaPageData *metaData;
186 BlockNumber blkno = InvalidBlockNumber;
188 GenericXLogState *state;
190 insertCtx = AllocSetContextCreate(CurrentMemoryContext,
191 "Bloom insert temporary context",
192 ALLOCSET_DEFAULT_MINSIZE,
193 ALLOCSET_DEFAULT_INITSIZE,
194 ALLOCSET_DEFAULT_MAXSIZE);
196 oldCtx = MemoryContextSwitchTo(insertCtx);
198 initBloomState(&blstate, index);
199 itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
202 * At first, try to insert new tuple to the first page in notFullPage
203 * array. If successful, we don't need to modify the meta page.
205 metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
206 LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
207 metaData = BloomPageGetMeta(BufferGetPage(metaBuffer, NULL, NULL,
208 BGP_NO_SNAPSHOT_TEST));
210 if (metaData->nEnd > metaData->nStart)
214 blkno = metaData->notFullPage[metaData->nStart];
215 Assert(blkno != InvalidBlockNumber);
217 /* Don't hold metabuffer lock while doing insert */
218 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
220 buffer = ReadBuffer(index, blkno);
221 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
223 state = GenericXLogStart(index);
224 page = GenericXLogRegisterBuffer(state, buffer, 0);
226 if (BloomPageAddItem(&blstate, page, itup))
228 /* Success! Apply the change, clean up, and exit */
229 GenericXLogFinish(state);
230 UnlockReleaseBuffer(buffer);
231 ReleaseBuffer(metaBuffer);
232 MemoryContextSwitchTo(oldCtx);
233 MemoryContextDelete(insertCtx);
237 /* Didn't fit, must try other pages */
238 GenericXLogAbort(state);
239 UnlockReleaseBuffer(buffer);
243 /* No entries in notFullPage */
244 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
248 * Try other pages in notFullPage array. We will have to change nStart in
249 * metapage. Thus, grab exclusive lock on metapage.
251 LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
253 /* nStart might have changed while we didn't have lock */
254 nStart = metaData->nStart;
256 /* Skip first page if we already tried it above */
257 if (nStart < metaData->nEnd &&
258 blkno == metaData->notFullPage[nStart])
262 * This loop iterates for each page we try from the notFullPage array, and
263 * will also initialize a GenericXLogState for the fallback case of having
264 * to allocate a new page.
268 state = GenericXLogStart(index);
270 /* get modifiable copy of metapage */
271 metaPage = GenericXLogRegisterBuffer(state, metaBuffer, 0);
272 metaData = BloomPageGetMeta(metaPage);
274 if (nStart >= metaData->nEnd)
275 break; /* no more entries in notFullPage array */
277 blkno = metaData->notFullPage[nStart];
278 Assert(blkno != InvalidBlockNumber);
280 buffer = ReadBuffer(index, blkno);
281 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
282 page = GenericXLogRegisterBuffer(state, buffer, 0);
284 if (BloomPageAddItem(&blstate, page, itup))
286 /* Success! Apply the changes, clean up, and exit */
287 metaData->nStart = nStart;
288 GenericXLogFinish(state);
289 UnlockReleaseBuffer(buffer);
290 UnlockReleaseBuffer(metaBuffer);
291 MemoryContextSwitchTo(oldCtx);
292 MemoryContextDelete(insertCtx);
296 /* Didn't fit, must try other pages */
297 GenericXLogAbort(state);
298 UnlockReleaseBuffer(buffer);
303 * Didn't find place to insert in notFullPage array. Allocate new page.
304 * (XXX is it good to do this while holding ex-lock on the metapage??)
306 buffer = BloomNewBuffer(index);
308 page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
309 BloomInitPage(page, 0);
311 if (!BloomPageAddItem(&blstate, page, itup))
313 /* We shouldn't be here since we're inserting to an empty page */
314 elog(ERROR, "could not add new bloom tuple to empty page");
317 /* Reset notFullPage array to contain just this new page */
318 metaData->nStart = 0;
320 metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
322 /* Apply the changes, clean up, and exit */
323 GenericXLogFinish(state);
325 UnlockReleaseBuffer(buffer);
326 UnlockReleaseBuffer(metaBuffer);
328 MemoryContextSwitchTo(oldCtx);
329 MemoryContextDelete(insertCtx);