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