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 each
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_SIZES);
134 initCachedPage(&buildstate);
136 /* Do the heap scan */
137 reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
138 bloomBuildCallback, (void *) &buildstate);
141 * There are could be some items in cached page. Flush this page if
144 if (buildstate.count > 0)
145 flushCachedPage(index, &buildstate);
147 MemoryContextDelete(buildstate.tmpCtx);
149 result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
150 result->heap_tuples = result->index_tuples = reltuples;
156 * Build an empty bloom index in the initialization fork.
159 blbuildempty(Relation index)
163 /* Construct metapage. */
164 metapage = (Page) palloc(BLCKSZ);
165 BloomFillMetapage(index, metapage);
167 /* Write the page. If archiving/streaming, XLOG it. */
168 PageSetChecksumInplace(metapage, BLOOM_METAPAGE_BLKNO);
169 smgrwrite(index->rd_smgr, INIT_FORKNUM, BLOOM_METAPAGE_BLKNO,
170 (char *) metapage, true);
172 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
173 BLOOM_METAPAGE_BLKNO, metapage, false);
176 * An immediate sync is required even if we xlog'd the page, because the
177 * write did not go through shared_buffers and therefore a concurrent
178 * checkpoint may have moved the redo pointer past our xlog record.
180 smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
184 * Insert new tuple to the bloom index.
187 blinsert(Relation index, Datum *values, bool *isnull,
188 ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
192 MemoryContext oldCtx;
193 MemoryContext insertCtx;
194 BloomMetaPageData *metaData;
199 BlockNumber blkno = InvalidBlockNumber;
201 GenericXLogState *state;
203 insertCtx = AllocSetContextCreate(CurrentMemoryContext,
204 "Bloom insert temporary context",
205 ALLOCSET_DEFAULT_SIZES);
207 oldCtx = MemoryContextSwitchTo(insertCtx);
209 initBloomState(&blstate, index);
210 itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
213 * At first, try to insert new tuple to the first page in notFullPage
214 * array. If successful, we don't need to modify the meta page.
216 metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
217 LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
218 metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
220 if (metaData->nEnd > metaData->nStart)
224 blkno = metaData->notFullPage[metaData->nStart];
225 Assert(blkno != InvalidBlockNumber);
227 /* Don't hold metabuffer lock while doing insert */
228 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
230 buffer = ReadBuffer(index, blkno);
231 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
233 state = GenericXLogStart(index);
234 page = GenericXLogRegisterBuffer(state, buffer, 0);
237 * We might have found a page that was recently deleted by VACUUM. If
238 * so, we can reuse it, but we must reinitialize it.
240 if (PageIsNew(page) || BloomPageIsDeleted(page))
241 BloomInitPage(page, 0);
243 if (BloomPageAddItem(&blstate, page, itup))
245 /* Success! Apply the change, clean up, and exit */
246 GenericXLogFinish(state);
247 UnlockReleaseBuffer(buffer);
248 ReleaseBuffer(metaBuffer);
249 MemoryContextSwitchTo(oldCtx);
250 MemoryContextDelete(insertCtx);
254 /* Didn't fit, must try other pages */
255 GenericXLogAbort(state);
256 UnlockReleaseBuffer(buffer);
260 /* No entries in notFullPage */
261 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
265 * Try other pages in notFullPage array. We will have to change nStart in
266 * metapage. Thus, grab exclusive lock on metapage.
268 LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
270 /* nStart might have changed while we didn't have lock */
271 nStart = metaData->nStart;
273 /* Skip first page if we already tried it above */
274 if (nStart < metaData->nEnd &&
275 blkno == metaData->notFullPage[nStart])
279 * This loop iterates for each page we try from the notFullPage array, and
280 * will also initialize a GenericXLogState for the fallback case of having
281 * to allocate a new page.
285 state = GenericXLogStart(index);
287 /* get modifiable copy of metapage */
288 metaPage = GenericXLogRegisterBuffer(state, metaBuffer, 0);
289 metaData = BloomPageGetMeta(metaPage);
291 if (nStart >= metaData->nEnd)
292 break; /* no more entries in notFullPage array */
294 blkno = metaData->notFullPage[nStart];
295 Assert(blkno != InvalidBlockNumber);
297 buffer = ReadBuffer(index, blkno);
298 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
299 page = GenericXLogRegisterBuffer(state, buffer, 0);
301 /* Basically same logic as above */
302 if (PageIsNew(page) || BloomPageIsDeleted(page))
303 BloomInitPage(page, 0);
305 if (BloomPageAddItem(&blstate, page, itup))
307 /* Success! Apply the changes, clean up, and exit */
308 metaData->nStart = nStart;
309 GenericXLogFinish(state);
310 UnlockReleaseBuffer(buffer);
311 UnlockReleaseBuffer(metaBuffer);
312 MemoryContextSwitchTo(oldCtx);
313 MemoryContextDelete(insertCtx);
317 /* Didn't fit, must try other pages */
318 GenericXLogAbort(state);
319 UnlockReleaseBuffer(buffer);
324 * Didn't find place to insert in notFullPage array. Allocate new page.
325 * (XXX is it good to do this while holding ex-lock on the metapage??)
327 buffer = BloomNewBuffer(index);
329 page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
330 BloomInitPage(page, 0);
332 if (!BloomPageAddItem(&blstate, page, itup))
334 /* We shouldn't be here since we're inserting to an empty page */
335 elog(ERROR, "could not add new bloom tuple to empty page");
338 /* Reset notFullPage array to contain just this new page */
339 metaData->nStart = 0;
341 metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
343 /* Apply the changes, clean up, and exit */
344 GenericXLogFinish(state);
346 UnlockReleaseBuffer(buffer);
347 UnlockReleaseBuffer(metaBuffer);
349 MemoryContextSwitchTo(oldCtx);
350 MemoryContextDelete(insertCtx);