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 "utils/array.h"
23 : #include "fmgr.h"
24 : #include "nodes/bitmapset.h"
25 : #include "nodes/nodes.h"
26 : #include "nodes/pg_list.h"
27 : #include "utils/builtins.h"
28 : #include "utils/timestamp.h"
29 :
30 0 : PG_MODULE_MAGIC;
31 :
32 : /* Bitmapset API functions in order of appearance in bitmapset.c */
33 0 : PG_FUNCTION_INFO_V1(test_bms_make_singleton);
34 0 : PG_FUNCTION_INFO_V1(test_bms_add_member);
35 0 : PG_FUNCTION_INFO_V1(test_bms_del_member);
36 0 : PG_FUNCTION_INFO_V1(test_bms_is_member);
37 0 : PG_FUNCTION_INFO_V1(test_bms_num_members);
38 0 : PG_FUNCTION_INFO_V1(test_bms_copy);
39 0 : PG_FUNCTION_INFO_V1(test_bms_equal);
40 0 : PG_FUNCTION_INFO_V1(test_bms_compare);
41 0 : PG_FUNCTION_INFO_V1(test_bms_is_subset);
42 0 : PG_FUNCTION_INFO_V1(test_bms_subset_compare);
43 0 : PG_FUNCTION_INFO_V1(test_bms_union);
44 0 : PG_FUNCTION_INFO_V1(test_bms_intersect);
45 0 : PG_FUNCTION_INFO_V1(test_bms_difference);
46 0 : PG_FUNCTION_INFO_V1(test_bms_is_empty);
47 0 : PG_FUNCTION_INFO_V1(test_bms_membership);
48 0 : PG_FUNCTION_INFO_V1(test_bms_singleton_member);
49 0 : PG_FUNCTION_INFO_V1(test_bms_get_singleton_member);
50 0 : PG_FUNCTION_INFO_V1(test_bms_next_member);
51 0 : PG_FUNCTION_INFO_V1(test_bms_prev_member);
52 0 : PG_FUNCTION_INFO_V1(test_bms_hash_value);
53 0 : PG_FUNCTION_INFO_V1(test_bms_overlap);
54 0 : PG_FUNCTION_INFO_V1(test_bms_overlap_list);
55 0 : PG_FUNCTION_INFO_V1(test_bms_nonempty_difference);
56 0 : PG_FUNCTION_INFO_V1(test_bms_member_index);
57 0 : PG_FUNCTION_INFO_V1(test_bms_add_range);
58 0 : PG_FUNCTION_INFO_V1(test_bms_add_members);
59 0 : PG_FUNCTION_INFO_V1(test_bms_int_members);
60 0 : PG_FUNCTION_INFO_V1(test_bms_del_members);
61 0 : PG_FUNCTION_INFO_V1(test_bms_replace_members);
62 0 : PG_FUNCTION_INFO_V1(test_bms_join);
63 0 : PG_FUNCTION_INFO_V1(test_bitmap_hash);
64 0 : PG_FUNCTION_INFO_V1(test_bitmap_match);
65 :
66 : /* Test utility functions */
67 0 : PG_FUNCTION_INFO_V1(test_random_operations);
68 :
69 : /* Convenient macros to test results */
70 : #define EXPECT_TRUE(expr) \
71 : do { \
72 : if (!(expr)) \
73 : elog(ERROR, \
74 : "%s was unexpectedly false in file \"%s\" line %u", \
75 : #expr, __FILE__, __LINE__); \
76 : } while (0)
77 :
78 : #define EXPECT_NOT_NULL(expr) \
79 : do { \
80 : if ((expr) == NULL) \
81 : elog(ERROR, \
82 : "%s was unexpectedly true in file \"%s\" line %u", \
83 : #expr, __FILE__, __LINE__); \
84 : } while (0)
85 :
86 : /* Encode/Decode to/from TEXT and Bitmapset */
87 : #define BITMAPSET_TO_TEXT(bms) cstring_to_text(nodeToString(bms))
88 : #define TEXT_TO_BITMAPSET(str) ((Bitmapset *) stringToNode(text_to_cstring(str)))
89 :
90 : /*
91 : * Helper macro to fetch text parameters as Bitmapsets. SQL-NULL means empty
92 : * set.
93 : */
94 : #define PG_ARG_GETBITMAPSET(n) \
95 : (PG_ARGISNULL(n) ? NULL : TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(n)))
96 :
97 : /*
98 : * Helper macro to handle converting sets back to text, returning the
99 : * resulting text representation of the set.
100 : */
101 : #define PG_RETURN_BITMAPSET_AS_TEXT(bms) \
102 : PG_RETURN_TEXT_P(BITMAPSET_TO_TEXT(bms))
103 :
104 : /*
105 : * Individual test functions for each bitmapset API function
106 : *
107 : * Primarily, we aim to keep these as close to simple wrapper functions as
108 : * possible in order to publish the functions of bitmapset.c to the SQL layer
109 : * with as little interference as possible. We opt to return SQL NULL in
110 : * cases where the input given to the SQL function isn't valid to pass to the
111 : * underlying bitmapset.c function. For example we cannot do much useful
112 : * testing if someone calls test_bms_make_singleton(NULL) since
113 : * bms_make_singleton() expects an integer argument.
114 : *
115 : * For function arguments which are to be converted to Bitmapsets, we accept
116 : * SQL NULL as a valid argument to mean an empty set. Optionally callers may
117 : * pass '(b)'.
118 : *
119 : * For the test functions which return a Bitmapset, these are converted back
120 : * to text with result generated by nodeToString().
121 : */
122 :
123 : Datum
124 0 : test_bms_add_member(PG_FUNCTION_ARGS)
125 : {
126 0 : Bitmapset *bms;
127 0 : int member;
128 :
129 0 : if (PG_ARGISNULL(1))
130 0 : PG_RETURN_NULL(); /* invalid input */
131 :
132 0 : bms = PG_ARG_GETBITMAPSET(0);
133 0 : member = PG_GETARG_INT32(1);
134 :
135 0 : bms = bms_add_member(bms, member);
136 :
137 0 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
138 0 : }
139 :
140 : Datum
141 0 : test_bms_add_members(PG_FUNCTION_ARGS)
142 : {
143 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
144 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
145 :
146 : /* left input is recycled */
147 0 : bms1 = bms_add_members(bms1, bms2);
148 :
149 0 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
150 0 : }
151 :
152 : Datum
153 0 : test_bms_del_member(PG_FUNCTION_ARGS)
154 : {
155 0 : Bitmapset *bms;
156 0 : int32 member;
157 :
158 0 : if (PG_ARGISNULL(1))
159 0 : PG_RETURN_NULL(); /* invalid input */
160 :
161 0 : bms = PG_ARG_GETBITMAPSET(0);
162 0 : member = PG_GETARG_INT32(1);
163 :
164 0 : bms = bms_del_member(bms, member);
165 :
166 0 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
167 0 : }
168 :
169 : Datum
170 0 : test_bms_is_member(PG_FUNCTION_ARGS)
171 : {
172 0 : Bitmapset *bms;
173 0 : int32 member;
174 0 : bool result;
175 :
176 0 : if (PG_ARGISNULL(1))
177 0 : PG_RETURN_NULL(); /* invalid input */
178 :
179 0 : bms = PG_ARG_GETBITMAPSET(0);
180 0 : member = PG_GETARG_INT32(1);
181 :
182 0 : result = bms_is_member(member, bms);
183 :
184 0 : PG_RETURN_BOOL(result);
185 0 : }
186 :
187 : Datum
188 0 : test_bms_num_members(PG_FUNCTION_ARGS)
189 : {
190 0 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
191 0 : int result;
192 :
193 0 : result = bms_num_members(bms);
194 :
195 0 : PG_RETURN_INT32(result);
196 0 : }
197 :
198 : Datum
199 0 : test_bms_make_singleton(PG_FUNCTION_ARGS)
200 : {
201 0 : Bitmapset *bms;
202 0 : int32 member;
203 :
204 0 : member = PG_GETARG_INT32(0);
205 0 : bms = bms_make_singleton(member);
206 :
207 0 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
208 0 : }
209 :
210 : Datum
211 0 : test_bms_copy(PG_FUNCTION_ARGS)
212 : {
213 0 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
214 0 : Bitmapset *copy_bms;
215 :
216 0 : copy_bms = bms_copy(bms);
217 :
218 0 : PG_RETURN_BITMAPSET_AS_TEXT(copy_bms);
219 0 : }
220 :
221 : Datum
222 0 : test_bms_equal(PG_FUNCTION_ARGS)
223 : {
224 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
225 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
226 0 : bool result;
227 :
228 0 : result = bms_equal(bms1, bms2);
229 :
230 0 : PG_RETURN_BOOL(result);
231 0 : }
232 :
233 : Datum
234 0 : test_bms_union(PG_FUNCTION_ARGS)
235 : {
236 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
237 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
238 0 : Bitmapset *result_bms;
239 :
240 0 : result_bms = bms_union(bms1, bms2);
241 :
242 0 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
243 0 : }
244 :
245 : Datum
246 0 : test_bms_membership(PG_FUNCTION_ARGS)
247 : {
248 0 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
249 0 : BMS_Membership result;
250 :
251 0 : result = bms_membership(bms);
252 :
253 0 : PG_RETURN_INT32((int32) result);
254 0 : }
255 :
256 : Datum
257 0 : test_bms_next_member(PG_FUNCTION_ARGS)
258 : {
259 0 : Bitmapset *bms;
260 0 : int32 prevmember;
261 0 : int result;
262 :
263 0 : if (PG_ARGISNULL(1))
264 0 : PG_RETURN_NULL(); /* invalid input */
265 :
266 0 : bms = PG_ARG_GETBITMAPSET(0);
267 0 : prevmember = PG_GETARG_INT32(1);
268 :
269 0 : result = bms_next_member(bms, prevmember);
270 :
271 0 : PG_RETURN_INT32(result);
272 0 : }
273 :
274 : Datum
275 0 : test_bms_intersect(PG_FUNCTION_ARGS)
276 : {
277 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
278 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
279 0 : Bitmapset *result_bms;
280 :
281 0 : result_bms = bms_intersect(bms1, bms2);
282 :
283 0 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
284 0 : }
285 :
286 : Datum
287 0 : test_bms_difference(PG_FUNCTION_ARGS)
288 : {
289 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
290 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
291 0 : Bitmapset *result_bms;
292 :
293 0 : result_bms = bms_difference(bms1, bms2);
294 :
295 0 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
296 0 : }
297 :
298 : Datum
299 0 : test_bms_compare(PG_FUNCTION_ARGS)
300 : {
301 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
302 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
303 0 : int result;
304 :
305 0 : result = bms_compare(bms1, bms2);
306 :
307 0 : PG_RETURN_INT32(result);
308 0 : }
309 :
310 : Datum
311 0 : test_bms_is_empty(PG_FUNCTION_ARGS)
312 : {
313 0 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
314 0 : bool result;
315 :
316 0 : result = bms_is_empty(bms);
317 :
318 0 : PG_RETURN_BOOL(result);
319 0 : }
320 :
321 : Datum
322 0 : test_bms_is_subset(PG_FUNCTION_ARGS)
323 : {
324 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
325 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
326 0 : bool result;
327 :
328 0 : result = bms_is_subset(bms1, bms2);
329 :
330 0 : PG_RETURN_BOOL(result);
331 0 : }
332 :
333 : Datum
334 0 : test_bms_subset_compare(PG_FUNCTION_ARGS)
335 : {
336 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
337 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
338 0 : BMS_Comparison result;
339 :
340 0 : result = bms_subset_compare(bms1, bms2);
341 :
342 0 : PG_RETURN_INT32((int32) result);
343 0 : }
344 :
345 : Datum
346 0 : test_bms_singleton_member(PG_FUNCTION_ARGS)
347 : {
348 0 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
349 0 : int result;
350 :
351 0 : result = bms_singleton_member(bms);
352 :
353 0 : PG_RETURN_INT32(result);
354 0 : }
355 :
356 : Datum
357 0 : test_bms_get_singleton_member(PG_FUNCTION_ARGS)
358 : {
359 0 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
360 0 : int member;
361 :
362 : /*
363 : * Keep this simple. Return -1 when we detect the set is not a singleton
364 : * set, otherwise return the singleton member.
365 : */
366 0 : if (!bms_get_singleton_member(bms, &member))
367 0 : member = -1;
368 :
369 0 : PG_RETURN_INT32(member);
370 0 : }
371 :
372 : Datum
373 0 : test_bms_prev_member(PG_FUNCTION_ARGS)
374 : {
375 0 : Bitmapset *bms;
376 0 : int32 prevmember;
377 0 : int result;
378 :
379 0 : if (PG_ARGISNULL(1))
380 0 : PG_RETURN_NULL(); /* invalid input */
381 :
382 0 : bms = PG_ARG_GETBITMAPSET(0);
383 0 : prevmember = PG_GETARG_INT32(1);
384 :
385 0 : result = bms_prev_member(bms, prevmember);
386 :
387 0 : PG_RETURN_INT32(result);
388 0 : }
389 :
390 : Datum
391 0 : test_bms_overlap(PG_FUNCTION_ARGS)
392 : {
393 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
394 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
395 0 : bool result;
396 :
397 0 : result = bms_overlap(bms1, bms2);
398 :
399 0 : PG_RETURN_BOOL(result);
400 0 : }
401 :
402 : Datum
403 0 : test_bms_overlap_list(PG_FUNCTION_ARGS)
404 : {
405 0 : Bitmapset *bms;
406 0 : ArrayType *array;
407 0 : List *int_list = NIL;
408 0 : bool result;
409 0 : Datum *elem_datums = NULL;
410 0 : bool *elem_nulls = NULL;
411 0 : int elem_count;
412 0 : int i;
413 :
414 0 : bms = PG_ARG_GETBITMAPSET(0);
415 :
416 0 : if (!PG_ARGISNULL(1))
417 : {
418 0 : array = PG_GETARG_ARRAYTYPE_P(1);
419 :
420 0 : deconstruct_array(array,
421 : INT4OID, sizeof(int32), true, 'i',
422 : &elem_datums, &elem_nulls, &elem_count);
423 :
424 0 : for (i = 0; i < elem_count; i++)
425 : {
426 0 : if (!elem_nulls[i])
427 : {
428 0 : int32 member = DatumGetInt32(elem_datums[i]);
429 :
430 0 : int_list = lappend_int(int_list, member);
431 0 : }
432 0 : }
433 0 : }
434 :
435 0 : result = bms_overlap_list(bms, int_list);
436 :
437 0 : list_free(int_list);
438 :
439 0 : if (elem_datums)
440 0 : pfree(elem_datums);
441 :
442 0 : if (elem_nulls)
443 0 : pfree(elem_nulls);
444 :
445 0 : PG_RETURN_BOOL(result);
446 0 : }
447 :
448 : Datum
449 0 : test_bms_nonempty_difference(PG_FUNCTION_ARGS)
450 : {
451 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
452 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
453 0 : bool result;
454 :
455 0 : result = bms_nonempty_difference(bms1, bms2);
456 :
457 0 : PG_RETURN_BOOL(result);
458 0 : }
459 :
460 : Datum
461 0 : test_bms_member_index(PG_FUNCTION_ARGS)
462 : {
463 0 : Bitmapset *bms;
464 0 : int32 member;
465 0 : int result;
466 :
467 0 : if (PG_ARGISNULL(1))
468 0 : PG_RETURN_NULL(); /* invalid input */
469 :
470 0 : bms = PG_ARG_GETBITMAPSET(0);
471 0 : member = PG_GETARG_INT32(1);
472 :
473 0 : result = bms_member_index(bms, member);
474 :
475 0 : PG_RETURN_INT32(result);
476 0 : }
477 :
478 : Datum
479 0 : test_bms_add_range(PG_FUNCTION_ARGS)
480 : {
481 0 : Bitmapset *bms;
482 0 : int32 lower,
483 : upper;
484 :
485 0 : if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
486 0 : PG_RETURN_NULL(); /* invalid input */
487 :
488 0 : bms = PG_ARG_GETBITMAPSET(0);
489 0 : lower = PG_GETARG_INT32(1);
490 0 : upper = PG_GETARG_INT32(2);
491 :
492 0 : bms = bms_add_range(bms, lower, upper);
493 :
494 0 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
495 0 : }
496 :
497 : Datum
498 0 : test_bms_int_members(PG_FUNCTION_ARGS)
499 : {
500 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
501 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
502 :
503 : /* left input gets recycled */
504 0 : bms1 = bms_int_members(bms1, bms2);
505 :
506 0 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
507 0 : }
508 :
509 : Datum
510 0 : test_bms_del_members(PG_FUNCTION_ARGS)
511 : {
512 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
513 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
514 :
515 : /* left input gets recycled */
516 0 : bms1 = bms_del_members(bms1, bms2);
517 :
518 0 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
519 0 : }
520 :
521 : Datum
522 0 : test_bms_replace_members(PG_FUNCTION_ARGS)
523 : {
524 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
525 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
526 :
527 : /* left input gets recycled */
528 0 : bms1 = bms_replace_members(bms1, bms2);
529 :
530 0 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
531 0 : }
532 :
533 : Datum
534 0 : test_bms_join(PG_FUNCTION_ARGS)
535 : {
536 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
537 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
538 0 : Bitmapset *result_bms;
539 :
540 : /* either input can be recycled */
541 0 : result_bms = bms_join(bms1, bms2);
542 :
543 0 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
544 0 : }
545 :
546 : Datum
547 0 : test_bms_hash_value(PG_FUNCTION_ARGS)
548 : {
549 0 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
550 0 : uint32 hash_result;
551 :
552 0 : hash_result = bms_hash_value(bms);
553 :
554 0 : PG_RETURN_INT32(hash_result);
555 0 : }
556 :
557 : Datum
558 0 : test_bitmap_hash(PG_FUNCTION_ARGS)
559 : {
560 0 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
561 0 : uint32 hash_result;
562 :
563 : /* Call bitmap_hash */
564 0 : hash_result = bitmap_hash(&bms, sizeof(Bitmapset *));
565 :
566 0 : PG_RETURN_INT32(hash_result);
567 0 : }
568 :
569 : Datum
570 0 : test_bitmap_match(PG_FUNCTION_ARGS)
571 : {
572 0 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
573 0 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
574 0 : int match_result;
575 :
576 : /* Call bitmap_match with addresses of the Bitmapset pointers */
577 0 : match_result = bitmap_match(&bms1, &bms2, sizeof(Bitmapset *));
578 :
579 0 : PG_RETURN_INT32(match_result);
580 0 : }
581 :
582 : /*
583 : * Contrary to all the other functions which are one-one mappings with the
584 : * equivalent C functions, this stresses Bitmapsets in a random fashion for
585 : * various operations.
586 : *
587 : * "min_value" is the minimal value used for the members, that will stand
588 : * up to a range of "max_range". "num_ops" defines the number of time each
589 : * operation is done. "seed" is a random seed used to calculate the member
590 : * values. When "seed" is <= 0, a random seed will be chosen automatically.
591 : *
592 : * The return value is the number of times all operations have been executed.
593 : */
594 : Datum
595 0 : test_random_operations(PG_FUNCTION_ARGS)
596 : {
597 0 : Bitmapset *bms1 = NULL;
598 0 : Bitmapset *bms2 = NULL;
599 0 : Bitmapset *bms = NULL;
600 0 : Bitmapset *result = NULL;
601 0 : pg_prng_state state;
602 0 : uint64 seed = GetCurrentTimestamp();
603 0 : int num_ops;
604 0 : int max_range;
605 0 : int min_value;
606 0 : int member;
607 0 : int *members;
608 0 : int num_members = 0;
609 0 : int total_ops = 0;
610 :
611 0 : if (PG_GETARG_INT32(0) > 0)
612 0 : seed = PG_GETARG_INT32(0);
613 :
614 0 : num_ops = PG_GETARG_INT32(1);
615 0 : max_range = PG_GETARG_INT32(2);
616 0 : min_value = PG_GETARG_INT32(3);
617 :
618 0 : pg_prng_seed(&state, seed);
619 :
620 : /*
621 : * There can be up to "num_ops" members added. This is very unlikely,
622 : * still possible if all the operations hit the "0" case during phase 4
623 : * where multiple operation types are mixed together.
624 : */
625 0 : members = palloc_array(int, num_ops);
626 :
627 : /* Phase 1: Random insertions in first set */
628 0 : for (int i = 0; i < num_ops / 2; i++)
629 : {
630 0 : member = pg_prng_uint32(&state) % max_range + min_value;
631 :
632 0 : if (!bms_is_member(member, bms1))
633 0 : members[num_members++] = member;
634 0 : bms1 = bms_add_member(bms1, member);
635 0 : }
636 :
637 : /* Phase 2: Random insertions in second set */
638 0 : for (int i = 0; i < num_ops / 4; i++)
639 : {
640 0 : member = pg_prng_uint32(&state) % max_range + min_value;
641 :
642 0 : if (!bms_is_member(member, bms2))
643 0 : members[num_members++] = member;
644 0 : bms2 = bms_add_member(bms2, member);
645 0 : }
646 :
647 : /* Test union */
648 0 : result = bms_union(bms1, bms2);
649 0 : EXPECT_NOT_NULL(result);
650 :
651 : /* Verify union contains all members from first and second sets */
652 0 : for (int i = 0; i < num_members; i++)
653 : {
654 0 : if (!bms_is_member(members[i], result))
655 0 : elog(ERROR, "union missing member %d", members[i]);
656 0 : }
657 0 : bms_free(result);
658 :
659 : /*
660 : * Test intersection, checking that all the members in the result are from
661 : * both the first and second sets.
662 : */
663 0 : result = bms_intersect(bms1, bms2);
664 0 : if (result != NULL)
665 : {
666 0 : member = -1;
667 :
668 0 : while ((member = bms_next_member(result, member)) >= 0)
669 : {
670 0 : if (!bms_is_member(member, bms1) || !bms_is_member(member, bms2))
671 0 : elog(ERROR, "intersection contains invalid member %d", member);
672 : }
673 0 : bms_free(result);
674 0 : }
675 :
676 : /* Phase 3: Test range operations */
677 0 : result = NULL;
678 0 : for (int i = 0; i < num_ops; i++)
679 : {
680 0 : int lower = pg_prng_uint32(&state) % 100;
681 0 : int upper = lower + (pg_prng_uint32(&state) % 20);
682 :
683 0 : result = bms_add_range(result, lower, upper);
684 0 : }
685 0 : if (result != NULL)
686 : {
687 0 : EXPECT_TRUE(bms_num_members(result) > 0);
688 0 : bms_free(result);
689 0 : }
690 :
691 0 : bms_free(bms1);
692 0 : bms_free(bms2);
693 :
694 : /*
695 : * Phase 4: mix of operations on a single set, cross-checking a bitmap
696 : * with a secondary state, "members".
697 : */
698 0 : num_members = 0;
699 :
700 0 : for (int op = 0; op < num_ops; op++)
701 : {
702 0 : switch (pg_prng_uint32(&state) % 3)
703 : {
704 : case 0: /* add */
705 0 : member = pg_prng_uint32(&state) % max_range + min_value;
706 0 : if (!bms_is_member(member, bms))
707 0 : members[num_members++] = member;
708 0 : bms = bms_add_member(bms, member);
709 0 : break;
710 : case 1: /* delete */
711 0 : if (num_members > 0)
712 : {
713 0 : int pos = pg_prng_uint32(&state) % num_members;
714 :
715 0 : member = members[pos];
716 0 : if (!bms_is_member(member, bms))
717 0 : elog(ERROR, "expected %d to be a valid member", member);
718 :
719 0 : bms = bms_del_member(bms, member);
720 :
721 : /*
722 : * Move the final array member at the position of the
723 : * member just deleted, reducing the array size by one.
724 : */
725 0 : members[pos] = members[--num_members];
726 0 : }
727 0 : break;
728 : case 2: /* test membership */
729 : /* Verify that bitmap contains all members */
730 0 : for (int i = 0; i < num_members; i++)
731 : {
732 0 : if (!bms_is_member(members[i], bms))
733 0 : elog(ERROR, "missing member %d", members[i]);
734 0 : }
735 0 : break;
736 : }
737 0 : total_ops++;
738 0 : }
739 :
740 0 : bms_free(bms);
741 0 : pfree(members);
742 :
743 0 : PG_RETURN_INT32(total_ops);
744 0 : }
|