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-2023, 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 
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 */
42 
43 /*
44  * Flush page cached in BloomBuildState.
45  */
46 static void
48 {
49  Page page;
50  Buffer buffer = BloomNewBuffer(index);
52 
55  memcpy(page, buildstate->data.data, BLCKSZ);
57  UnlockReleaseBuffer(buffer);
58 }
59 
60 /*
61  * (Re)initialize cached page in BloomBuildState.
62  */
63 static void
65 {
66  BloomInitPage(buildstate->data.data, 0);
67  buildstate->count = 0;
68 }
69 
70 /*
71  * Per-tuple callback for table_index_build_scan.
72  */
73 static void
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, tid, values, isnull);
84 
85  /* Try to add next item to cached page */
86  if (BloomPageAddItem(&buildstate->blstate, buildstate->data.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 
97 
98  initCachedPage(buildstate);
99 
100  if (!BloomPageAddItem(&buildstate->blstate, buildstate->data.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  /* Next item was added successfully */
107  buildstate->count++;
108  }
109 
110  /* Update total tuple count */
111  buildstate->indtuples += 1;
112 
113  MemoryContextSwitchTo(oldCtx);
114  MemoryContextReset(buildstate->tmpCtx);
115 }
116 
117 /*
118  * Build a new bloom index.
119  */
122 {
123  IndexBuildResult *result;
124  double reltuples;
125  BloomBuildState buildstate;
126 
128  elog(ERROR, "index \"%s\" already contains data",
130 
131  /* Initialize the meta page */
133 
134  /* Initialize the bloom build state */
135  memset(&buildstate, 0, sizeof(buildstate));
136  initBloomState(&buildstate.blstate, index);
138  "Bloom build temporary context",
140  initCachedPage(&buildstate);
141 
142  /* Do the heap scan */
143  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) */
148  if (buildstate.count > 0)
149  flushCachedPage(index, &buildstate);
150 
151  MemoryContextDelete(buildstate.tmpCtx);
152 
153  result = (IndexBuildResult *) palloc(sizeof(IndexBuildResult));
154  result->heap_tuples = reltuples;
155  result->index_tuples = buildstate.indtuples;
156 
157  return result;
158 }
159 
160 /*
161  * Build an empty bloom index in the initialization fork.
162  */
163 void
165 {
166  Page metapage;
167 
168  /* Construct metapage. */
169  metapage = (Page) palloc(BLCKSZ);
170  BloomFillMetapage(index, metapage);
171 
172  /*
173  * Write the page and log it. It might seem that an immediate sync would
174  * be sufficient to guarantee that the file exists on disk, but recovery
175  * itself might remove it while replaying, for example, an
176  * XLOG_DBASE_CREATE* or XLOG_TBLSPC_CREATE record. Therefore, we need
177  * this even when wal_level=minimal.
178  */
181  metapage, true);
182  log_newpage(&(RelationGetSmgr(index))->smgr_rlocator.locator, INIT_FORKNUM,
183  BLOOM_METAPAGE_BLKNO, metapage, true);
184 
185  /*
186  * An immediate sync is required even if we xlog'd the page, because the
187  * write did not go through shared_buffers and therefore a concurrent
188  * checkpoint may have moved the redo pointer past our xlog record.
189  */
191 }
192 
193 /*
194  * Insert new tuple to the bloom index.
195  */
196 bool
198  ItemPointer ht_ctid, Relation heapRel,
199  IndexUniqueCheck checkUnique,
200  bool indexUnchanged,
201  IndexInfo *indexInfo)
202 {
203  BloomState blstate;
204  BloomTuple *itup;
205  MemoryContext oldCtx;
206  MemoryContext insertCtx;
207  BloomMetaPageData *metaData;
208  Buffer buffer,
209  metaBuffer;
210  Page page,
211  metaPage;
213  OffsetNumber nStart;
215 
217  "Bloom insert temporary context",
219 
220  oldCtx = MemoryContextSwitchTo(insertCtx);
221 
222  initBloomState(&blstate, index);
223  itup = BloomFormTuple(&blstate, ht_ctid, values, isnull);
224 
225  /*
226  * At first, try to insert new tuple to the first page in notFullPage
227  * array. If successful, we don't need to modify the meta page.
228  */
229  metaBuffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
230  LockBuffer(metaBuffer, BUFFER_LOCK_SHARE);
231  metaData = BloomPageGetMeta(BufferGetPage(metaBuffer));
232 
233  if (metaData->nEnd > metaData->nStart)
234  {
235  blkno = metaData->notFullPage[metaData->nStart];
236  Assert(blkno != InvalidBlockNumber);
237 
238  /* Don't hold metabuffer lock while doing insert */
239  LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
240 
241  buffer = ReadBuffer(index, blkno);
243 
245  page = GenericXLogRegisterBuffer(state, buffer, 0);
246 
247  /*
248  * We might have found a page that was recently deleted by VACUUM. If
249  * so, we can reuse it, but we must reinitialize it.
250  */
251  if (PageIsNew(page) || BloomPageIsDeleted(page))
252  BloomInitPage(page, 0);
253 
254  if (BloomPageAddItem(&blstate, page, itup))
255  {
256  /* Success! Apply the change, clean up, and exit */
258  UnlockReleaseBuffer(buffer);
259  ReleaseBuffer(metaBuffer);
260  MemoryContextSwitchTo(oldCtx);
261  MemoryContextDelete(insertCtx);
262  return false;
263  }
264 
265  /* Didn't fit, must try other pages */
267  UnlockReleaseBuffer(buffer);
268  }
269  else
270  {
271  /* No entries in notFullPage */
272  LockBuffer(metaBuffer, BUFFER_LOCK_UNLOCK);
273  }
274 
275  /*
276  * Try other pages in notFullPage array. We will have to change nStart in
277  * metapage. Thus, grab exclusive lock on metapage.
278  */
279  LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
280 
281  /* nStart might have changed while we didn't have lock */
282  nStart = metaData->nStart;
283 
284  /* Skip first page if we already tried it above */
285  if (nStart < metaData->nEnd &&
286  blkno == metaData->notFullPage[nStart])
287  nStart++;
288 
289  /*
290  * This loop iterates for each page we try from the notFullPage array, and
291  * will also initialize a GenericXLogState for the fallback case of having
292  * to allocate a new page.
293  */
294  for (;;)
295  {
297 
298  /* get modifiable copy of metapage */
299  metaPage = GenericXLogRegisterBuffer(state, metaBuffer, 0);
300  metaData = BloomPageGetMeta(metaPage);
301 
302  if (nStart >= metaData->nEnd)
303  break; /* no more entries in notFullPage array */
304 
305  blkno = metaData->notFullPage[nStart];
306  Assert(blkno != InvalidBlockNumber);
307 
308  buffer = ReadBuffer(index, blkno);
310  page = GenericXLogRegisterBuffer(state, buffer, 0);
311 
312  /* Basically same logic as above */
313  if (PageIsNew(page) || BloomPageIsDeleted(page))
314  BloomInitPage(page, 0);
315 
316  if (BloomPageAddItem(&blstate, page, itup))
317  {
318  /* Success! Apply the changes, clean up, and exit */
319  metaData->nStart = nStart;
321  UnlockReleaseBuffer(buffer);
322  UnlockReleaseBuffer(metaBuffer);
323  MemoryContextSwitchTo(oldCtx);
324  MemoryContextDelete(insertCtx);
325  return false;
326  }
327 
328  /* Didn't fit, must try other pages */
330  UnlockReleaseBuffer(buffer);
331  nStart++;
332  }
333 
334  /*
335  * Didn't find place to insert in notFullPage array. Allocate new page.
336  * (XXX is it good to do this while holding ex-lock on the metapage??)
337  */
338  buffer = BloomNewBuffer(index);
339 
341  BloomInitPage(page, 0);
342 
343  if (!BloomPageAddItem(&blstate, page, itup))
344  {
345  /* We shouldn't be here since we're inserting to an empty page */
346  elog(ERROR, "could not add new bloom tuple to empty page");
347  }
348 
349  /* Reset notFullPage array to contain just this new page */
350  metaData->nStart = 0;
351  metaData->nEnd = 1;
352  metaData->notFullPage[0] = BufferGetBlockNumber(buffer);
353 
354  /* Apply the changes, clean up, and exit */
356 
357  UnlockReleaseBuffer(buffer);
358  UnlockReleaseBuffer(metaBuffer);
359 
360  MemoryContextSwitchTo(oldCtx);
361  MemoryContextDelete(insertCtx);
362 
363  return false;
364 }
static void initCachedPage(BloomBuildState *buildstate)
Definition: blinsert.c:64
PG_MODULE_MAGIC
Definition: blinsert.c:27
void blbuildempty(Relation index)
Definition: blinsert.c:164
IndexBuildResult * blbuild(Relation heap, Relation index, IndexInfo *indexInfo)
Definition: blinsert.c:121
static void flushCachedPage(Relation index, BloomBuildState *buildstate)
Definition: blinsert.c:47
static void bloomBuildCallback(Relation index, ItemPointer tid, Datum *values, bool *isnull, bool tupleIsAlive, void *state)
Definition: blinsert.c:74
bool blinsert(Relation index, Datum *values, bool *isnull, ItemPointer ht_ctid, Relation heapRel, IndexUniqueCheck checkUnique, bool indexUnchanged, IndexInfo *indexInfo)
Definition: blinsert.c:197
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 BloomInitMetapage(Relation index)
Definition: blutils.c:454
void BloomInitPage(Page page, uint16 flags)
Definition: blutils.c:407
#define BloomPageGetMeta(page)
Definition: bloom.h:136
bool BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
Definition: blutils.c:318
Buffer BloomNewBuffer(Relation index)
Definition: blutils.c:353
void BloomFillMetapage(Relation index, Page metaPage)
Definition: blutils.c:422
#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:156
int Buffer
Definition: buf.h:23
BlockNumber BufferGetBlockNumber(Buffer buffer)
Definition: bufmgr.c:2811
void ReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4005
void UnlockReleaseBuffer(Buffer buffer)
Definition: bufmgr.c:4028
void LockBuffer(Buffer buffer, int mode)
Definition: bufmgr.c:4246
Buffer ReadBuffer(Relation reln, BlockNumber blockNum)
Definition: bufmgr.c:704
#define BUFFER_LOCK_UNLOCK
Definition: bufmgr.h:110
#define BUFFER_LOCK_SHARE
Definition: bufmgr.h:111
#define RelationGetNumberOfBlocks(reln)
Definition: bufmgr.h:161
static Page BufferGetPage(Buffer buffer)
Definition: bufmgr.h:285
#define BUFFER_LOCK_EXCLUSIVE
Definition: bufmgr.h:112
void PageSetChecksumInplace(Page page, BlockNumber blkno)
Definition: bufpage.c:1539
Pointer Page
Definition: bufpage.h:78
static bool PageIsNew(Page page)
Definition: bufpage.h:230
#define ERROR
Definition: elog.h:39
IndexUniqueCheck
Definition: genam.h:115
Page GenericXLogRegisterBuffer(GenericXLogState *state, Buffer buffer, int flags)
Definition: generic_xlog.c:295
GenericXLogState * GenericXLogStart(Relation relation)
Definition: generic_xlog.c:267
XLogRecPtr GenericXLogFinish(GenericXLogState *state)
Definition: generic_xlog.c:333
void GenericXLogAbort(GenericXLogState *state)
Definition: generic_xlog.c:444
#define GENERIC_XLOG_FULL_IMAGE
Definition: generic_xlog.h:26
Assert(fmt[strlen(fmt) - 1] !='\n')
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:314
MemoryContext CurrentMemoryContext
Definition: mcxt.c:135
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:387
void * palloc(Size size)
Definition: mcxt.c:1210
#define AllocSetContextCreate
Definition: memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:153
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:121
uint16 OffsetNumber
Definition: off.h:24
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:138
uintptr_t Datum
Definition: postgres.h:64
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:571
#define RelationGetRelationName(relation)
Definition: rel.h:537
@ INIT_FORKNUM
Definition: relpath.h:53
void smgrwrite(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum, const void *buffer, bool skipFsync)
Definition: smgr.c:554
void smgrimmedsync(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:691
int64 indtuples
Definition: blinsert.c:36
BloomState blstate
Definition: blinsert.c:35
MemoryContext tmpCtx
Definition: blinsert.c:37
PGAlignedBlock data
Definition: blinsert.c:39
FreeBlockNumberArray notFullPage
Definition: bloom.h:127
uint16 nEnd
Definition: bloom.h:125
uint16 nStart
Definition: bloom.h:124
double heap_tuples
Definition: genam.h:32
double index_tuples
Definition: genam.h:33
Definition: type.h:95
Definition: regguts.h:318
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:1782
char data[BLCKSZ]
Definition: c.h:1130
XLogRecPtr log_newpage(RelFileLocator *rlocator, ForkNumber forknum, BlockNumber blkno, Page page, bool page_std)
Definition: xloginsert.c:1097