Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * test_bitmapset.c
4 : : * Test the Bitmapset data structure.
5 : : *
6 : : * This module tests the Bitmapset implementation in PostgreSQL, covering
7 : : * all public API functions.
8 : : *
9 : : * Copyright (c) 2025-2026, PostgreSQL Global Development Group
10 : : *
11 : : * IDENTIFICATION
12 : : * src/test/modules/test_bitmapset/test_bitmapset.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : :
17 : : #include "postgres.h"
18 : :
19 : : #include <stddef.h>
20 : : #include "catalog/pg_type.h"
21 : : #include "common/pg_prng.h"
22 : : #include "fmgr.h"
23 : : #include "miscadmin.h"
24 : : #include "nodes/bitmapset.h"
25 : : #include "nodes/nodes.h"
26 : : #include "nodes/pg_list.h"
27 : : #include "nodes/readfuncs.h"
28 : : #include "utils/array.h"
29 : : #include "utils/builtins.h"
30 : : #include "utils/timestamp.h"
31 : :
363 michael@paquier.xyz 32 :CBC 1 : PG_MODULE_MAGIC;
33 : :
34 : : /* Bitmapset API functions in order of appearance in bitmapset.c */
35 : 2 : PG_FUNCTION_INFO_V1(test_bms_make_singleton);
36 : 2 : PG_FUNCTION_INFO_V1(test_bms_add_member);
37 : 2 : PG_FUNCTION_INFO_V1(test_bms_del_member);
38 : 2 : PG_FUNCTION_INFO_V1(test_bms_is_member);
39 : 2 : PG_FUNCTION_INFO_V1(test_bms_num_members);
40 : 2 : PG_FUNCTION_INFO_V1(test_bms_copy);
41 : 2 : PG_FUNCTION_INFO_V1(test_bms_equal);
42 : 2 : PG_FUNCTION_INFO_V1(test_bms_compare);
43 : 2 : PG_FUNCTION_INFO_V1(test_bms_is_subset);
44 : 2 : PG_FUNCTION_INFO_V1(test_bms_subset_compare);
45 : 2 : PG_FUNCTION_INFO_V1(test_bms_union);
46 : 2 : PG_FUNCTION_INFO_V1(test_bms_intersect);
47 : 2 : PG_FUNCTION_INFO_V1(test_bms_difference);
73 drowley@postgresql.o 48 :GNC 2 : PG_FUNCTION_INFO_V1(test_bms_offset_members);
363 michael@paquier.xyz 49 :CBC 2 : PG_FUNCTION_INFO_V1(test_bms_is_empty);
50 : 2 : PG_FUNCTION_INFO_V1(test_bms_membership);
51 : 2 : PG_FUNCTION_INFO_V1(test_bms_singleton_member);
52 : 2 : PG_FUNCTION_INFO_V1(test_bms_get_singleton_member);
53 : 2 : PG_FUNCTION_INFO_V1(test_bms_next_member);
54 : 2 : PG_FUNCTION_INFO_V1(test_bms_prev_member);
55 : 2 : PG_FUNCTION_INFO_V1(test_bms_hash_value);
56 : 2 : PG_FUNCTION_INFO_V1(test_bms_overlap);
57 : 2 : PG_FUNCTION_INFO_V1(test_bms_overlap_list);
58 : 2 : PG_FUNCTION_INFO_V1(test_bms_nonempty_difference);
59 : 2 : PG_FUNCTION_INFO_V1(test_bms_member_index);
60 : 2 : PG_FUNCTION_INFO_V1(test_bms_add_range);
61 : 2 : PG_FUNCTION_INFO_V1(test_bms_add_members);
62 : 2 : PG_FUNCTION_INFO_V1(test_bms_int_members);
356 63 : 2 : PG_FUNCTION_INFO_V1(test_bms_del_members);
363 64 : 2 : PG_FUNCTION_INFO_V1(test_bms_replace_members);
65 : 2 : PG_FUNCTION_INFO_V1(test_bms_join);
66 : 2 : PG_FUNCTION_INFO_V1(test_bitmap_hash);
67 : 2 : PG_FUNCTION_INFO_V1(test_bitmap_match);
68 : :
69 : : /* Test utility functions */
70 : 2 : PG_FUNCTION_INFO_V1(test_random_operations);
73 drowley@postgresql.o 71 :GNC 2 : PG_FUNCTION_INFO_V1(test_random_offset_operations);
72 : :
73 : : /* Convenient macros to test results */
74 : : #define EXPECT_TRUE(expr) \
75 : : do { \
76 : : if (!(expr)) \
77 : : elog(ERROR, \
78 : : "%s was unexpectedly false in file \"%s\" line %u", \
79 : : #expr, __FILE__, __LINE__); \
80 : : } while (0)
81 : :
82 : : #define EXPECT_NOT_NULL(expr) \
83 : : do { \
84 : : if ((expr) == NULL) \
85 : : elog(ERROR, \
86 : : "%s was unexpectedly true in file \"%s\" line %u", \
87 : : #expr, __FILE__, __LINE__); \
88 : : } while (0)
89 : :
90 : : /* Encode Bitmapset to TEXT */
91 : : #define BITMAPSET_TO_TEXT(bms) cstring_to_text(nodeToString(bms))
92 : :
93 : : /*
94 : : * Decode Bitmapset from text
95 : : */
96 : : static Bitmapset *
10 michael@paquier.xyz 97 : 347 : text_to_bitmapset(text *txt)
98 : : {
99 : 347 : char *str = text_to_cstring(txt);
100 : : ReadNodeContext ctx;
101 : : const char *token;
102 : : int length;
103 : 347 : bool is_bitmapset = false;
104 : : Node *node;
105 : :
106 : 347 : ctx.str = str;
107 : :
108 : 347 : token = pg_strtok(&ctx, &length);
109 : :
110 : : /* Check empty case, translated to "<>". */
111 [ + + + + ]: 347 : if (token != NULL && length == 0)
112 : 2 : return NULL;
113 : :
114 : : /* First token has to be a single '('. */
115 [ + + + + : 345 : if (token != NULL && length == 1 && token[0] == '(')
+ + ]
116 : : {
117 : : /* Second token has to be a single 'b'. */
118 : 342 : token = pg_strtok(&ctx, &length);
119 [ + - + - : 342 : is_bitmapset = (token != NULL && length == 1 && token[0] == 'b');
+ + ]
120 : : }
121 : :
122 [ + + ]: 345 : if (!is_bitmapset)
123 [ + - ]: 5 : elog(ERROR, "argument is not a Bitmapset");
124 : :
125 : 340 : node = stringToNode(str);
126 [ + + - + ]: 340 : Assert(node == NULL || IsA(node, Bitmapset));
127 : :
128 : 340 : return (Bitmapset *) node;
129 : : }
130 : :
131 : : /*
132 : : * Helper macro to fetch text parameters as Bitmapsets. SQL-NULL means empty
133 : : * set.
134 : : */
135 : : #define PG_ARG_GETBITMAPSET(n) \
136 : : (PG_ARGISNULL(n) ? NULL : text_to_bitmapset(PG_GETARG_TEXT_PP(n)))
137 : :
138 : : /*
139 : : * Helper macro to handle converting sets back to text, returning the
140 : : * resulting text representation of the set.
141 : : */
142 : : #define PG_RETURN_BITMAPSET_AS_TEXT(bms) \
143 : : PG_RETURN_TEXT_P(BITMAPSET_TO_TEXT(bms))
144 : :
145 : : /*
146 : : * Individual test functions for each bitmapset API function
147 : : *
148 : : * Primarily, we aim to keep these as close to simple wrapper functions as
149 : : * possible in order to publish the functions of bitmapset.c to the SQL layer
150 : : * with as little interference as possible. We opt to return SQL NULL in
151 : : * cases where the input given to the SQL function isn't valid to pass to the
152 : : * underlying bitmapset.c function. For example we cannot do much useful
153 : : * testing if someone calls test_bms_make_singleton(NULL) since
154 : : * bms_make_singleton() expects an integer argument.
155 : : *
156 : : * For function arguments which are to be converted to Bitmapsets, we accept
157 : : * SQL NULL as a valid argument to mean an empty set. Optionally callers may
158 : : * pass '(b)'.
159 : : *
160 : : * For the test functions which return a Bitmapset, these are converted back
161 : : * to text with result generated by nodeToString().
162 : : */
163 : :
164 : : Datum
363 michael@paquier.xyz 165 :CBC 7 : test_bms_add_member(PG_FUNCTION_ARGS)
166 : : {
167 : : Bitmapset *bms;
168 : : int member;
169 : :
170 [ + + ]: 7 : if (PG_ARGISNULL(1))
354 171 : 1 : PG_RETURN_NULL(); /* invalid input */
172 : :
173 [ + - ]: 6 : bms = PG_ARG_GETBITMAPSET(0);
363 174 : 6 : member = PG_GETARG_INT32(1);
175 : :
354 176 : 6 : bms = bms_add_member(bms, member);
177 : :
178 : 4 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
179 : : }
180 : :
181 : : Datum
363 182 : 3 : test_bms_add_members(PG_FUNCTION_ARGS)
183 : : {
354 184 [ + - ]: 3 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
185 [ + - ]: 3 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
186 : :
187 : : /* left input is recycled */
363 188 : 3 : bms1 = bms_add_members(bms1, bms2);
189 : :
354 190 : 3 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
191 : : }
192 : :
193 : : Datum
363 194 : 12 : test_bms_del_member(PG_FUNCTION_ARGS)
195 : : {
196 : : Bitmapset *bms;
197 : : int32 member;
198 : :
199 [ + + ]: 12 : if (PG_ARGISNULL(1))
354 200 : 1 : PG_RETURN_NULL(); /* invalid input */
201 : :
202 [ + - ]: 11 : bms = PG_ARG_GETBITMAPSET(0);
363 203 : 11 : member = PG_GETARG_INT32(1);
204 : :
354 205 : 11 : bms = bms_del_member(bms, member);
206 : :
207 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
208 : : }
209 : :
210 : : Datum
363 211 : 6 : test_bms_is_member(PG_FUNCTION_ARGS)
212 : : {
213 : : Bitmapset *bms;
214 : : int32 member;
215 : : bool result;
216 : :
217 [ + + ]: 6 : if (PG_ARGISNULL(1))
354 218 : 1 : PG_RETURN_NULL(); /* invalid input */
219 : :
220 [ + - ]: 5 : bms = PG_ARG_GETBITMAPSET(0);
363 221 : 5 : member = PG_GETARG_INT32(1);
222 : :
354 223 : 5 : result = bms_is_member(member, bms);
224 : :
363 225 : 4 : PG_RETURN_BOOL(result);
226 : : }
227 : :
228 : : Datum
229 : 9 : test_bms_num_members(PG_FUNCTION_ARGS)
230 : : {
354 231 [ + - ]: 9 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
232 : : int result;
233 : :
363 234 : 4 : result = bms_num_members(bms);
235 : :
236 : 4 : PG_RETURN_INT32(result);
237 : : }
238 : :
239 : : Datum
240 : 4 : test_bms_make_singleton(PG_FUNCTION_ARGS)
241 : : {
242 : : Bitmapset *bms;
243 : : int32 member;
244 : :
245 : 4 : member = PG_GETARG_INT32(0);
246 : 4 : bms = bms_make_singleton(member);
247 : :
354 248 : 3 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
249 : : }
250 : :
251 : : Datum
363 252 : 3 : test_bms_copy(PG_FUNCTION_ARGS)
253 : : {
354 254 [ + + ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
255 : : Bitmapset *copy_bms;
256 : :
363 257 : 3 : copy_bms = bms_copy(bms);
258 : :
354 259 : 3 : PG_RETURN_BITMAPSET_AS_TEXT(copy_bms);
260 : : }
261 : :
262 : : Datum
363 263 : 13 : test_bms_equal(PG_FUNCTION_ARGS)
264 : : {
354 265 [ + + ]: 13 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
266 [ + + ]: 13 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
267 : : bool result;
268 : :
363 269 : 13 : result = bms_equal(bms1, bms2);
270 : :
271 : 13 : PG_RETURN_BOOL(result);
272 : : }
273 : :
274 : : Datum
275 : 9 : test_bms_union(PG_FUNCTION_ARGS)
276 : : {
354 277 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
278 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
279 : : Bitmapset *result_bms;
280 : :
363 281 : 9 : result_bms = bms_union(bms1, bms2);
282 : :
354 283 : 9 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
284 : : }
285 : :
286 : : Datum
363 287 : 4 : test_bms_membership(PG_FUNCTION_ARGS)
288 : : {
354 289 [ + + ]: 4 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
290 : : BMS_Membership result;
291 : :
363 292 : 4 : result = bms_membership(bms);
293 : :
294 : 4 : PG_RETURN_INT32((int32) result);
295 : : }
296 : :
297 : : Datum
298 : 6 : test_bms_next_member(PG_FUNCTION_ARGS)
299 : : {
300 : : Bitmapset *bms;
301 : : int32 prevmember;
302 : : int result;
303 : :
354 304 [ + + ]: 6 : if (PG_ARGISNULL(1))
305 : 1 : PG_RETURN_NULL(); /* invalid input */
306 : :
307 [ + + ]: 5 : bms = PG_ARG_GETBITMAPSET(0);
363 308 : 5 : prevmember = PG_GETARG_INT32(1);
309 : :
354 310 : 5 : result = bms_next_member(bms, prevmember);
311 : :
363 312 : 5 : PG_RETURN_INT32(result);
313 : : }
314 : :
315 : : Datum
316 : 8 : test_bms_intersect(PG_FUNCTION_ARGS)
317 : : {
354 318 [ + + ]: 8 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
319 [ + + ]: 8 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
320 : : Bitmapset *result_bms;
321 : :
363 322 : 8 : result_bms = bms_intersect(bms1, bms2);
323 : :
354 324 : 8 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
325 : : }
326 : :
327 : : Datum
363 328 : 10 : test_bms_difference(PG_FUNCTION_ARGS)
329 : : {
354 330 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
331 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
332 : : Bitmapset *result_bms;
333 : :
363 334 : 10 : result_bms = bms_difference(bms1, bms2);
335 : :
354 336 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
337 : : }
338 : :
339 : : Datum
73 drowley@postgresql.o 340 :GNC 13 : test_bms_offset_members(PG_FUNCTION_ARGS)
341 : : {
342 [ + - ]: 13 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
343 : 13 : int offset = PG_GETARG_INT32(1);
344 : :
345 : 13 : bms = bms_offset_members(bms, offset);
346 : :
347 : 11 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
348 : : }
349 : :
350 : : Datum
363 michael@paquier.xyz 351 :CBC 10 : test_bms_compare(PG_FUNCTION_ARGS)
352 : : {
354 353 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
354 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
355 : : int result;
356 : :
363 357 : 10 : result = bms_compare(bms1, bms2);
358 : :
359 : 10 : PG_RETURN_INT32(result);
360 : : }
361 : :
362 : : Datum
363 : 3 : test_bms_is_empty(PG_FUNCTION_ARGS)
364 : : {
354 365 [ + + ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
366 : : bool result;
367 : :
363 368 : 3 : result = bms_is_empty(bms);
369 : :
370 : 3 : PG_RETURN_BOOL(result);
371 : : }
372 : :
373 : : Datum
374 : 10 : test_bms_is_subset(PG_FUNCTION_ARGS)
375 : : {
354 376 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
377 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
378 : : bool result;
379 : :
363 380 : 10 : result = bms_is_subset(bms1, bms2);
381 : :
382 : 10 : PG_RETURN_BOOL(result);
383 : : }
384 : :
385 : : Datum
386 : 23 : test_bms_subset_compare(PG_FUNCTION_ARGS)
387 : : {
354 388 [ + + ]: 23 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
389 [ + + ]: 23 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
390 : : BMS_Comparison result;
391 : :
363 392 : 23 : result = bms_subset_compare(bms1, bms2);
393 : :
394 : 23 : PG_RETURN_INT32((int32) result);
395 : : }
396 : :
397 : : Datum
398 : 3 : test_bms_singleton_member(PG_FUNCTION_ARGS)
399 : : {
354 400 [ + - ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
401 : : int result;
402 : :
363 403 : 3 : result = bms_singleton_member(bms);
404 : :
405 : 1 : PG_RETURN_INT32(result);
406 : : }
407 : :
408 : : Datum
409 : 4 : test_bms_get_singleton_member(PG_FUNCTION_ARGS)
410 : : {
354 411 [ + + ]: 4 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
412 : : int member;
413 : :
414 : : /*
415 : : * Keep this simple. Return -1 when we detect the set is not a singleton
416 : : * set, otherwise return the singleton member.
417 : : */
418 [ + + ]: 4 : if (!bms_get_singleton_member(bms, &member))
419 : 3 : member = -1;
420 : :
421 : 4 : PG_RETURN_INT32(member);
422 : : }
423 : :
424 : : Datum
363 425 : 7 : test_bms_prev_member(PG_FUNCTION_ARGS)
426 : : {
427 : : Bitmapset *bms;
428 : : int32 prevmember;
429 : : int result;
430 : :
354 431 [ + + ]: 7 : if (PG_ARGISNULL(1))
432 : 1 : PG_RETURN_NULL(); /* invalid input */
433 : :
434 [ + + ]: 6 : bms = PG_ARG_GETBITMAPSET(0);
363 435 : 6 : prevmember = PG_GETARG_INT32(1);
436 : :
437 : 6 : result = bms_prev_member(bms, prevmember);
438 : :
439 : 6 : PG_RETURN_INT32(result);
440 : : }
441 : :
442 : : Datum
443 : 6 : test_bms_overlap(PG_FUNCTION_ARGS)
444 : : {
354 445 [ + + ]: 6 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
446 [ + + ]: 6 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
447 : : bool result;
448 : :
363 449 : 6 : result = bms_overlap(bms1, bms2);
450 : :
451 : 6 : PG_RETURN_BOOL(result);
452 : : }
453 : :
454 : : Datum
455 : 11 : test_bms_overlap_list(PG_FUNCTION_ARGS)
456 : : {
457 : : Bitmapset *bms;
458 : : ArrayType *array;
459 : 11 : List *int_list = NIL;
460 : : bool result;
354 461 : 11 : Datum *elem_datums = NULL;
462 : 11 : bool *elem_nulls = NULL;
463 : : int elem_count;
464 : : int i;
465 : :
466 [ + + ]: 11 : bms = PG_ARG_GETBITMAPSET(0);
467 : :
468 [ + + ]: 11 : if (!PG_ARGISNULL(1))
469 : : {
470 : 9 : array = PG_GETARG_ARRAYTYPE_P(1);
471 : :
472 : 9 : deconstruct_array(array,
473 : : INT4OID, sizeof(int32), true, 'i',
474 : : &elem_datums, &elem_nulls, &elem_count);
475 : :
476 [ + + ]: 31 : for (i = 0; i < elem_count; i++)
477 : : {
478 [ + - ]: 22 : if (!elem_nulls[i])
479 : : {
480 : 22 : int32 member = DatumGetInt32(elem_datums[i]);
481 : :
482 : 22 : int_list = lappend_int(int_list, member);
483 : : }
484 : : }
485 : : }
486 : :
363 487 : 11 : result = bms_overlap_list(bms, int_list);
488 : :
489 : 9 : list_free(int_list);
490 : :
354 491 [ + + ]: 9 : if (elem_datums)
492 : 7 : pfree(elem_datums);
493 : :
494 [ + + ]: 9 : if (elem_nulls)
495 : 7 : pfree(elem_nulls);
496 : :
363 497 : 9 : PG_RETURN_BOOL(result);
498 : : }
499 : :
500 : : Datum
501 : 9 : test_bms_nonempty_difference(PG_FUNCTION_ARGS)
502 : : {
354 503 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
504 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
505 : : bool result;
506 : :
363 507 : 9 : result = bms_nonempty_difference(bms1, bms2);
508 : :
509 : 9 : PG_RETURN_BOOL(result);
510 : : }
511 : :
512 : : Datum
513 : 8 : test_bms_member_index(PG_FUNCTION_ARGS)
514 : : {
515 : : Bitmapset *bms;
516 : : int32 member;
517 : : int result;
518 : :
354 519 [ + + ]: 8 : if (PG_ARGISNULL(1))
520 : 1 : PG_RETURN_NULL(); /* invalid input */
521 : :
522 [ + + ]: 7 : bms = PG_ARG_GETBITMAPSET(0);
363 523 : 7 : member = PG_GETARG_INT32(1);
524 : :
525 : 7 : result = bms_member_index(bms, member);
526 : :
527 : 7 : PG_RETURN_INT32(result);
528 : : }
529 : :
530 : : Datum
531 : 25 : test_bms_add_range(PG_FUNCTION_ARGS)
532 : : {
533 : : Bitmapset *bms;
534 : : int32 lower,
535 : : upper;
536 : :
537 [ + + + + ]: 25 : if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
354 538 : 3 : PG_RETURN_NULL(); /* invalid input */
539 : :
540 [ + + ]: 22 : bms = PG_ARG_GETBITMAPSET(0);
363 541 : 22 : lower = PG_GETARG_INT32(1);
542 : 22 : upper = PG_GETARG_INT32(2);
543 : :
544 : 22 : bms = bms_add_range(bms, lower, upper);
545 : :
354 546 : 21 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
547 : : }
548 : :
549 : : Datum
363 550 : 7 : test_bms_int_members(PG_FUNCTION_ARGS)
551 : : {
354 552 [ + + ]: 7 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
553 [ + + ]: 7 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
554 : :
555 : : /* left input gets recycled */
363 556 : 7 : bms1 = bms_int_members(bms1, bms2);
557 : :
354 558 : 7 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
559 : : }
560 : :
561 : : Datum
356 562 : 10 : test_bms_del_members(PG_FUNCTION_ARGS)
563 : : {
354 564 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
565 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
566 : :
567 : : /* left input gets recycled */
568 : 10 : bms1 = bms_del_members(bms1, bms2);
569 : :
570 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
571 : : }
572 : :
573 : : Datum
363 574 : 6 : test_bms_replace_members(PG_FUNCTION_ARGS)
575 : : {
354 576 [ + + ]: 6 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
577 [ + + ]: 6 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
578 : :
579 : : /* left input gets recycled */
580 : 6 : bms1 = bms_replace_members(bms1, bms2);
581 : :
582 : 6 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
583 : : }
584 : :
585 : : Datum
363 586 : 9 : test_bms_join(PG_FUNCTION_ARGS)
587 : : {
354 588 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
589 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
590 : : Bitmapset *result_bms;
591 : :
592 : : /* either input can be recycled */
363 593 : 9 : result_bms = bms_join(bms1, bms2);
594 : :
354 595 : 9 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
596 : : }
597 : :
598 : : Datum
363 599 : 7 : test_bms_hash_value(PG_FUNCTION_ARGS)
600 : : {
354 601 [ + + ]: 7 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
602 : : uint32 hash_result;
603 : :
363 604 : 7 : hash_result = bms_hash_value(bms);
605 : :
606 : 7 : PG_RETURN_INT32(hash_result);
607 : : }
608 : :
609 : : Datum
610 : 7 : test_bitmap_hash(PG_FUNCTION_ARGS)
611 : : {
354 612 [ + + ]: 7 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
613 : : uint32 hash_result;
614 : :
615 : : /* Call bitmap_hash */
616 : 7 : hash_result = bitmap_hash(&bms, sizeof(Bitmapset *));
617 : :
363 618 : 7 : PG_RETURN_INT32(hash_result);
619 : : }
620 : :
621 : : Datum
622 : 12 : test_bitmap_match(PG_FUNCTION_ARGS)
623 : : {
354 624 [ + + ]: 12 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
625 [ + + ]: 12 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
626 : : int match_result;
627 : :
628 : : /* Call bitmap_match with addresses of the Bitmapset pointers */
629 : 12 : match_result = bitmap_match(&bms1, &bms2, sizeof(Bitmapset *));
630 : :
363 631 : 12 : PG_RETURN_INT32(match_result);
632 : : }
633 : :
634 : : /*
635 : : * Contrary to most of the other functions which are one-one mappings with the
636 : : * equivalent C functions, this stresses Bitmapsets in a random fashion for
637 : : * various operations.
638 : : *
639 : : * Arguments:
640 : : * arg1: optional random seed. NULL autoselects the seed.
641 : : * arg2: defines the number of times each operation is done.
642 : : * arg3: the minimum bitmapset member number to use in the random set.
643 : : * arg4: the maximum bitmapset member number to use in the random set.
644 : : *
645 : : * The return value is the number of times all operations have been executed.
646 : : */
647 : : Datum
648 : 1 : test_random_operations(PG_FUNCTION_ARGS)
649 : : {
650 : 1 : Bitmapset *bms1 = NULL;
651 : 1 : Bitmapset *bms2 = NULL;
652 : 1 : Bitmapset *bms = NULL;
653 : 1 : Bitmapset *result = NULL;
654 : : pg_prng_state state;
655 : 1 : uint64 seed = GetCurrentTimestamp();
656 : : int num_ops;
657 : : int min_value;
658 : : int max_value;
659 : : int member;
660 : : uint32 range;
661 : : int *members;
662 : 1 : int num_members = 0;
352 drowley@postgresql.o 663 : 1 : int total_ops = 0;
664 : :
153 665 [ - + ]: 1 : if (!PG_ARGISNULL(0))
153 drowley@postgresql.o 666 :UBC 0 : seed = PG_GETARG_INT64(0);
667 : :
26 drowley@postgresql.o 668 [ + - - + ]:CBC 1 : if (PG_ARGISNULL(1) || PG_GETARG_INT32(1) <= 0)
153 drowley@postgresql.o 669 [ # # ]:UBC 0 : elog(ERROR, "invalid number of operations");
26 drowley@postgresql.o 670 [ + - - + ]:CBC 1 : if (PG_ARGISNULL(2) || PG_GETARG_INT32(2) < 0)
153 drowley@postgresql.o 671 [ # # ]:UBC 0 : elog(ERROR, "invalid minimum value");
26 drowley@postgresql.o 672 [ + - - + ]:CBC 1 : if (PG_ARGISNULL(3) || PG_GETARG_INT32(3) < 0)
26 drowley@postgresql.o 673 [ # # ]:UBC 0 : elog(ERROR, "invalid maximum value");
674 : :
26 drowley@postgresql.o 675 :CBC 1 : num_ops = PG_GETARG_INT32(1);
676 : 1 : min_value = PG_GETARG_INT32(2);
677 : 1 : max_value = PG_GETARG_INT32(3);
678 : :
679 [ - + ]: 1 : if (max_value < min_value)
26 drowley@postgresql.o 680 [ # # ]:UBC 0 : elog(ERROR, "maximum value must be greater than or equal to minimum value");
681 : :
363 michael@paquier.xyz 682 :CBC 1 : pg_prng_seed(&state, seed);
26 drowley@postgresql.o 683 : 1 : range = (uint32) max_value - (uint32) min_value + 1;
684 : :
685 : : /*
686 : : * There can be up to "num_ops" members added. This is very unlikely,
687 : : * still possible if all the operations hit the "0" case during phase 4
688 : : * where multiple operation types are mixed together.
689 : : */
285 michael@paquier.xyz 690 : 1 : members = palloc_array(int, num_ops);
691 : :
692 : : /* Phase 1: Random insertions in first set */
363 693 [ + + ]: 5001 : for (int i = 0; i < num_ops / 2; i++)
694 : : {
153 drowley@postgresql.o 695 [ - + ]: 5000 : CHECK_FOR_INTERRUPTS();
696 : :
26 697 : 5000 : member = min_value + (pg_prng_uint32(&state) % range);
698 : :
363 michael@paquier.xyz 699 [ + + ]: 5000 : if (!bms_is_member(member, bms1))
700 : 4863 : members[num_members++] = member;
345 701 : 5000 : bms1 = bms_add_member(bms1, member);
702 : : }
703 : :
704 : : /* Phase 2: Random insertions in second set */
363 705 [ + + ]: 2501 : for (int i = 0; i < num_ops / 4; i++)
706 : : {
153 drowley@postgresql.o 707 [ - + ]: 2500 : CHECK_FOR_INTERRUPTS();
708 : :
26 709 : 2500 : member = min_value + (pg_prng_uint32(&state) % range);
710 : :
345 michael@paquier.xyz 711 [ + + ]: 2500 : if (!bms_is_member(member, bms2))
712 : 2462 : members[num_members++] = member;
363 713 : 2500 : bms2 = bms_add_member(bms2, member);
714 : : }
715 : :
716 : : /* Test union */
717 : 1 : result = bms_union(bms1, bms2);
718 [ - + - - ]: 1 : EXPECT_NOT_NULL(result);
719 : :
720 : : /* Verify union contains all members from first and second sets */
721 [ + + ]: 7326 : for (int i = 0; i < num_members; i++)
722 : : {
153 drowley@postgresql.o 723 [ - + ]: 7325 : CHECK_FOR_INTERRUPTS();
724 : :
363 michael@paquier.xyz 725 [ - + ]: 7325 : if (!bms_is_member(members[i], result))
104 peter@eisentraut.org 726 [ # # ]:UBC 0 : elog(ERROR, "union missing member %d, seed " UINT64_FORMAT,
727 : : members[i], seed);
728 : : }
363 michael@paquier.xyz 729 :CBC 1 : bms_free(result);
730 : :
731 : : /*
732 : : * Test intersection, checking that all the members in the result are from
733 : : * both the first and second sets.
734 : : */
735 : 1 : result = bms_intersect(bms1, bms2);
736 [ + - ]: 1 : if (result != NULL)
737 : : {
738 : 1 : member = -1;
739 : :
740 [ + + ]: 169 : while ((member = bms_next_member(result, member)) >= 0)
741 : : {
153 drowley@postgresql.o 742 [ - + ]: 168 : CHECK_FOR_INTERRUPTS();
743 : :
363 michael@paquier.xyz 744 [ + - - + ]: 168 : if (!bms_is_member(member, bms1) || !bms_is_member(member, bms2))
104 peter@eisentraut.org 745 [ # # ]:UBC 0 : elog(ERROR, "intersection contains invalid member %d, seed " UINT64_FORMAT,
746 : : member, seed);
747 : : }
363 michael@paquier.xyz 748 :CBC 1 : bms_free(result);
749 : : }
750 : :
751 : : /* Phase 3: Test range operations */
752 : 1 : result = NULL;
753 [ + + ]: 10001 : for (int i = 0; i < num_ops; i++)
754 : : {
755 : 10000 : int lower = pg_prng_uint32(&state) % 100;
756 : 10000 : int upper = lower + (pg_prng_uint32(&state) % 20);
757 : :
153 drowley@postgresql.o 758 [ - + ]: 10000 : CHECK_FOR_INTERRUPTS();
759 : :
363 michael@paquier.xyz 760 : 10000 : result = bms_add_range(result, lower, upper);
761 : : }
762 [ + - ]: 1 : if (result != NULL)
763 : : {
764 [ - + - - ]: 1 : EXPECT_TRUE(bms_num_members(result) > 0);
765 : 1 : bms_free(result);
766 : : }
767 : :
768 : 1 : bms_free(bms1);
769 : 1 : bms_free(bms2);
770 : :
771 : : /*
772 : : * Phase 4: mix of operations on a single set, cross-checking a bitmap
773 : : * with a secondary state, "members".
774 : : */
345 775 : 1 : num_members = 0;
776 : :
777 [ + + ]: 10001 : for (int op = 0; op < num_ops; op++)
778 : : {
153 drowley@postgresql.o 779 [ - + ]: 10000 : CHECK_FOR_INTERRUPTS();
780 : :
363 michael@paquier.xyz 781 [ + + + - ]: 10000 : switch (pg_prng_uint32(&state) % 3)
782 : : {
783 : 3310 : case 0: /* add */
26 drowley@postgresql.o 784 : 3310 : member = min_value + (pg_prng_uint32(&state) % range);
345 michael@paquier.xyz 785 [ + - ]: 3310 : if (!bms_is_member(member, bms))
786 : 3310 : members[num_members++] = member;
363 787 : 3310 : bms = bms_add_member(bms, member);
788 : 3310 : break;
789 : 3331 : case 1: /* delete */
345 790 [ + + ]: 3331 : if (num_members > 0)
791 : : {
792 : 3225 : int pos = pg_prng_uint32(&state) % num_members;
793 : :
794 : 3225 : member = members[pos];
795 [ - + ]: 3225 : if (!bms_is_member(member, bms))
104 peter@eisentraut.org 796 [ # # ]:UBC 0 : elog(ERROR, "expected %d to be a valid member, seed " UINT64_FORMAT,
797 : : member, seed);
798 : :
363 michael@paquier.xyz 799 :CBC 3225 : bms = bms_del_member(bms, member);
800 : :
801 : : /*
802 : : * Move the final array member at the position of the
803 : : * member just deleted, reducing the array size by one.
804 : : */
345 805 : 3225 : members[pos] = members[--num_members];
806 : : }
363 807 : 3331 : break;
808 : 3359 : case 2: /* test membership */
809 : : /* Verify that bitmap contains all members */
345 810 [ + + ]: 174209 : for (int i = 0; i < num_members; i++)
811 : : {
812 [ - + ]: 170850 : if (!bms_is_member(members[i], bms))
104 peter@eisentraut.org 813 [ # # ]:UBC 0 : elog(ERROR, "missing member %d, seed " UINT64_FORMAT,
814 : : members[i], seed);
815 : : }
363 michael@paquier.xyz 816 :CBC 3359 : break;
817 : : }
818 : 10000 : total_ops++;
819 : : }
820 : :
354 821 : 1 : bms_free(bms);
345 822 : 1 : pfree(members);
823 : :
363 824 : 1 : PG_RETURN_INT32(total_ops);
825 : : }
826 : :
827 : : /*
828 : : * Random testing for bms_offset_members(). Generates a random set and then
829 : : * picks a number to offset the members by. We then create another set, which
830 : : * is built by looping over the members of the random set and performing
831 : : * bms_add_member and adding on the offset to create a known good set to
832 : : * compare the result of bms_offset_members() to.
833 : : *
834 : : * Arguments:
835 : : * arg1: optional random seed. NULL means use a random seed.
836 : : * arg2: the number of operations to perform.
837 : : * arg3: the minimum bitmapset member number to use in the random set.
838 : : * arg4: the maximum bitmapset member number to use in the random set.
839 : : */
840 : : Datum
73 drowley@postgresql.o 841 :GNC 1 : test_random_offset_operations(PG_FUNCTION_ARGS)
842 : : {
843 : : pg_prng_state state;
844 : : int64 seed;
845 : : int num_ops;
846 : : int min_value;
847 : : int max_value;
848 : : int member;
849 : : uint32 range;
850 : :
851 [ + - ]: 1 : if (PG_ARGISNULL(0))
852 : 1 : seed = GetCurrentTimestamp();
853 : : else
73 drowley@postgresql.o 854 :UNC 0 : seed = PG_GETARG_INT64(0);
855 : :
27 drowley@postgresql.o 856 [ + - - + ]:GNC 1 : if (PG_ARGISNULL(1) || PG_GETARG_INT32(1) <= 0)
73 drowley@postgresql.o 857 [ # # ]:UNC 0 : elog(ERROR, "invalid number of operations");
27 drowley@postgresql.o 858 [ + - - + ]:GNC 1 : if (PG_ARGISNULL(2) || PG_GETARG_INT32(2) < 0)
73 drowley@postgresql.o 859 [ # # ]:UNC 0 : elog(ERROR, "invalid minimum value");
27 drowley@postgresql.o 860 [ + - - + ]:GNC 1 : if (PG_ARGISNULL(3) || PG_GETARG_INT32(3) < 0)
27 drowley@postgresql.o 861 [ # # ]:UNC 0 : elog(ERROR, "invalid maximum value");
862 : :
27 drowley@postgresql.o 863 :GNC 1 : num_ops = PG_GETARG_INT32(1);
864 : 1 : min_value = PG_GETARG_INT32(2);
865 : 1 : max_value = PG_GETARG_INT32(3);
866 : :
867 [ - + ]: 1 : if (max_value < min_value)
27 drowley@postgresql.o 868 [ # # ]:UNC 0 : elog(ERROR, "maximum value must be greater than or equal to minimum value");
869 : :
73 drowley@postgresql.o 870 :GNC 1 : pg_prng_seed(&state, (uint64) seed);
27 871 : 1 : range = (uint32) max_value - (uint32) min_value + 1;
872 : :
73 873 [ + + ]: 1001 : for (int op = 0; op < num_ops; op++)
874 : : {
875 : 1000 : Bitmapset *random_bms = NULL;
876 : : Bitmapset *offset_bms1;
877 : 1000 : Bitmapset *offset_bms2 = NULL;
878 : : int offset;
879 : : uint32 nmembers;
880 : :
881 [ - + ]: 1000 : CHECK_FOR_INTERRUPTS();
882 : :
883 : : /*
884 : : * Choose a random offset for passing to bms_offset_members(). We
885 : : * want a number between -max_value and max_value so we test both left
886 : : * and right shifting and also test cases that push members,
887 : : * occasionally all of them, off the bottom of the set.
888 : : */
27 889 : 1000 : offset = (int) (pg_prng_uint32(&state) % ((uint32) max_value + 1));
890 : 1000 : offset -= (int) (pg_prng_uint32(&state) % ((uint32) max_value + 1));
891 : :
892 : : /* decide how many members to add */
893 : 1000 : nmembers = pg_prng_uint32(&state) % range;
894 : :
895 : : /*
896 : : * Add a random number of members with values between the minimum and
897 : : * maximum values.
898 : : */
899 [ + + ]: 511709 : for (uint32 i = 0; i < nmembers; i++)
900 : : {
901 : 510709 : member = min_value + (pg_prng_uint32(&state) % range);
73 902 : 510709 : random_bms = bms_add_member(random_bms, member);
903 : : }
904 : :
905 : : /* create a known-good set the old fashioned way */
906 : 1000 : offset_bms2 = NULL;
907 : 1000 : member = -1;
908 [ + + ]: 377873 : while ((member = bms_next_member(random_bms, member)) >= 0)
909 : : {
910 [ + + ]: 376873 : if (member + offset >= 0)
911 : 313465 : offset_bms2 = bms_add_member(offset_bms2, member + offset);
912 : : }
913 : :
914 : : /* do the offsetting */
915 : 1000 : offset_bms1 = bms_offset_members(random_bms, offset);
916 : :
917 : : /* check against the known-good set */
918 [ - + ]: 1000 : if (!bms_equal(offset_bms1, offset_bms2))
73 drowley@postgresql.o 919 [ # # ]:UNC 0 : elog(ERROR, "bms_offset_members failed with offset %d seed " INT64_FORMAT, offset, seed);
920 : :
921 : : /* Cleanup before the next loop */
73 drowley@postgresql.o 922 :GNC 1000 : bms_free(random_bms);
923 : 1000 : bms_free(offset_bms1);
924 : 1000 : bms_free(offset_bms2);
925 : : }
926 : :
927 : 1 : PG_RETURN_INT32(num_ops);
928 : : }
|