Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * test_bitmapset.c
4 : : * Test the Bitmapset data structure.
5 : : *
6 : : * This module tests the Bitmapset implementation in PostgreSQL, covering
7 : : * all public API functions.
8 : : *
9 : : * Copyright (c) 2025-2026, PostgreSQL Global Development Group
10 : : *
11 : : * IDENTIFICATION
12 : : * src/test/modules/test_bitmapset/test_bitmapset.c
13 : : *
14 : : *-------------------------------------------------------------------------
15 : : */
16 : :
17 : : #include "postgres.h"
18 : :
19 : : #include <stddef.h>
20 : : #include "catalog/pg_type.h"
21 : : #include "common/pg_prng.h"
22 : : #include "fmgr.h"
23 : : #include "miscadmin.h"
24 : : #include "nodes/bitmapset.h"
25 : : #include "nodes/nodes.h"
26 : : #include "nodes/pg_list.h"
27 : : #include "utils/array.h"
28 : : #include "utils/builtins.h"
29 : : #include "utils/timestamp.h"
30 : :
339 michael@paquier.xyz 31 :CBC 1 : PG_MODULE_MAGIC;
32 : :
33 : : /* Bitmapset API functions in order of appearance in bitmapset.c */
34 : 2 : PG_FUNCTION_INFO_V1(test_bms_make_singleton);
35 : 2 : PG_FUNCTION_INFO_V1(test_bms_add_member);
36 : 2 : PG_FUNCTION_INFO_V1(test_bms_del_member);
37 : 2 : PG_FUNCTION_INFO_V1(test_bms_is_member);
38 : 2 : PG_FUNCTION_INFO_V1(test_bms_num_members);
39 : 2 : PG_FUNCTION_INFO_V1(test_bms_copy);
40 : 2 : PG_FUNCTION_INFO_V1(test_bms_equal);
41 : 2 : PG_FUNCTION_INFO_V1(test_bms_compare);
42 : 2 : PG_FUNCTION_INFO_V1(test_bms_is_subset);
43 : 2 : PG_FUNCTION_INFO_V1(test_bms_subset_compare);
44 : 2 : PG_FUNCTION_INFO_V1(test_bms_union);
45 : 2 : PG_FUNCTION_INFO_V1(test_bms_intersect);
46 : 2 : PG_FUNCTION_INFO_V1(test_bms_difference);
49 drowley@postgresql.o 47 :GNC 2 : PG_FUNCTION_INFO_V1(test_bms_offset_members);
339 michael@paquier.xyz 48 :CBC 2 : PG_FUNCTION_INFO_V1(test_bms_is_empty);
49 : 2 : PG_FUNCTION_INFO_V1(test_bms_membership);
50 : 2 : PG_FUNCTION_INFO_V1(test_bms_singleton_member);
51 : 2 : PG_FUNCTION_INFO_V1(test_bms_get_singleton_member);
52 : 2 : PG_FUNCTION_INFO_V1(test_bms_next_member);
53 : 2 : PG_FUNCTION_INFO_V1(test_bms_prev_member);
54 : 2 : PG_FUNCTION_INFO_V1(test_bms_hash_value);
55 : 2 : PG_FUNCTION_INFO_V1(test_bms_overlap);
56 : 2 : PG_FUNCTION_INFO_V1(test_bms_overlap_list);
57 : 2 : PG_FUNCTION_INFO_V1(test_bms_nonempty_difference);
58 : 2 : PG_FUNCTION_INFO_V1(test_bms_member_index);
59 : 2 : PG_FUNCTION_INFO_V1(test_bms_add_range);
60 : 2 : PG_FUNCTION_INFO_V1(test_bms_add_members);
61 : 2 : PG_FUNCTION_INFO_V1(test_bms_int_members);
332 62 : 2 : PG_FUNCTION_INFO_V1(test_bms_del_members);
339 63 : 2 : PG_FUNCTION_INFO_V1(test_bms_replace_members);
64 : 2 : PG_FUNCTION_INFO_V1(test_bms_join);
65 : 2 : PG_FUNCTION_INFO_V1(test_bitmap_hash);
66 : 2 : PG_FUNCTION_INFO_V1(test_bitmap_match);
67 : :
68 : : /* Test utility functions */
69 : 2 : PG_FUNCTION_INFO_V1(test_random_operations);
49 drowley@postgresql.o 70 :GNC 2 : PG_FUNCTION_INFO_V1(test_random_offset_operations);
71 : :
72 : : /* Convenient macros to test results */
73 : : #define EXPECT_TRUE(expr) \
74 : : do { \
75 : : if (!(expr)) \
76 : : elog(ERROR, \
77 : : "%s was unexpectedly false in file \"%s\" line %u", \
78 : : #expr, __FILE__, __LINE__); \
79 : : } while (0)
80 : :
81 : : #define EXPECT_NOT_NULL(expr) \
82 : : do { \
83 : : if ((expr) == NULL) \
84 : : elog(ERROR, \
85 : : "%s was unexpectedly true in file \"%s\" line %u", \
86 : : #expr, __FILE__, __LINE__); \
87 : : } while (0)
88 : :
89 : : /* Encode/Decode to/from TEXT and Bitmapset */
90 : : #define BITMAPSET_TO_TEXT(bms) cstring_to_text(nodeToString(bms))
91 : : #define TEXT_TO_BITMAPSET(str) ((Bitmapset *) stringToNode(text_to_cstring(str)))
92 : :
93 : : /*
94 : : * Helper macro to fetch text parameters as Bitmapsets. SQL-NULL means empty
95 : : * set.
96 : : */
97 : : #define PG_ARG_GETBITMAPSET(n) \
98 : : (PG_ARGISNULL(n) ? NULL : TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(n)))
99 : :
100 : : /*
101 : : * Helper macro to handle converting sets back to text, returning the
102 : : * resulting text representation of the set.
103 : : */
104 : : #define PG_RETURN_BITMAPSET_AS_TEXT(bms) \
105 : : PG_RETURN_TEXT_P(BITMAPSET_TO_TEXT(bms))
106 : :
107 : : /*
108 : : * Individual test functions for each bitmapset API function
109 : : *
110 : : * Primarily, we aim to keep these as close to simple wrapper functions as
111 : : * possible in order to publish the functions of bitmapset.c to the SQL layer
112 : : * with as little interference as possible. We opt to return SQL NULL in
113 : : * cases where the input given to the SQL function isn't valid to pass to the
114 : : * underlying bitmapset.c function. For example we cannot do much useful
115 : : * testing if someone calls test_bms_make_singleton(NULL) since
116 : : * bms_make_singleton() expects an integer argument.
117 : : *
118 : : * For function arguments which are to be converted to Bitmapsets, we accept
119 : : * SQL NULL as a valid argument to mean an empty set. Optionally callers may
120 : : * pass '(b)'.
121 : : *
122 : : * For the test functions which return a Bitmapset, these are converted back
123 : : * to text with result generated by nodeToString().
124 : : */
125 : :
126 : : Datum
339 michael@paquier.xyz 127 :CBC 7 : test_bms_add_member(PG_FUNCTION_ARGS)
128 : : {
129 : : Bitmapset *bms;
130 : : int member;
131 : :
132 [ + + ]: 7 : if (PG_ARGISNULL(1))
330 133 : 1 : PG_RETURN_NULL(); /* invalid input */
134 : :
135 [ + - ]: 6 : bms = PG_ARG_GETBITMAPSET(0);
339 136 : 6 : member = PG_GETARG_INT32(1);
137 : :
330 138 : 6 : bms = bms_add_member(bms, member);
139 : :
140 : 4 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
141 : : }
142 : :
143 : : Datum
339 144 : 3 : test_bms_add_members(PG_FUNCTION_ARGS)
145 : : {
330 146 [ + - ]: 3 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
147 [ + - ]: 3 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
148 : :
149 : : /* left input is recycled */
339 150 : 3 : bms1 = bms_add_members(bms1, bms2);
151 : :
330 152 : 3 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
153 : : }
154 : :
155 : : Datum
339 156 : 12 : test_bms_del_member(PG_FUNCTION_ARGS)
157 : : {
158 : : Bitmapset *bms;
159 : : int32 member;
160 : :
161 [ + + ]: 12 : if (PG_ARGISNULL(1))
330 162 : 1 : PG_RETURN_NULL(); /* invalid input */
163 : :
164 [ + - ]: 11 : bms = PG_ARG_GETBITMAPSET(0);
339 165 : 11 : member = PG_GETARG_INT32(1);
166 : :
330 167 : 11 : bms = bms_del_member(bms, member);
168 : :
169 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
170 : : }
171 : :
172 : : Datum
339 173 : 6 : test_bms_is_member(PG_FUNCTION_ARGS)
174 : : {
175 : : Bitmapset *bms;
176 : : int32 member;
177 : : bool result;
178 : :
179 [ + + ]: 6 : if (PG_ARGISNULL(1))
330 180 : 1 : PG_RETURN_NULL(); /* invalid input */
181 : :
182 [ + - ]: 5 : bms = PG_ARG_GETBITMAPSET(0);
339 183 : 5 : member = PG_GETARG_INT32(1);
184 : :
330 185 : 5 : result = bms_is_member(member, bms);
186 : :
339 187 : 4 : PG_RETURN_BOOL(result);
188 : : }
189 : :
190 : : Datum
191 : 3 : test_bms_num_members(PG_FUNCTION_ARGS)
192 : : {
330 193 [ + - ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
194 : : int result;
195 : :
339 196 : 3 : result = bms_num_members(bms);
197 : :
198 : 3 : PG_RETURN_INT32(result);
199 : : }
200 : :
201 : : Datum
202 : 4 : test_bms_make_singleton(PG_FUNCTION_ARGS)
203 : : {
204 : : Bitmapset *bms;
205 : : int32 member;
206 : :
207 : 4 : member = PG_GETARG_INT32(0);
208 : 4 : bms = bms_make_singleton(member);
209 : :
330 210 : 3 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
211 : : }
212 : :
213 : : Datum
339 214 : 2 : test_bms_copy(PG_FUNCTION_ARGS)
215 : : {
330 216 [ + + ]: 2 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
217 : : Bitmapset *copy_bms;
218 : :
339 219 : 2 : copy_bms = bms_copy(bms);
220 : :
330 221 : 2 : PG_RETURN_BITMAPSET_AS_TEXT(copy_bms);
222 : : }
223 : :
224 : : Datum
339 225 : 13 : test_bms_equal(PG_FUNCTION_ARGS)
226 : : {
330 227 [ + + ]: 13 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
228 [ + + ]: 13 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
229 : : bool result;
230 : :
339 231 : 13 : result = bms_equal(bms1, bms2);
232 : :
233 : 13 : PG_RETURN_BOOL(result);
234 : : }
235 : :
236 : : Datum
237 : 9 : test_bms_union(PG_FUNCTION_ARGS)
238 : : {
330 239 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
240 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
241 : : Bitmapset *result_bms;
242 : :
339 243 : 9 : result_bms = bms_union(bms1, bms2);
244 : :
330 245 : 9 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
246 : : }
247 : :
248 : : Datum
339 249 : 4 : test_bms_membership(PG_FUNCTION_ARGS)
250 : : {
330 251 [ + + ]: 4 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
252 : : BMS_Membership result;
253 : :
339 254 : 4 : result = bms_membership(bms);
255 : :
256 : 4 : PG_RETURN_INT32((int32) result);
257 : : }
258 : :
259 : : Datum
260 : 6 : test_bms_next_member(PG_FUNCTION_ARGS)
261 : : {
262 : : Bitmapset *bms;
263 : : int32 prevmember;
264 : : int result;
265 : :
330 266 [ + + ]: 6 : if (PG_ARGISNULL(1))
267 : 1 : PG_RETURN_NULL(); /* invalid input */
268 : :
269 [ + + ]: 5 : bms = PG_ARG_GETBITMAPSET(0);
339 270 : 5 : prevmember = PG_GETARG_INT32(1);
271 : :
330 272 : 5 : result = bms_next_member(bms, prevmember);
273 : :
339 274 : 5 : PG_RETURN_INT32(result);
275 : : }
276 : :
277 : : Datum
278 : 8 : test_bms_intersect(PG_FUNCTION_ARGS)
279 : : {
330 280 [ + + ]: 8 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
281 [ + + ]: 8 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
282 : : Bitmapset *result_bms;
283 : :
339 284 : 8 : result_bms = bms_intersect(bms1, bms2);
285 : :
330 286 : 8 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
287 : : }
288 : :
289 : : Datum
339 290 : 10 : test_bms_difference(PG_FUNCTION_ARGS)
291 : : {
330 292 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
293 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
294 : : Bitmapset *result_bms;
295 : :
339 296 : 10 : result_bms = bms_difference(bms1, bms2);
297 : :
330 298 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
299 : : }
300 : :
301 : : Datum
49 drowley@postgresql.o 302 :GNC 13 : test_bms_offset_members(PG_FUNCTION_ARGS)
303 : : {
304 [ + - ]: 13 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
305 : 13 : int offset = PG_GETARG_INT32(1);
306 : :
307 : 13 : bms = bms_offset_members(bms, offset);
308 : :
309 : 11 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
310 : : }
311 : :
312 : : Datum
339 michael@paquier.xyz 313 :CBC 10 : test_bms_compare(PG_FUNCTION_ARGS)
314 : : {
330 315 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
316 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
317 : : int result;
318 : :
339 319 : 10 : result = bms_compare(bms1, bms2);
320 : :
321 : 10 : PG_RETURN_INT32(result);
322 : : }
323 : :
324 : : Datum
325 : 3 : test_bms_is_empty(PG_FUNCTION_ARGS)
326 : : {
330 327 [ + + ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
328 : : bool result;
329 : :
339 330 : 3 : result = bms_is_empty(bms);
331 : :
332 : 3 : PG_RETURN_BOOL(result);
333 : : }
334 : :
335 : : Datum
336 : 10 : test_bms_is_subset(PG_FUNCTION_ARGS)
337 : : {
330 338 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
339 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
340 : : bool result;
341 : :
339 342 : 10 : result = bms_is_subset(bms1, bms2);
343 : :
344 : 10 : PG_RETURN_BOOL(result);
345 : : }
346 : :
347 : : Datum
348 : 23 : test_bms_subset_compare(PG_FUNCTION_ARGS)
349 : : {
330 350 [ + + ]: 23 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
351 [ + + ]: 23 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
352 : : BMS_Comparison result;
353 : :
339 354 : 23 : result = bms_subset_compare(bms1, bms2);
355 : :
356 : 23 : PG_RETURN_INT32((int32) result);
357 : : }
358 : :
359 : : Datum
360 : 3 : test_bms_singleton_member(PG_FUNCTION_ARGS)
361 : : {
330 362 [ + - ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
363 : : int result;
364 : :
339 365 : 3 : result = bms_singleton_member(bms);
366 : :
367 : 1 : PG_RETURN_INT32(result);
368 : : }
369 : :
370 : : Datum
371 : 4 : test_bms_get_singleton_member(PG_FUNCTION_ARGS)
372 : : {
330 373 [ + + ]: 4 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
374 : : int member;
375 : :
376 : : /*
377 : : * Keep this simple. Return -1 when we detect the set is not a singleton
378 : : * set, otherwise return the singleton member.
379 : : */
380 [ + + ]: 4 : if (!bms_get_singleton_member(bms, &member))
381 : 3 : member = -1;
382 : :
383 : 4 : PG_RETURN_INT32(member);
384 : : }
385 : :
386 : : Datum
339 387 : 7 : test_bms_prev_member(PG_FUNCTION_ARGS)
388 : : {
389 : : Bitmapset *bms;
390 : : int32 prevmember;
391 : : int result;
392 : :
330 393 [ + + ]: 7 : if (PG_ARGISNULL(1))
394 : 1 : PG_RETURN_NULL(); /* invalid input */
395 : :
396 [ + + ]: 6 : bms = PG_ARG_GETBITMAPSET(0);
339 397 : 6 : prevmember = PG_GETARG_INT32(1);
398 : :
399 : 6 : result = bms_prev_member(bms, prevmember);
400 : :
401 : 6 : PG_RETURN_INT32(result);
402 : : }
403 : :
404 : : Datum
405 : 6 : test_bms_overlap(PG_FUNCTION_ARGS)
406 : : {
330 407 [ + + ]: 6 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
408 [ + + ]: 6 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
409 : : bool result;
410 : :
339 411 : 6 : result = bms_overlap(bms1, bms2);
412 : :
413 : 6 : PG_RETURN_BOOL(result);
414 : : }
415 : :
416 : : Datum
417 : 11 : test_bms_overlap_list(PG_FUNCTION_ARGS)
418 : : {
419 : : Bitmapset *bms;
420 : : ArrayType *array;
421 : 11 : List *int_list = NIL;
422 : : bool result;
330 423 : 11 : Datum *elem_datums = NULL;
424 : 11 : bool *elem_nulls = NULL;
425 : : int elem_count;
426 : : int i;
427 : :
428 [ + + ]: 11 : bms = PG_ARG_GETBITMAPSET(0);
429 : :
430 [ + + ]: 11 : if (!PG_ARGISNULL(1))
431 : : {
432 : 9 : array = PG_GETARG_ARRAYTYPE_P(1);
433 : :
434 : 9 : deconstruct_array(array,
435 : : INT4OID, sizeof(int32), true, 'i',
436 : : &elem_datums, &elem_nulls, &elem_count);
437 : :
438 [ + + ]: 31 : for (i = 0; i < elem_count; i++)
439 : : {
440 [ + - ]: 22 : if (!elem_nulls[i])
441 : : {
442 : 22 : int32 member = DatumGetInt32(elem_datums[i]);
443 : :
444 : 22 : int_list = lappend_int(int_list, member);
445 : : }
446 : : }
447 : : }
448 : :
339 449 : 11 : result = bms_overlap_list(bms, int_list);
450 : :
451 : 9 : list_free(int_list);
452 : :
330 453 [ + + ]: 9 : if (elem_datums)
454 : 7 : pfree(elem_datums);
455 : :
456 [ + + ]: 9 : if (elem_nulls)
457 : 7 : pfree(elem_nulls);
458 : :
339 459 : 9 : PG_RETURN_BOOL(result);
460 : : }
461 : :
462 : : Datum
463 : 9 : test_bms_nonempty_difference(PG_FUNCTION_ARGS)
464 : : {
330 465 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
466 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
467 : : bool result;
468 : :
339 469 : 9 : result = bms_nonempty_difference(bms1, bms2);
470 : :
471 : 9 : PG_RETURN_BOOL(result);
472 : : }
473 : :
474 : : Datum
475 : 8 : test_bms_member_index(PG_FUNCTION_ARGS)
476 : : {
477 : : Bitmapset *bms;
478 : : int32 member;
479 : : int result;
480 : :
330 481 [ + + ]: 8 : if (PG_ARGISNULL(1))
482 : 1 : PG_RETURN_NULL(); /* invalid input */
483 : :
484 [ + + ]: 7 : bms = PG_ARG_GETBITMAPSET(0);
339 485 : 7 : member = PG_GETARG_INT32(1);
486 : :
487 : 7 : result = bms_member_index(bms, member);
488 : :
489 : 7 : PG_RETURN_INT32(result);
490 : : }
491 : :
492 : : Datum
493 : 25 : test_bms_add_range(PG_FUNCTION_ARGS)
494 : : {
495 : : Bitmapset *bms;
496 : : int32 lower,
497 : : upper;
498 : :
499 [ + + + + ]: 25 : if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
330 500 : 3 : PG_RETURN_NULL(); /* invalid input */
501 : :
502 [ + + ]: 22 : bms = PG_ARG_GETBITMAPSET(0);
339 503 : 22 : lower = PG_GETARG_INT32(1);
504 : 22 : upper = PG_GETARG_INT32(2);
505 : :
506 : 22 : bms = bms_add_range(bms, lower, upper);
507 : :
330 508 : 21 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
509 : : }
510 : :
511 : : Datum
339 512 : 7 : test_bms_int_members(PG_FUNCTION_ARGS)
513 : : {
330 514 [ + + ]: 7 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
515 [ + + ]: 7 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
516 : :
517 : : /* left input gets recycled */
339 518 : 7 : bms1 = bms_int_members(bms1, bms2);
519 : :
330 520 : 7 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
521 : : }
522 : :
523 : : Datum
332 524 : 10 : test_bms_del_members(PG_FUNCTION_ARGS)
525 : : {
330 526 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
527 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
528 : :
529 : : /* left input gets recycled */
530 : 10 : bms1 = bms_del_members(bms1, bms2);
531 : :
532 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
533 : : }
534 : :
535 : : Datum
339 536 : 6 : test_bms_replace_members(PG_FUNCTION_ARGS)
537 : : {
330 538 [ + + ]: 6 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
539 [ + + ]: 6 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
540 : :
541 : : /* left input gets recycled */
542 : 6 : bms1 = bms_replace_members(bms1, bms2);
543 : :
544 : 6 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
545 : : }
546 : :
547 : : Datum
339 548 : 9 : test_bms_join(PG_FUNCTION_ARGS)
549 : : {
330 550 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
551 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
552 : : Bitmapset *result_bms;
553 : :
554 : : /* either input can be recycled */
339 555 : 9 : result_bms = bms_join(bms1, bms2);
556 : :
330 557 : 9 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
558 : : }
559 : :
560 : : Datum
339 561 : 7 : test_bms_hash_value(PG_FUNCTION_ARGS)
562 : : {
330 563 [ + + ]: 7 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
564 : : uint32 hash_result;
565 : :
339 566 : 7 : hash_result = bms_hash_value(bms);
567 : :
568 : 7 : PG_RETURN_INT32(hash_result);
569 : : }
570 : :
571 : : Datum
572 : 7 : test_bitmap_hash(PG_FUNCTION_ARGS)
573 : : {
330 574 [ + + ]: 7 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
575 : : uint32 hash_result;
576 : :
577 : : /* Call bitmap_hash */
578 : 7 : hash_result = bitmap_hash(&bms, sizeof(Bitmapset *));
579 : :
339 580 : 7 : PG_RETURN_INT32(hash_result);
581 : : }
582 : :
583 : : Datum
584 : 12 : test_bitmap_match(PG_FUNCTION_ARGS)
585 : : {
330 586 [ + + ]: 12 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
587 [ + + ]: 12 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
588 : : int match_result;
589 : :
590 : : /* Call bitmap_match with addresses of the Bitmapset pointers */
591 : 12 : match_result = bitmap_match(&bms1, &bms2, sizeof(Bitmapset *));
592 : :
339 593 : 12 : PG_RETURN_INT32(match_result);
594 : : }
595 : :
596 : : /*
597 : : * Contrary to most of the other functions which are one-one mappings with the
598 : : * equivalent C functions, this stresses Bitmapsets in a random fashion for
599 : : * various operations.
600 : : *
601 : : * Arguments:
602 : : * arg1: optional random seed. NULL autoselects the seed.
603 : : * arg2: defines the number of times each operation is done.
604 : : * arg3: the minimum bitmapset member number to use in the random set.
605 : : * arg4: the maximum bitmapset member number to use in the random set.
606 : : *
607 : : * The return value is the number of times all operations have been executed.
608 : : */
609 : : Datum
610 : 1 : test_random_operations(PG_FUNCTION_ARGS)
611 : : {
612 : 1 : Bitmapset *bms1 = NULL;
613 : 1 : Bitmapset *bms2 = NULL;
614 : 1 : Bitmapset *bms = NULL;
615 : 1 : Bitmapset *result = NULL;
616 : : pg_prng_state state;
617 : 1 : uint64 seed = GetCurrentTimestamp();
618 : : int num_ops;
619 : : int min_value;
620 : : int max_value;
621 : : int member;
622 : : uint32 range;
623 : : int *members;
624 : 1 : int num_members = 0;
328 drowley@postgresql.o 625 : 1 : int total_ops = 0;
626 : :
129 627 [ - + ]: 1 : if (!PG_ARGISNULL(0))
129 drowley@postgresql.o 628 :UBC 0 : seed = PG_GETARG_INT64(0);
629 : :
2 drowley@postgresql.o 630 [ + - - + ]:CBC 1 : if (PG_ARGISNULL(1) || PG_GETARG_INT32(1) <= 0)
129 drowley@postgresql.o 631 [ # # ]:UBC 0 : elog(ERROR, "invalid number of operations");
2 drowley@postgresql.o 632 [ + - - + ]:CBC 1 : if (PG_ARGISNULL(2) || PG_GETARG_INT32(2) < 0)
129 drowley@postgresql.o 633 [ # # ]:UBC 0 : elog(ERROR, "invalid minimum value");
2 drowley@postgresql.o 634 [ + - - + ]:CBC 1 : if (PG_ARGISNULL(3) || PG_GETARG_INT32(3) < 0)
2 drowley@postgresql.o 635 [ # # ]:UBC 0 : elog(ERROR, "invalid maximum value");
636 : :
2 drowley@postgresql.o 637 :CBC 1 : num_ops = PG_GETARG_INT32(1);
638 : 1 : min_value = PG_GETARG_INT32(2);
639 : 1 : max_value = PG_GETARG_INT32(3);
640 : :
641 [ - + ]: 1 : if (max_value < min_value)
2 drowley@postgresql.o 642 [ # # ]:UBC 0 : elog(ERROR, "maximum value must be greater than or equal to minimum value");
643 : :
339 michael@paquier.xyz 644 :CBC 1 : pg_prng_seed(&state, seed);
2 drowley@postgresql.o 645 : 1 : range = (uint32) max_value - (uint32) min_value + 1;
646 : :
647 : : /*
648 : : * There can be up to "num_ops" members added. This is very unlikely,
649 : : * still possible if all the operations hit the "0" case during phase 4
650 : : * where multiple operation types are mixed together.
651 : : */
261 michael@paquier.xyz 652 : 1 : members = palloc_array(int, num_ops);
653 : :
654 : : /* Phase 1: Random insertions in first set */
339 655 [ + + ]: 5001 : for (int i = 0; i < num_ops / 2; i++)
656 : : {
129 drowley@postgresql.o 657 [ - + ]: 5000 : CHECK_FOR_INTERRUPTS();
658 : :
2 659 : 5000 : member = min_value + (pg_prng_uint32(&state) % range);
660 : :
339 michael@paquier.xyz 661 [ + + ]: 5000 : if (!bms_is_member(member, bms1))
662 : 4852 : members[num_members++] = member;
321 663 : 5000 : bms1 = bms_add_member(bms1, member);
664 : : }
665 : :
666 : : /* Phase 2: Random insertions in second set */
339 667 [ + + ]: 2501 : for (int i = 0; i < num_ops / 4; i++)
668 : : {
129 drowley@postgresql.o 669 [ - + ]: 2500 : CHECK_FOR_INTERRUPTS();
670 : :
2 671 : 2500 : member = min_value + (pg_prng_uint32(&state) % range);
672 : :
321 michael@paquier.xyz 673 [ + + ]: 2500 : if (!bms_is_member(member, bms2))
674 : 2474 : members[num_members++] = member;
339 675 : 2500 : bms2 = bms_add_member(bms2, member);
676 : : }
677 : :
678 : : /* Test union */
679 : 1 : result = bms_union(bms1, bms2);
680 [ - + - - ]: 1 : EXPECT_NOT_NULL(result);
681 : :
682 : : /* Verify union contains all members from first and second sets */
683 [ + + ]: 7327 : for (int i = 0; i < num_members; i++)
684 : : {
129 drowley@postgresql.o 685 [ - + ]: 7326 : CHECK_FOR_INTERRUPTS();
686 : :
339 michael@paquier.xyz 687 [ - + ]: 7326 : if (!bms_is_member(members[i], result))
80 peter@eisentraut.org 688 [ # # ]:UBC 0 : elog(ERROR, "union missing member %d, seed " UINT64_FORMAT,
689 : : members[i], seed);
690 : : }
339 michael@paquier.xyz 691 :CBC 1 : bms_free(result);
692 : :
693 : : /*
694 : : * Test intersection, checking that all the members in the result are from
695 : : * both the first and second sets.
696 : : */
697 : 1 : result = bms_intersect(bms1, bms2);
698 [ + - ]: 1 : if (result != NULL)
699 : : {
700 : 1 : member = -1;
701 : :
702 [ + + ]: 127 : while ((member = bms_next_member(result, member)) >= 0)
703 : : {
129 drowley@postgresql.o 704 [ - + ]: 126 : CHECK_FOR_INTERRUPTS();
705 : :
339 michael@paquier.xyz 706 [ + - - + ]: 126 : if (!bms_is_member(member, bms1) || !bms_is_member(member, bms2))
80 peter@eisentraut.org 707 [ # # ]:UBC 0 : elog(ERROR, "intersection contains invalid member %d, seed " UINT64_FORMAT,
708 : : member, seed);
709 : : }
339 michael@paquier.xyz 710 :CBC 1 : bms_free(result);
711 : : }
712 : :
713 : : /* Phase 3: Test range operations */
714 : 1 : result = NULL;
715 [ + + ]: 10001 : for (int i = 0; i < num_ops; i++)
716 : : {
717 : 10000 : int lower = pg_prng_uint32(&state) % 100;
718 : 10000 : int upper = lower + (pg_prng_uint32(&state) % 20);
719 : :
129 drowley@postgresql.o 720 [ - + ]: 10000 : CHECK_FOR_INTERRUPTS();
721 : :
339 michael@paquier.xyz 722 : 10000 : result = bms_add_range(result, lower, upper);
723 : : }
724 [ + - ]: 1 : if (result != NULL)
725 : : {
726 [ - + - - ]: 1 : EXPECT_TRUE(bms_num_members(result) > 0);
727 : 1 : bms_free(result);
728 : : }
729 : :
730 : 1 : bms_free(bms1);
731 : 1 : bms_free(bms2);
732 : :
733 : : /*
734 : : * Phase 4: mix of operations on a single set, cross-checking a bitmap
735 : : * with a secondary state, "members".
736 : : */
321 737 : 1 : num_members = 0;
738 : :
739 [ + + ]: 10001 : for (int op = 0; op < num_ops; op++)
740 : : {
129 drowley@postgresql.o 741 [ - + ]: 10000 : CHECK_FOR_INTERRUPTS();
742 : :
339 michael@paquier.xyz 743 [ + + + - ]: 10000 : switch (pg_prng_uint32(&state) % 3)
744 : : {
745 : 3412 : case 0: /* add */
2 drowley@postgresql.o 746 : 3412 : member = min_value + (pg_prng_uint32(&state) % range);
321 michael@paquier.xyz 747 [ + - ]: 3412 : if (!bms_is_member(member, bms))
748 : 3412 : members[num_members++] = member;
339 749 : 3412 : bms = bms_add_member(bms, member);
750 : 3412 : break;
751 : 3351 : case 1: /* delete */
321 752 [ + + ]: 3351 : if (num_members > 0)
753 : : {
754 : 3306 : int pos = pg_prng_uint32(&state) % num_members;
755 : :
756 : 3306 : member = members[pos];
757 [ - + ]: 3306 : if (!bms_is_member(member, bms))
80 peter@eisentraut.org 758 [ # # ]:UBC 0 : elog(ERROR, "expected %d to be a valid member, seed " UINT64_FORMAT,
759 : : member, seed);
760 : :
339 michael@paquier.xyz 761 :CBC 3306 : bms = bms_del_member(bms, member);
762 : :
763 : : /*
764 : : * Move the final array member at the position of the
765 : : * member just deleted, reducing the array size by one.
766 : : */
321 767 : 3306 : members[pos] = members[--num_members];
768 : : }
339 769 : 3351 : break;
770 : 3237 : case 2: /* test membership */
771 : : /* Verify that bitmap contains all members */
321 772 [ + + ]: 158900 : for (int i = 0; i < num_members; i++)
773 : : {
774 [ - + ]: 155663 : if (!bms_is_member(members[i], bms))
80 peter@eisentraut.org 775 [ # # ]:UBC 0 : elog(ERROR, "missing member %d, seed " UINT64_FORMAT,
776 : : members[i], seed);
777 : : }
339 michael@paquier.xyz 778 :CBC 3237 : break;
779 : : }
780 : 10000 : total_ops++;
781 : : }
782 : :
330 783 : 1 : bms_free(bms);
321 784 : 1 : pfree(members);
785 : :
339 786 : 1 : PG_RETURN_INT32(total_ops);
787 : : }
788 : :
789 : : /*
790 : : * Random testing for bms_offset_members(). Generates a random set and then
791 : : * picks a number to offset the members by. We then create another set, which
792 : : * is built by looping over the members of the random set and performing
793 : : * bms_add_member and adding on the offset to create a known good set to
794 : : * compare the result of bms_offset_members() to.
795 : : *
796 : : * Arguments:
797 : : * arg1: optional random seed. NULL means use a random seed.
798 : : * arg2: the number of operations to perform.
799 : : * arg3: the minimum bitmapset member number to use in the random set.
800 : : * arg4: the maximum bitmapset member number to use in the random set.
801 : : */
802 : : Datum
49 drowley@postgresql.o 803 :GNC 1 : test_random_offset_operations(PG_FUNCTION_ARGS)
804 : : {
805 : : pg_prng_state state;
806 : : int64 seed;
807 : : int num_ops;
808 : : int min_value;
809 : : int max_value;
810 : : int member;
811 : : uint32 range;
812 : :
813 [ + - ]: 1 : if (PG_ARGISNULL(0))
814 : 1 : seed = GetCurrentTimestamp();
815 : : else
49 drowley@postgresql.o 816 :UNC 0 : seed = PG_GETARG_INT64(0);
817 : :
3 drowley@postgresql.o 818 [ + - - + ]:GNC 1 : if (PG_ARGISNULL(1) || PG_GETARG_INT32(1) <= 0)
49 drowley@postgresql.o 819 [ # # ]:UNC 0 : elog(ERROR, "invalid number of operations");
3 drowley@postgresql.o 820 [ + - - + ]:GNC 1 : if (PG_ARGISNULL(2) || PG_GETARG_INT32(2) < 0)
49 drowley@postgresql.o 821 [ # # ]:UNC 0 : elog(ERROR, "invalid minimum value");
3 drowley@postgresql.o 822 [ + - - + ]:GNC 1 : if (PG_ARGISNULL(3) || PG_GETARG_INT32(3) < 0)
3 drowley@postgresql.o 823 [ # # ]:UNC 0 : elog(ERROR, "invalid maximum value");
824 : :
3 drowley@postgresql.o 825 :GNC 1 : num_ops = PG_GETARG_INT32(1);
826 : 1 : min_value = PG_GETARG_INT32(2);
827 : 1 : max_value = PG_GETARG_INT32(3);
828 : :
829 [ - + ]: 1 : if (max_value < min_value)
3 drowley@postgresql.o 830 [ # # ]:UNC 0 : elog(ERROR, "maximum value must be greater than or equal to minimum value");
831 : :
49 drowley@postgresql.o 832 :GNC 1 : pg_prng_seed(&state, (uint64) seed);
3 833 : 1 : range = (uint32) max_value - (uint32) min_value + 1;
834 : :
49 835 [ + + ]: 1001 : for (int op = 0; op < num_ops; op++)
836 : : {
837 : 1000 : Bitmapset *random_bms = NULL;
838 : : Bitmapset *offset_bms1;
839 : 1000 : Bitmapset *offset_bms2 = NULL;
840 : : int offset;
841 : : uint32 nmembers;
842 : :
843 [ - + ]: 1000 : CHECK_FOR_INTERRUPTS();
844 : :
845 : : /*
846 : : * Choose a random offset for passing to bms_offset_members(). We
847 : : * want a number between -max_value and max_value so we test both left
848 : : * and right shifting and also test cases that push members,
849 : : * occasionally all of them, off the bottom of the set.
850 : : */
3 851 : 1000 : offset = (int) (pg_prng_uint32(&state) % ((uint32) max_value + 1));
852 : 1000 : offset -= (int) (pg_prng_uint32(&state) % ((uint32) max_value + 1));
853 : :
854 : : /* decide how many members to add */
855 : 1000 : nmembers = pg_prng_uint32(&state) % range;
856 : :
857 : : /*
858 : : * Add a random number of members with values between the minimum and
859 : : * maximum values.
860 : : */
861 [ + + ]: 511590 : for (uint32 i = 0; i < nmembers; i++)
862 : : {
863 : 510590 : member = min_value + (pg_prng_uint32(&state) % range);
49 864 : 510590 : random_bms = bms_add_member(random_bms, member);
865 : : }
866 : :
867 : : /* create a known-good set the old fashioned way */
868 : 1000 : offset_bms2 = NULL;
869 : 1000 : member = -1;
870 [ + + ]: 377354 : while ((member = bms_next_member(random_bms, member)) >= 0)
871 : : {
872 [ + + ]: 376354 : if (member + offset >= 0)
873 : 317213 : offset_bms2 = bms_add_member(offset_bms2, member + offset);
874 : : }
875 : :
876 : : /* do the offsetting */
877 : 1000 : offset_bms1 = bms_offset_members(random_bms, offset);
878 : :
879 : : /* check against the known-good set */
880 [ - + ]: 1000 : if (!bms_equal(offset_bms1, offset_bms2))
49 drowley@postgresql.o 881 [ # # ]:UNC 0 : elog(ERROR, "bms_offset_members failed with offset %d seed " INT64_FORMAT, offset, seed);
882 : :
883 : : /* Cleanup before the next loop */
49 drowley@postgresql.o 884 :GNC 1000 : bms_free(random_bms);
885 : 1000 : bms_free(offset_bms1);
886 : 1000 : bms_free(offset_bms2);
887 : : }
888 : :
889 : 1 : PG_RETURN_INT32(num_ops);
890 : : }
|