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 = GenericXLogRegister(state, buffer, true);
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) == false)
101 /* We shouldn't be here since we're inserting to the empty page */
102 elog(ERROR, "can not add new tuple");
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 success 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));
209 if (metaData->nEnd > metaData->nStart)
213 blkno = metaData->notFullPage[metaData->nStart];
215 Assert(blkno != InvalidBlockNumber);
216 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
218 buffer = ReadBuffer(index, blkno);
219 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
220 state = GenericXLogStart(index);
221 page = GenericXLogRegister(state, buffer, false);
223 if (BloomPageAddItem(&blstate, page, itup))
225 GenericXLogFinish(state);
226 UnlockReleaseBuffer(buffer);
227 ReleaseBuffer(metaBuffer);
228 MemoryContextSwitchTo(oldCtx);
229 MemoryContextDelete(insertCtx);
234 GenericXLogAbort(state);
235 UnlockReleaseBuffer(buffer);
240 /* First page in notFullPage isn't suitable */
241 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
245 * Try other pages in notFullPage array. We will have to change nStart in
246 * metapage. Thus, grab exclusive lock on metapage.
248 LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
250 state = GenericXLogStart(index);
251 metaPage = GenericXLogRegister(state, metaBuffer, false);
252 metaData = BloomPageGetMeta(metaPage);
255 * Iterate over notFullPage array. Skip page we already tried first.
257 nStart = metaData->nStart;
258 if (metaData->nEnd > nStart &&
259 blkno == metaData->notFullPage[nStart])
262 while (metaData->nEnd > nStart)
264 blkno = metaData->notFullPage[nStart];
265 Assert(blkno != InvalidBlockNumber);
267 buffer = ReadBuffer(index, blkno);
268 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
269 page = GenericXLogRegister(state, buffer, false);
271 if (BloomPageAddItem(&blstate, page, itup))
273 metaData->nStart = nStart;
274 GenericXLogFinish(state);
275 UnlockReleaseBuffer(buffer);
276 UnlockReleaseBuffer(metaBuffer);
277 MemoryContextSwitchTo(oldCtx);
278 MemoryContextDelete(insertCtx);
283 GenericXLogUnregister(state, buffer);
284 UnlockReleaseBuffer(buffer);
289 GenericXLogAbort(state);
292 * Didn't find place to insert in notFullPage array. Allocate new page.
294 buffer = BloomNewBuffer(index);
296 state = GenericXLogStart(index);
297 metaPage = GenericXLogRegister(state, metaBuffer, false);
298 metaData = BloomPageGetMeta(metaPage);
299 page = GenericXLogRegister(state, buffer, true);
300 BloomInitPage(page, 0);
301 BloomPageAddItem(&blstate, page, itup);
303 metaData->nStart = 0;
305 metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
307 GenericXLogFinish(state);
309 UnlockReleaseBuffer(buffer);
310 UnlockReleaseBuffer(metaBuffer);