Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * blinsert.c
4 : : * Bloom index build and insert functions.
5 : : *
6 : : * Copyright (c) 2016-2025, PostgreSQL Global Development Group
7 : : *
8 : : * IDENTIFICATION
9 : : * contrib/bloom/blinsert.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : : #include "postgres.h"
14 : :
15 : : #include "access/genam.h"
16 : : #include "access/generic_xlog.h"
17 : : #include "access/tableam.h"
18 : : #include "bloom.h"
19 : : #include "miscadmin.h"
20 : : #include "nodes/execnodes.h"
21 : : #include "storage/bufmgr.h"
22 : : #include "utils/memutils.h"
23 : : #include "utils/rel.h"
24 : :
164 tgl@sss.pgh.pa.us 25 :CBC 126 : PG_MODULE_MAGIC_EXT(
26 : : .name = "bloom",
27 : : .version = PG_VERSION
28 : : );
29 : :
30 : : /*
31 : : * State of bloom index build. We accumulate one page data here before
32 : : * flushing it to buffer manager.
33 : : */
34 : : typedef struct
35 : : {
36 : : BloomState blstate; /* bloom index state */
37 : : int64 indtuples; /* total number of tuples indexed */
38 : : MemoryContext tmpCtx; /* temporary memory context reset after each
39 : : * tuple */
40 : : PGAlignedBlock data; /* cached page */
41 : : int count; /* number of tuples in cached page */
42 : : } BloomBuildState;
43 : :
44 : : /*
45 : : * Flush page cached in BloomBuildState.
46 : : */
47 : : static void
3445 teodor@sigaev.ru 48 : 36 : flushCachedPage(Relation index, BloomBuildState *buildstate)
49 : : {
50 : : Page page;
51 : 36 : Buffer buffer = BloomNewBuffer(index);
52 : : GenericXLogState *state;
53 : :
54 : 36 : state = GenericXLogStart(index);
3434 tgl@sss.pgh.pa.us 55 : 36 : page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
2562 56 : 36 : memcpy(page, buildstate->data.data, BLCKSZ);
3445 teodor@sigaev.ru 57 : 36 : GenericXLogFinish(state);
58 : 36 : UnlockReleaseBuffer(buffer);
59 : 36 : }
60 : :
61 : : /*
62 : : * (Re)initialize cached page in BloomBuildState.
63 : : */
64 : : static void
65 : 36 : initCachedPage(BloomBuildState *buildstate)
66 : : {
2562 tgl@sss.pgh.pa.us 67 : 36 : BloomInitPage(buildstate->data.data, 0);
3445 teodor@sigaev.ru 68 : 36 : buildstate->count = 0;
69 : 36 : }
70 : :
71 : : /*
72 : : * Per-tuple callback for table_index_build_scan.
73 : : */
74 : : static void
2129 andres@anarazel.de 75 : 18750 : bloomBuildCallback(Relation index, ItemPointer tid, Datum *values,
76 : : bool *isnull, bool tupleIsAlive, void *state)
77 : : {
3445 teodor@sigaev.ru 78 : 18750 : BloomBuildState *buildstate = (BloomBuildState *) state;
79 : : MemoryContext oldCtx;
80 : : BloomTuple *itup;
81 : :
82 : 18750 : oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
83 : :
2129 andres@anarazel.de 84 : 18750 : itup = BloomFormTuple(&buildstate->blstate, tid, values, isnull);
85 : :
86 : : /* Try to add next item to cached page */
2562 tgl@sss.pgh.pa.us 87 [ + + ]: 18750 : if (BloomPageAddItem(&buildstate->blstate, buildstate->data.data, itup))
88 : : {
89 : : /* Next item was added successfully */
3445 teodor@sigaev.ru 90 : 18719 : buildstate->count++;
91 : : }
92 : : else
93 : : {
94 : : /* Cached page is full, flush it out and make a new one */
95 : 31 : flushCachedPage(index, buildstate);
96 : :
97 [ - + ]: 31 : CHECK_FOR_INTERRUPTS();
98 : :
99 : 31 : initCachedPage(buildstate);
100 : :
2562 tgl@sss.pgh.pa.us 101 [ - + ]: 31 : if (!BloomPageAddItem(&buildstate->blstate, buildstate->data.data, itup))
102 : : {
103 : : /* We shouldn't be here since we're inserting to the empty page */
3443 tgl@sss.pgh.pa.us 104 [ # # ]:UBC 0 : elog(ERROR, "could not add new bloom tuple to empty page");
105 : : }
106 : :
107 : : /* Next item was added successfully */
2725 tgl@sss.pgh.pa.us 108 :CBC 31 : buildstate->count++;
109 : : }
110 : :
111 : : /* Update total tuple count */
112 : 18750 : buildstate->indtuples += 1;
113 : :
3445 teodor@sigaev.ru 114 : 18750 : MemoryContextSwitchTo(oldCtx);
115 : 18750 : MemoryContextReset(buildstate->tmpCtx);
116 : 18750 : }
117 : :
118 : : /*
119 : : * Build a new bloom index.
120 : : */
121 : : IndexBuildResult *
122 : 5 : blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
123 : : {
124 : : IndexBuildResult *result;
125 : : double reltuples;
126 : : BloomBuildState buildstate;
127 : :
128 [ - + ]: 5 : if (RelationGetNumberOfBlocks(index) != 0)
3445 teodor@sigaev.ru 129 [ # # ]:UBC 0 : elog(ERROR, "index \"%s\" already contains data",
130 : : RelationGetRelationName(index));
131 : :
132 : : /* Initialize the meta page */
745 heikki.linnakangas@i 133 :CBC 5 : BloomInitMetapage(index, MAIN_FORKNUM);
134 : :
135 : : /* Initialize the bloom build state */
3445 teodor@sigaev.ru 136 : 5 : memset(&buildstate, 0, sizeof(buildstate));
137 : 5 : initBloomState(&buildstate.blstate, index);
138 : 5 : buildstate.tmpCtx = AllocSetContextCreate(CurrentMemoryContext,
139 : : "Bloom build temporary context",
140 : : ALLOCSET_DEFAULT_SIZES);
141 : 5 : initCachedPage(&buildstate);
142 : :
143 : : /* Do the heap scan */
2349 alvherre@alvh.no-ip. 144 : 5 : reltuples = table_index_build_scan(heap, index, indexInfo, true, true,
145 : : bloomBuildCallback, &buildstate,
146 : : NULL);
147 : :
148 : : /* Flush last page if needed (it will be, unless heap was empty) */
3445 teodor@sigaev.ru 149 [ + - ]: 5 : if (buildstate.count > 0)
150 : 5 : flushCachedPage(index, &buildstate);
151 : :
152 : 5 : MemoryContextDelete(buildstate.tmpCtx);
153 : :
154 : 5 : result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
2725 tgl@sss.pgh.pa.us 155 : 5 : result->heap_tuples = reltuples;
156 : 5 : result->index_tuples = buildstate.indtuples;
157 : :
3445 teodor@sigaev.ru 158 : 5 : return result;
159 : : }
160 : :
161 : : /*
162 : : * Build an empty bloom index in the initialization fork.
163 : : */
164 : : void
165 : 1 : blbuildempty(Relation index)
166 : : {
167 : : /* Initialize the meta page */
745 heikki.linnakangas@i 168 : 1 : BloomInitMetapage(index, INIT_FORKNUM);
3445 teodor@sigaev.ru 169 : 1 : }
170 : :
171 : : /*
172 : : * Insert new tuple to the bloom index.
173 : : */
174 : : bool
175 : 104000 : blinsert(Relation index, Datum *values, bool *isnull,
176 : : ItemPointer ht_ctid, Relation heapRel,
177 : : IndexUniqueCheck checkUnique,
178 : : bool indexUnchanged,
179 : : IndexInfo *indexInfo)
180 : : {
181 : : BloomState blstate;
182 : : BloomTuple *itup;
183 : : MemoryContext oldCtx;
184 : : MemoryContext insertCtx;
185 : : BloomMetaPageData *metaData;
186 : : Buffer buffer,
187 : : metaBuffer;
188 : : Page page,
189 : : metaPage;
190 : 104000 : BlockNumber blkno = InvalidBlockNumber;
191 : : OffsetNumber nStart;
192 : : GenericXLogState *state;
193 : :
194 : 104000 : insertCtx = AllocSetContextCreate(CurrentMemoryContext,
195 : : "Bloom insert temporary context",
196 : : ALLOCSET_DEFAULT_SIZES);
197 : :
198 : 104000 : oldCtx = MemoryContextSwitchTo(insertCtx);
199 : :
200 : 104000 : initBloomState(&blstate, index);
201 : 104000 : itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
202 : :
203 : : /*
204 : : * At first, try to insert new tuple to the first page in notFullPage
205 : : * array. If successful, we don't need to modify the meta page.
206 : : */
207 : 104000 : metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
208 : 104000 : LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
3426 kgrittn@postgresql.o 209 : 104000 : metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
210 : :
3445 teodor@sigaev.ru 211 [ + + ]: 104000 : if (metaData->nEnd > metaData->nStart)
212 : : {
213 : 103999 : blkno = metaData->notFullPage[metaData->nStart];
214 [ - + ]: 103999 : Assert(blkno != InvalidBlockNumber);
215 : :
216 : : /* Don't hold metabuffer lock while doing insert */
217 : 103999 : LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
218 : :
219 : 103999 : buffer = ReadBuffer(index, blkno);
220 : 103999 : LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
221 : :
222 : 103999 : state = GenericXLogStart(index);
3434 tgl@sss.pgh.pa.us 223 : 103999 : page = GenericXLogRegisterBuffer(state, buffer, 0);
224 : :
225 : : /*
226 : : * We might have found a page that was recently deleted by VACUUM. If
227 : : * so, we can reuse it, but we must reinitialize it.
228 : : */
3311 229 [ + - - + ]: 103999 : if (PageIsNew(page) || BloomPageIsDeleted(page))
3311 tgl@sss.pgh.pa.us 230 :UBC 0 : BloomInitPage(page, 0);
231 : :
3445 teodor@sigaev.ru 232 [ + + ]:CBC 103999 : if (BloomPageAddItem(&blstate, page, itup))
233 : : {
234 : : /* Success! Apply the change, clean up, and exit */
235 : 103242 : GenericXLogFinish(state);
236 : 103242 : UnlockReleaseBuffer(buffer);
237 : 103242 : ReleaseBuffer(metaBuffer);
238 : 103242 : MemoryContextSwitchTo(oldCtx);
239 : 103242 : MemoryContextDelete(insertCtx);
240 : 103242 : return false;
241 : : }
242 : :
243 : : /* Didn't fit, must try other pages */
3437 tgl@sss.pgh.pa.us 244 : 757 : GenericXLogAbort(state);
245 : 757 : UnlockReleaseBuffer(buffer);
246 : : }
247 : : else
248 : : {
249 : : /* No entries in notFullPage */
3445 teodor@sigaev.ru 250 : 1 : LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
251 : : }
252 : :
253 : : /*
254 : : * Try other pages in notFullPage array. We will have to change nStart in
255 : : * metapage. Thus, grab exclusive lock on metapage.
256 : : */
257 : 758 : LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
258 : :
259 : : /* nStart might have changed while we didn't have lock */
260 : 758 : nStart = metaData->nStart;
261 : :
262 : : /* Skip first page if we already tried it above */
3437 tgl@sss.pgh.pa.us 263 [ + + ]: 758 : if (nStart < metaData->nEnd &&
3445 teodor@sigaev.ru 264 [ + - ]: 757 : blkno == metaData->notFullPage[nStart])
265 : 757 : nStart++;
266 : :
267 : : /*
268 : : * This loop iterates for each page we try from the notFullPage array, and
269 : : * will also initialize a GenericXLogState for the fallback case of having
270 : : * to allocate a new page.
271 : : */
272 : : for (;;)
273 : : {
3437 tgl@sss.pgh.pa.us 274 : 758 : state = GenericXLogStart(index);
275 : :
276 : : /* get modifiable copy of metapage */
3434 277 : 758 : metaPage = GenericXLogRegisterBuffer(state, metaBuffer, 0);
3437 278 : 758 : metaData = BloomPageGetMeta(metaPage);
279 : :
280 [ + + ]: 758 : if (nStart >= metaData->nEnd)
281 : 113 : break; /* no more entries in notFullPage array */
282 : :
3445 teodor@sigaev.ru 283 : 645 : blkno = metaData->notFullPage[nStart];
284 [ - + ]: 645 : Assert(blkno != InvalidBlockNumber);
285 : :
286 : 645 : buffer = ReadBuffer(index, blkno);
287 : 645 : LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
3434 tgl@sss.pgh.pa.us 288 : 645 : page = GenericXLogRegisterBuffer(state, buffer, 0);
289 : :
290 : : /* Basically same logic as above */
3311 291 [ + - - + ]: 645 : if (PageIsNew(page) || BloomPageIsDeleted(page))
3311 tgl@sss.pgh.pa.us 292 :UBC 0 : BloomInitPage(page, 0);
293 : :
3445 teodor@sigaev.ru 294 [ + - ]:CBC 645 : if (BloomPageAddItem(&blstate, page, itup))
295 : : {
296 : : /* Success! Apply the changes, clean up, and exit */
297 : 645 : metaData->nStart = nStart;
298 : 645 : GenericXLogFinish(state);
299 : 645 : UnlockReleaseBuffer(buffer);
300 : 645 : UnlockReleaseBuffer(metaBuffer);
301 : 645 : MemoryContextSwitchTo(oldCtx);
302 : 645 : MemoryContextDelete(insertCtx);
303 : 645 : return false;
304 : : }
305 : :
306 : : /* Didn't fit, must try other pages */
3437 tgl@sss.pgh.pa.us 307 :UBC 0 : GenericXLogAbort(state);
308 : 0 : UnlockReleaseBuffer(buffer);
3445 teodor@sigaev.ru 309 : 0 : nStart++;
310 : : }
311 : :
312 : : /*
313 : : * Didn't find place to insert in notFullPage array. Allocate new page.
314 : : * (XXX is it good to do this while holding ex-lock on the metapage??)
315 : : */
3445 teodor@sigaev.ru 316 :CBC 113 : buffer = BloomNewBuffer(index);
317 : :
3434 tgl@sss.pgh.pa.us 318 : 113 : page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
3445 teodor@sigaev.ru 319 : 113 : BloomInitPage(page, 0);
320 : :
3443 tgl@sss.pgh.pa.us 321 [ - + ]: 113 : if (!BloomPageAddItem(&blstate, page, itup))
322 : : {
323 : : /* We shouldn't be here since we're inserting to an empty page */
3443 tgl@sss.pgh.pa.us 324 [ # # ]:UBC 0 : elog(ERROR, "could not add new bloom tuple to empty page");
325 : : }
326 : :
327 : : /* Reset notFullPage array to contain just this new page */
3445 teodor@sigaev.ru 328 :CBC 113 : metaData->nStart = 0;
329 : 113 : metaData->nEnd = 1;
330 : 113 : metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
331 : :
332 : : /* Apply the changes, clean up, and exit */
333 : 113 : GenericXLogFinish(state);
334 : :
335 : 113 : UnlockReleaseBuffer(buffer);
336 : 113 : UnlockReleaseBuffer(metaBuffer);
337 : :
3437 tgl@sss.pgh.pa.us 338 : 113 : MemoryContextSwitchTo(oldCtx);
339 : 113 : MemoryContextDelete(insertCtx);
340 : :
3445 teodor@sigaev.ru 341 : 113 : return false;
342 : : }
|