]> granicus.if.org Git - postgresql/blob - contrib/bloom/blinsert.c
Fix contrib/bloom to work for unlogged indexes.
[postgresql] / contrib / bloom / blinsert.c
1 /*-------------------------------------------------------------------------
2  *
3  * blinsert.c
4  *              Bloom index build and insert functions.
5  *
6  * Copyright (c) 2016, 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 "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"
24
25 #include "bloom.h"
26
27 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         MemoryContext tmpCtx;           /* temporary memory context reset after
37                                                                  * each tuple */
38         char            data[BLCKSZ];   /* cached page */
39         int64           count;                  /* number of tuples in cached page */
40 }       BloomBuildState;
41
42 /*
43  * Flush page cached in BloomBuildState.
44  */
45 static void
46 flushCachedPage(Relation index, BloomBuildState *buildstate)
47 {
48         Page            page;
49         Buffer          buffer = BloomNewBuffer(index);
50         GenericXLogState *state;
51
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);
57 }
58
59 /*
60  * (Re)initialize cached page in BloomBuildState.
61  */
62 static void
63 initCachedPage(BloomBuildState *buildstate)
64 {
65         memset(buildstate->data, 0, BLCKSZ);
66         BloomInitPage(buildstate->data, 0);
67         buildstate->count = 0;
68 }
69
70 /*
71  * Per-tuple callback from IndexBuildHeapScan.
72  */
73 static void
74 bloomBuildCallback(Relation index, HeapTuple htup, Datum *values,
75                                    bool *isnull, bool tupleIsAlive, void *state)
76 {
77         BloomBuildState *buildstate = (BloomBuildState *) state;
78         MemoryContext oldCtx;
79         BloomTuple *itup;
80
81         oldCtx = MemoryContextSwitchTo(buildstate->tmpCtx);
82
83         itup = BloomFormTuple(&buildstate->blstate, &htup->t_self, values, isnull);
84
85         /* Try to add next item to cached page */
86         if (BloomPageAddItem(&buildstate->blstate, buildstate->data, itup))
87         {
88                 /* Next item was added successfully */
89                 buildstate->count++;
90         }
91         else
92         {
93                 /* Cached page is full, flush it out and make a new one */
94                 flushCachedPage(index, buildstate);
95
96                 CHECK_FOR_INTERRUPTS();
97
98                 initCachedPage(buildstate);
99
100                 if (!BloomPageAddItem(&buildstate->blstate, buildstate->data, itup))
101                 {
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");
104                 }
105         }
106
107         MemoryContextSwitchTo(oldCtx);
108         MemoryContextReset(buildstate->tmpCtx);
109 }
110
111 /*
112  * Build a new bloom index.
113  */
114 IndexBuildResult *
115 blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
116 {
117         IndexBuildResult *result;
118         double          reltuples;
119         BloomBuildState buildstate;
120
121         if (RelationGetNumberOfBlocks(index) != 0)
122                 elog(ERROR, "index \"%s\" already contains data",
123                          RelationGetRelationName(index));
124
125         /* Initialize the meta page */
126         BloomInitMetapage(index);
127
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);
137
138         /* Do the heap scan */
139         reltuples = IndexBuildHeapScan(heap, index, indexInfo, true,
140                                                                    bloomBuildCallback, (void *) &buildstate);
141
142         /*
143          * There are could be some items in cached page.  Flush this page
144          * if needed.
145          */
146         if (buildstate.count > 0)
147                 flushCachedPage(index, &buildstate);
148
149         MemoryContextDelete(buildstate.tmpCtx);
150
151         result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
152         result->heap_tuples = result->index_tuples = reltuples;
153
154         return result;
155 }
156
157 /*
158  * Build an empty bloom index in the initialization fork.
159  */
160 void
161 blbuildempty(Relation index)
162 {
163         Page            metapage;
164
165         /* Construct metapage. */
166         metapage = (Page) palloc(BLCKSZ);
167         BloomFillMetapage(index, metapage);
168
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);
173         if (XLogIsNeeded())
174                 log_newpage(&index->rd_smgr->smgr_rnode.node, INIT_FORKNUM,
175                                         BLOOM_METAPAGE_BLKNO, metapage, false);
176
177         /*
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.
181          */
182         smgrimmedsync(index->rd_smgr, INIT_FORKNUM);
183 }
184
185 /*
186  * Insert new tuple to the bloom index.
187  */
188 bool
189 blinsert(Relation index, Datum *values, bool *isnull,
190                  ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique)
191 {
192         BloomState      blstate;
193         BloomTuple *itup;
194         MemoryContext oldCtx;
195         MemoryContext insertCtx;
196         BloomMetaPageData *metaData;
197         Buffer          buffer,
198                                 metaBuffer;
199         Page            page,
200                                 metaPage;
201         BlockNumber blkno = InvalidBlockNumber;
202         OffsetNumber nStart;
203         GenericXLogState *state;
204
205         insertCtx = AllocSetContextCreate(CurrentMemoryContext,
206                                                                           "Bloom insert temporary context",
207                                                                           ALLOCSET_DEFAULT_MINSIZE,
208                                                                           ALLOCSET_DEFAULT_INITSIZE,
209                                                                           ALLOCSET_DEFAULT_MAXSIZE);
210
211         oldCtx = MemoryContextSwitchTo(insertCtx);
212
213         initBloomState(&blstate, index);
214         itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
215
216         /*
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.
219          */
220         metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
221         LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
222         metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
223
224         if (metaData->nEnd > metaData->nStart)
225         {
226                 Page            page;
227
228                 blkno = metaData->notFullPage[metaData->nStart];
229                 Assert(blkno != InvalidBlockNumber);
230
231                 /* Don't hold metabuffer lock while doing insert */
232                 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
233
234                 buffer = ReadBuffer(index, blkno);
235                 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
236
237                 state = GenericXLogStart(index);
238                 page = GenericXLogRegisterBuffer(state, buffer, 0);
239
240                 if (BloomPageAddItem(&blstate, page, itup))
241                 {
242                         /* Success!  Apply the change, clean up, and exit */
243                         GenericXLogFinish(state);
244                         UnlockReleaseBuffer(buffer);
245                         ReleaseBuffer(metaBuffer);
246                         MemoryContextSwitchTo(oldCtx);
247                         MemoryContextDelete(insertCtx);
248                         return false;
249                 }
250
251                 /* Didn't fit, must try other pages */
252                 GenericXLogAbort(state);
253                 UnlockReleaseBuffer(buffer);
254         }
255         else
256         {
257                 /* No entries in notFullPage */
258                 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
259         }
260
261         /*
262          * Try other pages in notFullPage array.  We will have to change nStart in
263          * metapage.  Thus, grab exclusive lock on metapage.
264          */
265         LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
266
267         /* nStart might have changed while we didn't have lock */
268         nStart = metaData->nStart;
269
270         /* Skip first page if we already tried it above */
271         if (nStart < metaData->nEnd &&
272                 blkno == metaData->notFullPage[nStart])
273                 nStart++;
274
275         /*
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.
279          */
280         for (;;)
281         {
282                 state = GenericXLogStart(index);
283
284                 /* get modifiable copy of metapage */
285                 metaPage = GenericXLogRegisterBuffer(state, metaBuffer, 0);
286                 metaData = BloomPageGetMeta(metaPage);
287
288                 if (nStart >= metaData->nEnd)
289                         break;                          /* no more entries in notFullPage array */
290
291                 blkno = metaData->notFullPage[nStart];
292                 Assert(blkno != InvalidBlockNumber);
293
294                 buffer = ReadBuffer(index, blkno);
295                 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
296                 page = GenericXLogRegisterBuffer(state, buffer, 0);
297
298                 if (BloomPageAddItem(&blstate, page, itup))
299                 {
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);
307                         return false;
308                 }
309
310                 /* Didn't fit, must try other pages */
311                 GenericXLogAbort(state);
312                 UnlockReleaseBuffer(buffer);
313                 nStart++;
314         }
315
316         /*
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??)
319          */
320         buffer = BloomNewBuffer(index);
321
322         page = GenericXLogRegisterBuffer(state, buffer, GENERIC_XLOG_FULL_IMAGE);
323         BloomInitPage(page, 0);
324
325         if (!BloomPageAddItem(&blstate, page, itup))
326         {
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");
329         }
330
331         /* Reset notFullPage array to contain just this new page */
332         metaData->nStart = 0;
333         metaData->nEnd = 1;
334         metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
335
336         /* Apply the changes, clean up, and exit */
337         GenericXLogFinish(state);
338
339         UnlockReleaseBuffer(buffer);
340         UnlockReleaseBuffer(metaBuffer);
341
342         MemoryContextSwitchTo(oldCtx);
343         MemoryContextDelete(insertCtx);
344
345         return false;
346 }