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