PostgreSQL Source Code git master
Loading...
Searching...
No Matches
test_bitmapset.c
Go to the documentation of this file.
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
32
33/* Bitmapset API functions in order of appearance in bitmapset.c */
66
67/* Test utility functions */
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
124Datum
126{
127 Bitmapset *bms;
128 int member;
129
130 if (PG_ARGISNULL(1))
131 PG_RETURN_NULL(); /* invalid input */
132
134 member = PG_GETARG_INT32(1);
135
136 bms = bms_add_member(bms, member);
137
139}
140
141Datum
152
153Datum
155{
156 Bitmapset *bms;
157 int32 member;
158
159 if (PG_ARGISNULL(1))
160 PG_RETURN_NULL(); /* invalid input */
161
163 member = PG_GETARG_INT32(1);
164
165 bms = bms_del_member(bms, member);
166
168}
169
170Datum
172{
173 Bitmapset *bms;
174 int32 member;
175 bool result;
176
177 if (PG_ARGISNULL(1))
178 PG_RETURN_NULL(); /* invalid input */
179
181 member = PG_GETARG_INT32(1);
182
183 result = bms_is_member(member, bms);
184
186}
187
188Datum
198
199Datum
201{
202 Bitmapset *bms;
203 int32 member;
204
205 member = PG_GETARG_INT32(0);
206 bms = bms_make_singleton(member);
207
209}
210
211Datum
221
222Datum
233
234Datum
245
246Datum
256
257Datum
259{
260 Bitmapset *bms;
262 int result;
263
264 if (PG_ARGISNULL(1))
265 PG_RETURN_NULL(); /* invalid input */
266
269
271
273}
274
275Datum
286
287Datum
298
299Datum
310
311Datum
321
322Datum
333
334Datum
345
346Datum
356
357Datum
359{
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 if (!bms_get_singleton_member(bms, &member))
368 member = -1;
369
370 PG_RETURN_INT32(member);
371}
372
373Datum
375{
376 Bitmapset *bms;
378 int result;
379
380 if (PG_ARGISNULL(1))
381 PG_RETURN_NULL(); /* invalid input */
382
385
387
389}
390
391Datum
402
403Datum
405{
406 Bitmapset *bms;
407 ArrayType *array;
408 List *int_list = NIL;
409 bool result;
411 bool *elem_nulls = NULL;
412 int elem_count;
413 int i;
414
416
417 if (!PG_ARGISNULL(1))
418 {
419 array = PG_GETARG_ARRAYTYPE_P(1);
420
421 deconstruct_array(array,
422 INT4OID, sizeof(int32), true, 'i',
423 &elem_datums, &elem_nulls, &elem_count);
424
425 for (i = 0; i < elem_count; i++)
426 {
427 if (!elem_nulls[i])
428 {
429 int32 member = DatumGetInt32(elem_datums[i]);
430
431 int_list = lappend_int(int_list, member);
432 }
433 }
434 }
435
437
439
440 if (elem_datums)
442
443 if (elem_nulls)
444 pfree(elem_nulls);
445
447}
448
449Datum
460
461Datum
463{
464 Bitmapset *bms;
465 int32 member;
466 int result;
467
468 if (PG_ARGISNULL(1))
469 PG_RETURN_NULL(); /* invalid input */
470
472 member = PG_GETARG_INT32(1);
473
474 result = bms_member_index(bms, member);
475
477}
478
479Datum
481{
482 Bitmapset *bms;
483 int32 lower,
484 upper;
485
486 if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
487 PG_RETURN_NULL(); /* invalid input */
488
492
494
496}
497
498Datum
509
510Datum
521
522Datum
533
534Datum
546
547Datum
557
558Datum
560{
563
564 /* Call bitmap_hash */
565 hash_result = bitmap_hash(&bms, sizeof(Bitmapset *));
566
568}
569
570Datum
572{
575 int match_result;
576
577 /* Call bitmap_match with addresses of the Bitmapset pointers */
579
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 */
595Datum
597{
600 Bitmapset *bms = NULL;
604 int num_ops;
605 int max_range;
606 int min_value;
607 int member;
608 int *members;
609 int num_members = 0;
610 int total_ops = 0;
611
612 if (!PG_ARGISNULL(0))
613 seed = PG_GETARG_INT64(0);
614
618
619 if (PG_ARGISNULL(1) || num_ops <= 0)
620 elog(ERROR, "invalid number of operations");
621 if (PG_ARGISNULL(2) || max_range <= 0)
622 elog(ERROR, "invalid maximum range");
623 if (PG_ARGISNULL(3) || min_value < 0)
624 elog(ERROR, "invalid minimum value");
625
626 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 members = palloc_array(int, num_ops);
634
635 /* Phase 1: Random insertions in first set */
636 for (int i = 0; i < num_ops / 2; i++)
637 {
639
641
642 if (!bms_is_member(member, bms1))
643 members[num_members++] = member;
644 bms1 = bms_add_member(bms1, member);
645 }
646
647 /* Phase 2: Random insertions in second set */
648 for (int i = 0; i < num_ops / 4; i++)
649 {
651
653
654 if (!bms_is_member(member, bms2))
655 members[num_members++] = member;
656 bms2 = bms_add_member(bms2, member);
657 }
658
659 /* Test union */
662
663 /* Verify union contains all members from first and second sets */
664 for (int i = 0; i < num_members; i++)
665 {
667
668 if (!bms_is_member(members[i], result))
669 elog(ERROR, "union missing member %d, seed " INT64_FORMAT,
670 members[i], seed);
671 }
673
674 /*
675 * Test intersection, checking that all the members in the result are from
676 * both the first and second sets.
677 */
679 if (result != NULL)
680 {
681 member = -1;
682
683 while ((member = bms_next_member(result, member)) >= 0)
684 {
686
687 if (!bms_is_member(member, bms1) || !bms_is_member(member, bms2))
688 elog(ERROR, "intersection contains invalid member %d, seed " INT64_FORMAT,
689 member, seed);
690 }
692 }
693
694 /* Phase 3: Test range operations */
695 result = NULL;
696 for (int i = 0; i < num_ops; i++)
697 {
698 int lower = pg_prng_uint32(&state) % 100;
699 int upper = lower + (pg_prng_uint32(&state) % 20);
700
702
704 }
705 if (result != NULL)
706 {
709 }
710
711 bms_free(bms1);
712 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 num_members = 0;
719
720 for (int op = 0; op < num_ops; op++)
721 {
723
724 switch (pg_prng_uint32(&state) % 3)
725 {
726 case 0: /* add */
728 if (!bms_is_member(member, bms))
729 members[num_members++] = member;
730 bms = bms_add_member(bms, member);
731 break;
732 case 1: /* delete */
733 if (num_members > 0)
734 {
735 int pos = pg_prng_uint32(&state) % num_members;
736
737 member = members[pos];
738 if (!bms_is_member(member, bms))
739 elog(ERROR, "expected %d to be a valid member, seed " INT64_FORMAT,
740 member, seed);
741
742 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 members[pos] = members[--num_members];
749 }
750 break;
751 case 2: /* test membership */
752 /* Verify that bitmap contains all members */
753 for (int i = 0; i < num_members; i++)
754 {
755 if (!bms_is_member(members[i], bms))
756 elog(ERROR, "missing member %d, seed " INT64_FORMAT,
757 members[i], seed);
758 }
759 break;
760 }
761 total_ops++;
762 }
763
764 bms_free(bms);
765 pfree(members);
766
768}
#define PG_GETARG_ARRAYTYPE_P(n)
Definition array.h:263
void deconstruct_array(const ArrayType *array, Oid elmtype, int elmlen, bool elmbyval, char elmalign, Datum **elemsp, bool **nullsp, int *nelemsp)
TimestampTz GetCurrentTimestamp(void)
Definition timestamp.c:1639
Bitmapset * bms_replace_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:956
Bitmapset * bms_difference(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:346
int bms_prev_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1352
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:216
Bitmapset * bms_int_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:1093
Bitmapset * bms_intersect(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:292
uint32 bitmap_hash(const void *key, Size keysize)
Definition bitmapset.c:1424
bool bms_equal(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:142
BMS_Comparison bms_subset_compare(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:445
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1290
uint32 bms_hash_value(const Bitmapset *a)
Definition bitmapset.c:1408
Bitmapset * bms_del_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:1145
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition bitmapset.c:1003
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition bitmapset.c:852
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:412
int bms_singleton_member(const Bitmapset *a)
Definition bitmapset.c:665
void bms_free(Bitmapset *a)
Definition bitmapset.c:239
int bms_num_members(const Bitmapset *a)
Definition bitmapset.c:744
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:510
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:901
Bitmapset * bms_union(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:251
int bitmap_match(const void *key1, const void *key2, Size keysize)
Definition bitmapset.c:1434
BMS_Membership bms_membership(const Bitmapset *a)
Definition bitmapset.c:765
int bms_member_index(Bitmapset *a, int x)
Definition bitmapset.c:539
bool bms_overlap(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:575
int bms_compare(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:183
bool bms_get_singleton_member(const Bitmapset *a, int *member)
Definition bitmapset.c:708
Bitmapset * bms_join(Bitmapset *a, Bitmapset *b)
Definition bitmapset.c:1214
bool bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b)
Definition bitmapset.c:634
Bitmapset * bms_copy(const Bitmapset *a)
Definition bitmapset.c:122
bool bms_overlap_list(const Bitmapset *a, const List *b)
Definition bitmapset.c:601
#define bms_is_empty(a)
Definition bitmapset.h:118
BMS_Comparison
Definition bitmapset.h:61
BMS_Membership
Definition bitmapset.h:70
#define INT64_FORMAT
Definition c.h:634
int32_t int32
Definition c.h:620
uint64_t uint64
Definition c.h:625
uint32_t uint32
Definition c.h:624
uint32 result
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define palloc_array(type, count)
Definition fe_memutils.h:76
#define PG_ARGISNULL(n)
Definition fmgr.h:209
#define PG_RETURN_NULL()
Definition fmgr.h:346
#define PG_GETARG_INT64(n)
Definition fmgr.h:284
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_RETURN_INT32(x)
Definition fmgr.h:355
#define PG_GETARG_INT32(n)
Definition fmgr.h:269
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition fmgr.h:360
int i
Definition isn.c:77
List * lappend_int(List *list, int datum)
Definition list.c:357
void list_free(List *list)
Definition list.c:1546
void pfree(void *pointer)
Definition mcxt.c:1616
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
Datum lower(PG_FUNCTION_ARGS)
Datum upper(PG_FUNCTION_ARGS)
#define NIL
Definition pg_list.h:68
uint32 pg_prng_uint32(pg_prng_state *state)
Definition pg_prng.c:227
void pg_prng_seed(pg_prng_state *state, uint64 seed)
Definition pg_prng.c:89
uint64_t Datum
Definition postgres.h:70
static int32 DatumGetInt32(Datum X)
Definition postgres.h:202
static int fb(int x)
Definition pg_list.h:54
Datum test_bms_compare(PG_FUNCTION_ARGS)
Datum test_bms_difference(PG_FUNCTION_ARGS)
Datum test_random_operations(PG_FUNCTION_ARGS)
#define PG_RETURN_BITMAPSET_AS_TEXT(bms)
Datum test_bms_hash_value(PG_FUNCTION_ARGS)
#define EXPECT_TRUE(expr)
Datum test_bms_del_member(PG_FUNCTION_ARGS)
Datum test_bitmap_match(PG_FUNCTION_ARGS)
Datum test_bms_is_member(PG_FUNCTION_ARGS)
#define PG_ARG_GETBITMAPSET(n)
Datum test_bms_is_empty(PG_FUNCTION_ARGS)
Datum test_bms_add_member(PG_FUNCTION_ARGS)
Datum test_bms_int_members(PG_FUNCTION_ARGS)
Datum test_bms_add_members(PG_FUNCTION_ARGS)
PG_MODULE_MAGIC
Datum test_bitmap_hash(PG_FUNCTION_ARGS)
Datum test_bms_member_index(PG_FUNCTION_ARGS)
Datum test_bms_singleton_member(PG_FUNCTION_ARGS)
Datum test_bms_overlap_list(PG_FUNCTION_ARGS)
Datum test_bms_subset_compare(PG_FUNCTION_ARGS)
Datum test_bms_overlap(PG_FUNCTION_ARGS)
Datum test_bms_join(PG_FUNCTION_ARGS)
#define EXPECT_NOT_NULL(expr)
Datum test_bms_prev_member(PG_FUNCTION_ARGS)
Datum test_bms_equal(PG_FUNCTION_ARGS)
Datum test_bms_add_range(PG_FUNCTION_ARGS)
Datum test_bms_copy(PG_FUNCTION_ARGS)
Datum test_bms_make_singleton(PG_FUNCTION_ARGS)
Datum test_bms_replace_members(PG_FUNCTION_ARGS)
Datum test_bms_get_singleton_member(PG_FUNCTION_ARGS)
Datum test_bms_num_members(PG_FUNCTION_ARGS)
Datum test_bms_next_member(PG_FUNCTION_ARGS)
Datum test_bms_membership(PG_FUNCTION_ARGS)
Datum test_bms_union(PG_FUNCTION_ARGS)
Datum test_bms_intersect(PG_FUNCTION_ARGS)
Datum test_bms_del_members(PG_FUNCTION_ARGS)
Datum test_bms_is_subset(PG_FUNCTION_ARGS)
Datum test_bms_nonempty_difference(PG_FUNCTION_ARGS)