From a3ce1eeb35d3430ce55e8e7189c3aa8e74645db6 Mon Sep 17 00:00:00 2001
From: Greg Burd <greg@burd.me>
Date: Thu, 25 Sep 2025 12:14:49 -0400
Subject: [PATCH v1] Further improve the test coverage of Bitmapset

Improve test coverage on both bitmapset.c and the code that tests
it, test_bitmapset.c with a few more tests that trigger branch
conditions and special cases.

This extends commit 00c3d87 and f83fe65.

Author: Greg Burd <greg@burd.me>
Reviewed-by: Michael Paquier <michael@paquier.xyz>
Discussion: https://postgr.es/m/3027069.1758606227@sss.pgh.pa.us
---
 .../expected/test_bitmapset.out               | 852 +++++++++++++++++-
 .../test_bitmapset/sql/test_bitmapset.sql     | 173 +++-
 .../test_bitmapset/test_bitmapset--1.0.sql    |   4 +
 .../modules/test_bitmapset/test_bitmapset.c   |  62 +-
 4 files changed, 1013 insertions(+), 78 deletions(-)

diff --git a/src/test/modules/test_bitmapset/expected/test_bitmapset.out b/src/test/modules/test_bitmapset/expected/test_bitmapset.out
index abbfef1f7a6..cb6d71fb7b1 100644
--- a/src/test/modules/test_bitmapset/expected/test_bitmapset.out
+++ b/src/test/modules/test_bitmapset/expected/test_bitmapset.out
@@ -83,6 +83,13 @@ SELECT test_bms_replace_members('(b 1 2)', '(b 3 5 7)') AS result;
  (b 3 5 7)
 (1 row)
 
+-- Force repalloc() with larger set
+SELECT test_bms_replace_members('(b 1 2 3 4 5)', '(b 500 600)') AS result;
+   result    
+-------------
+ (b 500 600)
+(1 row)
+
 -- bms_del_member()
 SELECT test_bms_del_member('(b)', -20); -- error
 ERROR:  negative bitmapset member not allowed
@@ -124,6 +131,74 @@ SELECT test_bms_del_member(test_bms_add_range('(b)', 30, 34), 32) AS result;
  (b 30 31 33 34)
 (1 row)
 
+-- Force word count changes
+SELECT test_bms_del_member('(b 1 200)', 200) AS result;
+ result 
+--------
+ (b 1)
+(1 row)
+
+SELECT test_bms_del_member('(b 1 50 100 200)', 200) AS result;
+    result    
+--------------
+ (b 1 50 100)
+(1 row)
+
+SELECT test_bms_del_member('(b 1 50 100 200)', 100) AS result;
+    result    
+--------------
+ (b 1 50 200)
+(1 row)
+
+-- bms_del_members()
+SELECT test_bms_del_members('(b)', '(b 10)') AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_del_members('(b 10)', '(b 10)') AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_del_members('(b 10)', '(b 5)') AS result;
+ result 
+--------
+ (b 10)
+(1 row)
+
+SELECT test_bms_del_members('(b 1 2 3)', '(b 2)') AS result;
+ result  
+---------
+ (b 1 3)
+(1 row)
+
+SELECT test_bms_del_members('(b 5 100)', '(b 100)') AS result;
+ result 
+--------
+ (b 5)
+(1 row)
+
+SELECT test_bms_del_members('(b 5 100 200)', '(b 200)') AS result;
+  result   
+-----------
+ (b 5 100)
+(1 row)
+
+SELECT test_bms_del_members('(b 1 2 100 200 300)', '(b 1 2)') AS result;
+     result      
+-----------------
+ (b 100 200 300)
+(1 row)
+
+SELECT test_bms_del_members('(b 1 2 100 200 300)', '(b 200 300)') AS result;
+   result    
+-------------
+ (b 1 2 100)
+(1 row)
+
 -- bms_join()
 SELECT test_bms_join('(b 1 3 5)', NULL) AS result;
   result   
@@ -149,8 +224,21 @@ SELECT test_bms_join('(b 1 3 5)', '(b 1 4 5)') AS result;
  (b 1 3 4 5)
 (1 row)
 
+-- Force word count changes
+SELECT test_bms_join('(b 5)', '(b 100)') AS result;
+  result   
+-----------
+ (b 5 100)
+(1 row)
+
+SELECT test_bms_join('(b 1 2)', '(b 100 200 300)') AS result;
+       result        
+---------------------
+ (b 1 2 100 200 300)
+(1 row)
+
 -- bms_union()
--- Overlapping sets.
+-- Overlapping sets
 SELECT test_bms_union('(b 1 3 5)', '(b 3 5 7)') AS result;
    result    
 -------------
@@ -181,6 +269,19 @@ SELECT test_bms_union(
  (b 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20)
 (1 row)
 
+-- Union with varrying word counts
+SELECT test_bms_union('(b 1 2)', '(b 100 300)') AS result;
+     result      
+-----------------
+ (b 1 2 100 300)
+(1 row)
+
+SELECT test_bms_union('(b 100 300)', '(b 1 2)') AS result;
+     result      
+-----------------
+ (b 1 2 100 300)
+(1 row)
+
 -- bms_intersect()
 -- Overlapping sets
 SELECT test_bms_intersect('(b 1 3 5)', '(b 3 5 7)') AS result;
@@ -196,13 +297,26 @@ SELECT test_bms_intersect('(b 1 3 5)', '(b 2 4 6)') AS result;
  
 (1 row)
 
--- Intersect with empty.
+-- Intersect with empty
 SELECT test_bms_intersect('(b 1 3 5)', '(b)') AS result;
  result 
 --------
  
 (1 row)
 
+-- Intersect with varrying word counts
+SELECT test_bms_intersect('(b 1 300)', '(b 1 2 3 4 5)') AS result;
+ result 
+--------
+ (b 1)
+(1 row)
+
+SELECT test_bms_intersect('(b 1 2 3 4 5)', '(b 1 300)') AS result;
+ result 
+--------
+ (b 1)
+(1 row)
+
 -- bms_int_members()
 -- Overlapping sets
 SELECT test_bms_int_members('(b 1 3 5)', '(b 3 5 7)') AS result;
@@ -218,7 +332,7 @@ SELECT test_bms_int_members('(b 1 3 5)', '(b 2 4 6)') AS result;
  
 (1 row)
 
--- Intersect with empty.
+-- Intersect with empty
 SELECT test_bms_int_members('(b 1 3 5)', '(b)') AS result;
  result 
 --------
@@ -262,13 +376,26 @@ SELECT test_bms_difference('(b 42)', '(b 42)') AS result;
 (1 row)
 
 -- Subtraction edge case
-SELECT test_bms_difference(
+SELECT length(test_bms_difference(
          test_bms_add_range('(b)', 0, 100),
          test_bms_add_range('(b)', 50, 150)
-       ) AS result;
-                                                                     result                                                                      
--------------------------------------------------------------------------------------------------------------------------------------------------
- (b 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49)
+       )) AS result;
+ result 
+--------
+    143
+(1 row)
+
+-- Difference with different word counts
+SELECT test_bms_difference('(b 5 100)', '(b 5)') AS result;
+ result  
+---------
+ (b 100)
+(1 row)
+
+SELECT test_bms_difference('(b 1 2 100 200)', '(b 1 2)') AS result;
+   result    
+-------------
+ (b 100 200)
 (1 row)
 
 -- bms_is_member()
@@ -323,6 +450,25 @@ SELECT test_bms_member_index('(b 1 3 5)', 3) AS result;
       1
 (1 row)
 
+-- Member index with various word positions
+SELECT test_bms_member_index('(b 100 200)', 100) AS result;
+ result 
+--------
+      0
+(1 row)
+
+SELECT test_bms_member_index('(b 100 200)', 200) AS result;
+ result 
+--------
+      1
+(1 row)
+
+SELECT test_bms_member_index('(b 1 50 100 200)', 200) AS result;
+ result 
+--------
+      3
+(1 row)
+
 -- bms_num_members()
 SELECT test_bms_num_members('(b)') AS result;
  result 
@@ -373,6 +519,19 @@ SELECT test_bms_equal('(b 1 3 5)', '(b 2 4 6)') AS result;
  f
 (1 row)
 
+-- Equal with different word counts
+SELECT test_bms_equal('(b 5)', '(b 100)') AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_equal('(b 5 10)', '(b 100 200 300)') AS result;
+ result 
+--------
+ f
+(1 row)
+
 -- bms_compare()
 SELECT test_bms_compare('(b)', '(b)') AS result;
  result 
@@ -474,6 +633,19 @@ SELECT length(test_bms_add_range('(b)', 1000, 1100)) AS result;
     508
 (1 row)
 
+-- Force word count expansion
+SELECT test_bms_add_range('(b 5)', 100, 105) AS result;
+            result             
+-------------------------------
+ (b 5 100 101 102 103 104 105)
+(1 row)
+
+SELECT length(test_bms_add_range('(b 1 2)', 200, 250)) AS result;
+ result 
+--------
+    211
+(1 row)
+
 -- bms_membership()
 SELECT test_bms_membership('(b)') AS result;
  result 
@@ -513,6 +685,8 @@ SELECT test_bms_is_empty('(b 1)') AS result;
 (1 row)
 
 -- bms_singleton_member()
+SELECT test_bms_singleton_member('(b)'); -- error
+ERROR:  bitmapset is empty
 SELECT test_bms_singleton_member('(b 1 2)'); -- error
 ERROR:  bitmapset has multiple members
 SELECT test_bms_singleton_member('(b 42)') AS result;
@@ -522,11 +696,17 @@ SELECT test_bms_singleton_member('(b 42)') AS result;
 (1 row)
 
 -- bms_get_singleton_member()
+SELECT test_bms_get_singleton_member('(b)', 1000); -- error
+ test_bms_get_singleton_member 
+-------------------------------
+                            -1
+(1 row)
+
 -- Not a singleton, returns input default
 SELECT test_bms_get_singleton_member('(b 3 6)', 1000) AS result;
  result 
 --------
-   1000
+     -1
 (1 row)
 
 -- Singletone, returns sole member
@@ -593,6 +773,13 @@ SELECT test_bms_prev_member('(b)', 100) AS result;
      -2
 (1 row)
 
+-- Negative prevbit should result in highest possible bit in set
+SELECT test_bms_prev_member('(b 0 63 64 127)', -1) AS result;
+ result 
+--------
+    127
+(1 row)
+
 -- bms_hash_value()
 SELECT test_bms_hash_value('(b)') = 0 AS result;
  result 
@@ -663,47 +850,156 @@ SELECT test_bms_is_subset(test_bms_add_range(NULL, 0, 31),
  t
 (1 row)
 
--- bms_subset_compare()
-SELECT test_bms_subset_compare(NULL, NULL) AS result;
+-- Is subset with shorter word counts
+SELECT test_bms_is_subset('(b 5 100)', '(b 5)') AS result;
  result 
 --------
-      0
+ f
 (1 row)
 
-SELECT test_bms_subset_compare('(b 1 3)', NULL) AS result;
+SELECT test_bms_is_subset('(b 1 2 50 100)', '(b 1 2)') AS result;
  result 
 --------
-      2
+ f
 (1 row)
 
-SELECT test_bms_subset_compare(NULL, '(b 1 3)') AS result;
- result 
---------
-      1
+-- bms_subset_compare()
+SELECT test_bms_subset_compare(NULL, NULL);
+ test_bms_subset_compare 
+-------------------------
+                       0
 (1 row)
 
-SELECT test_bms_subset_compare('(b 1 3 5)', '(b 1 3)') AS result;
- result 
---------
-      2
+SELECT test_bms_subset_compare(NULL, '(b 1 3)');
+ test_bms_subset_compare 
+-------------------------
+                       1
 (1 row)
 
-SELECT test_bms_subset_compare('(b 1 3)', '(b 1 3 5)') AS result;
- result 
---------
-      1
+SELECT test_bms_subset_compare('(b)', '(b)');
+ test_bms_subset_compare 
+-------------------------
+                       0
 (1 row)
 
-SELECT test_bms_subset_compare('(b 1 3 5)', '(b 1 3 5)') AS result;
- result 
---------
-      0
+SELECT test_bms_subset_compare('(b)', '(b 1)');
+ test_bms_subset_compare 
+-------------------------
+                       1
 (1 row)
 
-SELECT test_bms_subset_compare('(b 1 3 5)', '(b 2 4 6)') AS result;
- result 
---------
-      3
+SELECT test_bms_subset_compare('(b 1)', '(b)');
+ test_bms_subset_compare 
+-------------------------
+                       2
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 3)', NULL);
+ test_bms_subset_compare 
+-------------------------
+                       2
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 3 5)', '(b 1 3 5)');
+ test_bms_subset_compare 
+-------------------------
+                       0
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 3)', '(b 1 3 5)');
+ test_bms_subset_compare 
+-------------------------
+                       1
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 3 5)', '(b 1 3)');
+ test_bms_subset_compare 
+-------------------------
+                       2
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 2)', '(b 1 3)');
+ test_bms_subset_compare 
+-------------------------
+                       3
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 2)', '(b 1 4)');
+ test_bms_subset_compare 
+-------------------------
+                       3
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 3)', '(b 1 3 64)');
+ test_bms_subset_compare 
+-------------------------
+                       1
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 3 64)', '(b 1 3)');
+ test_bms_subset_compare 
+-------------------------
+                       2
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 3 64)', '(b 1 3 65)');
+ test_bms_subset_compare 
+-------------------------
+                       3
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 3)', '(b 2 4)');
+ test_bms_subset_compare 
+-------------------------
+                       3
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1)', '(b 64)');
+ test_bms_subset_compare 
+-------------------------
+                       3
+(1 row)
+
+SELECT test_bms_subset_compare('(b 0)', '(b 32)');
+ test_bms_subset_compare 
+-------------------------
+                       3
+(1 row)
+
+SELECT test_bms_subset_compare('(b 0)', '(b 64)');
+ test_bms_subset_compare 
+-------------------------
+                       3
+(1 row)
+
+SELECT test_bms_subset_compare('(b 64)', '(b 1)');
+ test_bms_subset_compare 
+-------------------------
+                       3
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 2)', '(b 1 2 64)');
+ test_bms_subset_compare 
+-------------------------
+                       1
+(1 row)
+
+SELECT test_bms_subset_compare('(b 64 200)', '(b 1 201)');
+ test_bms_subset_compare 
+-------------------------
+                       3
+(1 row)
+
+SELECT test_bms_subset_compare('(b 1 64 65)', '(b 1 2 64)');
+ test_bms_subset_compare 
+-------------------------
+                       3
+(1 row)
+
+SELECT test_bms_subset_compare('(b 2 64 128)', '(b 1 65)');
+ test_bms_subset_compare 
+-------------------------
+                       3
 (1 row)
 
 -- bms_copy()
@@ -720,12 +1016,6 @@ SELECT test_bms_copy('(b 1 3 5 7)') AS result;
 (1 row)
 
 -- bms_add_members()
-SELECT test_bms_add_member('(b)', 1000); -- error
- test_bms_add_member 
----------------------
- (b 1000)
-(1 row)
-
 SELECT test_bms_add_members('(b 1 3)', '(b 5 7)') AS result;
    result    
 -------------
@@ -866,6 +1156,11 @@ SELECT test_bms_overlap_list('(b 1)', ARRAY[]::integer[]) AS result;
  f
 (1 row)
 
+-- Overlap list with negative numbers
+SELECT test_bms_overlap_list('(b 5 10)', ARRAY[-1,5]) AS result; -- error
+ERROR:  negative bitmapset member not allowed
+SELECT test_bms_overlap_list('(b 1 2 3)', ARRAY[-5,-1,0]) AS result; -- error
+ERROR:  negative bitmapset member not allowed
 -- bms_nonempty_difference()
 SELECT test_bms_nonempty_difference(NULL, '(b 1 3 5)') AS result;
  result 
@@ -897,6 +1192,25 @@ SELECT test_bms_nonempty_difference('(b 1 3 5)', '(b 1 3 5)') AS result;
  f
 (1 row)
 
+-- Difference with different word counts
+SELECT test_bms_nonempty_difference('(b 5)', '(b 100)') AS result;
+ result 
+--------
+ t
+(1 row)
+
+SELECT test_bms_nonempty_difference('(b 100)', '(b 5)') AS result;
+ result 
+--------
+ t
+(1 row)
+
+SELECT test_bms_nonempty_difference('(b 1 2)', '(b 50 100)') AS result;
+ result 
+--------
+ t
+(1 row)
+
 -- random operations
 SELECT test_random_operations(-1, 10000, 81920, 0) > 0 AS result;
  result 
@@ -904,4 +1218,462 @@ SELECT test_random_operations(-1, 10000, 81920, 0) > 0 AS result;
  t
 (1 row)
 
+-- Cover the test API's corner cases such as when ARGISNULL,
+-- or VARSIZE_ANY_EXHDR == 0, etc.
+SELECT test_bitmap_hash(NULL) AS result;
+ result 
+--------
+      0
+(1 row)
+
+SELECT test_bitmap_match('(b 5)', NULL) AS result;
+ result 
+--------
+      1
+(1 row)
+
+SELECT test_bitmap_match(NULL, '(b 5)') AS result;
+ result 
+--------
+      1
+(1 row)
+
+SELECT test_bitmap_match(NULL, NULL) AS result;
+ result 
+--------
+      0
+(1 row)
+
+SELECT test_bms_add_member('(b)', NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_add_range('(b 5)', 5, NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_add_range('(b 5)', NULL, 10) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_add_range('(b 5)', NULL, NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_add_range(NULL, 5, 10) AS result;
+      result      
+------------------
+ (b 5 6 7 8 9 10)
+(1 row)
+
+SELECT test_bms_add_range(NULL, 10, 5) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_add_range(NULL, NULL, NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_compare('(b 5)', NULL) AS result;
+ result 
+--------
+      1
+(1 row)
+
+SELECT test_bms_compare(NULL, '(b 5)') AS result;
+ result 
+--------
+     -1
+(1 row)
+
+SELECT test_bms_compare(NULL, NULL) AS result;
+ result 
+--------
+      0
+(1 row)
+
+SELECT test_bms_copy(NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_del_member('(b 42)', 42) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_del_member('(b 5)', NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_del_members('(b 5)', NULL) AS result;
+ result 
+--------
+ (b 5)
+(1 row)
+
+SELECT test_bms_del_members(NULL, '(b 5)') AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_difference('(b 5)', '(b 5 10)') AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_difference('(b 5)', NULL) AS result;
+ result 
+--------
+ (b 5)
+(1 row)
+
+SELECT test_bms_difference(NULL, '(b 5)') AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_difference(NULL, NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_equal('(b 5)', NULL) AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_equal(NULL, '(b 5)') AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_equal(NULL, NULL) AS result;
+ result 
+--------
+ t
+(1 row)
+
+SELECT test_bms_get_singleton_member('', 1000) AS result;
+ result 
+--------
+     -1
+(1 row)
+
+SELECT test_bms_get_singleton_member(NULL, -1) AS result;
+ result 
+--------
+     -1
+(1 row)
+
+SELECT test_bms_hash_value(NULL) AS result;
+ result 
+--------
+      0
+(1 row)
+
+SELECT test_bms_int_members('(b 1)', '(b 2)') AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_int_members('(b 5)', NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_int_members(NULL, '(b 5)') AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_int_members(NULL, NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_intersect('(b 1)', '(b 2)') AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_intersect('(b 5)', NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_intersect(NULL, '(b 5)') AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_intersect(NULL, NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_is_empty(NULL) AS result;
+ result 
+--------
+ t
+(1 row)
+
+SELECT test_bms_is_member('(b 5)', NULL) AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_is_subset('(b 5)', NULL) AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_is_subset(NULL, '(b 5)') AS result;
+ result 
+--------
+ t
+(1 row)
+
+SELECT test_bms_is_subset(NULL, NULL) AS result;
+ result 
+--------
+ t
+(1 row)
+
+SELECT test_bms_join('(b 5)', NULL) AS result;
+ result 
+--------
+ (b 5)
+(1 row)
+
+SELECT test_bms_join(NULL, '(b 5)') AS result;
+ result 
+--------
+ (b 5)
+(1 row)
+
+SELECT test_bms_join(NULL, NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_make_singleton(NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_member_index('', 5) AS result;
+ result 
+--------
+     -1
+(1 row)
+
+SELECT test_bms_member_index(NULL, 5) AS result;
+ result 
+--------
+     -1
+(1 row)
+
+SELECT test_bms_membership(NULL) AS result;
+ result 
+--------
+      0
+(1 row)
+
+SELECT test_bms_next_member('', 5) AS result;
+ result 
+--------
+     -2
+(1 row)
+
+SELECT test_bms_next_member('(b 5)', NULL) AS result;
+ result 
+--------
+     -2
+(1 row)
+
+SELECT test_bms_next_member(NULL, 5) AS result;
+ result 
+--------
+     -2
+(1 row)
+
+SELECT test_bms_next_member(NULL, NULL) AS result;
+ result 
+--------
+     -2
+(1 row)
+
+SELECT test_bms_nonempty_difference('(b 5)', NULL) AS result;
+ result 
+--------
+ t
+(1 row)
+
+SELECT test_bms_nonempty_difference(NULL, '(b 5)') AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_nonempty_difference(NULL, NULL) AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_overlap('(b 5)', NULL) AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_overlap(NULL, '(b 5)') AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_overlap(NULL, NULL) AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_overlap_list('(b 5)', NULL) AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_overlap_list(NULL, ARRAY[1,2,3]) AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_overlap_list(NULL, NULL) AS result;
+ result 
+--------
+ f
+(1 row)
+
+SELECT test_bms_prev_member('', 5) AS result;
+ result 
+--------
+     -2
+(1 row)
+
+SELECT test_bms_prev_member('(b 5)', NULL) AS result;
+ result 
+--------
+     -2
+(1 row)
+
+SELECT test_bms_prev_member(NULL, 5) AS result;
+ result 
+--------
+     -2
+(1 row)
+
+SELECT test_bms_replace_members('(b 1 2 3)', NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_replace_members('(b 5)', NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_replace_members(NULL, '(b 5)') AS result;
+ result 
+--------
+ (b 5)
+(1 row)
+
+SELECT test_bms_replace_members(NULL, NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
+SELECT test_bms_singleton_member(NULL) AS result;
+ result 
+--------
+       
+(1 row)
+
+SELECT test_bms_subset_compare('(b 5)', NULL) AS result;
+ result 
+--------
+      2
+(1 row)
+
+SELECT test_bms_subset_compare(NULL, '(b 5)') AS result;
+ result 
+--------
+      1
+(1 row)
+
+SELECT test_bms_subset_compare(NULL, NULL) AS result;
+ result 
+--------
+      0
+(1 row)
+
+SELECT test_bms_union('(b 5)', NULL) AS result;
+ result 
+--------
+ (b 5)
+(1 row)
+
+SELECT test_bms_union(NULL, '(b 5)') AS result;
+ result 
+--------
+ (b 5)
+(1 row)
+
+SELECT test_bms_union(NULL, NULL) AS result;
+ result 
+--------
+ 
+(1 row)
+
 DROP EXTENSION test_bitmapset;
diff --git a/src/test/modules/test_bitmapset/sql/test_bitmapset.sql b/src/test/modules/test_bitmapset/sql/test_bitmapset.sql
index 2b2c72c876b..67e0f01ccb6 100644
--- a/src/test/modules/test_bitmapset/sql/test_bitmapset.sql
+++ b/src/test/modules/test_bitmapset/sql/test_bitmapset.sql
@@ -23,6 +23,8 @@ SELECT test_bms_replace_members('(b 1 2 3)', NULL) AS result;
 SELECT test_bms_replace_members('(b 1 2 3)', '(b 3 5 6)') AS result;
 SELECT test_bms_replace_members('(b 1 2 3)', '(b 3 5)') AS result;
 SELECT test_bms_replace_members('(b 1 2)', '(b 3 5 7)') AS result;
+-- Force repalloc() with larger set
+SELECT test_bms_replace_members('(b 1 2 3 4 5)', '(b 500 600)') AS result;
 
 -- bms_del_member()
 SELECT test_bms_del_member('(b)', -20); -- error
@@ -34,15 +36,32 @@ SELECT test_bms_del_member('(b 1 2 3)', 2) AS result;
 SELECT test_bms_del_member(test_bms_del_member('(b 0 31 32 63 64)', 32), 63) AS result;
 -- Word boundary
 SELECT test_bms_del_member(test_bms_add_range('(b)', 30, 34), 32) AS result;
+-- Force word count changes
+SELECT test_bms_del_member('(b 1 200)', 200) AS result;
+SELECT test_bms_del_member('(b 1 50 100 200)', 200) AS result;
+SELECT test_bms_del_member('(b 1 50 100 200)', 100) AS result;
+
+-- bms_del_members()
+SELECT test_bms_del_members('(b)', '(b 10)') AS result;
+SELECT test_bms_del_members('(b 10)', '(b 10)') AS result;
+SELECT test_bms_del_members('(b 10)', '(b 5)') AS result;
+SELECT test_bms_del_members('(b 1 2 3)', '(b 2)') AS result;
+SELECT test_bms_del_members('(b 5 100)', '(b 100)') AS result;
+SELECT test_bms_del_members('(b 5 100 200)', '(b 200)') AS result;
+SELECT test_bms_del_members('(b 1 2 100 200 300)', '(b 1 2)') AS result;
+SELECT test_bms_del_members('(b 1 2 100 200 300)', '(b 200 300)') AS result;
 
 -- bms_join()
 SELECT test_bms_join('(b 1 3 5)', NULL) AS result;
 SELECT test_bms_join(NULL, '(b 2 4 6)') AS result;
 SELECT test_bms_join('(b 1 3 5)', '(b 2 4 6)') AS result;
 SELECT test_bms_join('(b 1 3 5)', '(b 1 4 5)') AS result;
+-- Force word count changes
+SELECT test_bms_join('(b 5)', '(b 100)') AS result;
+SELECT test_bms_join('(b 1 2)', '(b 100 200 300)') AS result;
 
 -- bms_union()
--- Overlapping sets.
+-- Overlapping sets
 SELECT test_bms_union('(b 1 3 5)', '(b 3 5 7)') AS result;
 -- Union with NULL
 SELECT test_bms_union('(b 1 3 5)', '(b)') AS result;
@@ -53,21 +72,27 @@ SELECT test_bms_union(
          test_bms_add_range('(b)', 0, 15),
          test_bms_add_range('(b)', 10, 20)
        ) AS result;
+-- Union with varrying word counts
+SELECT test_bms_union('(b 1 2)', '(b 100 300)') AS result;
+SELECT test_bms_union('(b 100 300)', '(b 1 2)') AS result;
 
 -- bms_intersect()
 -- Overlapping sets
 SELECT test_bms_intersect('(b 1 3 5)', '(b 3 5 7)') AS result;
 -- Disjoint sets
 SELECT test_bms_intersect('(b 1 3 5)', '(b 2 4 6)') AS result;
--- Intersect with empty.
+-- Intersect with empty
 SELECT test_bms_intersect('(b 1 3 5)', '(b)') AS result;
+-- Intersect with varrying word counts
+SELECT test_bms_intersect('(b 1 300)', '(b 1 2 3 4 5)') AS result;
+SELECT test_bms_intersect('(b 1 2 3 4 5)', '(b 1 300)') AS result;
 
 -- bms_int_members()
 -- Overlapping sets
 SELECT test_bms_int_members('(b 1 3 5)', '(b 3 5 7)') AS result;
 -- Disjoint sets
 SELECT test_bms_int_members('(b 1 3 5)', '(b 2 4 6)') AS result;
--- Intersect with empty.
+-- Intersect with empty
 SELECT test_bms_int_members('(b 1 3 5)', '(b)') AS result;
 -- Multiple members
 SELECT test_bms_int_members('(b 0 31 32 63 64)', '(b 31 32 64 65)') AS result;
@@ -82,10 +107,13 @@ SELECT test_bms_difference('(b 1 3 5)', '(b 1 3 5)') AS result;
 -- Substraction to empty
 SELECT test_bms_difference('(b 42)', '(b 42)') AS result;
 -- Subtraction edge case
-SELECT test_bms_difference(
+SELECT length(test_bms_difference(
          test_bms_add_range('(b)', 0, 100),
          test_bms_add_range('(b)', 50, 150)
-       ) AS result;
+       )) AS result;
+-- Difference with different word counts
+SELECT test_bms_difference('(b 5 100)', '(b 5)') AS result;
+SELECT test_bms_difference('(b 1 2 100 200)', '(b 1 2)') AS result;
 
 -- bms_is_member()
 SELECT test_bms_is_member('(b)', -5); -- error
@@ -99,6 +127,10 @@ SELECT test_bms_member_index(NULL, 1) AS result;
 SELECT test_bms_member_index('(b 1 3 5)', 2) AS result;
 SELECT test_bms_member_index('(b 1 3 5)', 1) AS result;
 SELECT test_bms_member_index('(b 1 3 5)', 3) AS result;
+-- Member index with various word positions
+SELECT test_bms_member_index('(b 100 200)', 100) AS result;
+SELECT test_bms_member_index('(b 100 200)', 200) AS result;
+SELECT test_bms_member_index('(b 1 50 100 200)', 200) AS result;
 
 -- bms_num_members()
 SELECT test_bms_num_members('(b)') AS result;
@@ -111,6 +143,9 @@ SELECT test_bms_equal('(b)', '(b 1 3 5)') AS result;
 SELECT test_bms_equal('(b 1 3 5)', '(b)') AS result;
 SELECT test_bms_equal('(b 1 3 5)', '(b 1 3 5)') AS result;
 SELECT test_bms_equal('(b 1 3 5)', '(b 2 4 6)') AS result;
+-- Equal with different word counts
+SELECT test_bms_equal('(b 5)', '(b 100)') AS result;
+SELECT test_bms_equal('(b 5 10)', '(b 100 200 300)') AS result;
 
 -- bms_compare()
 SELECT test_bms_compare('(b)', '(b)') AS result;
@@ -138,6 +173,9 @@ SELECT length(test_bms_add_range('(b)', 0, 1000)) AS result;
 -- Force reallocations
 SELECT length(test_bms_add_range('(b)', 0, 200)) AS result;
 SELECT length(test_bms_add_range('(b)', 1000, 1100)) AS result;
+-- Force word count expansion
+SELECT test_bms_add_range('(b 5)', 100, 105) AS result;
+SELECT length(test_bms_add_range('(b 1 2)', 200, 250)) AS result;
 
 -- bms_membership()
 SELECT test_bms_membership('(b)') AS result;
@@ -150,10 +188,12 @@ SELECT test_bms_is_empty('(b)') AS result;
 SELECT test_bms_is_empty('(b 1)') AS result;
 
 -- bms_singleton_member()
+SELECT test_bms_singleton_member('(b)'); -- error
 SELECT test_bms_singleton_member('(b 1 2)'); -- error
 SELECT test_bms_singleton_member('(b 42)') AS result;
 
 -- bms_get_singleton_member()
+SELECT test_bms_get_singleton_member('(b)', 1000); -- error
 -- Not a singleton, returns input default
 SELECT test_bms_get_singleton_member('(b 3 6)', 1000) AS result;
 -- Singletone, returns sole member
@@ -176,6 +216,8 @@ SELECT test_bms_prev_member('(b 5 10 15 20)', 20) AS result;
 SELECT test_bms_prev_member('(b 5 10 15 20)', 5) AS result;
 -- Empty set
 SELECT test_bms_prev_member('(b)', 100) AS result;
+-- Negative prevbit should result in highest possible bit in set
+SELECT test_bms_prev_member('(b 0 63 64 127)', -1) AS result;
 
 -- bms_hash_value()
 SELECT test_bms_hash_value('(b)') = 0 AS result;
@@ -194,22 +236,40 @@ SELECT test_bms_is_subset('(b 1 3 5)', '(b 1 3)') AS result;
 SELECT test_bms_is_subset('(b 1 3)', '(b 2 4)') AS result;
 SELECT test_bms_is_subset(test_bms_add_range(NULL, 0, 31),
                           test_bms_add_range(NULL, 0, 63)) AS result;
+-- Is subset with shorter word counts
+SELECT test_bms_is_subset('(b 5 100)', '(b 5)') AS result;
+SELECT test_bms_is_subset('(b 1 2 50 100)', '(b 1 2)') AS result;
 
 -- bms_subset_compare()
-SELECT test_bms_subset_compare(NULL, NULL) AS result;
-SELECT test_bms_subset_compare('(b 1 3)', NULL) AS result;
-SELECT test_bms_subset_compare(NULL, '(b 1 3)') AS result;
-SELECT test_bms_subset_compare('(b 1 3 5)', '(b 1 3)') AS result;
-SELECT test_bms_subset_compare('(b 1 3)', '(b 1 3 5)') AS result;
-SELECT test_bms_subset_compare('(b 1 3 5)', '(b 1 3 5)') AS result;
-SELECT test_bms_subset_compare('(b 1 3 5)', '(b 2 4 6)') AS result;
+SELECT test_bms_subset_compare(NULL, NULL);
+SELECT test_bms_subset_compare(NULL, '(b 1 3)');
+SELECT test_bms_subset_compare('(b)', '(b)');
+SELECT test_bms_subset_compare('(b)', '(b 1)');
+SELECT test_bms_subset_compare('(b 1)', '(b)');
+SELECT test_bms_subset_compare('(b 1 3)', NULL);
+SELECT test_bms_subset_compare('(b 1 3 5)', '(b 1 3 5)');
+SELECT test_bms_subset_compare('(b 1 3)', '(b 1 3 5)');
+SELECT test_bms_subset_compare('(b 1 3 5)', '(b 1 3)');
+SELECT test_bms_subset_compare('(b 1 2)', '(b 1 3)');
+SELECT test_bms_subset_compare('(b 1 2)', '(b 1 4)');
+SELECT test_bms_subset_compare('(b 1 3)', '(b 1 3 64)');
+SELECT test_bms_subset_compare('(b 1 3 64)', '(b 1 3)');
+SELECT test_bms_subset_compare('(b 1 3 64)', '(b 1 3 65)');
+SELECT test_bms_subset_compare('(b 1 3)', '(b 2 4)');
+SELECT test_bms_subset_compare('(b 1)', '(b 64)');
+SELECT test_bms_subset_compare('(b 0)', '(b 32)');
+SELECT test_bms_subset_compare('(b 0)', '(b 64)');
+SELECT test_bms_subset_compare('(b 64)', '(b 1)');
+SELECT test_bms_subset_compare('(b 1 2)', '(b 1 2 64)');
+SELECT test_bms_subset_compare('(b 64 200)', '(b 1 201)');
+SELECT test_bms_subset_compare('(b 1 64 65)', '(b 1 2 64)');
+SELECT test_bms_subset_compare('(b 2 64 128)', '(b 1 65)');
 
 -- bms_copy()
 SELECT test_bms_copy(NULL) AS result;
 SELECT test_bms_copy('(b 1 3 5 7)') AS result;
 
 -- bms_add_members()
-SELECT test_bms_add_member('(b)', 1000); -- error
 SELECT test_bms_add_members('(b 1 3)', '(b 5 7)') AS result;
 SELECT test_bms_add_members('(b 1 3 5)', '(b 2 5 7)') AS result;
 SELECT test_bms_add_members('(b 1 3 5)', '(b 100 200 300)') AS result;
@@ -243,6 +303,9 @@ SELECT test_bms_overlap_list('(b 7 10)', ARRAY[6,7,8,9]) AS result;
 SELECT test_bms_overlap_list('(b 1 5)', ARRAY[6,7,8,9]) AS result;
 -- Empty list
 SELECT test_bms_overlap_list('(b 1)', ARRAY[]::integer[]) AS result;
+-- Overlap list with negative numbers
+SELECT test_bms_overlap_list('(b 5 10)', ARRAY[-1,5]) AS result; -- error
+SELECT test_bms_overlap_list('(b 1 2 3)', ARRAY[-5,-1,0]) AS result; -- error
 
 -- bms_nonempty_difference()
 SELECT test_bms_nonempty_difference(NULL, '(b 1 3 5)') AS result;
@@ -250,8 +313,92 @@ SELECT test_bms_nonempty_difference('(b 1 3 5)', NULL) AS result;
 SELECT test_bms_nonempty_difference('(b 1 3 5)', '(b 2 4 6)') AS result;
 SELECT test_bms_nonempty_difference('(b 1 3 5)', '(b 1 5)') AS result;
 SELECT test_bms_nonempty_difference('(b 1 3 5)', '(b 1 3 5)') AS result;
+-- Difference with different word counts
+SELECT test_bms_nonempty_difference('(b 5)', '(b 100)') AS result;
+SELECT test_bms_nonempty_difference('(b 100)', '(b 5)') AS result;
+SELECT test_bms_nonempty_difference('(b 1 2)', '(b 50 100)') AS result;
 
 -- random operations
 SELECT test_random_operations(-1, 10000, 81920, 0) > 0 AS result;
 
+-- Cover the test API's corner cases such as when ARGISNULL,
+-- or VARSIZE_ANY_EXHDR == 0, etc.
+
+SELECT test_bitmap_hash(NULL) AS result;
+SELECT test_bitmap_match('(b 5)', NULL) AS result;
+SELECT test_bitmap_match(NULL, '(b 5)') AS result;
+SELECT test_bitmap_match(NULL, NULL) AS result;
+SELECT test_bms_add_member('(b)', NULL) AS result;
+SELECT test_bms_add_range('(b 5)', 5, NULL) AS result;
+SELECT test_bms_add_range('(b 5)', NULL, 10) AS result;
+SELECT test_bms_add_range('(b 5)', NULL, NULL) AS result;
+SELECT test_bms_add_range(NULL, 5, 10) AS result;
+SELECT test_bms_add_range(NULL, 10, 5) AS result;
+SELECT test_bms_add_range(NULL, NULL, NULL) AS result;
+SELECT test_bms_compare('(b 5)', NULL) AS result;
+SELECT test_bms_compare(NULL, '(b 5)') AS result;
+SELECT test_bms_compare(NULL, NULL) AS result;
+SELECT test_bms_copy(NULL) AS result;
+SELECT test_bms_del_member('(b 42)', 42) AS result;
+SELECT test_bms_del_member('(b 5)', NULL) AS result;
+SELECT test_bms_del_members('(b 5)', NULL) AS result;
+SELECT test_bms_del_members(NULL, '(b 5)') AS result;
+SELECT test_bms_difference('(b 5)', '(b 5 10)') AS result;
+SELECT test_bms_difference('(b 5)', NULL) AS result;
+SELECT test_bms_difference(NULL, '(b 5)') AS result;
+SELECT test_bms_difference(NULL, NULL) AS result;
+SELECT test_bms_equal('(b 5)', NULL) AS result;
+SELECT test_bms_equal(NULL, '(b 5)') AS result;
+SELECT test_bms_equal(NULL, NULL) AS result;
+SELECT test_bms_get_singleton_member('', 1000) AS result;
+SELECT test_bms_get_singleton_member(NULL, -1) AS result;
+SELECT test_bms_hash_value(NULL) AS result;
+SELECT test_bms_int_members('(b 1)', '(b 2)') AS result;
+SELECT test_bms_int_members('(b 5)', NULL) AS result;
+SELECT test_bms_int_members(NULL, '(b 5)') AS result;
+SELECT test_bms_int_members(NULL, NULL) AS result;
+SELECT test_bms_intersect('(b 1)', '(b 2)') AS result;
+SELECT test_bms_intersect('(b 5)', NULL) AS result;
+SELECT test_bms_intersect(NULL, '(b 5)') AS result;
+SELECT test_bms_intersect(NULL, NULL) AS result;
+SELECT test_bms_is_empty(NULL) AS result;
+SELECT test_bms_is_member('(b 5)', NULL) AS result;
+SELECT test_bms_is_subset('(b 5)', NULL) AS result;
+SELECT test_bms_is_subset(NULL, '(b 5)') AS result;
+SELECT test_bms_is_subset(NULL, NULL) AS result;
+SELECT test_bms_join('(b 5)', NULL) AS result;
+SELECT test_bms_join(NULL, '(b 5)') AS result;
+SELECT test_bms_join(NULL, NULL) AS result;
+SELECT test_bms_make_singleton(NULL) AS result;
+SELECT test_bms_member_index('', 5) AS result;
+SELECT test_bms_member_index(NULL, 5) AS result;
+SELECT test_bms_membership(NULL) AS result;
+SELECT test_bms_next_member('', 5) AS result;
+SELECT test_bms_next_member('(b 5)', NULL) AS result;
+SELECT test_bms_next_member(NULL, 5) AS result;
+SELECT test_bms_next_member(NULL, NULL) AS result;
+SELECT test_bms_nonempty_difference('(b 5)', NULL) AS result;
+SELECT test_bms_nonempty_difference(NULL, '(b 5)') AS result;
+SELECT test_bms_nonempty_difference(NULL, NULL) AS result;
+SELECT test_bms_overlap('(b 5)', NULL) AS result;
+SELECT test_bms_overlap(NULL, '(b 5)') AS result;
+SELECT test_bms_overlap(NULL, NULL) AS result;
+SELECT test_bms_overlap_list('(b 5)', NULL) AS result;
+SELECT test_bms_overlap_list(NULL, ARRAY[1,2,3]) AS result;
+SELECT test_bms_overlap_list(NULL, NULL) AS result;
+SELECT test_bms_prev_member('', 5) AS result;
+SELECT test_bms_prev_member('(b 5)', NULL) AS result;
+SELECT test_bms_prev_member(NULL, 5) AS result;
+SELECT test_bms_replace_members('(b 1 2 3)', NULL) AS result;
+SELECT test_bms_replace_members('(b 5)', NULL) AS result;
+SELECT test_bms_replace_members(NULL, '(b 5)') AS result;
+SELECT test_bms_replace_members(NULL, NULL) AS result;
+SELECT test_bms_singleton_member(NULL) AS result;
+SELECT test_bms_subset_compare('(b 5)', NULL) AS result;
+SELECT test_bms_subset_compare(NULL, '(b 5)') AS result;
+SELECT test_bms_subset_compare(NULL, NULL) AS result;
+SELECT test_bms_union('(b 5)', NULL) AS result;
+SELECT test_bms_union(NULL, '(b 5)') AS result;
+SELECT test_bms_union(NULL, NULL) AS result;
+
 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
index 95f5ee02e3f..b95c4d0dda5 100644
--- a/src/test/modules/test_bitmapset/test_bitmapset--1.0.sql
+++ b/src/test/modules/test_bitmapset/test_bitmapset--1.0.sql
@@ -112,6 +112,10 @@ CREATE FUNCTION test_bms_int_members(text, text)
 RETURNS text
 AS 'MODULE_PATHNAME' LANGUAGE C;
 
+CREATE FUNCTION test_bms_del_members(text, text)
+RETURNS text
+AS 'MODULE_PATHNAME' LANGUAGE C;
+
 CREATE FUNCTION test_bms_replace_members(text, text)
 RETURNS text
 AS 'MODULE_PATHNAME' LANGUAGE C;
diff --git a/src/test/modules/test_bitmapset/test_bitmapset.c b/src/test/modules/test_bitmapset/test_bitmapset.c
index 5bc4daa23f1..77b6c70af66 100644
--- a/src/test/modules/test_bitmapset/test_bitmapset.c
+++ b/src/test/modules/test_bitmapset/test_bitmapset.c
@@ -58,6 +58,7 @@ 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_int_members);
+PG_FUNCTION_INFO_V1(test_bms_del_members);
 PG_FUNCTION_INFO_V1(test_bms_replace_members);
 PG_FUNCTION_INFO_V1(test_bms_join);
 PG_FUNCTION_INFO_V1(test_bitmap_hash);
@@ -111,9 +112,6 @@ test_bms_add_member(PG_FUNCTION_ARGS)
 	if (bms)
 		bms_free(bms);
 
-	if (result == NULL)
-		PG_RETURN_NULL();
-
 	PG_RETURN_TEXT_P(result);
 }
 
@@ -136,9 +134,6 @@ test_bms_add_members(PG_FUNCTION_ARGS)
 	if (bms2)
 		bms_free(bms2);
 
-	if (bms1 == NULL)
-		PG_RETURN_NULL();
-
 	result = BITMAPSET_TO_TEXT(bms1);
 	bms_free(bms1);
 
@@ -161,12 +156,8 @@ test_bms_del_member(PG_FUNCTION_ARGS)
 	member = PG_GETARG_INT32(1);
 	bms = bms_del_member(bms, member);
 
-	if (bms == NULL || bms_is_empty(bms))
-	{
-		if (bms)
-			bms_free(bms);
+	if (bms_is_empty(bms))
 		PG_RETURN_NULL();
-	}
 
 	result = BITMAPSET_TO_TEXT(bms);
 	bms_free(bms);
@@ -514,12 +505,10 @@ Datum
 test_bms_get_singleton_member(PG_FUNCTION_ARGS)
 {
 	Bitmapset  *bms = NULL;
-	int32		default_member = PG_GETARG_INT32(1);
-	int			member;
-	bool		success;
+	int			member = - 1;
 
 	if (PG_ARGISNULL(0))
-		PG_RETURN_INT32(default_member);
+		PG_RETURN_INT32(member);
 
 	bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
 
@@ -527,13 +516,10 @@ test_bms_get_singleton_member(PG_FUNCTION_ARGS)
 	 * bms_get_singleton_member returns bool and stores result in member
 	 * pointer
 	 */
-	success = bms_get_singleton_member(bms, &member);
+	bms_get_singleton_member(bms, &member);
 	bms_free(bms);
 
-	if (success)
-		PG_RETURN_INT32(member);
-	else
-		PG_RETURN_INT32(default_member);
+	PG_RETURN_INT32(member);
 }
 
 Datum
@@ -609,11 +595,6 @@ test_bms_overlap_list(PG_FUNCTION_ARGS)
 
 	array = PG_GETARG_ARRAYTYPE_P(1);
 
-	if (ARR_ELEMTYPE(array) != INT4OID)
-		ereport(ERROR,
-				(errcode(ERRCODE_DATATYPE_MISMATCH),
-				 errmsg("integer array expected")));
-
 	deconstruct_array(array,
 					  INT4OID, sizeof(int32), true, 'i',
 					  &elem_datums, &elem_nulls, &elem_count);
@@ -752,6 +733,37 @@ test_bms_int_members(PG_FUNCTION_ARGS)
 	PG_RETURN_TEXT_P(result);
 }
 
+Datum
+test_bms_del_members(PG_FUNCTION_ARGS)
+{
+	Bitmapset  *bms1 = NULL,
+			   *bms2 = NULL;
+	Bitmapset  *result_bms;
+	text	   *result;
+
+	if (!PG_ARGISNULL(0))
+		bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
+
+	if (!PG_ARGISNULL(1))
+		bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
+
+	/* IMPORTANT: bms_del_members modifies/frees the first argument */
+	result_bms = bms_del_members(bms1, bms2);
+
+	/* bms1 is now invalid, do not free it */
+
+	if (bms2)
+		bms_free(bms2);
+
+	if (result_bms == NULL)
+		PG_RETURN_NULL();
+
+	result = BITMAPSET_TO_TEXT(result_bms);
+	bms_free(result_bms);
+
+	PG_RETURN_TEXT_P(result);
+}
+
 Datum
 test_bms_replace_members(PG_FUNCTION_ARGS)
 {
-- 
2.49.0

