Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * blutils.c
4 : : * Bloom index utilities.
5 : : *
6 : : * Portions Copyright (c) 2016-2025, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1990-1993, Regents of the University of California
8 : : *
9 : : * IDENTIFICATION
10 : : * contrib/bloom/blutils.c
11 : : *
12 : : *-------------------------------------------------------------------------
13 : : */
14 : : #include "postgres.h"
15 : :
16 : : #include "access/amapi.h"
17 : : #include "access/generic_xlog.h"
18 : : #include "access/reloptions.h"
19 : : #include "bloom.h"
20 : : #include "commands/vacuum.h"
21 : : #include "storage/bufmgr.h"
22 : : #include "storage/indexfsm.h"
23 : : #include "utils/memutils.h"
24 : : #include "varatt.h"
25 : :
26 : : /* Signature dealing macros - note i is assumed to be of type int */
27 : : #define GETWORD(x,i) ( *( (BloomSignatureWord *)(x) + ( (i) / SIGNWORDBITS ) ) )
28 : : #define CLRBIT(x,i) GETWORD(x,i) &= ~( 0x01 << ( (i) % SIGNWORDBITS ) )
29 : : #define SETBIT(x,i) GETWORD(x,i) |= ( 0x01 << ( (i) % SIGNWORDBITS ) )
30 : : #define GETBIT(x,i) ( (GETWORD(x,i) >> ( (i) % SIGNWORDBITS )) & 0x01 )
31 : :
3445 teodor@sigaev.ru 32 :CBC 128 : PG_FUNCTION_INFO_V1(blhandler);
33 : :
34 : : /* Kind of relation options for bloom index */
35 : : static relopt_kind bl_relopt_kind;
36 : :
37 : : /* parse table for fillRelOptions */
38 : : static relopt_parse_elt bl_relopt_tab[INDEX_MAX_KEYS + 1];
39 : :
40 : : static int32 myRand(void);
41 : : static void mySrand(uint32 seed);
42 : :
43 : : /*
44 : : * Module initialize function: initialize info about Bloom relation options.
45 : : *
46 : : * Note: keep this in sync with makeDefaultBloomOptions().
47 : : */
48 : : void
49 : 126 : _PG_init(void)
50 : : {
51 : : int i;
52 : : char buf[16];
53 : :
54 : 126 : bl_relopt_kind = add_reloption_kind();
55 : :
56 : : /* Option for length of signature */
57 : 126 : add_int_reloption(bl_relopt_kind, "length",
58 : : "Length of signature in bits",
59 : : DEFAULT_BLOOM_LENGTH, 1, MAX_BLOOM_LENGTH,
60 : : AccessExclusiveLock);
3382 tgl@sss.pgh.pa.us 61 : 126 : bl_relopt_tab[0].optname = "length";
62 : 126 : bl_relopt_tab[0].opttype = RELOPT_TYPE_INT;
63 : 126 : bl_relopt_tab[0].offset = offsetof(BloomOptions, bloomLength);
64 : :
65 : : /* Number of bits for each possible index column: col1, col2, ... */
3445 teodor@sigaev.ru 66 [ + + ]: 4158 : for (i = 0; i < INDEX_MAX_KEYS; i++)
67 : : {
3382 tgl@sss.pgh.pa.us 68 : 4032 : snprintf(buf, sizeof(buf), "col%d", i + 1);
3445 teodor@sigaev.ru 69 : 4032 : add_int_reloption(bl_relopt_kind, buf,
70 : : "Number of bits generated for each index column",
71 : : DEFAULT_BLOOM_BITS, 1, MAX_BLOOM_BITS,
72 : : AccessExclusiveLock);
3382 tgl@sss.pgh.pa.us 73 : 4032 : bl_relopt_tab[i + 1].optname = MemoryContextStrdup(TopMemoryContext,
74 : : buf);
75 : 4032 : bl_relopt_tab[i + 1].opttype = RELOPT_TYPE_INT;
2999 76 : 4032 : bl_relopt_tab[i + 1].offset = offsetof(BloomOptions, bitSize[0]) + sizeof(int) * i;
77 : : }
3445 teodor@sigaev.ru 78 : 126 : }
79 : :
80 : : /*
81 : : * Construct a default set of Bloom options.
82 : : */
83 : : static BloomOptions *
3382 tgl@sss.pgh.pa.us 84 :UBC 0 : makeDefaultBloomOptions(void)
85 : : {
86 : : BloomOptions *opts;
87 : : int i;
88 : :
89 : 0 : opts = (BloomOptions *) palloc0(sizeof(BloomOptions));
90 : : /* Convert DEFAULT_BLOOM_LENGTH from # of bits to # of words */
91 : 0 : opts->bloomLength = (DEFAULT_BLOOM_LENGTH + SIGNWORDBITS - 1) / SIGNWORDBITS;
92 [ # # ]: 0 : for (i = 0; i < INDEX_MAX_KEYS; i++)
93 : 0 : opts->bitSize[i] = DEFAULT_BLOOM_BITS;
94 : 0 : SET_VARSIZE(opts, sizeof(BloomOptions));
95 : 0 : return opts;
96 : : }
97 : :
98 : : /*
99 : : * Bloom handler function: return IndexAmRoutine with access method parameters
100 : : * and callbacks.
101 : : */
102 : : Datum
3445 teodor@sigaev.ru 103 :CBC 726 : blhandler(PG_FUNCTION_ARGS)
104 : : {
105 : 726 : IndexAmRoutine *amroutine = makeNode(IndexAmRoutine);
106 : :
3418 107 : 726 : amroutine->amstrategies = BLOOM_NSTRATEGIES;
108 : 726 : amroutine->amsupport = BLOOM_NPROC;
1986 akorotkov@postgresql 109 : 726 : amroutine->amoptsprocnum = BLOOM_OPTIONS_PROC;
3445 teodor@sigaev.ru 110 : 726 : amroutine->amcanorder = false;
111 : 726 : amroutine->amcanorderbyop = false;
191 peter@eisentraut.org 112 : 726 : amroutine->amcanhash = false;
183 113 : 726 : amroutine->amconsistentequality = false;
114 : 726 : amroutine->amconsistentordering = false;
3445 teodor@sigaev.ru 115 : 726 : amroutine->amcanbackward = false;
116 : 726 : amroutine->amcanunique = false;
117 : 726 : amroutine->amcanmulticol = true;
118 : 726 : amroutine->amoptionalkey = true;
119 : 726 : amroutine->amsearcharray = false;
120 : 726 : amroutine->amsearchnulls = false;
121 : 726 : amroutine->amstorage = false;
122 : 726 : amroutine->amclusterable = false;
123 : 726 : amroutine->ampredlocks = false;
3125 rhaas@postgresql.org 124 : 726 : amroutine->amcanparallel = false;
638 tomas.vondra@postgre 125 : 726 : amroutine->amcanbuildparallel = false;
2709 teodor@sigaev.ru 126 : 726 : amroutine->amcaninclude = false;
2061 akapila@postgresql.o 127 : 726 : amroutine->amusemaintenanceworkmem = false;
128 : 726 : amroutine->amparallelvacuumoptions =
129 : : VACUUM_OPTION_PARALLEL_BULKDEL | VACUUM_OPTION_PARALLEL_CLEANUP;
3313 tgl@sss.pgh.pa.us 130 : 726 : amroutine->amkeytype = InvalidOid;
131 : :
3445 teodor@sigaev.ru 132 : 726 : amroutine->ambuild = blbuild;
133 : 726 : amroutine->ambuildempty = blbuildempty;
3313 tgl@sss.pgh.pa.us 134 : 726 : amroutine->aminsert = blinsert;
651 tomas.vondra@postgre 135 : 726 : amroutine->aminsertcleanup = NULL;
3445 teodor@sigaev.ru 136 : 726 : amroutine->ambulkdelete = blbulkdelete;
137 : 726 : amroutine->amvacuumcleanup = blvacuumcleanup;
138 : 726 : amroutine->amcanreturn = NULL;
139 : 726 : amroutine->amcostestimate = blcostestimate;
361 peter@eisentraut.org 140 : 726 : amroutine->amgettreeheight = NULL;
3445 teodor@sigaev.ru 141 : 726 : amroutine->amoptions = bloptions;
3311 tgl@sss.pgh.pa.us 142 : 726 : amroutine->amproperty = NULL;
2349 alvherre@alvh.no-ip. 143 : 726 : amroutine->ambuildphasename = NULL;
3445 teodor@sigaev.ru 144 : 726 : amroutine->amvalidate = blvalidate;
1862 tgl@sss.pgh.pa.us 145 : 726 : amroutine->amadjustmembers = NULL;
3313 146 : 726 : amroutine->ambeginscan = blbeginscan;
147 : 726 : amroutine->amrescan = blrescan;
148 : 726 : amroutine->amgettuple = NULL;
149 : 726 : amroutine->amgetbitmap = blgetbitmap;
150 : 726 : amroutine->amendscan = blendscan;
151 : 726 : amroutine->ammarkpos = NULL;
152 : 726 : amroutine->amrestrpos = NULL;
3147 rhaas@postgresql.org 153 : 726 : amroutine->amestimateparallelscan = NULL;
154 : 726 : amroutine->aminitparallelscan = NULL;
155 : 726 : amroutine->amparallelrescan = NULL;
216 peter@eisentraut.org 156 : 726 : amroutine->amtranslatestrategy = NULL;
157 : 726 : amroutine->amtranslatecmptype = NULL;
158 : :
3445 teodor@sigaev.ru 159 : 726 : PG_RETURN_POINTER(amroutine);
160 : : }
161 : :
162 : : /*
163 : : * Fill BloomState structure for particular index.
164 : : */
165 : : void
166 : 104403 : initBloomState(BloomState *state, Relation index)
167 : : {
168 : : int i;
169 : :
170 : 104403 : state->nColumns = index->rd_att->natts;
171 : :
172 : : /* Initialize hash function for each attribute */
173 [ + + ]: 313209 : for (i = 0; i < index->rd_att->natts; i++)
174 : : {
175 : 208806 : fmgr_info_copy(&(state->hashFn[i]),
176 : 208806 : index_getprocinfo(index, i + 1, BLOOM_HASH_PROC),
177 : : CurrentMemoryContext);
2360 peter@eisentraut.org 178 : 208806 : state->collations[i] = index->rd_indcollation[i];
179 : : }
180 : :
181 : : /* Initialize amcache if needed with options from metapage */
3445 teodor@sigaev.ru 182 [ + + ]: 104403 : if (!index->rd_amcache)
183 : : {
184 : : Buffer buffer;
185 : : Page page;
186 : : BloomMetaPageData *meta;
187 : : BloomOptions *opts;
188 : :
189 : 91 : opts = MemoryContextAlloc(index->rd_indexcxt, sizeof(BloomOptions));
190 : :
191 : 91 : buffer = ReadBuffer(index, BLOOM_METAPAGE_BLKNO);
192 : 91 : LockBuffer(buffer, BUFFER_LOCK_SHARE);
193 : :
3426 kgrittn@postgresql.o 194 : 91 : page = BufferGetPage(buffer);
195 : :
3445 teodor@sigaev.ru 196 [ - + ]: 91 : if (!BloomPageIsMeta(page))
3445 teodor@sigaev.ru 197 [ # # ]:UBC 0 : elog(ERROR, "Relation is not a bloom index");
3426 kgrittn@postgresql.o 198 :CBC 91 : meta = BloomPageGetMeta(BufferGetPage(buffer));
199 : :
3445 teodor@sigaev.ru 200 [ - + ]: 91 : if (meta->magickNumber != BLOOM_MAGICK_NUMBER)
3445 teodor@sigaev.ru 201 [ # # ]:UBC 0 : elog(ERROR, "Relation is not a bloom index");
202 : :
3445 teodor@sigaev.ru 203 :CBC 91 : *opts = meta->opts;
204 : :
205 : 91 : UnlockReleaseBuffer(buffer);
206 : :
282 peter@eisentraut.org 207 : 91 : index->rd_amcache = opts;
208 : : }
209 : :
3443 tgl@sss.pgh.pa.us 210 : 104403 : memcpy(&state->opts, index->rd_amcache, sizeof(state->opts));
3445 teodor@sigaev.ru 211 : 104403 : state->sizeOfBloomTuple = BLOOMTUPLEHDRSZ +
3382 tgl@sss.pgh.pa.us 212 : 104403 : sizeof(BloomSignatureWord) * state->opts.bloomLength;
3445 teodor@sigaev.ru 213 : 104403 : }
214 : :
215 : : /*
216 : : * Random generator copied from FreeBSD. Using own random generator here for
217 : : * two reasons:
218 : : *
219 : : * 1) In this case random numbers are used for on-disk storage. Usage of
220 : : * PostgreSQL number generator would obstruct it from all possible changes.
221 : : * 2) Changing seed of PostgreSQL random generator would be undesirable side
222 : : * effect.
223 : : */
224 : : static int32 next;
225 : :
226 : : static int32
3434 tgl@sss.pgh.pa.us 227 : 865433 : myRand(void)
228 : : {
229 : : /*----------
230 : : * Compute x = (7^5 * x) mod (2^31 - 1)
231 : : * without overflowing 31 bits:
232 : : * (2^31 - 1) = 127773 * (7^5) + 2836
233 : : * From "Random number generators: good ones are hard to find",
234 : : * Park and Miller, Communications of the ACM, vol. 31, no. 10,
235 : : * October 1988, p. 1195.
236 : : *----------
237 : : */
238 : : int32 hi,
239 : : lo,
240 : : x;
241 : :
242 : : /* Must be in [1, 0x7ffffffe] range at this point. */
3445 teodor@sigaev.ru 243 : 865433 : hi = next / 127773;
244 : 865433 : lo = next % 127773;
245 : 865433 : x = 16807 * lo - 2836 * hi;
246 [ + + ]: 865433 : if (x < 0)
247 : 175256 : x += 0x7fffffff;
248 : 865433 : next = x;
249 : : /* Transform to [0, 0x7ffffffd] range. */
250 : 865433 : return (x - 1);
251 : : }
252 : :
253 : : static void
254 : 492032 : mySrand(uint32 seed)
255 : : {
256 : 492032 : next = seed;
257 : : /* Transform to [1, 0x7ffffffe] range. */
258 : 492032 : next = (next % 0x7ffffffe) + 1;
259 : 492032 : }
260 : :
261 : : /*
262 : : * Add bits of given value to the signature.
263 : : */
264 : : void
3382 tgl@sss.pgh.pa.us 265 : 246016 : signValue(BloomState *state, BloomSignatureWord *sign, Datum value, int attno)
266 : : {
267 : : uint32 hashVal;
268 : : int nBit,
269 : : j;
270 : :
271 : : /*
272 : : * init generator with "column's" number to get "hashed" seed for new
273 : : * value. We don't want to map the same numbers from different columns
274 : : * into the same bits!
275 : : */
3445 teodor@sigaev.ru 276 : 246016 : mySrand(attno);
277 : :
278 : : /*
279 : : * Init hash sequence to map our value into bits. the same values in
280 : : * different columns will be mapped into different bits because of step
281 : : * above
282 : : */
2360 peter@eisentraut.org 283 : 246016 : hashVal = DatumGetInt32(FunctionCall1Coll(&state->hashFn[attno], state->collations[attno], value));
3445 teodor@sigaev.ru 284 : 246016 : mySrand(hashVal ^ myRand());
285 : :
3443 tgl@sss.pgh.pa.us 286 [ + + ]: 865433 : for (j = 0; j < state->opts.bitSize[attno]; j++)
287 : : {
288 : : /* prevent multiple evaluation in SETBIT macro */
3382 289 : 619417 : nBit = myRand() % (state->opts.bloomLength * SIGNWORDBITS);
3445 teodor@sigaev.ru 290 : 619417 : SETBIT(sign, nBit);
291 : : }
292 : 246016 : }
293 : :
294 : : /*
295 : : * Make bloom tuple from values.
296 : : */
297 : : BloomTuple *
298 : 122750 : BloomFormTuple(BloomState *state, ItemPointer iptr, Datum *values, bool *isnull)
299 : : {
300 : : int i;
301 : 122750 : BloomTuple *res = (BloomTuple *) palloc0(state->sizeOfBloomTuple);
302 : :
303 : 122750 : res->heapPtr = *iptr;
304 : :
305 : : /* Blooming each column */
306 [ + + ]: 368250 : for (i = 0; i < state->nColumns; i++)
307 : : {
308 : : /* skip nulls */
309 [ - + ]: 245500 : if (isnull[i])
3445 teodor@sigaev.ru 310 :UBC 0 : continue;
311 : :
3445 teodor@sigaev.ru 312 :CBC 245500 : signValue(state, res->sign, values[i], i);
313 : : }
314 : :
315 : 122750 : return res;
316 : : }
317 : :
318 : : /*
319 : : * Add new bloom tuple to the page. Returns true if new tuple was successfully
320 : : * added to the page. Returns false if it doesn't fit on the page.
321 : : */
322 : : bool
323 : 123538 : BloomPageAddItem(BloomState *state, Page page, BloomTuple *tuple)
324 : : {
325 : : BloomTuple *itup;
326 : : BloomPageOpaque opaque;
327 : : Pointer ptr;
328 : :
329 : : /* We shouldn't be pointed to an invalid page */
3311 tgl@sss.pgh.pa.us 330 [ + - - + ]: 123538 : Assert(!PageIsNew(page) && !BloomPageIsDeleted(page));
331 : :
332 : : /* Does new tuple fit on the page? */
3445 teodor@sigaev.ru 333 [ + + ]: 123538 : if (BloomPageGetFreeSpace(state, page) < state->sizeOfBloomTuple)
334 : 788 : return false;
335 : :
336 : : /* Copy new tuple to the end of page */
337 : 122750 : opaque = BloomPageGetOpaque(page);
338 : 122750 : itup = BloomPageGetTuple(state, page, opaque->maxoff + 1);
339 : 122750 : memcpy((Pointer) itup, (Pointer) tuple, state->sizeOfBloomTuple);
340 : :
341 : : /* Adjust maxoff and pd_lower */
342 : 122750 : opaque->maxoff++;
343 : 122750 : ptr = (Pointer) BloomPageGetTuple(state, page, opaque->maxoff + 1);
344 : 122750 : ((PageHeader) page)->pd_lower = ptr - page;
345 : :
346 : : /* Assert we didn't overrun available space */
3311 tgl@sss.pgh.pa.us 347 [ - + ]: 122750 : Assert(((PageHeader) page)->pd_lower <= ((PageHeader) page)->pd_upper);
348 : :
3445 teodor@sigaev.ru 349 : 122750 : return true;
350 : : }
351 : :
352 : : /*
353 : : * Allocate a new page (either by recycling, or by extending the index file)
354 : : * The returned buffer is already pinned and exclusive-locked
355 : : * Caller is responsible for initializing the page by calling BloomInitPage
356 : : */
357 : : Buffer
358 : 149 : BloomNewBuffer(Relation index)
359 : : {
360 : : Buffer buffer;
361 : :
362 : : /* First, try to get a page from FSM */
363 : : for (;;)
3445 teodor@sigaev.ru 364 :UBC 0 : {
3445 teodor@sigaev.ru 365 :CBC 149 : BlockNumber blkno = GetFreeIndexPage(index);
366 : :
367 [ + + ]: 149 : if (blkno == InvalidBlockNumber)
368 : 148 : break;
369 : :
370 : 1 : buffer = ReadBuffer(index, blkno);
371 : :
372 : : /*
373 : : * We have to guard against the possibility that someone else already
374 : : * recycled this page; the buffer may be locked if so.
375 : : */
376 [ + - ]: 1 : if (ConditionalLockBuffer(buffer))
377 : : {
3426 kgrittn@postgresql.o 378 : 1 : Page page = BufferGetPage(buffer);
379 : :
3445 teodor@sigaev.ru 380 [ - + ]: 1 : if (PageIsNew(page))
3445 teodor@sigaev.ru 381 :UBC 0 : return buffer; /* OK to use, if never initialized */
382 : :
3445 teodor@sigaev.ru 383 [ + - ]:CBC 1 : if (BloomPageIsDeleted(page))
384 : 1 : return buffer; /* OK to use */
385 : :
3445 teodor@sigaev.ru 386 :UBC 0 : LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
387 : : }
388 : :
389 : : /* Can't use it, so release buffer and try again */
390 : 0 : ReleaseBuffer(buffer);
391 : : }
392 : :
393 : : /* Must extend the file */
745 tmunro@postgresql.or 394 :CBC 148 : buffer = ExtendBufferedRel(BMR_REL(index), MAIN_FORKNUM, NULL,
395 : : EB_LOCK_FIRST);
396 : :
3445 teodor@sigaev.ru 397 : 148 : return buffer;
398 : : }
399 : :
400 : : /*
401 : : * Initialize any page of a bloom index.
402 : : */
403 : : void
404 : 155 : BloomInitPage(Page page, uint16 flags)
405 : : {
406 : : BloomPageOpaque opaque;
407 : :
408 : 155 : PageInit(page, BLCKSZ, sizeof(BloomPageOpaqueData));
409 : :
410 : 155 : opaque = BloomPageGetOpaque(page);
411 : 155 : opaque->flags = flags;
3434 412 : 155 : opaque->bloom_page_id = BLOOM_PAGE_ID;
3445 413 : 155 : }
414 : :
415 : : /*
416 : : * Fill in metapage for bloom index.
417 : : */
418 : : void
3392 tgl@sss.pgh.pa.us 419 : 6 : BloomFillMetapage(Relation index, Page metaPage)
420 : : {
421 : : BloomOptions *opts;
422 : : BloomMetaPageData *metadata;
423 : :
424 : : /*
425 : : * Choose the index's options. If reloptions have been assigned, use
426 : : * those, otherwise create default options.
427 : : */
428 : 6 : opts = (BloomOptions *) index->rd_options;
429 [ - + ]: 6 : if (!opts)
3382 tgl@sss.pgh.pa.us 430 :UBC 0 : opts = makeDefaultBloomOptions();
431 : :
432 : : /*
433 : : * Initialize contents of meta page, including a copy of the options,
434 : : * which are now frozen for the life of the index.
435 : : */
3392 tgl@sss.pgh.pa.us 436 :CBC 6 : BloomInitPage(metaPage, BLOOM_META);
437 : 6 : metadata = BloomPageGetMeta(metaPage);
438 : 6 : memset(metadata, 0, sizeof(BloomMetaPageData));
439 : 6 : metadata->magickNumber = BLOOM_MAGICK_NUMBER;
440 : 6 : metadata->opts = *opts;
441 : 6 : ((PageHeader) metaPage)->pd_lower += sizeof(BloomMetaPageData);
442 : :
443 : : /* If this fails, probably FreeBlockNumberArray size calc is wrong: */
3311 444 [ - + ]: 6 : Assert(((PageHeader) metaPage)->pd_lower <= ((PageHeader) metaPage)->pd_upper);
3392 445 : 6 : }
446 : :
447 : : /*
448 : : * Initialize metapage for bloom index.
449 : : */
450 : : void
745 heikki.linnakangas@i 451 : 6 : BloomInitMetapage(Relation index, ForkNumber forknum)
452 : : {
453 : : Buffer metaBuffer;
454 : : Page metaPage;
455 : : GenericXLogState *state;
456 : :
457 : : /*
458 : : * Make a new page; since it is first page it should be associated with
459 : : * block number 0 (BLOOM_METAPAGE_BLKNO). No need to hold the extension
460 : : * lock because there cannot be concurrent inserters yet.
461 : : */
462 : 6 : metaBuffer = ReadBufferExtended(index, forknum, P_NEW, RBM_NORMAL, NULL);
463 : 6 : LockBuffer(metaBuffer, BUFFER_LOCK_EXCLUSIVE);
3445 teodor@sigaev.ru 464 [ - + ]: 6 : Assert(BufferGetBlockNumber(metaBuffer) == BLOOM_METAPAGE_BLKNO);
465 : :
466 : : /* Initialize contents of meta page */
467 : 6 : state = GenericXLogStart(index);
3392 tgl@sss.pgh.pa.us 468 : 6 : metaPage = GenericXLogRegisterBuffer(state, metaBuffer,
469 : : GENERIC_XLOG_FULL_IMAGE);
470 : 6 : BloomFillMetapage(index, metaPage);
3445 teodor@sigaev.ru 471 : 6 : GenericXLogFinish(state);
472 : :
473 : 6 : UnlockReleaseBuffer(metaBuffer);
474 : 6 : }
475 : :
476 : : /*
477 : : * Parse reloptions for bloom index, producing a BloomOptions struct.
478 : : */
479 : : bytea *
480 : 133 : bloptions(Datum reloptions, bool validate)
481 : : {
482 : : BloomOptions *rdopts;
483 : :
484 : : /* Parse the user-given reloptions */
2132 michael@paquier.xyz 485 : 133 : rdopts = (BloomOptions *) build_reloptions(reloptions, validate,
486 : : bl_relopt_kind,
487 : : sizeof(BloomOptions),
488 : : bl_relopt_tab,
489 : : lengthof(bl_relopt_tab));
490 : :
491 : : /* Convert signature length from # of bits to # to words, rounding up */
492 [ + - ]: 131 : if (rdopts)
493 : 131 : rdopts->bloomLength = (rdopts->bloomLength + SIGNWORDBITS - 1) / SIGNWORDBITS;
494 : :
3445 teodor@sigaev.ru 495 : 131 : return (bytea *) rdopts;
496 : : }
|