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 "storage/smgr.h"
22 #include "utils/memutils.h"
23 #include "utils/rel.h"
30 * State of bloom index build. We accumulate one page data here before
31 * flushing it to buffer manager.
35 BloomState blstate; /* bloom index state */
36 MemoryContext tmpCtx; /* temporary memory context reset after
38 char data[BLCKSZ]; /* cached page */
39 int64 count; /* number of tuples in cached page */
43 * Flush page cached in BloomBuildState.
46 flushCachedPage(Relation index, BloomBuildState *buildstate)
49 Buffer buffer = BloomNewBuffer(index);
50 GenericXLogState *state;
52 state = GenericXLogStart(index);
53 page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
54 memcpy(page, buildstate->data, BLCKSZ);
55 GenericXLogFinish(state);
56 UnlockReleaseBuffer(buffer);
60 * (Re)initialize cached page in BloomBuildState.
63 initCachedPage(BloomBuildState *buildstate)
65 memset(buildstate->data, 0, BLCKSZ);
66 BloomInitPage(buildstate->data, 0);
67 buildstate->count = 0;
71 * Per-tuple callback from IndexBuildHeapScan.
74 bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
75 bool *isnull, bool tupleIsAlive, void *state)
77 BloomBuildState *buildstate = (BloomBuildState *) state;
81 oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
83 itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
85 /* Try to add next item to cached page */
86 if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup))
88 /* Next item was added successfully */
93 /* Cached page is full, flush it out and make a new one */
94 flushCachedPage(index, buildstate);
96 CHECK_FOR_INTERRUPTS();
98 initCachedPage(buildstate);
100 if (!BloomPageAddItem(&buildstate->blstate, buildstate->data, itup))
102 /* We shouldn't be here since we're inserting to the empty page */
103 elog(ERROR, "could not add new bloom tuple to empty page");
107 MemoryContextSwitchTo(oldCtx);
108 MemoryContextReset(buildstate->tmpCtx);
112 * Build a new bloom index.
115 blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
117 IndexBuildResult *result;
119 BloomBuildState buildstate;
121 if (RelationGetNumberOfBlocks(index) != 0)
122 elog(ERROR, "index \"%s\" already contains data",
123 RelationGetRelationName(index));
125 /* Initialize the meta page */
126 BloomInitMetapage(index);
128 /* Initialize the bloom build state */
129 memset(&buildstate, 0, sizeof(buildstate));
130 initBloomState(&buildstate.blstate, index);
131 buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
132 "Bloom build temporary context",
133 ALLOCSET_DEFAULT_MINSIZE,
134 ALLOCSET_DEFAULT_INITSIZE,
135 ALLOCSET_DEFAULT_MAXSIZE);
136 initCachedPage(&buildstate);
138 /* Do the heap scan */
139 reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
140 bloomBuildCallback, (void *) &buildstate);
143 * There are could be some items in cached page. Flush this page
146 if (buildstate.count > 0)
147 flushCachedPage(index, &buildstate);
149 MemoryContextDelete(buildstate.tmpCtx);
151 result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
152 result->heap_tuples = result->index_tuples = reltuples;
158 * Build an empty bloom index in the initialization fork.
161 blbuildempty(Relation index)
165 /* Construct metapage. */
166 metapage = (Page) palloc(BLCKSZ);
167 BloomFillMetapage(index, metapage);
169 /* Write the page. If archiving/streaming, XLOG it. */
170 PageSetChecksumInplace(metapage, BLOOM_METAPAGE_BLKNO);
171 smgrwrite(index->rd_smgr, INIT_FORKNUM, BLOOM_METAPAGE_BLKNO,
172 (char *) metapage, true);
174 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
175 BLOOM_METAPAGE_BLKNO, metapage, false);
178 * An immediate sync is required even if we xlog'd the page, because the
179 * write did not go through shared_buffers and therefore a concurrent
180 * checkpoint may have moved the redo pointer past our xlog record.
182 smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
186 * Insert new tuple to the bloom index.
189 blinsert(Relation index, Datum *values, bool *isnull,
190 ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
194 MemoryContext oldCtx;
195 MemoryContext insertCtx;
196 BloomMetaPageData *metaData;
201 BlockNumber blkno = InvalidBlockNumber;
203 GenericXLogState *state;
205 insertCtx = AllocSetContextCreate(CurrentMemoryContext,
206 "Bloom insert temporary context",
207 ALLOCSET_DEFAULT_MINSIZE,
208 ALLOCSET_DEFAULT_INITSIZE,
209 ALLOCSET_DEFAULT_MAXSIZE);
211 oldCtx = MemoryContextSwitchTo(insertCtx);
213 initBloomState(&blstate, index);
214 itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
217 * At first, try to insert new tuple to the first page in notFullPage
218 * array. If successful, we don't need to modify the meta page.
220 metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
221 LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
222 metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
224 if (metaData->nEnd > metaData->nStart)
228 blkno = metaData->notFullPage[metaData->nStart];
229 Assert(blkno != InvalidBlockNumber);
231 /* Don't hold metabuffer lock while doing insert */
232 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
234 buffer = ReadBuffer(index, blkno);
235 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
237 state = GenericXLogStart(index);
238 page = GenericXLogRegisterBuffer(state, buffer, 0);
240 if (BloomPageAddItem(&blstate, page, itup))
242 /* Success! Apply the change, clean up, and exit */
243 GenericXLogFinish(state);
244 UnlockReleaseBuffer(buffer);
245 ReleaseBuffer(metaBuffer);
246 MemoryContextSwitchTo(oldCtx);
247 MemoryContextDelete(insertCtx);
251 /* Didn't fit, must try other pages */
252 GenericXLogAbort(state);
253 UnlockReleaseBuffer(buffer);
257 /* No entries in notFullPage */
258 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
262 * Try other pages in notFullPage array. We will have to change nStart in
263 * metapage. Thus, grab exclusive lock on metapage.
265 LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
267 /* nStart might have changed while we didn't have lock */
268 nStart = metaData->nStart;
270 /* Skip first page if we already tried it above */
271 if (nStart < metaData->nEnd &&
272 blkno == metaData->notFullPage[nStart])
276 * This loop iterates for each page we try from the notFullPage array, and
277 * will also initialize a GenericXLogState for the fallback case of having
278 * to allocate a new page.
282 state = GenericXLogStart(index);
284 /* get modifiable copy of metapage */
285 metaPage = GenericXLogRegisterBuffer(state, metaBuffer, 0);
286 metaData = BloomPageGetMeta(metaPage);
288 if (nStart >= metaData->nEnd)
289 break; /* no more entries in notFullPage array */
291 blkno = metaData->notFullPage[nStart];
292 Assert(blkno != InvalidBlockNumber);
294 buffer = ReadBuffer(index, blkno);
295 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
296 page = GenericXLogRegisterBuffer(state, buffer, 0);
298 if (BloomPageAddItem(&blstate, page, itup))
300 /* Success! Apply the changes, clean up, and exit */
301 metaData->nStart = nStart;
302 GenericXLogFinish(state);
303 UnlockReleaseBuffer(buffer);
304 UnlockReleaseBuffer(metaBuffer);
305 MemoryContextSwitchTo(oldCtx);
306 MemoryContextDelete(insertCtx);
310 /* Didn't fit, must try other pages */
311 GenericXLogAbort(state);
312 UnlockReleaseBuffer(buffer);
317 * Didn't find place to insert in notFullPage array. Allocate new page.
318 * (XXX is it good to do this while holding ex-lock on the metapage??)
320 buffer = BloomNewBuffer(index);
322 page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
323 BloomInitPage(page, 0);
325 if (!BloomPageAddItem(&blstate, page, itup))
327 /* We shouldn't be here since we're inserting to an empty page */
328 elog(ERROR, "could not add new bloom tuple to empty page");
331 /* Reset notFullPage array to contain just this new page */
332 metaData->nStart = 0;
334 metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
336 /* Apply the changes, clean up, and exit */
337 GenericXLogFinish(state);
339 UnlockReleaseBuffer(buffer);
340 UnlockReleaseBuffer(metaBuffer);
342 MemoryContextSwitchTo(oldCtx);
343 MemoryContextDelete(insertCtx);