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