1 /*-------------------------------------------------------------------------
4 * Bloom index utilities.
6 * Portions Copyright (c) 2016-2019, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1990-1993, Regents of the University of California
10 * contrib/bloom/blutils.c
12 *-------------------------------------------------------------------------
16 #include "access/amapi.h"
17 #include "access/generic_xlog.h"
18 #include "access/reloptions.h"
20 #include "catalog/index.h"
21 #include "miscadmin.h"
22 #include "storage/bufmgr.h"
23 #include "storage/freespace.h"
24 #include "storage/indexfsm.h"
25 #include "storage/lmgr.h"
26 #include "utils/memutils.h"
28 /* Signature dealing macros - note i is assumed to be of type int */
29 #define GETWORD(x,i) ( *( (BloomSignatureWord *)(x) + ( (i) / SIGNWORDBITS ) ) )
30 #define CLRBIT(x,i) GETWORD(x,i) &= ~( 0x01 << ( (i) % SIGNWORDBITS ) )
31 #define SETBIT(x,i) GETWORD(x,i) |= ( 0x01 << ( (i) % SIGNWORDBITS ) )
32 #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % SIGNWORDBITS )) & 0x01 )
34 PG_FUNCTION_INFO_V1(blhandler);
36 /* Kind of relation options for bloom index */
37 static relopt_kind bl_relopt_kind;
39 /* parse table for fillRelOptions */
40 static relopt_parse_elt bl_relopt_tab[INDEX_MAX_KEYS + 1];
42 static int32 myRand(void);
43 static void mySrand(uint32 seed);
46 * Module initialize function: initialize info about Bloom relation options.
48 * Note: keep this in sync with makeDefaultBloomOptions().
56 bl_relopt_kind = add_reloption_kind();
58 /* Option for length of signature */
59 add_int_reloption(bl_relopt_kind, "length",
60 "Length of signature in bits",
61 DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH,
63 bl_relopt_tab[0].optname = "length";
64 bl_relopt_tab[0].opttype = RELOPT_TYPE_INT;
65 bl_relopt_tab[0].offset = offsetof(BloomOptions, bloomLength);
67 /* Number of bits for each possible index column: col1, col2, ... */
68 for (i = 0; i < INDEX_MAX_KEYS; i++)
70 snprintf(buf, sizeof(buf), "col%d", i + 1);
71 add_int_reloption(bl_relopt_kind, buf,
72 "Number of bits generated for each index column",
73 DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS,
75 bl_relopt_tab[i + 1].optname = MemoryContextStrdup(TopMemoryContext,
77 bl_relopt_tab[i + 1].opttype = RELOPT_TYPE_INT;
78 bl_relopt_tab[i + 1].offset = offsetof(BloomOptions, bitSize[0]) + sizeof(int) * i;
83 * Construct a default set of Bloom options.
86 makeDefaultBloomOptions(void)
91 opts = (BloomOptions *) palloc0(sizeof(BloomOptions));
92 /* Convert DEFAULT_BLOOM_LENGTH from # of bits to # of words */
93 opts->bloomLength = (DEFAULT_BLOOM_LENGTH + SIGNWORDBITS - 1) / SIGNWORDBITS;
94 for (i = 0; i < INDEX_MAX_KEYS; i++)
95 opts->bitSize[i] = DEFAULT_BLOOM_BITS;
96 SET_VARSIZE(opts, sizeof(BloomOptions));
101 * Bloom handler function: return IndexAmRoutine with access method parameters
105 blhandler(PG_FUNCTION_ARGS)
107 IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
109 amroutine->amstrategies = BLOOM_NSTRATEGIES;
110 amroutine->amsupport = BLOOM_NPROC;
111 amroutine->amcanorder = false;
112 amroutine->amcanorderbyop = false;
113 amroutine->amcanbackward = false;
114 amroutine->amcanunique = false;
115 amroutine->amcanmulticol = true;
116 amroutine->amoptionalkey = true;
117 amroutine->amsearcharray = false;
118 amroutine->amsearchnulls = false;
119 amroutine->amstorage = false;
120 amroutine->amclusterable = false;
121 amroutine->ampredlocks = false;
122 amroutine->amcanparallel = false;
123 amroutine->amcaninclude = false;
124 amroutine->amkeytype = InvalidOid;
126 amroutine->ambuild = blbuild;
127 amroutine->ambuildempty = blbuildempty;
128 amroutine->aminsert = blinsert;
129 amroutine->ambulkdelete = blbulkdelete;
130 amroutine->amvacuumcleanup = blvacuumcleanup;
131 amroutine->amcanreturn = NULL;
132 amroutine->amcostestimate = blcostestimate;
133 amroutine->amoptions = bloptions;
134 amroutine->amproperty = NULL;
135 amroutine->ambuildphasename = NULL;
136 amroutine->amvalidate = blvalidate;
137 amroutine->ambeginscan = blbeginscan;
138 amroutine->amrescan = blrescan;
139 amroutine->amgettuple = NULL;
140 amroutine->amgetbitmap = blgetbitmap;
141 amroutine->amendscan = blendscan;
142 amroutine->ammarkpos = NULL;
143 amroutine->amrestrpos = NULL;
144 amroutine->amestimateparallelscan = NULL;
145 amroutine->aminitparallelscan = NULL;
146 amroutine->amparallelrescan = NULL;
148 PG_RETURN_POINTER(amroutine);
152 * Fill BloomState structure for particular index.
155 initBloomState(BloomState *state, Relation index)
159 state->nColumns = index->rd_att->natts;
161 /* Initialize hash function for each attribute */
162 for (i = 0; i < index->rd_att->natts; i++)
164 fmgr_info_copy(&(state->hashFn[i]),
165 index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
166 CurrentMemoryContext);
167 state->collations[i] = index->rd_indcollation[i];
170 /* Initialize amcache if needed with options from metapage */
171 if (!index->rd_amcache)
175 BloomMetaPageData *meta;
178 opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
180 buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
181 LockBuffer(buffer, BUFFER_LOCK_SHARE);
183 page = BufferGetPage(buffer);
185 if (!BloomPageIsMeta(page))
186 elog(ERROR, "Relation is not a bloom index");
187 meta = BloomPageGetMeta(BufferGetPage(buffer));
189 if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
190 elog(ERROR, "Relation is not a bloom index");
194 UnlockReleaseBuffer(buffer);
196 index->rd_amcache = (void *) opts;
199 memcpy(&state->opts, index->rd_amcache, sizeof(state->opts));
200 state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
201 sizeof(BloomSignatureWord) * state->opts.bloomLength;
205 * Random generator copied from FreeBSD. Using own random generator here for
208 * 1) In this case random numbers are used for on-disk storage. Usage of
209 * PostgreSQL number generator would obstruct it from all possible changes.
210 * 2) Changing seed of PostgreSQL random generator would be undesirable side
219 * Compute x = (7^5 * x) mod (2^31 - 1)
220 * without overflowing 31 bits:
221 * (2^31 - 1) = 127773 * (7^5) + 2836
222 * From "Random number generators: good ones are hard to find",
223 * Park and Miller, Communications of the ACM, vol. 31, no. 10,
224 * October 1988, p. 1195.
231 /* Must be in [1, 0x7ffffffe] range at this point. */
234 x = 16807 * lo - 2836 * hi;
238 /* Transform to [0, 0x7ffffffd] range. */
246 /* Transform to [1, 0x7ffffffe] range. */
247 next = (next % 0x7ffffffe) + 1;
251 * Add bits of given value to the signature.
254 signValue(BloomState *state, BloomSignatureWord *sign, Datum value, int attno)
261 * init generator with "column's" number to get "hashed" seed for new
262 * value. We don't want to map the same numbers from different columns
263 * into the same bits!
268 * Init hash sequence to map our value into bits. the same values in
269 * different columns will be mapped into different bits because of step
272 hashVal = DatumGetInt32(FunctionCall1Coll(&state->hashFn[attno], state->collations[attno], value));
273 mySrand(hashVal ^ myRand());
275 for (j = 0; j < state->opts.bitSize[attno]; j++)
277 /* prevent multiple evaluation in SETBIT macro */
278 nBit = myRand() % (state->opts.bloomLength * SIGNWORDBITS);
284 * Make bloom tuple from values.
287 BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
290 BloomTuple *res = (BloomTuple *) palloc0(state->sizeOfBloomTuple);
292 res->heapPtr = *iptr;
294 /* Blooming each column */
295 for (i = 0; i < state->nColumns; i++)
301 signValue(state, res->sign, values[i], i);
308 * Add new bloom tuple to the page. Returns true if new tuple was successfully
309 * added to the page. Returns false if it doesn't fit on the page.
312 BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
315 BloomPageOpaque opaque;
318 /* We shouldn't be pointed to an invalid page */
319 Assert(!PageIsNew(page) && !BloomPageIsDeleted(page));
321 /* Does new tuple fit on the page? */
322 if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
325 /* Copy new tuple to the end of page */
326 opaque = BloomPageGetOpaque(page);
327 itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
328 memcpy((Pointer) itup, (Pointer) tuple, state->sizeOfBloomTuple);
330 /* Adjust maxoff and pd_lower */
332 ptr = (Pointer) BloomPageGetTuple(state, page, opaque->maxoff + 1);
333 ((PageHeader) page)->pd_lower = ptr - page;
335 /* Assert we didn't overrun available space */
336 Assert(((PageHeader) page)->pd_lower <= ((PageHeader) page)->pd_upper);
342 * Allocate a new page (either by recycling, or by extending the index file)
343 * The returned buffer is already pinned and exclusive-locked
344 * Caller is responsible for initializing the page by calling BloomInitPage
347 BloomNewBuffer(Relation index)
352 /* First, try to get a page from FSM */
355 BlockNumber blkno = GetFreeIndexPage(index);
357 if (blkno == InvalidBlockNumber)
360 buffer = ReadBuffer(index, blkno);
363 * We have to guard against the possibility that someone else already
364 * recycled this page; the buffer may be locked if so.
366 if (ConditionalLockBuffer(buffer))
368 Page page = BufferGetPage(buffer);
371 return buffer; /* OK to use, if never initialized */
373 if (BloomPageIsDeleted(page))
374 return buffer; /* OK to use */
376 LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
379 /* Can't use it, so release buffer and try again */
380 ReleaseBuffer(buffer);
383 /* Must extend the file */
384 needLock = !RELATION_IS_LOCAL(index);
386 LockRelationForExtension(index, ExclusiveLock);
388 buffer = ReadBuffer(index, P_NEW);
389 LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
392 UnlockRelationForExtension(index, ExclusiveLock);
398 * Initialize any page of a bloom index.
401 BloomInitPage(Page page, uint16 flags)
403 BloomPageOpaque opaque;
405 PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
407 opaque = BloomPageGetOpaque(page);
408 memset(opaque, 0, sizeof(BloomPageOpaqueData));
409 opaque->flags = flags;
410 opaque->bloom_page_id = BLOOM_PAGE_ID;
414 * Fill in metapage for bloom index.
417 BloomFillMetapage(Relation index, Page metaPage)
420 BloomMetaPageData *metadata;
423 * Choose the index's options. If reloptions have been assigned, use
424 * those, otherwise create default options.
426 opts = (BloomOptions *) index->rd_options;
428 opts = makeDefaultBloomOptions();
431 * Initialize contents of meta page, including a copy of the options,
432 * which are now frozen for the life of the index.
434 BloomInitPage(metaPage, BLOOM_META);
435 metadata = BloomPageGetMeta(metaPage);
436 memset(metadata, 0, sizeof(BloomMetaPageData));
437 metadata->magickNumber = BLOOM_MAGICK_NUMBER;
438 metadata->opts = *opts;
439 ((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
441 /* If this fails, probably FreeBlockNumberArray size calc is wrong: */
442 Assert(((PageHeader) metaPage)->pd_lower <= ((PageHeader) metaPage)->pd_upper);
446 * Initialize metapage for bloom index.
449 BloomInitMetapage(Relation index)
453 GenericXLogState *state;
456 * Make a new page; since it is first page it should be associated with
457 * block number 0 (BLOOM_METAPAGE_BLKNO).
459 metaBuffer = BloomNewBuffer(index);
460 Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
462 /* Initialize contents of meta page */
463 state = GenericXLogStart(index);
464 metaPage = GenericXLogRegisterBuffer(state, metaBuffer,
465 GENERIC_XLOG_FULL_IMAGE);
466 BloomFillMetapage(index, metaPage);
467 GenericXLogFinish(state);
469 UnlockReleaseBuffer(metaBuffer);
473 * Parse reloptions for bloom index, producing a BloomOptions struct.
476 bloptions(Datum reloptions, bool validate)
478 relopt_value *options;
480 BloomOptions *rdopts;
482 /* Parse the user-given reloptions */
483 options = parseRelOptions(reloptions, validate, bl_relopt_kind, &numoptions);
484 rdopts = allocateReloptStruct(sizeof(BloomOptions), options, numoptions);
485 fillRelOptions((void *) rdopts, sizeof(BloomOptions), options, numoptions,
486 validate, bl_relopt_tab, lengthof(bl_relopt_tab));
488 /* Convert signature length from # of bits to # to words, rounding up */
489 rdopts->bloomLength = (rdopts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
491 return (bytea *) rdopts;