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