]> granicus.if.org Git - postgresql/blob - contrib/bloom/blinsert.c
Bloom index contrib module
[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 = GenericXLogRegister(state, buffer, true);
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) == false)
100                 {
101                         /* We shouldn't be here since we're inserting to the empty page */
102                         elog(ERROR, "can not add new tuple");
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 success 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));
208
209         if (metaData->nEnd > metaData->nStart)
210         {
211                 Page            page;
212
213                 blkno = metaData->notFullPage[metaData->nStart];
214
215                 Assert(blkno != InvalidBlockNumber);
216                 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
217
218                 buffer = ReadBuffer(index, blkno);
219                 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
220                 state = GenericXLogStart(index);
221                 page = GenericXLogRegister(state, buffer, false);
222
223                 if (BloomPageAddItem(&blstate, page, itup))
224                 {
225                         GenericXLogFinish(state);
226                         UnlockReleaseBuffer(buffer);
227                         ReleaseBuffer(metaBuffer);
228                         MemoryContextSwitchTo(oldCtx);
229                         MemoryContextDelete(insertCtx);
230                         return false;
231                 }
232                 else
233                 {
234                         GenericXLogAbort(state);
235                         UnlockReleaseBuffer(buffer);
236                 }
237         }
238         else
239         {
240                 /* First page in notFullPage isn't suitable */
241                 LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
242         }
243
244         /*
245          * Try other pages in notFullPage array.  We will have to change nStart in
246          * metapage.  Thus, grab exclusive lock on metapage.
247          */
248         LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
249
250         state = GenericXLogStart(index);
251         metaPage = GenericXLogRegister(state, metaBuffer, false);
252         metaData = BloomPageGetMeta(metaPage);
253
254         /*
255          * Iterate over notFullPage array.  Skip page we already tried first.
256          */
257         nStart = metaData->nStart;
258         if (metaData->nEnd > nStart &&
259                 blkno == metaData->notFullPage[nStart])
260                 nStart++;
261
262         while (metaData->nEnd > nStart)
263         {
264                 blkno = metaData->notFullPage[nStart];
265                 Assert(blkno != InvalidBlockNumber);
266
267                 buffer = ReadBuffer(index, blkno);
268                 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
269                 page = GenericXLogRegister(state, buffer, false);
270
271                 if (BloomPageAddItem(&blstate, page, itup))
272                 {
273                         metaData->nStart = nStart;
274                         GenericXLogFinish(state);
275                         UnlockReleaseBuffer(buffer);
276                         UnlockReleaseBuffer(metaBuffer);
277                         MemoryContextSwitchTo(oldCtx);
278                         MemoryContextDelete(insertCtx);
279                         return false;
280                 }
281                 else
282                 {
283                         GenericXLogUnregister(state, buffer);
284                         UnlockReleaseBuffer(buffer);
285                 }
286                 nStart++;
287         }
288
289         GenericXLogAbort(state);
290
291         /*
292          * Didn't find place to insert in notFullPage array.  Allocate new page.
293          */
294         buffer = BloomNewBuffer(index);
295
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);
302
303         metaData->nStart = 0;
304         metaData->nEnd = 1;
305         metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
306
307         GenericXLogFinish(state);
308
309         UnlockReleaseBuffer(buffer);
310         UnlockReleaseBuffer(metaBuffer);
311
312         return false;
313 }