public inbox for [email protected]
help / color / mirror / Atom feedFrom: Greg Burd <[email protected]>
To: Robert Haas <[email protected]>
Cc: Michael Paquier <[email protected]>
Cc: PostgreSQL Hackers <[email protected]>
Cc: Nathan Bossart <[email protected]>
Cc: Masahiko Sawada <[email protected]>
Subject: Re: [PATCH] Add tests for Bitmapset
Date: Wed, 17 Sep 2025 10:53:26 -0400
Message-ID: <[email protected]> (raw)
In-Reply-To: <CA+TgmobSF4J_JWKdX=MWbG134Bn3OZ8LJyBP-_RQCykB=geaJw@mail.gmail.com>
References: <CA+TgmobSF4J_JWKdX=MWbG134Bn3OZ8LJyBP-_RQCykB=geaJw@mail.gmail.com>
On Sep 17 2025, at 9:55 am, Robert Haas <[email protected]> wrote:
> On Wed, Sep 17, 2025 at 9:18 AM Greg Burd <[email protected]> wrote:
>> > +static void
>> > +elog_bitmapset(int elevel, const char *label, const Bitmapset *bms)
>> >
>>
>> I added a function bms_values() that prints out the values contained in
>> a bitmapset and changed a few tests to use it. I removed the
>> elog_bitmapset() as it isn't being used and now that it's easy to output
>> the set in SQL why keep it for debugging?
>
> How about using outBitmapset() which already exists?
Robert,
Thanks for chiming in, not sure how I missed that one. :)
> --
> Robert Haas
> EDB: http://www.enterprisedb.com
I've updated the hash value for 32bit systems and changed the
bms_values() function to use outBitmapset() via bmsToString().
best.
-greg
Attachments:
[application/octet-stream] v6-0001-Add-a-module-that-tests-Bitmapset.patch (69.3K, 2-v6-0001-Add-a-module-that-tests-Bitmapset.patch)
download | inline diff:
From 2ba16fa314b01931a7fe20bec0e2917237df88ea Mon Sep 17 00:00:00 2001
From: Greg Burd <[email protected]>
Date: Wed, 13 Aug 2025 15:40:31 -0400
Subject: [PATCH v6] Add a module that tests Bitmapset
Basic tests for Bitmapset to ensure functionality and help prevent
unintentional breaking changes in the future.
---
src/test/modules/Makefile | 1 +
src/test/modules/meson.build | 1 +
src/test/modules/test_bitmapset/.gitignore | 4 +
src/test/modules/test_bitmapset/Makefile | 23 +
.../expected/test_bitmapset.out | 634 ++++++++++
src/test/modules/test_bitmapset/meson.build | 33 +
.../test_bitmapset/sql/test_bitmapset.sql | 372 ++++++
.../test_bitmapset/test_bitmapset--1.0.sql | 155 +++
.../modules/test_bitmapset/test_bitmapset.c | 1079 +++++++++++++++++
.../test_bitmapset/test_bitmapset.control | 4 +
10 files changed, 2306 insertions(+)
create mode 100644 src/test/modules/test_bitmapset/.gitignore
create mode 100644 src/test/modules/test_bitmapset/Makefile
create mode 100644 src/test/modules/test_bitmapset/expected/test_bitmapset.out
create mode 100644 src/test/modules/test_bitmapset/meson.build
create mode 100644 src/test/modules/test_bitmapset/sql/test_bitmapset.sql
create mode 100644 src/test/modules/test_bitmapset/test_bitmapset--1.0.sql
create mode 100644 src/test/modules/test_bitmapset/test_bitmapset.c
create mode 100644 src/test/modules/test_bitmapset/test_bitmapset.control
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 903a8ac151a..94071ec0e16 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -16,6 +16,7 @@ SUBDIRS = \
spgist_name_ops \
test_aio \
test_binaryheap \
+ test_bitmapset \
test_bloomfilter \
test_copy_callbacks \
test_custom_rmgrs \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 93be0f57289..d8f5c9c7494 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -15,6 +15,7 @@ subdir('spgist_name_ops')
subdir('ssl_passphrase_callback')
subdir('test_aio')
subdir('test_binaryheap')
+subdir('test_bitmapset')
subdir('test_bloomfilter')
subdir('test_copy_callbacks')
subdir('test_custom_rmgrs')
diff --git a/src/test/modules/test_bitmapset/.gitignore b/src/test/modules/test_bitmapset/.gitignore
new file mode 100644
index 00000000000..5dcb3ff9723
--- /dev/null
+++ b/src/test/modules/test_bitmapset/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/src/test/modules/test_bitmapset/Makefile b/src/test/modules/test_bitmapset/Makefile
new file mode 100644
index 00000000000..67199d40d73
--- /dev/null
+++ b/src/test/modules/test_bitmapset/Makefile
@@ -0,0 +1,23 @@
+# src/test/modules/test_bitmapset/Makefile
+
+MODULE_big = test_bitmapset
+OBJS = \
+ $(WIN32RES) \
+ test_bitmapset.o
+PGFILEDESC = "test_bitmapset - test code for src/include/nodes/bitmapset.h"
+
+EXTENSION = test_bitmapset
+DATA = test_bitmapset--1.0.sql
+
+REGRESS = test_bitmapset
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_bitmapset
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_bitmapset/expected/test_bitmapset.out b/src/test/modules/test_bitmapset/expected/test_bitmapset.out
new file mode 100644
index 00000000000..e609ec1c5e6
--- /dev/null
+++ b/src/test/modules/test_bitmapset/expected/test_bitmapset.out
@@ -0,0 +1,634 @@
+CREATE EXTENSION IF NOT EXISTS test_bitmapset;
+-- BASIC FUNCTIONALITY TESTS
+-- Test bms_values function
+SELECT 'values NULL' as test, bms_values(NULL) as result;
+ test | result
+-------------+--------
+ values NULL | (b)
+(1 row)
+
+SELECT 'values empty' as test, bms_values(test_bms_from_array(ARRAY[]::integer[])) as result;
+ test | result
+--------------+--------
+ values empty | (b)
+(1 row)
+
+SELECT 'values singleton' as test, bms_values(test_bms_make_singleton(42)) as result;
+ test | result
+------------------+--------
+ values singleton | (b 42)
+(1 row)
+
+SELECT 'values small set' as test, bms_values(test_bms_from_array(ARRAY[1,3,5])) as result;
+ test | result
+------------------+-----------
+ values small set | (b 1 3 5)
+(1 row)
+
+SELECT 'values larger set' as test, bms_values(test_bms_from_array(ARRAY[0,5,10,15,20])) as result;
+ test | result
+-------------------+------------------
+ values larger set | (b 0 5 10 15 20)
+(1 row)
+
+SELECT 'values unsorted input' as test, bms_values(test_bms_from_array(ARRAY[20,5,15,0,10])) as result;
+ test | result
+-----------------------+------------------
+ values unsorted input | (b 0 5 10 15 20)
+(1 row)
+
+-- Test 1: Basic utility functions
+SELECT 'NULL input to from_array' as test, test_bms_from_array(NULL) IS NULL as result;
+ test | result
+--------------------------+--------
+ NULL input to from_array | t
+(1 row)
+
+SELECT 'Empty array to from_array' as test, test_bms_from_array(ARRAY[]::integer[]) IS NULL as result;
+ test | result
+---------------------------+--------
+ Empty array to from_array | t
+(1 row)
+
+SELECT 'NULL input to to_array' as test, test_bms_to_array(NULL) IS NULL as result;
+ test | result
+------------------------+--------
+ NULL input to to_array | t
+(1 row)
+
+-- Test 2: Singleton operations
+SELECT 'make_singleton(42)' as test, test_bms_to_array(test_bms_make_singleton(42)) = ARRAY[42] as result;
+ test | result
+--------------------+--------
+ make_singleton(42) | t
+(1 row)
+
+SELECT 'make_singleton(0)' as test, test_bms_to_array(test_bms_make_singleton(0)) = ARRAY[0] as result;
+ test | result
+-------------------+--------
+ make_singleton(0) | t
+(1 row)
+
+SELECT 'make_singleton(1000)' as test, test_bms_to_array(test_bms_make_singleton(1000)) = ARRAY[1000] as result;
+ test | result
+----------------------+--------
+ make_singleton(1000) | t
+(1 row)
+
+-- Test 3: Add member operations
+SELECT 'add_member(NULL, 10)' as test, test_bms_to_array(test_bms_add_member(NULL, 10)) = ARRAY[10] as result;
+ test | result
+----------------------+--------
+ add_member(NULL, 10) | t
+(1 row)
+
+SELECT 'add_member consistency' as test, bms_values(test_bms_add_member(NULL, 10)) as values;
+ test | values
+------------------------+--------
+ add_member consistency | (b 10)
+(1 row)
+
+SELECT 'add_member to existing' as test, test_bms_to_array(test_bms_add_member(test_bms_make_singleton(5), 10)) = ARRAY[5,10] as result;
+ test | result
+------------------------+--------
+ add_member to existing | t
+(1 row)
+
+SELECT 'add_member sorted' as test, test_bms_to_array(test_bms_add_member(test_bms_make_singleton(10), 5)) = ARRAY[5,10] as result;
+ test | result
+-------------------+--------
+ add_member sorted | t
+(1 row)
+
+SELECT 'add_member idempotent' as test, bms_values(test_bms_add_member(test_bms_make_singleton(10), 10)) as values;
+ test | values
+-----------------------+--------
+ add_member idempotent | (b 10)
+(1 row)
+
+-- Test 4: Delete member operations
+SELECT 'del_member from NULL' as test, test_bms_del_member(NULL, 10) IS NULL as result;
+ test | result
+----------------------+--------
+ del_member from NULL | t
+(1 row)
+
+SELECT 'del_member singleton becomes empty' as test, test_bms_del_member(test_bms_make_singleton(10), 10) IS NULL as result;
+ test | result
+------------------------------------+--------
+ del_member singleton becomes empty | t
+(1 row)
+
+SELECT 'del_member no change' as test, bms_values(test_bms_del_member(test_bms_make_singleton(10), 5)) as values;
+ test | values
+----------------------+--------
+ del_member no change | (b 10)
+(1 row)
+
+SELECT 'del_member from middle' as test, test_bms_to_array(test_bms_del_member(test_bms_from_array(ARRAY[1,2,3]), 2)) = ARRAY[1,3] as result;
+ test | result
+------------------------+--------
+ del_member from middle | t
+(1 row)
+
+-- SET OPERATIONS TESTS
+-- Test 5: Union operations
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135,
+ test_bms_from_array(ARRAY[3,5,7]) AS set_357,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'union overlapping sets' as test,
+ test_bms_to_array(test_bms_union(set_135, set_357)) = ARRAY[1,3,5,7] as result
+FROM test_sets
+UNION ALL
+SELECT 'union with NULL' as test,
+ test_bms_to_array(test_bms_union(set_135, NULL)) = ARRAY[1,3,5] as result
+FROM test_sets
+UNION ALL
+SELECT 'union NULL with NULL' as test,
+ test_bms_union(NULL, NULL) IS NULL as result
+FROM test_sets;
+ test | result
+------------------------+--------
+ union overlapping sets | t
+ union with NULL | t
+ union NULL with NULL | t
+(3 rows)
+
+-- Test 6: Intersection operations
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135,
+ test_bms_from_array(ARRAY[3,5,7]) AS set_357,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'intersect overlapping sets' as test,
+ test_bms_to_array(test_bms_intersect(set_135, set_357)) = ARRAY[3,5] as result
+FROM test_sets
+UNION ALL
+SELECT 'intersect disjoint sets' as test,
+ test_bms_intersect(set_135, set_246) IS NULL as result
+FROM test_sets
+UNION ALL
+SELECT 'intersect with NULL' as test,
+ test_bms_intersect(set_135, NULL) IS NULL as result
+FROM test_sets;
+ test | result
+----------------------------+--------
+ intersect overlapping sets | t
+ intersect disjoint sets | t
+ intersect with NULL | t
+(3 rows)
+
+-- Test 7: Difference operations
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135,
+ test_bms_from_array(ARRAY[3,5,7]) AS set_357,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'difference overlapping sets' as test,
+ test_bms_to_array(test_bms_difference(set_135, set_357)) = ARRAY[1] as result
+FROM test_sets
+UNION ALL
+SELECT 'difference disjoint sets' as test,
+ test_bms_to_array(test_bms_difference(set_135, set_246)) = ARRAY[1,3,5] as result
+FROM test_sets
+UNION ALL
+SELECT 'difference identical sets' as test,
+ test_bms_difference(set_135, set_135) IS NULL as result
+FROM test_sets;
+ test | result
+-----------------------------+--------
+ difference overlapping sets | t
+ difference disjoint sets | t
+ difference identical sets | t
+(3 rows)
+
+-- MEMBERSHIP AND COMPARISON TESTS
+-- Test 8: Membership tests
+WITH test_set AS (SELECT test_bms_from_array(ARRAY[1,3,5]) AS set_135)
+SELECT 'is_member existing' as test, test_bms_is_member(set_135, 1) = true as result FROM test_set
+UNION ALL
+SELECT 'is_member missing' as test, test_bms_is_member(set_135, 2) = false as result FROM test_set
+UNION ALL
+SELECT 'is_member existing middle' as test, test_bms_is_member(set_135, 3) = true as result FROM test_set
+UNION ALL
+SELECT 'is_member NULL set' as test, test_bms_is_member(NULL, 1) = false as result FROM test_set;
+ test | result
+---------------------------+--------
+ is_member existing | t
+ is_member missing | t
+ is_member existing middle | t
+ is_member NULL set | t
+(4 rows)
+
+-- Test 9: Set cardinality
+SELECT 'num_members NULL' as test, test_bms_num_members(NULL) = 0 as result;
+ test | result
+------------------+--------
+ num_members NULL | t
+(1 row)
+
+SELECT 'num_members small set' as test, test_bms_num_members(test_bms_from_array(ARRAY[1,3,5])) = 3 as result;
+ test | result
+-----------------------+--------
+ num_members small set | t
+(1 row)
+
+SELECT 'num_members larger set' as test, test_bms_num_members(test_bms_from_array(ARRAY[2,4,6,8,10])) = 5 as result;
+ test | result
+------------------------+--------
+ num_members larger set | t
+(1 row)
+
+-- Test 10: Set equality and comparison
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_a,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_b,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246,
+ test_bms_from_array(ARRAY[1,3]) AS set_13
+)
+SELECT 'equal NULL NULL' as test, test_bms_equal(NULL, NULL) = true as result FROM test_sets
+UNION ALL
+SELECT 'equal NULL set' as test, test_bms_equal(NULL, set_135_a) = false as result FROM test_sets
+UNION ALL
+SELECT 'equal set NULL' as test, test_bms_equal(set_135_a, NULL) = false as result FROM test_sets
+UNION ALL
+SELECT 'equal identical sets' as test, test_bms_equal(set_135_a, set_135_b) = true as result FROM test_sets
+UNION ALL
+SELECT 'equal different sets' as test, test_bms_equal(set_135_a, set_246) = false as result FROM test_sets
+UNION ALL
+SELECT 'compare NULL NULL' as test, test_bms_compare(NULL, NULL) = 0 as result FROM test_sets
+UNION ALL
+SELECT 'compare NULL set' as test, test_bms_compare(NULL, set_13) = -1 as result FROM test_sets
+UNION ALL
+SELECT 'compare set NULL' as test, test_bms_compare(set_13, NULL) = 1 as result FROM test_sets
+UNION ALL
+SELECT 'compare equal sets' as test, test_bms_compare(set_13, set_13) = 0 as result FROM test_sets
+UNION ALL
+SELECT 'compare subset superset' as test, test_bms_compare(set_13, set_135_a) = -1 as result FROM test_sets
+UNION ALL
+SELECT 'compare superset subset' as test, test_bms_compare(set_135_a, set_13) = 1 as result FROM test_sets;
+ test | result
+-------------------------+--------
+ equal NULL NULL | t
+ equal NULL set | t
+ equal set NULL | t
+ equal identical sets | t
+ equal different sets | t
+ compare NULL NULL | t
+ compare NULL set | t
+ compare set NULL | t
+ compare equal sets | t
+ compare subset superset | t
+ compare superset subset | t
+(11 rows)
+
+-- ADVANCED OPERATIONS TESTS
+-- Test 11: Range operations
+SELECT 'add_range basic' as test, test_bms_to_array(test_bms_add_range(NULL, 5, 7)) = ARRAY[5,6,7] as result;
+ test | result
+-----------------+--------
+ add_range basic | t
+(1 row)
+
+SELECT 'add_range single element' as test, test_bms_to_array(test_bms_add_range(NULL, 5, 5)) = ARRAY[5] as result;
+ test | result
+--------------------------+--------
+ add_range single element | t
+(1 row)
+
+SELECT 'add_range to existing' as test, test_bms_to_array(test_bms_add_range(test_bms_from_array(ARRAY[1,10]), 5, 7)) = ARRAY[1,5,6,7,10] as result;
+ test | result
+-----------------------+--------
+ add_range to existing | t
+(1 row)
+
+-- Test 12: Membership types
+SELECT 'membership empty' as test, test_bms_membership(NULL) = 0 as result;
+ test | result
+------------------+--------
+ membership empty | t
+(1 row)
+
+SELECT 'membership singleton' as test, test_bms_membership(test_bms_from_array(ARRAY[42])) = 1 as result;
+ test | result
+----------------------+--------
+ membership singleton | t
+(1 row)
+
+SELECT 'membership multiple' as test, test_bms_membership(test_bms_from_array(ARRAY[1,2,3])) = 2 as result;
+ test | result
+---------------------+--------
+ membership multiple | t
+(1 row)
+
+-- Test 13: Singleton member extraction
+SELECT 'singleton_member valid' as test, test_bms_singleton_member(test_bms_from_array(ARRAY[42])) = 42 as result;
+ test | result
+------------------------+--------
+ singleton_member valid | t
+(1 row)
+
+-- Test 14: Set iteration
+WITH test_set AS (SELECT test_bms_from_array(ARRAY[5,10,15,20]) AS set_numbers)
+SELECT 'next_member first' as test, test_bms_next_member(set_numbers, -1) = 5 as result FROM test_set
+UNION ALL
+SELECT 'next_member second' as test, test_bms_next_member(set_numbers, 5) = 10 as result FROM test_set
+UNION ALL
+SELECT 'next_member past end' as test, test_bms_next_member(set_numbers, 20) = -2 as result FROM test_set
+UNION ALL
+SELECT 'next_member empty set' as test, test_bms_next_member(NULL, -1) = -2 as result FROM test_set
+UNION ALL
+SELECT 'prev_member last' as test, test_bms_prev_member(set_numbers, 21) = 20 as result FROM test_set
+UNION ALL
+SELECT 'prev_member penultimate' as test, test_bms_prev_member(set_numbers, 20) = 15 as result FROM test_set
+UNION ALL
+SELECT 'prev_member past beginning' as test, test_bms_prev_member(set_numbers, 5) = -2 as result FROM test_set
+UNION ALL
+SELECT 'prev_member empty set' as test, test_bms_prev_member(NULL, 100) = -2 as result FROM test_set;
+ test | result
+----------------------------+--------
+ next_member first | t
+ next_member second | t
+ next_member past end | t
+ next_member empty set | t
+ prev_member last | t
+ prev_member penultimate | t
+ prev_member past beginning | t
+ prev_member empty set | t
+(8 rows)
+
+-- Test 15: Hash functions
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135a,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135b,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'hash NULL' as test, test_bms_hash_value(NULL) = 0 as result FROM test_sets
+UNION ALL
+SELECT 'hash consistency' as test, test_bms_hash_value(set_135a) = test_bms_hash_value(set_135b) as result FROM test_sets
+UNION ALL
+SELECT 'hash different sets' as test, test_bms_hash_value(set_135a) != test_bms_hash_value(set_246) as result FROM test_sets;
+ test | result
+---------------------+--------
+ hash NULL | t
+ hash consistency | t
+ hash different sets | t
+(3 rows)
+
+-- Test 16: Set overlap
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135,
+ test_bms_from_array(ARRAY[3,5,7]) AS set_357,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'overlap existing' as test, test_bms_overlap(set_135, set_357) = true as result FROM test_sets
+UNION ALL
+SELECT 'overlap none' as test, test_bms_overlap(set_135, set_246) = false as result FROM test_sets
+UNION ALL
+SELECT 'overlap with NULL' as test, test_bms_overlap(NULL, set_135) = false as result FROM test_sets;
+ test | result
+-------------------+--------
+ overlap existing | t
+ overlap none | t
+ overlap with NULL | t
+(3 rows)
+
+-- Test 17: Subset relations
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3]) AS set_13,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135,
+ test_bms_from_array(ARRAY[2,4]) AS set_24
+)
+SELECT 'subset NULL is subset of all' as test, test_bms_is_subset(NULL, set_135) = true as result FROM test_sets
+UNION ALL
+SELECT 'subset proper subset' as test, test_bms_is_subset(set_13, set_135) = true as result FROM test_sets
+UNION ALL
+SELECT 'subset improper subset' as test, test_bms_is_subset(set_135, set_13) = false as result FROM test_sets
+UNION ALL
+SELECT 'subset disjoint sets' as test, test_bms_is_subset(set_13, set_24) = false as result FROM test_sets;
+ test | result
+------------------------------+--------
+ subset NULL is subset of all | t
+ subset proper subset | t
+ subset improper subset | t
+ subset disjoint sets | t
+(4 rows)
+
+-- Test 18: Copy operations
+WITH test_set AS (SELECT test_bms_from_array(ARRAY[1,3,5,7]) AS original)
+SELECT 'copy NULL' as test, test_bms_copy(NULL) IS NULL as result FROM test_set
+UNION ALL
+SELECT 'copy equality' as test, test_bms_equal(original, test_bms_copy(original)) = true as result FROM test_set;
+ test | result
+---------------+--------
+ copy NULL | t
+ copy equality | t
+(2 rows)
+
+-- Test 19: Add members operation
+WITH base_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3]) AS set_13,
+ test_bms_from_array(ARRAY[5,7]) AS set_57
+)
+SELECT 'add_members operation' as test,
+ test_bms_to_array(test_bms_add_members(set_13, set_57)) = ARRAY[1,3,5,7] as result
+FROM base_sets;
+ test | result
+-----------------------+--------
+ add_members operation | t
+(1 row)
+
+-- Test 20: Test hash consistency
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_a,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_b,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'bitmap_hash NULL' as test,
+ test_bitmap_hash(NULL) = 0 as result
+UNION ALL
+SELECT 'bitmap_hash consistency' as test,
+ test_bitmap_hash(set_135_a) = test_bitmap_hash(set_135_b) as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_hash vs bms_hash_value' as test,
+ test_bitmap_hash(set_135_a) = test_bms_hash_value(set_135_a) as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_hash different sets' as test,
+ test_bitmap_hash(set_135_a) != test_bitmap_hash(set_246) as result
+FROM test_sets;
+ test | result
+-------------------------------+--------
+ bitmap_hash NULL | t
+ bitmap_hash consistency | t
+ bitmap_hash vs bms_hash_value | t
+ bitmap_hash different sets | t
+(4 rows)
+
+-- Architecture-aware hash testing
+WITH arch_info AS (
+ SELECT
+ CASE
+ WHEN pg_column_size(1::bigint) = 8 THEN '64bit'
+ ELSE '32bit'
+ END as architecture
+),
+expected_values AS (
+ SELECT
+ architecture,
+ CASE architecture
+ WHEN '64bit' THEN 0
+ WHEN '32bit' THEN 0
+ END as hash_null,
+ CASE architecture
+ WHEN '64bit' THEN 49870778
+ WHEN '32bit' THEN 1509752520
+ END as hash_135,
+ CASE architecture
+ WHEN '64bit' THEN -303921606
+ WHEN '32bit' THEN -507280666
+ END as hash_246
+ FROM arch_info
+)
+SELECT 'expected hash NULL' as test,
+ test_bitmap_hash(NULL) = hash_null as result
+FROM expected_values
+UNION ALL
+SELECT 'expected hash [1,3,5]' as test,
+ test_bitmap_hash(test_bms_from_array(ARRAY[1,3,5])) = hash_135 as result
+FROM expected_values
+UNION ALL
+SELECT 'expected hash [2,4,6]' as test,
+ test_bitmap_hash(test_bms_from_array(ARRAY[2,4,6])) = hash_246 as result
+FROM expected_values;
+ test | result
+-----------------------+--------
+ expected hash NULL | t
+ expected hash [1,3,5] | t
+ expected hash [2,4,6] | t
+(3 rows)
+
+-- Test 21: bitmap_match function
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_a,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_b,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246,
+ test_bms_from_array(ARRAY[1,3]) AS set_13
+)
+SELECT 'bitmap_match NULL NULL (should be 0)' as test,
+ test_bitmap_match(NULL, NULL) = 0 as result
+UNION ALL
+SELECT 'bitmap_match NULL set (should be 1)' as test,
+ test_bitmap_match(NULL, set_135_a) = 1 as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match set NULL (should be 1)' as test,
+ test_bitmap_match(set_135_a, NULL) = 1 as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match identical sets (should be 0)' as test,
+ test_bitmap_match(set_135_a, set_135_b) = 0 as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match different sets (should be 1)' as test,
+ test_bitmap_match(set_135_a, set_246) = 1 as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match subset/superset (should be 1)' as test,
+ test_bitmap_match(set_13, set_135_a) = 1 as result
+FROM test_sets;
+ test | result
+--------------------------------------------+--------
+ bitmap_match NULL NULL (should be 0) | t
+ bitmap_match NULL set (should be 1) | t
+ bitmap_match set NULL (should be 1) | t
+ bitmap_match identical sets (should be 0) | t
+ bitmap_match different sets (should be 1) | t
+ bitmap_match subset/superset (should be 1) | t
+(6 rows)
+
+-- Test relationship with bms_equal
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_a,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_b,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'bitmap_match vs bms_equal (equal sets)' as test,
+ (test_bitmap_match(set_135_a, set_135_b) = 0) = test_bms_equal(set_135_a, set_135_b) as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match vs bms_equal (different sets)' as test,
+ (test_bitmap_match(set_135_a, set_246) = 0) = test_bms_equal(set_135_a, set_246) as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match vs bms_equal (NULL cases)' as test,
+ (test_bitmap_match(NULL, NULL) = 0) = test_bms_equal(NULL, NULL) as result
+FROM test_sets;
+ test | result
+--------------------------------------------+--------
+ bitmap_match vs bms_equal (equal sets) | t
+ bitmap_match vs bms_equal (different sets) | t
+ bitmap_match vs bms_equal (NULL cases) | t
+(3 rows)
+
+-- Test specific match values for debugging
+SELECT 'bitmap_match [1,3,5] vs [1,3,5]' as test,
+ test_bitmap_match(test_bms_from_array(ARRAY[1,3,5]), test_bms_from_array(ARRAY[1,3,5])) as match_value;
+ test | match_value
+---------------------------------+-------------
+ bitmap_match [1,3,5] vs [1,3,5] | 0
+(1 row)
+
+SELECT 'bitmap_match [1,3,5] vs [2,4,6]' as test,
+ test_bitmap_match(test_bms_from_array(ARRAY[1,3,5]), test_bms_from_array(ARRAY[2,4,6])) as match_value;
+ test | match_value
+---------------------------------+-------------
+ bitmap_match [1,3,5] vs [2,4,6] | 1
+(1 row)
+
+-- Test edge cases
+SELECT 'bitmap_match empty arrays' as test,
+ test_bitmap_match(test_bms_from_array(ARRAY[]::integer[]), test_bms_from_array(ARRAY[]::integer[])) = 0 as result;
+ test | result
+---------------------------+--------
+ bitmap_match empty arrays | t
+(1 row)
+
+-- Test 22: Random operations stress test
+SELECT 'random operations' as test, test_random_operations(12345, 1000) > 0 as result;
+ test | result
+-------------------+--------
+ random operations | t
+(1 row)
+
+-- ERROR CONDITION TESTS
+-- Test 23: Error conditions (these should produce ERRORs)
+-- Negative member tests
+SELECT test_bms_make_singleton(-1);
+ERROR: negative bitmapset member not allowed
+SELECT test_bms_is_member(NULL, -5);
+ERROR: negative bitmapset member not allowed
+SELECT test_bms_add_member(NULL, -10);
+ERROR: negative bitmapset member not allowed
+SELECT test_bms_del_member(NULL, -20);
+ERROR: negative bitmapset member not allowed
+SELECT test_bms_add_range(NULL, -5, 10);
+ERROR: negative bitmapset member not allowed
+-- Singleton member errors
+SELECT test_bms_singleton_member(test_bms_from_array(ARRAY[1,2]));
+ERROR: bitmapset has multiple members
+DROP EXTENSION test_bitmapset;
diff --git a/src/test/modules/test_bitmapset/meson.build b/src/test/modules/test_bitmapset/meson.build
new file mode 100644
index 00000000000..848f7a44eb9
--- /dev/null
+++ b/src/test/modules/test_bitmapset/meson.build
@@ -0,0 +1,33 @@
+# Copyright (c) 2024-2025, PostgreSQL Global Development Group
+
+test_bitmapset_sources = files(
+ 'test_bitmapset.c',
+)
+
+if host_system == 'windows'
+ test_bitmapset_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'test_bitmapset',
+ '--FILEDESC', 'test_bitmapset - test code for src/include/nodes/bitmapset.h',])
+endif
+
+test_bitmapset = shared_module('test_bitmapset',
+ test_bitmapset_sources,
+ kwargs: pg_test_mod_args,
+)
+test_install_libs += test_bitmapset
+
+test_install_data += files(
+ 'test_bitmapset.control',
+ 'test_bitmapset--1.0.sql',
+)
+
+tests += {
+ 'name': 'test_bitmapset',
+ 'sd': meson.current_source_dir(),
+ 'bd': meson.current_build_dir(),
+ 'regress': {
+ 'sql': [
+ 'test_bitmapset',
+ ],
+ },
+}
diff --git a/src/test/modules/test_bitmapset/sql/test_bitmapset.sql b/src/test/modules/test_bitmapset/sql/test_bitmapset.sql
new file mode 100644
index 00000000000..cb3496f45f5
--- /dev/null
+++ b/src/test/modules/test_bitmapset/sql/test_bitmapset.sql
@@ -0,0 +1,372 @@
+CREATE EXTENSION IF NOT EXISTS test_bitmapset;
+
+-- BASIC FUNCTIONALITY TESTS
+
+-- Test bms_values function
+
+SELECT 'values NULL' as test, bms_values(NULL) as result;
+SELECT 'values empty' as test, bms_values(test_bms_from_array(ARRAY[]::integer[])) as result;
+SELECT 'values singleton' as test, bms_values(test_bms_make_singleton(42)) as result;
+SELECT 'values small set' as test, bms_values(test_bms_from_array(ARRAY[1,3,5])) as result;
+SELECT 'values larger set' as test, bms_values(test_bms_from_array(ARRAY[0,5,10,15,20])) as result;
+SELECT 'values unsorted input' as test, bms_values(test_bms_from_array(ARRAY[20,5,15,0,10])) as result;
+
+-- Test 1: Basic utility functions
+SELECT 'NULL input to from_array' as test, test_bms_from_array(NULL) IS NULL as result;
+SELECT 'Empty array to from_array' as test, test_bms_from_array(ARRAY[]::integer[]) IS NULL as result;
+SELECT 'NULL input to to_array' as test, test_bms_to_array(NULL) IS NULL as result;
+
+-- Test 2: Singleton operations
+SELECT 'make_singleton(42)' as test, test_bms_to_array(test_bms_make_singleton(42)) = ARRAY[42] as result;
+SELECT 'make_singleton(0)' as test, test_bms_to_array(test_bms_make_singleton(0)) = ARRAY[0] as result;
+SELECT 'make_singleton(1000)' as test, test_bms_to_array(test_bms_make_singleton(1000)) = ARRAY[1000] as result;
+
+-- Test 3: Add member operations
+SELECT 'add_member(NULL, 10)' as test, test_bms_to_array(test_bms_add_member(NULL, 10)) = ARRAY[10] as result;
+SELECT 'add_member consistency' as test, bms_values(test_bms_add_member(NULL, 10)) as values;
+SELECT 'add_member to existing' as test, test_bms_to_array(test_bms_add_member(test_bms_make_singleton(5), 10)) = ARRAY[5,10] as result;
+SELECT 'add_member sorted' as test, test_bms_to_array(test_bms_add_member(test_bms_make_singleton(10), 5)) = ARRAY[5,10] as result;
+SELECT 'add_member idempotent' as test, bms_values(test_bms_add_member(test_bms_make_singleton(10), 10)) as values;
+
+
+-- Test 4: Delete member operations
+SELECT 'del_member from NULL' as test, test_bms_del_member(NULL, 10) IS NULL as result;
+SELECT 'del_member singleton becomes empty' as test, test_bms_del_member(test_bms_make_singleton(10), 10) IS NULL as result;
+SELECT 'del_member no change' as test, bms_values(test_bms_del_member(test_bms_make_singleton(10), 5)) as values;
+SELECT 'del_member from middle' as test, test_bms_to_array(test_bms_del_member(test_bms_from_array(ARRAY[1,2,3]), 2)) = ARRAY[1,3] as result;
+
+-- SET OPERATIONS TESTS
+
+-- Test 5: Union operations
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135,
+ test_bms_from_array(ARRAY[3,5,7]) AS set_357,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'union overlapping sets' as test,
+ test_bms_to_array(test_bms_union(set_135, set_357)) = ARRAY[1,3,5,7] as result
+FROM test_sets
+UNION ALL
+SELECT 'union with NULL' as test,
+ test_bms_to_array(test_bms_union(set_135, NULL)) = ARRAY[1,3,5] as result
+FROM test_sets
+UNION ALL
+SELECT 'union NULL with NULL' as test,
+ test_bms_union(NULL, NULL) IS NULL as result
+FROM test_sets;
+
+-- Test 6: Intersection operations
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135,
+ test_bms_from_array(ARRAY[3,5,7]) AS set_357,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'intersect overlapping sets' as test,
+ test_bms_to_array(test_bms_intersect(set_135, set_357)) = ARRAY[3,5] as result
+FROM test_sets
+UNION ALL
+SELECT 'intersect disjoint sets' as test,
+ test_bms_intersect(set_135, set_246) IS NULL as result
+FROM test_sets
+UNION ALL
+SELECT 'intersect with NULL' as test,
+ test_bms_intersect(set_135, NULL) IS NULL as result
+FROM test_sets;
+
+-- Test 7: Difference operations
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135,
+ test_bms_from_array(ARRAY[3,5,7]) AS set_357,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'difference overlapping sets' as test,
+ test_bms_to_array(test_bms_difference(set_135, set_357)) = ARRAY[1] as result
+FROM test_sets
+UNION ALL
+SELECT 'difference disjoint sets' as test,
+ test_bms_to_array(test_bms_difference(set_135, set_246)) = ARRAY[1,3,5] as result
+FROM test_sets
+UNION ALL
+SELECT 'difference identical sets' as test,
+ test_bms_difference(set_135, set_135) IS NULL as result
+FROM test_sets;
+
+-- MEMBERSHIP AND COMPARISON TESTS
+
+-- Test 8: Membership tests
+WITH test_set AS (SELECT test_bms_from_array(ARRAY[1,3,5]) AS set_135)
+SELECT 'is_member existing' as test, test_bms_is_member(set_135, 1) = true as result FROM test_set
+UNION ALL
+SELECT 'is_member missing' as test, test_bms_is_member(set_135, 2) = false as result FROM test_set
+UNION ALL
+SELECT 'is_member existing middle' as test, test_bms_is_member(set_135, 3) = true as result FROM test_set
+UNION ALL
+SELECT 'is_member NULL set' as test, test_bms_is_member(NULL, 1) = false as result FROM test_set;
+
+-- Test 9: Set cardinality
+SELECT 'num_members NULL' as test, test_bms_num_members(NULL) = 0 as result;
+SELECT 'num_members small set' as test, test_bms_num_members(test_bms_from_array(ARRAY[1,3,5])) = 3 as result;
+SELECT 'num_members larger set' as test, test_bms_num_members(test_bms_from_array(ARRAY[2,4,6,8,10])) = 5 as result;
+
+-- Test 10: Set equality and comparison
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_a,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_b,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246,
+ test_bms_from_array(ARRAY[1,3]) AS set_13
+)
+SELECT 'equal NULL NULL' as test, test_bms_equal(NULL, NULL) = true as result FROM test_sets
+UNION ALL
+SELECT 'equal NULL set' as test, test_bms_equal(NULL, set_135_a) = false as result FROM test_sets
+UNION ALL
+SELECT 'equal set NULL' as test, test_bms_equal(set_135_a, NULL) = false as result FROM test_sets
+UNION ALL
+SELECT 'equal identical sets' as test, test_bms_equal(set_135_a, set_135_b) = true as result FROM test_sets
+UNION ALL
+SELECT 'equal different sets' as test, test_bms_equal(set_135_a, set_246) = false as result FROM test_sets
+UNION ALL
+SELECT 'compare NULL NULL' as test, test_bms_compare(NULL, NULL) = 0 as result FROM test_sets
+UNION ALL
+SELECT 'compare NULL set' as test, test_bms_compare(NULL, set_13) = -1 as result FROM test_sets
+UNION ALL
+SELECT 'compare set NULL' as test, test_bms_compare(set_13, NULL) = 1 as result FROM test_sets
+UNION ALL
+SELECT 'compare equal sets' as test, test_bms_compare(set_13, set_13) = 0 as result FROM test_sets
+UNION ALL
+SELECT 'compare subset superset' as test, test_bms_compare(set_13, set_135_a) = -1 as result FROM test_sets
+UNION ALL
+SELECT 'compare superset subset' as test, test_bms_compare(set_135_a, set_13) = 1 as result FROM test_sets;
+
+-- ADVANCED OPERATIONS TESTS
+
+-- Test 11: Range operations
+SELECT 'add_range basic' as test, test_bms_to_array(test_bms_add_range(NULL, 5, 7)) = ARRAY[5,6,7] as result;
+SELECT 'add_range single element' as test, test_bms_to_array(test_bms_add_range(NULL, 5, 5)) = ARRAY[5] as result;
+SELECT 'add_range to existing' as test, test_bms_to_array(test_bms_add_range(test_bms_from_array(ARRAY[1,10]), 5, 7)) = ARRAY[1,5,6,7,10] as result;
+
+-- Test 12: Membership types
+SELECT 'membership empty' as test, test_bms_membership(NULL) = 0 as result;
+SELECT 'membership singleton' as test, test_bms_membership(test_bms_from_array(ARRAY[42])) = 1 as result;
+SELECT 'membership multiple' as test, test_bms_membership(test_bms_from_array(ARRAY[1,2,3])) = 2 as result;
+
+-- Test 13: Singleton member extraction
+SELECT 'singleton_member valid' as test, test_bms_singleton_member(test_bms_from_array(ARRAY[42])) = 42 as result;
+
+-- Test 14: Set iteration
+WITH test_set AS (SELECT test_bms_from_array(ARRAY[5,10,15,20]) AS set_numbers)
+SELECT 'next_member first' as test, test_bms_next_member(set_numbers, -1) = 5 as result FROM test_set
+UNION ALL
+SELECT 'next_member second' as test, test_bms_next_member(set_numbers, 5) = 10 as result FROM test_set
+UNION ALL
+SELECT 'next_member past end' as test, test_bms_next_member(set_numbers, 20) = -2 as result FROM test_set
+UNION ALL
+SELECT 'next_member empty set' as test, test_bms_next_member(NULL, -1) = -2 as result FROM test_set
+UNION ALL
+SELECT 'prev_member last' as test, test_bms_prev_member(set_numbers, 21) = 20 as result FROM test_set
+UNION ALL
+SELECT 'prev_member penultimate' as test, test_bms_prev_member(set_numbers, 20) = 15 as result FROM test_set
+UNION ALL
+SELECT 'prev_member past beginning' as test, test_bms_prev_member(set_numbers, 5) = -2 as result FROM test_set
+UNION ALL
+SELECT 'prev_member empty set' as test, test_bms_prev_member(NULL, 100) = -2 as result FROM test_set;
+
+-- Test 15: Hash functions
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135a,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135b,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'hash NULL' as test, test_bms_hash_value(NULL) = 0 as result FROM test_sets
+UNION ALL
+SELECT 'hash consistency' as test, test_bms_hash_value(set_135a) = test_bms_hash_value(set_135b) as result FROM test_sets
+UNION ALL
+SELECT 'hash different sets' as test, test_bms_hash_value(set_135a) != test_bms_hash_value(set_246) as result FROM test_sets;
+
+-- Test 16: Set overlap
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135,
+ test_bms_from_array(ARRAY[3,5,7]) AS set_357,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'overlap existing' as test, test_bms_overlap(set_135, set_357) = true as result FROM test_sets
+UNION ALL
+SELECT 'overlap none' as test, test_bms_overlap(set_135, set_246) = false as result FROM test_sets
+UNION ALL
+SELECT 'overlap with NULL' as test, test_bms_overlap(NULL, set_135) = false as result FROM test_sets;
+
+-- Test 17: Subset relations
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3]) AS set_13,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135,
+ test_bms_from_array(ARRAY[2,4]) AS set_24
+)
+SELECT 'subset NULL is subset of all' as test, test_bms_is_subset(NULL, set_135) = true as result FROM test_sets
+UNION ALL
+SELECT 'subset proper subset' as test, test_bms_is_subset(set_13, set_135) = true as result FROM test_sets
+UNION ALL
+SELECT 'subset improper subset' as test, test_bms_is_subset(set_135, set_13) = false as result FROM test_sets
+UNION ALL
+SELECT 'subset disjoint sets' as test, test_bms_is_subset(set_13, set_24) = false as result FROM test_sets;
+
+-- Test 18: Copy operations
+WITH test_set AS (SELECT test_bms_from_array(ARRAY[1,3,5,7]) AS original)
+SELECT 'copy NULL' as test, test_bms_copy(NULL) IS NULL as result FROM test_set
+UNION ALL
+SELECT 'copy equality' as test, test_bms_equal(original, test_bms_copy(original)) = true as result FROM test_set;
+
+-- Test 19: Add members operation
+WITH base_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3]) AS set_13,
+ test_bms_from_array(ARRAY[5,7]) AS set_57
+)
+SELECT 'add_members operation' as test,
+ test_bms_to_array(test_bms_add_members(set_13, set_57)) = ARRAY[1,3,5,7] as result
+FROM base_sets;
+
+-- Test 20: Test hash consistency
+
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_a,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_b,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'bitmap_hash NULL' as test,
+ test_bitmap_hash(NULL) = 0 as result
+UNION ALL
+SELECT 'bitmap_hash consistency' as test,
+ test_bitmap_hash(set_135_a) = test_bitmap_hash(set_135_b) as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_hash vs bms_hash_value' as test,
+ test_bitmap_hash(set_135_a) = test_bms_hash_value(set_135_a) as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_hash different sets' as test,
+ test_bitmap_hash(set_135_a) != test_bitmap_hash(set_246) as result
+FROM test_sets;
+
+-- Architecture-aware hash testing
+WITH arch_info AS (
+ SELECT
+ CASE
+ WHEN pg_column_size(1::bigint) = 8 THEN '64bit'
+ ELSE '32bit'
+ END as architecture
+),
+expected_values AS (
+ SELECT
+ architecture,
+ CASE architecture
+ WHEN '64bit' THEN 0
+ WHEN '32bit' THEN 0
+ END as hash_null,
+ CASE architecture
+ WHEN '64bit' THEN 49870778
+ WHEN '32bit' THEN 1509752520
+ END as hash_135,
+ CASE architecture
+ WHEN '64bit' THEN -303921606
+ WHEN '32bit' THEN -507280666
+ END as hash_246
+ FROM arch_info
+)
+SELECT 'expected hash NULL' as test,
+ test_bitmap_hash(NULL) = hash_null as result
+FROM expected_values
+UNION ALL
+SELECT 'expected hash [1,3,5]' as test,
+ test_bitmap_hash(test_bms_from_array(ARRAY[1,3,5])) = hash_135 as result
+FROM expected_values
+UNION ALL
+SELECT 'expected hash [2,4,6]' as test,
+ test_bitmap_hash(test_bms_from_array(ARRAY[2,4,6])) = hash_246 as result
+FROM expected_values;
+
+-- Test 21: bitmap_match function
+
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_a,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_b,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246,
+ test_bms_from_array(ARRAY[1,3]) AS set_13
+)
+SELECT 'bitmap_match NULL NULL (should be 0)' as test,
+ test_bitmap_match(NULL, NULL) = 0 as result
+UNION ALL
+SELECT 'bitmap_match NULL set (should be 1)' as test,
+ test_bitmap_match(NULL, set_135_a) = 1 as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match set NULL (should be 1)' as test,
+ test_bitmap_match(set_135_a, NULL) = 1 as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match identical sets (should be 0)' as test,
+ test_bitmap_match(set_135_a, set_135_b) = 0 as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match different sets (should be 1)' as test,
+ test_bitmap_match(set_135_a, set_246) = 1 as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match subset/superset (should be 1)' as test,
+ test_bitmap_match(set_13, set_135_a) = 1 as result
+FROM test_sets;
+
+-- Test relationship with bms_equal
+WITH test_sets AS (
+ SELECT
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_a,
+ test_bms_from_array(ARRAY[1,3,5]) AS set_135_b,
+ test_bms_from_array(ARRAY[2,4,6]) AS set_246
+)
+SELECT 'bitmap_match vs bms_equal (equal sets)' as test,
+ (test_bitmap_match(set_135_a, set_135_b) = 0) = test_bms_equal(set_135_a, set_135_b) as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match vs bms_equal (different sets)' as test,
+ (test_bitmap_match(set_135_a, set_246) = 0) = test_bms_equal(set_135_a, set_246) as result
+FROM test_sets
+UNION ALL
+SELECT 'bitmap_match vs bms_equal (NULL cases)' as test,
+ (test_bitmap_match(NULL, NULL) = 0) = test_bms_equal(NULL, NULL) as result
+FROM test_sets;
+
+-- Test specific match values for debugging
+SELECT 'bitmap_match [1,3,5] vs [1,3,5]' as test,
+ test_bitmap_match(test_bms_from_array(ARRAY[1,3,5]), test_bms_from_array(ARRAY[1,3,5])) as match_value;
+SELECT 'bitmap_match [1,3,5] vs [2,4,6]' as test,
+ test_bitmap_match(test_bms_from_array(ARRAY[1,3,5]), test_bms_from_array(ARRAY[2,4,6])) as match_value;
+
+-- Test edge cases
+SELECT 'bitmap_match empty arrays' as test,
+ test_bitmap_match(test_bms_from_array(ARRAY[]::integer[]), test_bms_from_array(ARRAY[]::integer[])) = 0 as result;
+
+-- Test 22: Random operations stress test
+SELECT 'random operations' as test, test_random_operations(12345, 1000) > 0 as result;
+
+-- ERROR CONDITION TESTS
+
+-- Test 23: Error conditions (these should produce ERRORs)
+
+-- Negative member tests
+SELECT test_bms_make_singleton(-1);
+SELECT test_bms_is_member(NULL, -5);
+SELECT test_bms_add_member(NULL, -10);
+SELECT test_bms_del_member(NULL, -20);
+SELECT test_bms_add_range(NULL, -5, 10);
+
+-- Singleton member errors
+SELECT test_bms_singleton_member(test_bms_from_array(ARRAY[1,2]));
+
+DROP EXTENSION test_bitmapset;
diff --git a/src/test/modules/test_bitmapset/test_bitmapset--1.0.sql b/src/test/modules/test_bitmapset/test_bitmapset--1.0.sql
new file mode 100644
index 00000000000..bcac98e2d85
--- /dev/null
+++ b/src/test/modules/test_bitmapset/test_bitmapset--1.0.sql
@@ -0,0 +1,155 @@
+/* src/test/modules/test_bitmapset/test_bitmapset--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_bitmapset" to load this file. \quit
+
+-- Utility functions
+CREATE FUNCTION test_bms_from_array(integer[])
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_to_array(bytea)
+RETURNS integer[]
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION bms_values(bytea)
+RETURNS text
+AS 'MODULE_PATHNAME' LANGUAGE C STABLE;
+
+-- Bitmapset API functions
+CREATE FUNCTION test_bms_make_singleton(integer)
+RETURNS bytea STRICT
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_add_member(bytea, integer)
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_del_member(bytea, integer)
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_is_member(bytea, integer)
+RETURNS boolean
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_num_members(bytea)
+RETURNS integer
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_copy(bytea)
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_equal(bytea, bytea)
+RETURNS boolean
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_compare(bytea, bytea)
+RETURNS integer
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_is_subset(bytea, bytea)
+RETURNS boolean
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_subset_compare(bytea, bytea)
+RETURNS integer
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_union(bytea, bytea)
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_intersect(bytea, bytea)
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_difference(bytea, bytea)
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_is_empty(bytea)
+RETURNS boolean
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_membership(bytea)
+RETURNS integer
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_singleton_member(bytea)
+RETURNS integer STRICT
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_get_singleton_member(bytea, integer)
+RETURNS integer
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_next_member(bytea, integer)
+RETURNS integer
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_prev_member(bytea, integer)
+RETURNS integer
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_hash_value(bytea)
+RETURNS integer
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_overlap(bytea, bytea)
+RETURNS boolean
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_overlap_list(bytea, bytea[])
+RETURNS boolean
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_nonempty_difference(bytea, bytea)
+RETURNS boolean
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_member_index(bytea, integer)
+RETURNS integer
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_add_range(bytea, integer, integer)
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_add_members(bytea, bytea)
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_replace_members(bytea, bytea)
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bms_join(bytea, bytea)
+RETURNS bytea
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bitmap_hash(bytea)
+RETURNS integer
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION test_bitmap_match(bytea, bytea)
+RETURNS int
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+-- Test utility functions
+CREATE FUNCTION test_random_operations(integer, integer)
+RETURNS integer STRICT
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
+CREATE FUNCTION create_test_bitmapset(members integer[])
+RETURNS bytea
+LANGUAGE SQL
+AS $$
+ SELECT CASE WHEN $1 IS NULL OR array_length($1, 1) = 0
+ THEN NULL::bytea
+ ELSE bitmapset_to_bytea($1)
+ END;
+$$;
+
+COMMENT ON EXTENSION test_bitmapset IS 'Test code for Bitmapset';
diff --git a/src/test/modules/test_bitmapset/test_bitmapset.c b/src/test/modules/test_bitmapset/test_bitmapset.c
new file mode 100644
index 00000000000..6dddd22b170
--- /dev/null
+++ b/src/test/modules/test_bitmapset/test_bitmapset.c
@@ -0,0 +1,1079 @@
+/*
+ * test_bitmapset.c
+ * Test module for bitmapset data structure
+ *
+ * This module tests the bitmapset implementation in PostgreSQL,
+ * covering all public API functions, edge cases, and memory usage.
+ *
+ * src/test/modules/test_bitmapset/test_bitmapset.c
+ */
+
+#include "postgres.h"
+
+#include <stddef.h>
+#include <string.h>
+
+#include "catalog/pg_type.h"
+#include "common/pg_prng.h"
+#include "utils/array.h"
+#include "fmgr.h"
+#include "nodes/bitmapset.h"
+#include "nodes/nodes.h"
+#include "nodes/pg_list.h"
+#include "utils/builtins.h"
+#include "varatt.h"
+
+PG_MODULE_MAGIC;
+
+/* Utility functions */
+PG_FUNCTION_INFO_V1(bms_values);
+PG_FUNCTION_INFO_V1(test_bms_from_array);
+PG_FUNCTION_INFO_V1(test_bms_to_array);
+
+/* Bitmapset API functions in order of appearance in bitmapset.c */
+PG_FUNCTION_INFO_V1(test_bms_make_singleton);
+PG_FUNCTION_INFO_V1(test_bms_add_member);
+PG_FUNCTION_INFO_V1(test_bms_del_member);
+PG_FUNCTION_INFO_V1(test_bms_is_member);
+PG_FUNCTION_INFO_V1(test_bms_num_members);
+PG_FUNCTION_INFO_V1(test_bms_copy);
+PG_FUNCTION_INFO_V1(test_bms_equal);
+PG_FUNCTION_INFO_V1(test_bms_compare);
+PG_FUNCTION_INFO_V1(test_bms_is_subset);
+PG_FUNCTION_INFO_V1(test_bms_subset_compare);
+PG_FUNCTION_INFO_V1(test_bms_union);
+PG_FUNCTION_INFO_V1(test_bms_intersect);
+PG_FUNCTION_INFO_V1(test_bms_difference);
+PG_FUNCTION_INFO_V1(test_bms_is_empty);
+PG_FUNCTION_INFO_V1(test_bms_membership);
+PG_FUNCTION_INFO_V1(test_bms_singleton_member);
+PG_FUNCTION_INFO_V1(test_bms_get_singleton_member);
+PG_FUNCTION_INFO_V1(test_bms_next_member);
+PG_FUNCTION_INFO_V1(test_bms_prev_member);
+PG_FUNCTION_INFO_V1(test_bms_hash_value);
+PG_FUNCTION_INFO_V1(test_bms_overlap);
+PG_FUNCTION_INFO_V1(test_bms_overlap_list);
+PG_FUNCTION_INFO_V1(test_bms_nonempty_difference);
+PG_FUNCTION_INFO_V1(test_bms_member_index);
+PG_FUNCTION_INFO_V1(test_bms_add_range);
+PG_FUNCTION_INFO_V1(test_bms_add_members);
+PG_FUNCTION_INFO_V1(test_bms_replace_members);
+PG_FUNCTION_INFO_V1(test_bms_join);
+PG_FUNCTION_INFO_V1(test_bitmap_hash);
+PG_FUNCTION_INFO_V1(test_bitmap_match);
+
+/* Test utility functions */
+PG_FUNCTION_INFO_V1(test_random_operations);
+
+/*
+ * Utility functions to convert between Bitmapset and bytea
+ * for passing data between SQL and C functions
+ */
+
+static bytea *
+encode_bms_to_bytea(Bitmapset *bms)
+{
+ bytea *result;
+ int bms_size;
+
+ if (bms == NULL)
+ return NULL;
+
+ bms_size = offsetof(Bitmapset, words) + bms->nwords * sizeof(bitmapword);
+ result = (bytea *) palloc0(VARHDRSZ + bms_size);
+ memcpy(VARDATA(result), bms, bms_size);
+ SET_VARSIZE(result, VARHDRSZ + bms_size);
+
+ return result;
+}
+
+static Bitmapset *
+decode_bytea_to_bms(bytea *data)
+{
+ Bitmapset *bms;
+ int bms_size;
+
+ if (data == NULL)
+ return NULL;
+
+ bms_size = VARSIZE_ANY_EXHDR(data);
+
+ if (bms_size < offsetof(Bitmapset, words))
+ return NULL;
+
+ bms = (Bitmapset *) palloc(bms_size);
+ memcpy(bms, VARDATA_ANY(data), bms_size);
+
+ if (bms->nwords < 0 ||
+ bms->nwords > (bms_size - offsetof(Bitmapset, words)) / sizeof(bitmapword))
+ {
+ pfree(bms);
+ return NULL;
+ }
+
+ return bms;
+}
+
+Datum
+bms_values(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms;
+ char *str;
+ text *result;
+
+ if (PG_ARGISNULL(0))
+ {
+ result = cstring_to_text("(b)");
+ PG_RETURN_TEXT_P(result);
+ }
+
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+ str = bmsToString(bms);
+
+ if (bms)
+ bms_free(bms);
+
+ result = cstring_to_text(str);
+
+ PG_RETURN_TEXT_P(result);
+}
+
+/*
+ * Individual test functions for each bitmapset API function
+ */
+
+Datum
+test_bms_add_member(PG_FUNCTION_ARGS)
+{
+ int member;
+ Bitmapset *bms = NULL;
+ Bitmapset *result_bms;
+ bytea *result;
+
+ if (PG_ARGISNULL(1))
+ PG_RETURN_NULL();
+
+ if (!PG_ARGISNULL(0))
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ member = PG_GETARG_INT32(1);
+ result_bms = bms_add_member(bms, member);
+ result = encode_bms_to_bytea(result_bms);
+
+ if (bms && bms != result_bms)
+ bms_free(bms);
+ if (result_bms)
+ bms_free(result_bms);
+
+ if (result == NULL)
+ PG_RETURN_NULL();
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_add_members(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ Bitmapset *result_bms;
+ bytea *result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ /* IMPORTANT: bms_add_members modifies/frees the first argument */
+ result_bms = bms_add_members(bms1, bms2);
+ /* bms1 is now invalid, don't free it */
+
+ if (bms2)
+ bms_free(bms2);
+
+ if (result_bms == NULL)
+ PG_RETURN_NULL();
+
+ result = encode_bms_to_bytea(result_bms);
+ bms_free(result_bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_del_member(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ int32 member;
+ bytea *result;
+
+ if (PG_ARGISNULL(1))
+ PG_RETURN_NULL();
+
+ if (!PG_ARGISNULL(0))
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ member = PG_GETARG_INT32(1);
+ bms = bms_del_member(bms, member);
+
+ if (bms == NULL || bms_is_empty(bms))
+ {
+ if (bms)
+ bms_free(bms);
+ PG_RETURN_NULL();
+ }
+
+ result = encode_bms_to_bytea(bms);
+ bms_free(bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_is_member(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ int32 member;
+ bool result;
+
+ if (PG_ARGISNULL(1))
+ PG_RETURN_BOOL(false);
+
+ if (!PG_ARGISNULL(0))
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ member = PG_GETARG_INT32(1);
+ result = bms_is_member(member, bms);
+
+ if (bms)
+ bms_free(bms);
+
+ PG_RETURN_BOOL(result);
+}
+
+Datum
+test_bms_num_members(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ int result = 0;
+
+ if (!PG_ARGISNULL(0))
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ result = bms_num_members(bms);
+
+ if (bms)
+ bms_free(bms);
+
+ PG_RETURN_INT32(result);
+}
+
+Datum
+test_bms_make_singleton(PG_FUNCTION_ARGS)
+{
+ int32 member;
+ Bitmapset *bms;
+ bytea *result;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ member = PG_GETARG_INT32(0);
+ bms = bms_make_singleton(member);
+
+ result = encode_bms_to_bytea(bms);
+ bms_free(bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_copy(PG_FUNCTION_ARGS)
+{
+ bytea *bms_data;
+ Bitmapset *bms = NULL;
+ Bitmapset *copy_bms;
+ bytea *result;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ bms_data = PG_GETARG_BYTEA_PP(0);
+ bms = decode_bytea_to_bms(bms_data);
+ copy_bms = bms_copy(bms);
+ result = encode_bms_to_bytea(copy_bms);
+
+ if (bms)
+ bms_free(bms);
+ if (copy_bms)
+ bms_free(copy_bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_equal(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ bool result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ result = bms_equal(bms1, bms2);
+
+ if (bms1)
+ bms_free(bms1);
+ if (bms2)
+ bms_free(bms2);
+
+ PG_RETURN_BOOL(result);
+}
+
+Datum
+test_bms_union(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ Bitmapset *result_bms;
+ bytea *result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ result_bms = bms_union(bms1, bms2);
+
+ if (bms1)
+ bms_free(bms1);
+ if (bms2)
+ bms_free(bms2);
+
+ if (result_bms == NULL)
+ PG_RETURN_NULL();
+
+ result = encode_bms_to_bytea(result_bms);
+ bms_free(result_bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_membership(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ BMS_Membership result;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_INT32(BMS_EMPTY_SET);
+
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+ result = bms_membership(bms);
+
+ if (bms)
+ bms_free(bms);
+
+ PG_RETURN_INT32((int32) result);
+}
+
+Datum
+test_bms_next_member(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ int32 prevmember;
+ int result;
+
+ if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
+ PG_RETURN_INT32(-2);
+
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+ prevmember = PG_GETARG_INT32(1);
+ result = bms_next_member(bms, prevmember);
+
+ if (bms)
+ bms_free(bms);
+
+ PG_RETURN_INT32(result);
+}
+
+Datum
+test_bms_from_array(PG_FUNCTION_ARGS)
+{
+ ArrayType *array;
+ int *members;
+ int nmembers;
+ Bitmapset *bms = NULL;
+ bytea *result;
+ int i;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ array = PG_GETARG_ARRAYTYPE_P(0);
+
+ /* Check element type first */
+ if (ARR_ELEMTYPE(array) != INT4OID)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("integer array expected")));
+
+ /* Handle empty arrays - ARR_NDIM can be 0 for empty arrays */
+ if (ARR_NDIM(array) == 0)
+ PG_RETURN_NULL();
+
+ /* Now check dimensions */
+ if (ARR_NDIM(array) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("integer array expected")));
+
+ nmembers = ARR_DIMS(array)[0];
+
+ /* Double-check for empty */
+ if (nmembers == 0)
+ PG_RETURN_NULL();
+
+ if (ARR_HASNULL(array))
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("integer array expected")));
+
+ members = (int *) ARR_DATA_PTR(array);
+
+ for (i = 0; i < nmembers; i++)
+ bms = bms_add_member(bms, members[i]);
+
+ result = encode_bms_to_bytea(bms);
+ bms_free(bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_to_array(PG_FUNCTION_ARGS)
+{
+ bytea *bms_data;
+ Bitmapset *bms = NULL;
+ ArrayType *result;
+ Datum *datums;
+ int nmembers;
+ int member = -1;
+ int i = 0;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ bms_data = PG_GETARG_BYTEA_PP(0);
+ bms = decode_bytea_to_bms(bms_data);
+
+ if (bms == NULL || bms_is_empty(bms))
+ {
+ if (bms)
+ bms_free(bms);
+ PG_RETURN_NULL();
+ }
+
+ nmembers = bms_num_members(bms);
+ datums = (Datum *) palloc(nmembers * sizeof(Datum));
+
+ while ((member = bms_next_member(bms, member)) >= 0)
+ datums[i++] = Int32GetDatum(member);
+
+ bms_free(bms);
+
+ result = construct_array(datums, nmembers,
+ INT4OID, sizeof(int32), true, 'i');
+
+ pfree(datums);
+ PG_RETURN_ARRAYTYPE_P(result);
+}
+
+Datum
+test_bms_intersect(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ Bitmapset *result_bms;
+ bytea *result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ result_bms = bms_intersect(bms1, bms2);
+
+ if (bms1)
+ bms_free(bms1);
+ if (bms2)
+ bms_free(bms2);
+
+ if (result_bms == NULL)
+ PG_RETURN_NULL();
+
+ result = encode_bms_to_bytea(result_bms);
+ bms_free(result_bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_difference(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ Bitmapset *result_bms;
+ bytea *result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ result_bms = bms_difference(bms1, bms2);
+
+ if (bms1)
+ bms_free(bms1);
+ if (bms2)
+ bms_free(bms2);
+
+ if (result_bms == NULL)
+ PG_RETURN_NULL();
+
+ result = encode_bms_to_bytea(result_bms);
+ bms_free(result_bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_compare(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ int result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ result = bms_compare(bms1, bms2);
+
+ if (bms1)
+ bms_free(bms1);
+ if (bms2)
+ bms_free(bms2);
+
+ PG_RETURN_INT32(result);
+}
+
+Datum
+test_bms_is_empty(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ bool result;
+
+ if (!PG_ARGISNULL(0))
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ result = bms_is_empty(bms);
+
+ if (bms)
+ bms_free(bms);
+
+ PG_RETURN_BOOL(result);
+}
+
+Datum
+test_bms_is_subset(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ bool result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ result = bms_is_subset(bms1, bms2);
+
+ if (bms1)
+ bms_free(bms1);
+ if (bms2)
+ bms_free(bms2);
+
+ PG_RETURN_BOOL(result);
+}
+
+Datum
+test_bms_subset_compare(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ BMS_Comparison result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ result = bms_subset_compare(bms1, bms2);
+
+ if (bms1)
+ bms_free(bms1);
+ if (bms2)
+ bms_free(bms2);
+
+ PG_RETURN_INT32((int32) result);
+}
+
+Datum
+test_bms_singleton_member(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ int result;
+
+ if (!PG_ARGISNULL(0))
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ result = bms_singleton_member(bms);
+
+ if (bms)
+ bms_free(bms);
+
+ PG_RETURN_INT32(result);
+}
+
+Datum
+test_bms_get_singleton_member(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ int32 default_member = PG_GETARG_INT32(1);
+ int member;
+ bool success;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_INT32(default_member);
+
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ /*
+ * bms_get_singleton_member returns bool and stores result in member
+ * pointer
+ */
+ success = bms_get_singleton_member(bms, &member);
+ bms_free(bms);
+
+ if (success)
+ PG_RETURN_INT32(member);
+ else
+ PG_RETURN_INT32(default_member);
+}
+
+Datum
+test_bms_prev_member(PG_FUNCTION_ARGS)
+{
+ bytea *bms_data;
+ Bitmapset *bms = NULL;
+ int32 prevmember;
+ int result;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_INT32(-2);
+
+ bms_data = PG_GETARG_BYTEA_PP(0);
+ prevmember = PG_GETARG_INT32(1);
+
+ if (VARSIZE_ANY_EXHDR(bms_data) == 0)
+ PG_RETURN_INT32(-2);
+
+ bms = decode_bytea_to_bms(bms_data);
+ result = bms_prev_member(bms, prevmember);
+ bms_free(bms);
+
+ PG_RETURN_INT32(result);
+}
+
+Datum
+test_bms_overlap(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ bool result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ result = bms_overlap(bms1, bms2);
+
+ if (bms1)
+ bms_free(bms1);
+ if (bms2)
+ bms_free(bms2);
+
+ PG_RETURN_BOOL(result);
+}
+
+Datum
+test_bms_overlap_list(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ ArrayType *array;
+ List *bitmapset_list = NIL;
+ ListCell *lc;
+ bool result;
+ Datum *elem_datums;
+ bool *elem_nulls;
+ int elem_count;
+ int i;
+
+ /* Handle first argument (the bitmapset) */
+ if (PG_ARGISNULL(0))
+ PG_RETURN_BOOL(false);
+
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ /* Handle second argument (array of bitmapsets) */
+ if (PG_ARGISNULL(1))
+ {
+ if (bms)
+ bms_free(bms);
+ PG_RETURN_BOOL(false);
+ }
+
+ array = PG_GETARG_ARRAYTYPE_P(1);
+
+ /* Validate array */
+ if (ARR_NDIM(array) != 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ errmsg("one-dimensional array expected")));
+
+ if (ARR_ELEMTYPE(array) != BYTEAOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("bytea array expected")));
+
+ /* Deconstruct the array */
+ deconstruct_array(array,
+ BYTEAOID, -1, false, 'i',
+ &elem_datums, &elem_nulls, &elem_count);
+
+ /* Convert each bytea element to a Bitmapset and add to list */
+ for (i = 0; i < elem_count; i++)
+ {
+ Bitmapset *list_bms = NULL;
+
+ if (!elem_nulls[i])
+ {
+ bytea *elem_bytea = DatumGetByteaP(elem_datums[i]);
+
+ list_bms = decode_bytea_to_bms(elem_bytea);
+ }
+
+ /* Add to list (even if NULL) */
+ bitmapset_list = lappend(bitmapset_list, list_bms);
+ }
+
+ /* Call the actual bms_overlap_list function */
+ result = bms_overlap_list(bms, bitmapset_list);
+
+ /* Clean up */
+ if (bms)
+ bms_free(bms);
+
+ /* Free all bitmapsets in the list */
+ foreach(lc, bitmapset_list)
+ {
+ Bitmapset *list_bms = (Bitmapset *) lfirst(lc);
+
+ if (list_bms)
+ bms_free(list_bms);
+ }
+ list_free(bitmapset_list);
+
+ /* Free array deconstruction results */
+ pfree(elem_datums);
+ pfree(elem_nulls);
+
+ PG_RETURN_BOOL(result);
+}
+
+Datum
+test_bms_nonempty_difference(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ bool result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ result = bms_nonempty_difference(bms1, bms2);
+
+ if (bms1)
+ bms_free(bms1);
+ if (bms2)
+ bms_free(bms2);
+
+ PG_RETURN_BOOL(result);
+}
+
+Datum
+test_bms_member_index(PG_FUNCTION_ARGS)
+{
+ bytea *bms_data;
+ Bitmapset *bms = NULL;
+ int32 member;
+ int result;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_INT32(-1);
+
+ bms_data = PG_GETARG_BYTEA_PP(0);
+ member = PG_GETARG_INT32(1);
+
+ if (VARSIZE_ANY_EXHDR(bms_data) == 0)
+ PG_RETURN_INT32(-1);
+
+ bms = decode_bytea_to_bms(bms_data);
+
+ result = bms_member_index(bms, member);
+ bms_free(bms);
+
+ PG_RETURN_INT32(result);
+}
+
+Datum
+test_bms_add_range(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ int32 lower,
+ upper;
+ bytea *result;
+
+ if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
+ PG_RETURN_NULL();
+
+ if (!PG_ARGISNULL(0))
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ lower = PG_GETARG_INT32(1);
+ upper = PG_GETARG_INT32(2);
+
+ /* Check for invalid range */
+ if (upper < lower)
+ {
+ if (bms)
+ bms_free(bms);
+ PG_RETURN_NULL();
+ }
+
+ bms = bms_add_range(bms, lower, upper);
+
+ result = encode_bms_to_bytea(bms);
+ if (bms)
+ bms_free(bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_replace_members(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ Bitmapset *result_bms;
+ bytea *result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ /* IMPORTANT: bms_replace_members modifies/frees the first argument */
+ result_bms = bms_replace_members(bms1, bms2);
+ /* bms1 is now invalid, don't free it */
+
+ if (bms2)
+ bms_free(bms2);
+
+ if (result_bms == NULL)
+ PG_RETURN_NULL();
+
+ result = encode_bms_to_bytea(result_bms);
+ bms_free(result_bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_join(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ Bitmapset *result_bms;
+ bytea *result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ /* IMPORTANT: bms_join modifies/frees the first argument */
+ result_bms = bms_join(bms1, bms2);
+ /* bms1 is now invalid! Don't free it */
+
+ if (bms2)
+ bms_free(bms2);
+
+ if (result_bms == NULL)
+ PG_RETURN_NULL();
+
+ result = encode_bms_to_bytea(result_bms);
+ bms_free(result_bms);
+
+ PG_RETURN_BYTEA_P(result);
+}
+
+Datum
+test_bms_hash_value(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ uint32 hash_result;
+
+ if (!PG_ARGISNULL(0))
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ hash_result = bms_hash_value(bms);
+
+ if (bms)
+ bms_free(bms);
+
+ PG_RETURN_INT32(hash_result);
+}
+
+Datum
+test_bitmap_hash(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms = NULL;
+ Bitmapset *bms_ptr;
+ uint32 hash_result;
+
+ if (!PG_ARGISNULL(0))
+ bms = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ bms_ptr = bms;
+
+ /* Call bitmap_hash */
+ hash_result = bitmap_hash(&bms_ptr, sizeof(Bitmapset *));
+
+ /* Clean up */
+ if (!PG_ARGISNULL(0) && bms_ptr)
+ bms_free(bms_ptr);
+
+ PG_RETURN_INT32(hash_result);
+}
+
+Datum
+test_bitmap_match(PG_FUNCTION_ARGS)
+{
+ Bitmapset *bms1 = NULL,
+ *bms2 = NULL;
+ Bitmapset *bms_ptr1,
+ *bms_ptr2;
+ int match_result;
+
+ if (!PG_ARGISNULL(0))
+ bms1 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(0));
+
+ if (!PG_ARGISNULL(1))
+ bms2 = decode_bytea_to_bms(PG_GETARG_BYTEA_PP(1));
+
+ /* Set up pointers to the Bitmapsets */
+ bms_ptr1 = bms1;
+ bms_ptr2 = bms2;
+
+ /* Call bitmap_match with addresses of the Bitmapset pointers */
+ match_result = bitmap_match(&bms_ptr1, &bms_ptr2, sizeof(Bitmapset *));
+
+ /* Clean up */
+ if (bms1)
+ bms_free(bms1);
+ if (bms2)
+ bms_free(bms2);
+
+ PG_RETURN_INT32(match_result);
+}
+
+Datum
+test_random_operations(PG_FUNCTION_ARGS)
+{
+ pg_prng_state state;
+ int32 seed;
+ int32 num_ops;
+ Bitmapset *bms = NULL;
+ int op,
+ member;
+ int total_ops = 0;
+
+ seed = PG_GETARG_INT32(0);
+ num_ops = PG_GETARG_INT32(1);
+
+ pg_prng_seed(&state, (uint64) seed);
+
+ for (int i = 0; i < num_ops; i++)
+ {
+ op = pg_prng_uint32(&state) % 3; /* 0=add, 1=delete, 2=test */
+ member = pg_prng_uint32(&state) % 1000;
+
+ switch (op)
+ {
+ case 0: /* add */
+ bms = bms_add_member(bms, member);
+ total_ops++;
+ break;
+ case 1: /* delete */
+ if (bms != NULL)
+ {
+ bms = bms_del_member(bms, member);
+ total_ops++;
+ }
+ break;
+ case 2: /* test membership */
+ if (bms != NULL)
+ {
+ bms_is_member(member, bms);
+ total_ops++;
+ }
+ break;
+ }
+ }
+
+ if (bms)
+ bms_free(bms);
+
+ PG_RETURN_INT32(total_ops);
+}
diff --git a/src/test/modules/test_bitmapset/test_bitmapset.control b/src/test/modules/test_bitmapset/test_bitmapset.control
new file mode 100644
index 00000000000..8d02ec8bf0a
--- /dev/null
+++ b/src/test/modules/test_bitmapset/test_bitmapset.control
@@ -0,0 +1,4 @@
+comment = 'Test code for Bitmapset'
+default_version = '1.0'
+module_pathname = '$libdir/test_bitmapset'
+relocatable = true
--
2.49.0
view thread (81+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: [PATCH] Add tests for Bitmapset
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox