diff options
-rw-r--r-- | src/test/modules/Makefile | 1 | ||||
-rw-r--r-- | src/test/modules/meson.build | 1 | ||||
-rw-r--r-- | src/test/modules/test_bitmapset/.gitignore | 4 | ||||
-rw-r--r-- | src/test/modules/test_bitmapset/Makefile | 23 | ||||
-rw-r--r-- | src/test/modules/test_bitmapset/expected/test_bitmapset.out | 907 | ||||
-rw-r--r-- | src/test/modules/test_bitmapset/meson.build | 33 | ||||
-rw-r--r-- | src/test/modules/test_bitmapset/sql/test_bitmapset.sql | 257 | ||||
-rw-r--r-- | src/test/modules/test_bitmapset/test_bitmapset--1.0.sql | 136 | ||||
-rw-r--r-- | src/test/modules/test_bitmapset/test_bitmapset.c | 1021 | ||||
-rw-r--r-- | src/test/modules/test_bitmapset/test_bitmapset.control | 4 |
10 files changed, 2387 insertions, 0 deletions
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile index 8a3cd2afab7..902a7954101 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 717e85066ba..14fc761c4cf 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..99fb22ae807 --- /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 bitmapset" + +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..abbfef1f7a6 --- /dev/null +++ b/src/test/modules/test_bitmapset/expected/test_bitmapset.out @@ -0,0 +1,907 @@ +-- Tests for Bitmapsets +CREATE EXTENSION test_bitmapset; +-- bms_make_singleton() +SELECT test_bms_make_singleton(-1); +ERROR: negative bitmapset member not allowed +SELECT test_bms_make_singleton(42) AS result; + result +-------- + (b 42) +(1 row) + +SELECT test_bms_make_singleton(0) AS result; + result +-------- + (b 0) +(1 row) + +SELECT test_bms_make_singleton(1000) AS result; + result +---------- + (b 1000) +(1 row) + +-- bms_add_member() +SELECT test_bms_add_member('(b 1)', -1); -- error +ERROR: negative bitmapset member not allowed +SELECT test_bms_add_member('(b)', -10); -- error +ERROR: negative bitmapset member not allowed +SELECT test_bms_add_member('(b)', 10) AS result; + result +-------- + (b 10) +(1 row) + +SELECT test_bms_add_member('(b 5)', 10) AS result; + result +---------- + (b 5 10) +(1 row) + +-- sort check +SELECT test_bms_add_member('(b 10)', 5) AS result; + result +---------- + (b 5 10) +(1 row) + +-- idempotent change +SELECT test_bms_add_member('(b 10)', 10) AS result; + result +-------- + (b 10) +(1 row) + +-- bms_replace_members() +SELECT test_bms_replace_members(NULL, '(b 1 2 3)') AS result; + result +----------- + (b 1 2 3) +(1 row) + +SELECT test_bms_replace_members('(b 1 2 3)', NULL) AS result; + result +-------- + +(1 row) + +SELECT test_bms_replace_members('(b 1 2 3)', '(b 3 5 6)') AS result; + result +----------- + (b 3 5 6) +(1 row) + +SELECT test_bms_replace_members('(b 1 2 3)', '(b 3 5)') AS result; + result +--------- + (b 3 5) +(1 row) + +SELECT test_bms_replace_members('(b 1 2)', '(b 3 5 7)') AS result; + result +----------- + (b 3 5 7) +(1 row) + +-- bms_del_member() +SELECT test_bms_del_member('(b)', -20); -- error +ERROR: negative bitmapset member not allowed +SELECT test_bms_del_member('(b)', 10) AS result; + result +-------- + +(1 row) + +SELECT test_bms_del_member('(b 10)', 10) AS result; + result +-------- + +(1 row) + +SELECT test_bms_del_member('(b 10)', 5) AS result; + result +-------- + (b 10) +(1 row) + +SELECT test_bms_del_member('(b 1 2 3)', 2) AS result; + result +--------- + (b 1 3) +(1 row) + +-- Reallocation check +SELECT test_bms_del_member(test_bms_del_member('(b 0 31 32 63 64)', 32), 63) AS result; + result +------------- + (b 0 31 64) +(1 row) + +-- Word boundary +SELECT test_bms_del_member(test_bms_add_range('(b)', 30, 34), 32) AS result; + result +----------------- + (b 30 31 33 34) +(1 row) + +-- bms_join() +SELECT test_bms_join('(b 1 3 5)', NULL) AS result; + result +----------- + (b 1 3 5) +(1 row) + +SELECT test_bms_join(NULL, '(b 2 4 6)') AS result; + result +----------- + (b 2 4 6) +(1 row) + +SELECT test_bms_join('(b 1 3 5)', '(b 2 4 6)') AS result; + result +----------------- + (b 1 2 3 4 5 6) +(1 row) + +SELECT test_bms_join('(b 1 3 5)', '(b 1 4 5)') AS result; + result +------------- + (b 1 3 4 5) +(1 row) + +-- bms_union() +-- Overlapping sets. +SELECT test_bms_union('(b 1 3 5)', '(b 3 5 7)') AS result; + result +------------- + (b 1 3 5 7) +(1 row) + +-- Union with NULL +SELECT test_bms_union('(b 1 3 5)', '(b)') AS result; + result +----------- + (b 1 3 5) +(1 row) + +-- Union of empty with empty +SELECT test_bms_union('(b)', '(b)') AS result; + result +-------- + +(1 row) + +-- Overlapping ranges +SELECT test_bms_union( + test_bms_add_range('(b)', 0, 15), + test_bms_add_range('(b)', 10, 20) + ) 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) +(1 row) + +-- bms_intersect() +-- Overlapping sets +SELECT test_bms_intersect('(b 1 3 5)', '(b 3 5 7)') AS result; + result +--------- + (b 3 5) +(1 row) + +-- Disjoint sets +SELECT test_bms_intersect('(b 1 3 5)', '(b 2 4 6)') AS result; + result +-------- + +(1 row) + +-- Intersect with empty. +SELECT test_bms_intersect('(b 1 3 5)', '(b)') AS result; + result +-------- + +(1 row) + +-- bms_int_members() +-- Overlapping sets +SELECT test_bms_int_members('(b 1 3 5)', '(b 3 5 7)') AS result; + result +--------- + (b 3 5) +(1 row) + +-- Disjoint sets +SELECT test_bms_int_members('(b 1 3 5)', '(b 2 4 6)') AS result; + result +-------- + +(1 row) + +-- Intersect with empty. +SELECT test_bms_int_members('(b 1 3 5)', '(b)') AS result; + result +-------- + +(1 row) + +-- Multiple members +SELECT test_bms_int_members('(b 0 31 32 63 64)', '(b 31 32 64 65)') AS result; + result +-------------- + (b 31 32 64) +(1 row) + +-- bms_difference() +-- Overlapping sets +SELECT test_bms_difference('(b 1 3 5)', '(b 3 5 7)') AS result; + result +-------- + (b 1) +(1 row) + +-- Disjoint sets +SELECT test_bms_difference('(b 1 3 5)', '(b 2 4 6)') AS result; + result +----------- + (b 1 3 5) +(1 row) + +-- Identical sets +SELECT test_bms_difference('(b 1 3 5)', '(b 1 3 5)') AS result; + result +-------- + +(1 row) + +-- Substraction to empty +SELECT test_bms_difference('(b 42)', '(b 42)') AS result; + result +-------- + +(1 row) + +-- Subtraction edge case +SELECT 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) +(1 row) + +-- bms_is_member() +SELECT test_bms_is_member('(b)', -5); -- error +ERROR: negative bitmapset member not allowed +SELECT test_bms_is_member('(b 1 3 5)', 1) AS result; + result +-------- + t +(1 row) + +SELECT test_bms_is_member('(b 1 3 5)', 2) AS result; + result +-------- + f +(1 row) + +SELECT test_bms_is_member('(b 1 3 5)', 3) AS result; + result +-------- + t +(1 row) + +SELECT test_bms_is_member('(b)', 1) AS result; + result +-------- + f +(1 row) + +-- bms_member_index() +SELECT test_bms_member_index(NULL, 1) AS result; + result +-------- + -1 +(1 row) + +SELECT test_bms_member_index('(b 1 3 5)', 2) AS result; + result +-------- + -1 +(1 row) + +SELECT test_bms_member_index('(b 1 3 5)', 1) AS result; + result +-------- + 0 +(1 row) + +SELECT test_bms_member_index('(b 1 3 5)', 3) AS result; + result +-------- + 1 +(1 row) + +-- bms_num_members() +SELECT test_bms_num_members('(b)') AS result; + result +-------- + 0 +(1 row) + +SELECT test_bms_num_members('(b 1 3 5)') AS result; + result +-------- + 3 +(1 row) + +SELECT test_bms_num_members('(b 2 4 6 8 10)') AS result; + result +-------- + 5 +(1 row) + +-- test_bms_equal() +SELECT test_bms_equal('(b)', '(b)') AS result; + result +-------- + t +(1 row) + +SELECT test_bms_equal('(b)', '(b 1 3 5)') AS result; + result +-------- + f +(1 row) + +SELECT test_bms_equal('(b 1 3 5)', '(b)') AS result; + result +-------- + f +(1 row) + +SELECT test_bms_equal('(b 1 3 5)', '(b 1 3 5)') AS result; + result +-------- + t +(1 row) + +SELECT test_bms_equal('(b 1 3 5)', '(b 2 4 6)') AS result; + result +-------- + f +(1 row) + +-- bms_compare() +SELECT test_bms_compare('(b)', '(b)') AS result; + result +-------- + 0 +(1 row) + +SELECT test_bms_compare('(b)', '(b 1 3)') AS result; + result +-------- + -1 +(1 row) + +SELECT test_bms_compare('(b 1 3)', '(b)') AS result; + result +-------- + 1 +(1 row) + +SELECT test_bms_compare('(b 1 3)', '(b 1 3)') AS result; + result +-------- + 0 +(1 row) + +SELECT test_bms_compare('(b 1 3)', '(b 1 3 5)') AS result; + result +-------- + -1 +(1 row) + +SELECT test_bms_compare('(b 1 3 5)', '(b 1 3)') AS result; + result +-------- + 1 +(1 row) + +SELECT test_bms_compare( + test_bms_add_range('(b)', 0, 63), + test_bms_add_range('(b)', 0, 64) + ) AS result; + result +-------- + -1 +(1 row) + +-- bms_add_range() +SELECT test_bms_add_range('(b)', -5, 10); -- error +ERROR: negative bitmapset member not allowed +SELECT test_bms_add_range('(b)', 5, 7) AS result; + result +----------- + (b 5 6 7) +(1 row) + +SELECT test_bms_add_range('(b)', 5, 5) AS result; + result +-------- + (b 5) +(1 row) + +SELECT test_bms_add_range('(b 1 10)', 5, 7) AS result; + result +---------------- + (b 1 5 6 7 10) +(1 row) + +-- Word boundary of 31 +SELECT test_bms_add_range('(b)', 30, 34) AS result; + result +-------------------- + (b 30 31 32 33 34) +(1 row) + +-- Word boundary of 63 +SELECT test_bms_add_range('(b)', 62, 66) AS result; + result +-------------------- + (b 62 63 64 65 66) +(1 row) + +-- Large range +SELECT length(test_bms_add_range('(b)', 0, 1000)) AS result; + result +-------- + 3898 +(1 row) + +-- Force reallocations +SELECT length(test_bms_add_range('(b)', 0, 200)) AS result; + result +-------- + 697 +(1 row) + +SELECT length(test_bms_add_range('(b)', 1000, 1100)) AS result; + result +-------- + 508 +(1 row) + +-- bms_membership() +SELECT test_bms_membership('(b)') AS result; + result +-------- + 0 +(1 row) + +SELECT test_bms_membership('(b 42)') AS result; + result +-------- + 1 +(1 row) + +SELECT test_bms_membership('(b 1 2)') AS result; + result +-------- + 2 +(1 row) + +-- bms_is_empty() +SELECT test_bms_is_empty(NULL) AS result; + result +-------- + t +(1 row) + +SELECT test_bms_is_empty('(b)') AS result; + result +-------- + t +(1 row) + +SELECT test_bms_is_empty('(b 1)') AS result; + result +-------- + f +(1 row) + +-- bms_singleton_member() +SELECT test_bms_singleton_member('(b 1 2)'); -- error +ERROR: bitmapset has multiple members +SELECT test_bms_singleton_member('(b 42)') AS result; + result +-------- + 42 +(1 row) + +-- bms_get_singleton_member() +-- Not a singleton, returns input default +SELECT test_bms_get_singleton_member('(b 3 6)', 1000) AS result; + result +-------- + 1000 +(1 row) + +-- Singletone, returns sole member +SELECT test_bms_get_singleton_member('(b 400)', 1000) AS result; + result +-------- + 400 +(1 row) + +-- bms_next_member() and bms_prev_member() +-- First member +SELECT test_bms_next_member('(b 5 10 15 20)', -1) AS result; + result +-------- + 5 +(1 row) + +-- Second member +SELECT test_bms_next_member('(b 5 10 15 20)', 5) AS result; + result +-------- + 10 +(1 row) + +-- Member past the end +SELECT test_bms_next_member('(b 5 10 15 20)', 20) AS result; + result +-------- + -2 +(1 row) + +-- Empty set +SELECT test_bms_next_member('(b)', -1) AS result; + result +-------- + -2 +(1 row) + +-- Last member +SELECT test_bms_prev_member('(b 5 10 15 20)', 21) AS result; + result +-------- + 20 +(1 row) + +-- Penultimate member +SELECT test_bms_prev_member('(b 5 10 15 20)', 20) AS result; + result +-------- + 15 +(1 row) + +-- Past beginning member +SELECT test_bms_prev_member('(b 5 10 15 20)', 5) AS result; + result +-------- + -2 +(1 row) + +-- Empty set +SELECT test_bms_prev_member('(b)', 100) AS result; + result +-------- + -2 +(1 row) + +-- bms_hash_value() +SELECT test_bms_hash_value('(b)') = 0 AS result; + result +-------- + t +(1 row) + +SELECT test_bms_hash_value('(b 1 3 5)') = test_bms_hash_value('(b 1 3 5)') AS result; + result +-------- + t +(1 row) + +SELECT test_bms_hash_value('(b 1 3 5)') != test_bms_hash_value('(b 2 4 6)') AS result; + result +-------- + t +(1 row) + +-- bms_overlap() +SELECT test_bms_overlap('(b 1 3 5)', '(b 3 5 7)') AS result; + result +-------- + t +(1 row) + +SELECT test_bms_overlap('(b 1 3 5)', '(b 2 4 6)') AS result; + result +-------- + f +(1 row) + +SELECT test_bms_overlap('(b)', '(b 1 3 5)') AS result; + result +-------- + f +(1 row) + +-- bms_is_subset() +SELECT test_bms_is_subset('(b)', '(b 1 3 5)') AS result; + result +-------- + t +(1 row) + +SELECT test_bms_is_subset('(b 1 3)', '(b 1 3 5)') AS result; + result +-------- + t +(1 row) + +SELECT test_bms_is_subset('(b 1 3 5)', '(b 1 3)') AS result; + result +-------- + f +(1 row) + +SELECT test_bms_is_subset('(b 1 3)', '(b 2 4)') AS result; + result +-------- + f +(1 row) + +SELECT test_bms_is_subset(test_bms_add_range(NULL, 0, 31), + test_bms_add_range(NULL, 0, 63)) AS result; + result +-------- + t +(1 row) + +-- bms_subset_compare() +SELECT test_bms_subset_compare(NULL, NULL) AS result; + result +-------- + 0 +(1 row) + +SELECT test_bms_subset_compare('(b 1 3)', NULL) AS result; + result +-------- + 2 +(1 row) + +SELECT test_bms_subset_compare(NULL, '(b 1 3)') AS result; + result +-------- + 1 +(1 row) + +SELECT test_bms_subset_compare('(b 1 3 5)', '(b 1 3)') AS result; + result +-------- + 2 +(1 row) + +SELECT test_bms_subset_compare('(b 1 3)', '(b 1 3 5)') AS result; + result +-------- + 1 +(1 row) + +SELECT test_bms_subset_compare('(b 1 3 5)', '(b 1 3 5)') AS result; + result +-------- + 0 +(1 row) + +SELECT test_bms_subset_compare('(b 1 3 5)', '(b 2 4 6)') AS result; + result +-------- + 3 +(1 row) + +-- bms_copy() +SELECT test_bms_copy(NULL) AS result; + result +-------- + +(1 row) + +SELECT test_bms_copy('(b 1 3 5 7)') AS result; + result +------------- + (b 1 3 5 7) +(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 +------------- + (b 1 3 5 7) +(1 row) + +SELECT test_bms_add_members('(b 1 3 5)', '(b 2 5 7)') AS result; + result +--------------- + (b 1 2 3 5 7) +(1 row) + +SELECT test_bms_add_members('(b 1 3 5)', '(b 100 200 300)') AS result; + result +----------------------- + (b 1 3 5 100 200 300) +(1 row) + +-- bitmap_hash() +SELECT test_bitmap_hash('(b)') = 0 AS result; + result +-------- + t +(1 row) + +SELECT test_bitmap_hash('(b 1 3 5)') = test_bitmap_hash('(b 1 3 5)') AS result; + result +-------- + t +(1 row) + +SELECT test_bitmap_hash('(b 1 3 5)') = test_bms_hash_value('(b 1 3 5)') AS result; + result +-------- + t +(1 row) + +SELECT test_bitmap_hash('(b 1 3 5)') != test_bitmap_hash('(b 2 4 6)') AS result; + result +-------- + t +(1 row) + +-- bitmap_match() +SELECT test_bitmap_match('(b)', '(b)') AS result; + result +-------- + 0 +(1 row) + +SELECT test_bitmap_match('(b)', '(b 1 3 5)') AS result; + result +-------- + 1 +(1 row) + +SELECT test_bitmap_match('(b 1 3 5)', '(b)') AS result; + result +-------- + 1 +(1 row) + +SELECT test_bitmap_match('(b 1 3 5)', '(b 1 3 5)') AS result; + result +-------- + 0 +(1 row) + +SELECT test_bitmap_match('(b 1 3 5)', '(b 2 4 6)') AS result; + result +-------- + 1 +(1 row) + +SELECT test_bitmap_match('(b 1 3)', '(b 1 3 5)') AS result; + result +-------- + 1 +(1 row) + +-- Check relationship of bitmap_match() with bms_equal() +SELECT (test_bitmap_match('(b 1 3 5)', '(b 1 3 5)') = 0) = + test_bms_equal('(b 1 3 5)', '(b 1 3 5)') AS result; + result +-------- + t +(1 row) + +SELECT (test_bitmap_match('(b 1 3 5)', '(b 2 4 6)') = 0) = + test_bms_equal('(b 1 3 5)', '(b 2 4 6)') AS result; + result +-------- + t +(1 row) + +SELECT (test_bitmap_match('(b)', '(b)') = 0) = + test_bms_equal('(b)', '(b)') AS result; + result +-------- + t +(1 row) + +-- bms_overlap_list() +SELECT test_bms_overlap_list('(b 0)', ARRAY[0]) AS result; + result +-------- + t +(1 row) + +SELECT test_bms_overlap_list('(b 2 3)', ARRAY[1,2]) AS result; + result +-------- + t +(1 row) + +SELECT test_bms_overlap_list('(b 3 4)', ARRAY[3,4,5]) AS result; + result +-------- + t +(1 row) + +SELECT test_bms_overlap_list('(b 7 10)', ARRAY[6,7,8,9]) AS result; + result +-------- + t +(1 row) + +SELECT test_bms_overlap_list('(b 1 5)', ARRAY[6,7,8,9]) AS result; + result +-------- + f +(1 row) + +-- Empty list +SELECT test_bms_overlap_list('(b 1)', ARRAY[]::integer[]) AS result; + result +-------- + f +(1 row) + +-- bms_nonempty_difference() +SELECT test_bms_nonempty_difference(NULL, '(b 1 3 5)') AS result; + result +-------- + f +(1 row) + +SELECT test_bms_nonempty_difference('(b 1 3 5)', NULL) AS result; + result +-------- + t +(1 row) + +SELECT test_bms_nonempty_difference('(b 1 3 5)', '(b 2 4 6)') AS result; + result +-------- + t +(1 row) + +SELECT test_bms_nonempty_difference('(b 1 3 5)', '(b 1 5)') AS result; + result +-------- + t +(1 row) + +SELECT test_bms_nonempty_difference('(b 1 3 5)', '(b 1 3 5)') AS result; + result +-------- + f +(1 row) + +-- random operations +SELECT test_random_operations(-1, 10000, 81920, 0) > 0 AS result; + result +-------- + t +(1 row) + +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..6c035d06f63 --- /dev/null +++ b/src/test/modules/test_bitmapset/meson.build @@ -0,0 +1,33 @@ +# Copyright (c) 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..2b2c72c876b --- /dev/null +++ b/src/test/modules/test_bitmapset/sql/test_bitmapset.sql @@ -0,0 +1,257 @@ +-- Tests for Bitmapsets +CREATE EXTENSION test_bitmapset; + +-- bms_make_singleton() +SELECT test_bms_make_singleton(-1); +SELECT test_bms_make_singleton(42) AS result; +SELECT test_bms_make_singleton(0) AS result; +SELECT test_bms_make_singleton(1000) AS result; + +-- bms_add_member() +SELECT test_bms_add_member('(b 1)', -1); -- error +SELECT test_bms_add_member('(b)', -10); -- error +SELECT test_bms_add_member('(b)', 10) AS result; +SELECT test_bms_add_member('(b 5)', 10) AS result; +-- sort check +SELECT test_bms_add_member('(b 10)', 5) AS result; +-- idempotent change +SELECT test_bms_add_member('(b 10)', 10) AS result; + +-- bms_replace_members() +SELECT test_bms_replace_members(NULL, '(b 1 2 3)') AS result; +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; + +-- bms_del_member() +SELECT test_bms_del_member('(b)', -20); -- error +SELECT test_bms_del_member('(b)', 10) AS result; +SELECT test_bms_del_member('(b 10)', 10) AS result; +SELECT test_bms_del_member('(b 10)', 5) AS result; +SELECT test_bms_del_member('(b 1 2 3)', 2) AS result; +-- Reallocation check +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; + +-- 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; + +-- bms_union() +-- 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; +-- Union of empty with empty +SELECT test_bms_union('(b)', '(b)') AS result; +-- Overlapping ranges +SELECT test_bms_union( + test_bms_add_range('(b)', 0, 15), + test_bms_add_range('(b)', 10, 20) + ) 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. +SELECT test_bms_intersect('(b 1 3 5)', '(b)') 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. +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; + +-- bms_difference() +-- Overlapping sets +SELECT test_bms_difference('(b 1 3 5)', '(b 3 5 7)') AS result; +-- Disjoint sets +SELECT test_bms_difference('(b 1 3 5)', '(b 2 4 6)') AS result; +-- Identical sets +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( + test_bms_add_range('(b)', 0, 100), + test_bms_add_range('(b)', 50, 150) + ) AS result; + +-- bms_is_member() +SELECT test_bms_is_member('(b)', -5); -- error +SELECT test_bms_is_member('(b 1 3 5)', 1) AS result; +SELECT test_bms_is_member('(b 1 3 5)', 2) AS result; +SELECT test_bms_is_member('(b 1 3 5)', 3) AS result; +SELECT test_bms_is_member('(b)', 1) AS result; + +-- bms_member_index() +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; + +-- bms_num_members() +SELECT test_bms_num_members('(b)') AS result; +SELECT test_bms_num_members('(b 1 3 5)') AS result; +SELECT test_bms_num_members('(b 2 4 6 8 10)') AS result; + +-- test_bms_equal() +SELECT test_bms_equal('(b)', '(b)') AS result; +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; + +-- bms_compare() +SELECT test_bms_compare('(b)', '(b)') AS result; +SELECT test_bms_compare('(b)', '(b 1 3)') AS result; +SELECT test_bms_compare('(b 1 3)', '(b)') AS result; +SELECT test_bms_compare('(b 1 3)', '(b 1 3)') AS result; +SELECT test_bms_compare('(b 1 3)', '(b 1 3 5)') AS result; +SELECT test_bms_compare('(b 1 3 5)', '(b 1 3)') AS result; +SELECT test_bms_compare( + test_bms_add_range('(b)', 0, 63), + test_bms_add_range('(b)', 0, 64) + ) AS result; + +-- bms_add_range() +SELECT test_bms_add_range('(b)', -5, 10); -- error +SELECT test_bms_add_range('(b)', 5, 7) AS result; +SELECT test_bms_add_range('(b)', 5, 5) AS result; +SELECT test_bms_add_range('(b 1 10)', 5, 7) AS result; +-- Word boundary of 31 +SELECT test_bms_add_range('(b)', 30, 34) AS result; +-- Word boundary of 63 +SELECT test_bms_add_range('(b)', 62, 66) AS result; +-- Large range +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; + +-- bms_membership() +SELECT test_bms_membership('(b)') AS result; +SELECT test_bms_membership('(b 42)') AS result; +SELECT test_bms_membership('(b 1 2)') AS result; + +-- bms_is_empty() +SELECT test_bms_is_empty(NULL) AS result; +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 1 2)'); -- error +SELECT test_bms_singleton_member('(b 42)') AS result; + +-- bms_get_singleton_member() +-- Not a singleton, returns input default +SELECT test_bms_get_singleton_member('(b 3 6)', 1000) AS result; +-- Singletone, returns sole member +SELECT test_bms_get_singleton_member('(b 400)', 1000) AS result; + +-- bms_next_member() and bms_prev_member() +-- First member +SELECT test_bms_next_member('(b 5 10 15 20)', -1) AS result; +-- Second member +SELECT test_bms_next_member('(b 5 10 15 20)', 5) AS result; +-- Member past the end +SELECT test_bms_next_member('(b 5 10 15 20)', 20) AS result; +-- Empty set +SELECT test_bms_next_member('(b)', -1) AS result; +-- Last member +SELECT test_bms_prev_member('(b 5 10 15 20)', 21) AS result; +-- Penultimate member +SELECT test_bms_prev_member('(b 5 10 15 20)', 20) AS result; +-- Past beginning member +SELECT test_bms_prev_member('(b 5 10 15 20)', 5) AS result; +-- Empty set +SELECT test_bms_prev_member('(b)', 100) AS result; + +-- bms_hash_value() +SELECT test_bms_hash_value('(b)') = 0 AS result; +SELECT test_bms_hash_value('(b 1 3 5)') = test_bms_hash_value('(b 1 3 5)') AS result; +SELECT test_bms_hash_value('(b 1 3 5)') != test_bms_hash_value('(b 2 4 6)') AS result; + +-- bms_overlap() +SELECT test_bms_overlap('(b 1 3 5)', '(b 3 5 7)') AS result; +SELECT test_bms_overlap('(b 1 3 5)', '(b 2 4 6)') AS result; +SELECT test_bms_overlap('(b)', '(b 1 3 5)') AS result; + +-- bms_is_subset() +SELECT test_bms_is_subset('(b)', '(b 1 3 5)') AS result; +SELECT test_bms_is_subset('(b 1 3)', '(b 1 3 5)') AS result; +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; + +-- 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; + +-- 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; + +-- bitmap_hash() +SELECT test_bitmap_hash('(b)') = 0 AS result; +SELECT test_bitmap_hash('(b 1 3 5)') = test_bitmap_hash('(b 1 3 5)') AS result; +SELECT test_bitmap_hash('(b 1 3 5)') = test_bms_hash_value('(b 1 3 5)') AS result; +SELECT test_bitmap_hash('(b 1 3 5)') != test_bitmap_hash('(b 2 4 6)') AS result; + +-- bitmap_match() +SELECT test_bitmap_match('(b)', '(b)') AS result; +SELECT test_bitmap_match('(b)', '(b 1 3 5)') AS result; +SELECT test_bitmap_match('(b 1 3 5)', '(b)') AS result; +SELECT test_bitmap_match('(b 1 3 5)', '(b 1 3 5)') AS result; +SELECT test_bitmap_match('(b 1 3 5)', '(b 2 4 6)') AS result; +SELECT test_bitmap_match('(b 1 3)', '(b 1 3 5)') AS result; +-- Check relationship of bitmap_match() with bms_equal() +SELECT (test_bitmap_match('(b 1 3 5)', '(b 1 3 5)') = 0) = + test_bms_equal('(b 1 3 5)', '(b 1 3 5)') AS result; +SELECT (test_bitmap_match('(b 1 3 5)', '(b 2 4 6)') = 0) = + test_bms_equal('(b 1 3 5)', '(b 2 4 6)') AS result; +SELECT (test_bitmap_match('(b)', '(b)') = 0) = + test_bms_equal('(b)', '(b)') AS result; + +-- bms_overlap_list() +SELECT test_bms_overlap_list('(b 0)', ARRAY[0]) AS result; +SELECT test_bms_overlap_list('(b 2 3)', ARRAY[1,2]) AS result; +SELECT test_bms_overlap_list('(b 3 4)', ARRAY[3,4,5]) AS result; +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; + +-- bms_nonempty_difference() +SELECT test_bms_nonempty_difference(NULL, '(b 1 3 5)') AS result; +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; + +-- random operations +SELECT test_random_operations(-1, 10000, 81920, 0) > 0 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 new file mode 100644 index 00000000000..95f5ee02e3f --- /dev/null +++ b/src/test/modules/test_bitmapset/test_bitmapset--1.0.sql @@ -0,0 +1,136 @@ +/* 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 + +-- Bitmapset API functions +CREATE FUNCTION test_bms_make_singleton(integer) +RETURNS text STRICT +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_add_member(text, integer) +RETURNS text +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_del_member(text, integer) +RETURNS text +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_is_member(text, integer) +RETURNS boolean +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_num_members(text) +RETURNS integer +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_copy(text) +RETURNS text +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_equal(text, text) +RETURNS boolean +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_compare(text, text) +RETURNS integer +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_is_subset(text, text) +RETURNS boolean +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_subset_compare(text, text) +RETURNS integer +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_union(text, text) +RETURNS text +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_intersect(text, text) +RETURNS text +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_difference(text, text) +RETURNS text +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_is_empty(text) +RETURNS boolean +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_membership(text) +RETURNS integer +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_singleton_member(text) +RETURNS integer STRICT +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_get_singleton_member(text, integer) +RETURNS integer +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_next_member(text, integer) +RETURNS integer +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_prev_member(text, integer) +RETURNS integer +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_hash_value(text) +RETURNS integer +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_overlap(text, text) +RETURNS boolean +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_overlap_list(text, int4[]) +RETURNS boolean +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_nonempty_difference(text, text) +RETURNS boolean +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_member_index(text, integer) +RETURNS integer +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_add_range(text, integer, integer) +RETURNS text +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_add_members(text, text) +RETURNS text +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bms_int_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; + +CREATE FUNCTION test_bms_join(text, text) +RETURNS text +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bitmap_hash(text) +RETURNS integer +AS 'MODULE_PATHNAME' LANGUAGE C; + +CREATE FUNCTION test_bitmap_match(text, text) +RETURNS int +AS 'MODULE_PATHNAME' LANGUAGE C; + +-- Test utility functions +CREATE FUNCTION test_random_operations(integer, integer, integer, integer) +RETURNS integer STRICT +AS 'MODULE_PATHNAME' LANGUAGE C; + +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..61f256f65a4 --- /dev/null +++ b/src/test/modules/test_bitmapset/test_bitmapset.c @@ -0,0 +1,1021 @@ +/*------------------------------------------------------------------------- + * + * test_bitmapset.c + * Test the Bitmapset data structure. + * + * This module tests the Bitmapset implementation in PostgreSQL, covering + * all public API functions. + * + * Copyright (c) 2025, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/test_bitmapset/test_bitmapset.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include <stddef.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 "utils/timestamp.h" +#include "varatt.h" + +PG_MODULE_MAGIC; + +/* 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_int_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); + +/* Convenient macros to test results */ +#define EXPECT_TRUE(expr) \ + do { \ + if (!(expr)) \ + elog(ERROR, \ + "%s was unexpectedly false in file \"%s\" line %u", \ + #expr, __FILE__, __LINE__); \ + } while (0) + +#define EXPECT_NOT_NULL(expr) \ + do { \ + if ((expr) == NULL) \ + elog(ERROR, \ + "%s was unexpectedly true in file \"%s\" line %u", \ + #expr, __FILE__, __LINE__); \ + } while (0) + +/* Encode/Decode to/from TEXT and Bitmapset */ +#define BITMAPSET_TO_TEXT(bms) (text *) CStringGetTextDatum(nodeToString((bms))) +#define TEXT_TO_BITMAPSET(str) (Bitmapset *) stringToNode(TextDatumGetCString((Datum) (str))) + +/* + * Individual test functions for each bitmapset API function + */ + +Datum +test_bms_add_member(PG_FUNCTION_ARGS) +{ + int member; + Bitmapset *bms = NULL; + text *result; + + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); + + if (!PG_ARGISNULL(0)) + bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0)); + + member = PG_GETARG_INT32(1); + bms = bms_add_member(bms, member); + result = BITMAPSET_TO_TEXT(bms); + + if (bms) + bms_free(bms); + + if (result == NULL) + PG_RETURN_NULL(); + + PG_RETURN_TEXT_P(result); +} + +Datum +test_bms_add_members(PG_FUNCTION_ARGS) +{ + Bitmapset *bms1 = NULL, + *bms2 = NULL; + 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_add_members modifies/frees the first argument */ + bms1 = bms_add_members(bms1, bms2); + + if (bms2) + bms_free(bms2); + + if (bms1 == NULL) + PG_RETURN_NULL(); + + result = BITMAPSET_TO_TEXT(bms1); + bms_free(bms1); + + PG_RETURN_TEXT_P(result); +} + +Datum +test_bms_del_member(PG_FUNCTION_ARGS) +{ + Bitmapset *bms = NULL; + int32 member; + text *result; + + if (PG_ARGISNULL(1)) + PG_RETURN_NULL(); + + if (!PG_ARGISNULL(0)) + bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 = BITMAPSET_TO_TEXT(bms); + bms_free(bms); + + PG_RETURN_TEXT_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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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; + text *result; + + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + + member = PG_GETARG_INT32(0); + bms = bms_make_singleton(member); + + result = BITMAPSET_TO_TEXT(bms); + bms_free(bms); + + PG_RETURN_TEXT_P(result); +} + +Datum +test_bms_copy(PG_FUNCTION_ARGS) +{ + text *bms_data; + Bitmapset *bms = NULL; + Bitmapset *copy_bms; + text *result; + + if (PG_ARGISNULL(0)) + PG_RETURN_NULL(); + + bms_data = PG_GETARG_TEXT_PP(0); + bms = TEXT_TO_BITMAPSET(bms_data); + copy_bms = bms_copy(bms); + result = BITMAPSET_TO_TEXT(copy_bms); + + if (bms) + bms_free(bms); + if (copy_bms) + bms_free(copy_bms); + + PG_RETURN_TEXT_P(result); +} + +Datum +test_bms_equal(PG_FUNCTION_ARGS) +{ + Bitmapset *bms1 = NULL, + *bms2 = NULL; + bool 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)); + + 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; + 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)); + + result_bms = bms_union(bms1, bms2); + + if (bms1) + bms_free(bms1); + 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_membership(PG_FUNCTION_ARGS) +{ + Bitmapset *bms = NULL; + BMS_Membership result; + + if (PG_ARGISNULL(0)) + PG_RETURN_INT32(BMS_EMPTY_SET); + + bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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_intersect(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)); + + result_bms = bms_intersect(bms1, bms2); + + if (bms1) + bms_free(bms1); + 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_difference(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)); + + result_bms = bms_difference(bms1, bms2); + + if (bms1) + bms_free(bms1); + 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_compare(PG_FUNCTION_ARGS) +{ + Bitmapset *bms1 = NULL, + *bms2 = NULL; + int 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)); + + 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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0)); + + if (!PG_ARGISNULL(1)) + bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0)); + + if (!PG_ARGISNULL(1)) + bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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) +{ + text *bms_data; + Bitmapset *bms = NULL; + int32 prevmember; + int result; + + if (PG_ARGISNULL(0)) + PG_RETURN_INT32(-2); + + bms_data = PG_GETARG_TEXT_PP(0); + prevmember = PG_GETARG_INT32(1); + + if (VARSIZE_ANY_EXHDR(bms_data) == 0) + PG_RETURN_INT32(-2); + + bms = TEXT_TO_BITMAPSET(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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0)); + + if (!PG_ARGISNULL(1)) + bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 *int_list = NIL; + bool result; + Datum *elem_datums; + bool *elem_nulls; + int elem_count; + int i; + + if (PG_ARGISNULL(0)) + PG_RETURN_BOOL(false); + + bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0)); + + if (PG_ARGISNULL(1)) + { + if (bms) + bms_free(bms); + PG_RETURN_BOOL(false); + } + + 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); + + for (i = 0; i < elem_count; i++) + { + if (!elem_nulls[i]) + { + int32 member = DatumGetInt32(elem_datums[i]); + + int_list = lappend_int(int_list, member); + } + } + + result = bms_overlap_list(bms, int_list); + + if (bms) + bms_free(bms); + + list_free(int_list); + + 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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0)); + + if (!PG_ARGISNULL(1)) + bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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) +{ + text *bms_data; + Bitmapset *bms = NULL; + int32 member; + int result; + + if (PG_ARGISNULL(0)) + PG_RETURN_INT32(-1); + + bms_data = PG_GETARG_TEXT_PP(0); + member = PG_GETARG_INT32(1); + + if (VARSIZE_ANY_EXHDR(bms_data) == 0) + PG_RETURN_INT32(-1); + + bms = TEXT_TO_BITMAPSET(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; + text *result; + + if (PG_ARGISNULL(1) || PG_ARGISNULL(2)) + PG_RETURN_NULL(); + + if (!PG_ARGISNULL(0)) + bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 = BITMAPSET_TO_TEXT(bms); + if (bms) + bms_free(bms); + + PG_RETURN_TEXT_P(result); +} + +Datum +test_bms_int_members(PG_FUNCTION_ARGS) +{ + Bitmapset *bms1 = NULL, + *bms2 = NULL; + 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)); + + bms1 = bms_int_members(bms1, bms2); + + if (bms2) + bms_free(bms2); + + if (bms1 == NULL) + PG_RETURN_NULL(); + + result = BITMAPSET_TO_TEXT(bms1); + + if (bms1) + bms_free(bms1); + + PG_RETURN_TEXT_P(result); +} + +Datum +test_bms_replace_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_replace_members modifies/frees the first argument */ + result_bms = bms_replace_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_join(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_join may recycle either input arguments */ + result_bms = bms_join(bms1, bms2); + + /* bms1 and bms2 may have been recycled! Do not free any of them. */ + + 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_hash_value(PG_FUNCTION_ARGS) +{ + Bitmapset *bms = NULL; + uint32 hash_result; + + if (!PG_ARGISNULL(0)) + bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0)); + + if (!PG_ARGISNULL(1)) + bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_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); +} + +/* + * Contrary to all the other functions which are one-one mappings with the + * equivalent C functions, this stresses Bitmapsets in a random fashion for + * various operations. + * + * "min_value" is the minimal value used for the members, that will stand + * up to a range of "max_range". "num_ops" defines the number of time each + * operation is done. "seed" is a random seed used to calculate the member + * values. + * + * The return value is the number of times all operations have been executed. + */ +Datum +test_random_operations(PG_FUNCTION_ARGS) +{ + Bitmapset *bms1 = NULL; + Bitmapset *bms2 = NULL; + Bitmapset *bms = NULL; + Bitmapset *result = NULL; + pg_prng_state state; + uint64 seed = GetCurrentTimestamp(); + int num_ops = 5000; + int total_ops = 0; + int max_range = 2000; + int min_value = 0; + int member; + int *members; + int num_members = 0; + + if (!PG_ARGISNULL(0) && PG_GETARG_INT32(0) > 0) + seed = PG_GETARG_INT32(0); + + if (!PG_ARGISNULL(1)) + num_ops = PG_GETARG_INT32(1); + + if (!PG_ARGISNULL(2)) + max_range = PG_GETARG_INT32(2); + + if (!PG_ARGISNULL(3)) + min_value = PG_GETARG_INT32(3); + + pg_prng_seed(&state, seed); + members = palloc(sizeof(int) * num_ops); + + /* Phase 1: Random insertions */ + for (int i = 0; i < num_ops / 2; i++) + { + member = pg_prng_uint32(&state) % max_range + min_value; + + if (!bms_is_member(member, bms1)) + { + members[num_members++] = member; + bms1 = bms_add_member(bms1, member); + } + } + + /* Phase 2: Random set operations */ + for (int i = 0; i < num_ops / 4; i++) + { + member = pg_prng_uint32(&state) % max_range + min_value; + + bms2 = bms_add_member(bms2, member); + } + + /* Test union */ + result = bms_union(bms1, bms2); + EXPECT_NOT_NULL(result); + + /* Verify union contains all members from first set */ + for (int i = 0; i < num_members; i++) + { + if (!bms_is_member(members[i], result)) + elog(ERROR, "union missing member %d", members[i]); + } + bms_free(result); + + /* Test intersection */ + result = bms_intersect(bms1, bms2); + if (result != NULL) + { + member = -1; + + while ((member = bms_next_member(result, member)) >= 0) + { + if (!bms_is_member(member, bms1) || !bms_is_member(member, bms2)) + elog(ERROR, "intersection contains invalid member %d", member); + } + bms_free(result); + } + + /* Phase 3: Test range operations */ + result = NULL; + for (int i = 0; i < num_ops; i++) + { + int lower = pg_prng_uint32(&state) % 100; + int upper = lower + (pg_prng_uint32(&state) % 20); + + result = bms_add_range(result, lower, upper); + } + if (result != NULL) + { + EXPECT_TRUE(bms_num_members(result) > 0); + bms_free(result); + } + + pfree(members); + bms_free(bms1); + bms_free(bms2); + + for (int i = 0; i < num_ops; i++) + { + member = pg_prng_uint32(&state) % max_range + min_value; + switch (pg_prng_uint32(&state) % 3) + { + case 0: /* add */ + bms = bms_add_member(bms, member); + break; + case 1: /* delete */ + if (bms != NULL) + { + bms = bms_del_member(bms, member); + } + break; + case 2: /* test membership */ + if (bms != NULL) + { + bms_is_member(member, bms); + } + break; + } + total_ops++; + } + + 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 |