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 : :
307 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);
17 drowley@postgresql.o 47 :GNC 2 : PG_FUNCTION_INFO_V1(test_bms_offset_members);
307 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);
300 62 : 2 : PG_FUNCTION_INFO_V1(test_bms_del_members);
307 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);
17 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
307 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))
298 133 : 1 : PG_RETURN_NULL(); /* invalid input */
134 : :
135 [ + - ]: 6 : bms = PG_ARG_GETBITMAPSET(0);
307 136 : 6 : member = PG_GETARG_INT32(1);
137 : :
298 138 : 6 : bms = bms_add_member(bms, member);
139 : :
140 : 4 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
141 : : }
142 : :
143 : : Datum
307 144 : 3 : test_bms_add_members(PG_FUNCTION_ARGS)
145 : : {
298 146 [ + - ]: 3 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
147 [ + - ]: 3 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
148 : :
149 : : /* left input is recycled */
307 150 : 3 : bms1 = bms_add_members(bms1, bms2);
151 : :
298 152 : 3 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
153 : : }
154 : :
155 : : Datum
307 156 : 12 : test_bms_del_member(PG_FUNCTION_ARGS)
157 : : {
158 : : Bitmapset *bms;
159 : : int32 member;
160 : :
161 [ + + ]: 12 : if (PG_ARGISNULL(1))
298 162 : 1 : PG_RETURN_NULL(); /* invalid input */
163 : :
164 [ + - ]: 11 : bms = PG_ARG_GETBITMAPSET(0);
307 165 : 11 : member = PG_GETARG_INT32(1);
166 : :
298 167 : 11 : bms = bms_del_member(bms, member);
168 : :
169 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
170 : : }
171 : :
172 : : Datum
307 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))
298 180 : 1 : PG_RETURN_NULL(); /* invalid input */
181 : :
182 [ + - ]: 5 : bms = PG_ARG_GETBITMAPSET(0);
307 183 : 5 : member = PG_GETARG_INT32(1);
184 : :
298 185 : 5 : result = bms_is_member(member, bms);
186 : :
307 187 : 4 : PG_RETURN_BOOL(result);
188 : : }
189 : :
190 : : Datum
191 : 3 : test_bms_num_members(PG_FUNCTION_ARGS)
192 : : {
298 193 [ + - ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
194 : : int result;
195 : :
307 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 : :
298 210 : 3 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
211 : : }
212 : :
213 : : Datum
307 214 : 2 : test_bms_copy(PG_FUNCTION_ARGS)
215 : : {
298 216 [ + + ]: 2 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
217 : : Bitmapset *copy_bms;
218 : :
307 219 : 2 : copy_bms = bms_copy(bms);
220 : :
298 221 : 2 : PG_RETURN_BITMAPSET_AS_TEXT(copy_bms);
222 : : }
223 : :
224 : : Datum
307 225 : 13 : test_bms_equal(PG_FUNCTION_ARGS)
226 : : {
298 227 [ + + ]: 13 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
228 [ + + ]: 13 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
229 : : bool result;
230 : :
307 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 : : {
298 239 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
240 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
241 : : Bitmapset *result_bms;
242 : :
307 243 : 9 : result_bms = bms_union(bms1, bms2);
244 : :
298 245 : 9 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
246 : : }
247 : :
248 : : Datum
307 249 : 4 : test_bms_membership(PG_FUNCTION_ARGS)
250 : : {
298 251 [ + + ]: 4 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
252 : : BMS_Membership result;
253 : :
307 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 : :
298 266 [ + + ]: 6 : if (PG_ARGISNULL(1))
267 : 1 : PG_RETURN_NULL(); /* invalid input */
268 : :
269 [ + + ]: 5 : bms = PG_ARG_GETBITMAPSET(0);
307 270 : 5 : prevmember = PG_GETARG_INT32(1);
271 : :
298 272 : 5 : result = bms_next_member(bms, prevmember);
273 : :
307 274 : 5 : PG_RETURN_INT32(result);
275 : : }
276 : :
277 : : Datum
278 : 8 : test_bms_intersect(PG_FUNCTION_ARGS)
279 : : {
298 280 [ + + ]: 8 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
281 [ + + ]: 8 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
282 : : Bitmapset *result_bms;
283 : :
307 284 : 8 : result_bms = bms_intersect(bms1, bms2);
285 : :
298 286 : 8 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
287 : : }
288 : :
289 : : Datum
307 290 : 10 : test_bms_difference(PG_FUNCTION_ARGS)
291 : : {
298 292 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
293 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
294 : : Bitmapset *result_bms;
295 : :
307 296 : 10 : result_bms = bms_difference(bms1, bms2);
297 : :
298 298 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
299 : : }
300 : :
301 : : Datum
17 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
307 michael@paquier.xyz 313 :CBC 10 : test_bms_compare(PG_FUNCTION_ARGS)
314 : : {
298 315 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
316 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
317 : : int result;
318 : :
307 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 : : {
298 327 [ + + ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
328 : : bool result;
329 : :
307 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 : : {
298 338 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
339 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
340 : : bool result;
341 : :
307 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 : : {
298 350 [ + + ]: 23 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
351 [ + + ]: 23 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
352 : : BMS_Comparison result;
353 : :
307 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 : : {
298 362 [ + - ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
363 : : int result;
364 : :
307 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 : : {
298 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
307 387 : 7 : test_bms_prev_member(PG_FUNCTION_ARGS)
388 : : {
389 : : Bitmapset *bms;
390 : : int32 prevmember;
391 : : int result;
392 : :
298 393 [ + + ]: 7 : if (PG_ARGISNULL(1))
394 : 1 : PG_RETURN_NULL(); /* invalid input */
395 : :
396 [ + + ]: 6 : bms = PG_ARG_GETBITMAPSET(0);
307 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 : : {
298 407 [ + + ]: 6 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
408 [ + + ]: 6 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
409 : : bool result;
410 : :
307 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;
298 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 : :
307 449 : 11 : result = bms_overlap_list(bms, int_list);
450 : :
451 : 9 : list_free(int_list);
452 : :
298 453 [ + + ]: 9 : if (elem_datums)
454 : 7 : pfree(elem_datums);
455 : :
456 [ + + ]: 9 : if (elem_nulls)
457 : 7 : pfree(elem_nulls);
458 : :
307 459 : 9 : PG_RETURN_BOOL(result);
460 : : }
461 : :
462 : : Datum
463 : 9 : test_bms_nonempty_difference(PG_FUNCTION_ARGS)
464 : : {
298 465 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
466 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
467 : : bool result;
468 : :
307 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 : :
298 481 [ + + ]: 8 : if (PG_ARGISNULL(1))
482 : 1 : PG_RETURN_NULL(); /* invalid input */
483 : :
484 [ + + ]: 7 : bms = PG_ARG_GETBITMAPSET(0);
307 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))
298 500 : 3 : PG_RETURN_NULL(); /* invalid input */
501 : :
502 [ + + ]: 22 : bms = PG_ARG_GETBITMAPSET(0);
307 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 : :
298 508 : 21 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
509 : : }
510 : :
511 : : Datum
307 512 : 7 : test_bms_int_members(PG_FUNCTION_ARGS)
513 : : {
298 514 [ + + ]: 7 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
515 [ + + ]: 7 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
516 : :
517 : : /* left input gets recycled */
307 518 : 7 : bms1 = bms_int_members(bms1, bms2);
519 : :
298 520 : 7 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
521 : : }
522 : :
523 : : Datum
300 524 : 10 : test_bms_del_members(PG_FUNCTION_ARGS)
525 : : {
298 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
307 536 : 6 : test_bms_replace_members(PG_FUNCTION_ARGS)
537 : : {
298 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
307 548 : 9 : test_bms_join(PG_FUNCTION_ARGS)
549 : : {
298 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 */
307 555 : 9 : result_bms = bms_join(bms1, bms2);
556 : :
298 557 : 9 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
558 : : }
559 : :
560 : : Datum
307 561 : 7 : test_bms_hash_value(PG_FUNCTION_ARGS)
562 : : {
298 563 [ + + ]: 7 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
564 : : uint32 hash_result;
565 : :
307 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 : : {
298 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 : :
307 580 : 7 : PG_RETURN_INT32(hash_result);
581 : : }
582 : :
583 : : Datum
584 : 12 : test_bitmap_match(PG_FUNCTION_ARGS)
585 : : {
298 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 : :
307 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 : : * "min_value" is the minimal value used for the members, that will stand
602 : : * up to a range of "max_range". "num_ops" defines the number of time each
603 : : * operation is done. "seed" is a random seed used to calculate the member
604 : : * values. When "seed" is NULL, a random seed will be chosen automatically.
605 : : *
606 : : * The return value is the number of times all operations have been executed.
607 : : */
608 : : Datum
609 : 1 : test_random_operations(PG_FUNCTION_ARGS)
610 : : {
611 : 1 : Bitmapset *bms1 = NULL;
612 : 1 : Bitmapset *bms2 = NULL;
613 : 1 : Bitmapset *bms = NULL;
614 : 1 : Bitmapset *result = NULL;
615 : : pg_prng_state state;
616 : 1 : uint64 seed = GetCurrentTimestamp();
617 : : int num_ops;
618 : : int max_range;
619 : : int min_value;
620 : : int member;
621 : : int *members;
622 : 1 : int num_members = 0;
296 drowley@postgresql.o 623 : 1 : int total_ops = 0;
624 : :
97 625 [ - + ]: 1 : if (!PG_ARGISNULL(0))
97 drowley@postgresql.o 626 :UBC 0 : seed = PG_GETARG_INT64(0);
627 : :
296 drowley@postgresql.o 628 :CBC 1 : num_ops = PG_GETARG_INT32(1);
629 : 1 : max_range = PG_GETARG_INT32(2);
630 : 1 : min_value = PG_GETARG_INT32(3);
631 : :
97 632 [ + - - + ]: 1 : if (PG_ARGISNULL(1) || num_ops <= 0)
97 drowley@postgresql.o 633 [ # # ]:UBC 0 : elog(ERROR, "invalid number of operations");
97 drowley@postgresql.o 634 [ + - - + ]:CBC 1 : if (PG_ARGISNULL(2) || max_range <= 0)
97 drowley@postgresql.o 635 [ # # ]:UBC 0 : elog(ERROR, "invalid maximum range");
97 drowley@postgresql.o 636 [ + - - + ]:CBC 1 : if (PG_ARGISNULL(3) || min_value < 0)
97 drowley@postgresql.o 637 [ # # ]:UBC 0 : elog(ERROR, "invalid minimum value");
638 : :
307 michael@paquier.xyz 639 :CBC 1 : pg_prng_seed(&state, seed);
640 : :
641 : : /*
642 : : * There can be up to "num_ops" members added. This is very unlikely,
643 : : * still possible if all the operations hit the "0" case during phase 4
644 : : * where multiple operation types are mixed together.
645 : : */
229 646 : 1 : members = palloc_array(int, num_ops);
647 : :
648 : : /* Phase 1: Random insertions in first set */
307 649 [ + + ]: 5001 : for (int i = 0; i < num_ops / 2; i++)
650 : : {
97 drowley@postgresql.o 651 [ - + ]: 5000 : CHECK_FOR_INTERRUPTS();
652 : :
307 michael@paquier.xyz 653 : 5000 : member = pg_prng_uint32(&state) % max_range + min_value;
654 : :
655 [ + + ]: 5000 : if (!bms_is_member(member, bms1))
656 : 4853 : members[num_members++] = member;
289 657 : 5000 : bms1 = bms_add_member(bms1, member);
658 : : }
659 : :
660 : : /* Phase 2: Random insertions in second set */
307 661 [ + + ]: 2501 : for (int i = 0; i < num_ops / 4; i++)
662 : : {
97 drowley@postgresql.o 663 [ - + ]: 2500 : CHECK_FOR_INTERRUPTS();
664 : :
307 michael@paquier.xyz 665 : 2500 : member = pg_prng_uint32(&state) % max_range + min_value;
666 : :
289 667 [ + + ]: 2500 : if (!bms_is_member(member, bms2))
668 : 2464 : members[num_members++] = member;
307 669 : 2500 : bms2 = bms_add_member(bms2, member);
670 : : }
671 : :
672 : : /* Test union */
673 : 1 : result = bms_union(bms1, bms2);
674 [ - + - - ]: 1 : EXPECT_NOT_NULL(result);
675 : :
676 : : /* Verify union contains all members from first and second sets */
677 [ + + ]: 7318 : for (int i = 0; i < num_members; i++)
678 : : {
97 drowley@postgresql.o 679 [ - + ]: 7317 : CHECK_FOR_INTERRUPTS();
680 : :
307 michael@paquier.xyz 681 [ - + ]: 7317 : if (!bms_is_member(members[i], result))
48 peter@eisentraut.org 682 [ # # ]:UBC 0 : elog(ERROR, "union missing member %d, seed " UINT64_FORMAT,
683 : : members[i], seed);
684 : : }
307 michael@paquier.xyz 685 :CBC 1 : bms_free(result);
686 : :
687 : : /*
688 : : * Test intersection, checking that all the members in the result are from
689 : : * both the first and second sets.
690 : : */
691 : 1 : result = bms_intersect(bms1, bms2);
692 [ + - ]: 1 : if (result != NULL)
693 : : {
694 : 1 : member = -1;
695 : :
696 [ + + ]: 127 : while ((member = bms_next_member(result, member)) >= 0)
697 : : {
97 drowley@postgresql.o 698 [ - + ]: 126 : CHECK_FOR_INTERRUPTS();
699 : :
307 michael@paquier.xyz 700 [ + - - + ]: 126 : if (!bms_is_member(member, bms1) || !bms_is_member(member, bms2))
48 peter@eisentraut.org 701 [ # # ]:UBC 0 : elog(ERROR, "intersection contains invalid member %d, seed " UINT64_FORMAT,
702 : : member, seed);
703 : : }
307 michael@paquier.xyz 704 :CBC 1 : bms_free(result);
705 : : }
706 : :
707 : : /* Phase 3: Test range operations */
708 : 1 : result = NULL;
709 [ + + ]: 10001 : for (int i = 0; i < num_ops; i++)
710 : : {
711 : 10000 : int lower = pg_prng_uint32(&state) % 100;
712 : 10000 : int upper = lower + (pg_prng_uint32(&state) % 20);
713 : :
97 drowley@postgresql.o 714 [ - + ]: 10000 : CHECK_FOR_INTERRUPTS();
715 : :
307 michael@paquier.xyz 716 : 10000 : result = bms_add_range(result, lower, upper);
717 : : }
718 [ + - ]: 1 : if (result != NULL)
719 : : {
720 [ - + - - ]: 1 : EXPECT_TRUE(bms_num_members(result) > 0);
721 : 1 : bms_free(result);
722 : : }
723 : :
724 : 1 : bms_free(bms1);
725 : 1 : bms_free(bms2);
726 : :
727 : : /*
728 : : * Phase 4: mix of operations on a single set, cross-checking a bitmap
729 : : * with a secondary state, "members".
730 : : */
289 731 : 1 : num_members = 0;
732 : :
733 [ + + ]: 10001 : for (int op = 0; op < num_ops; op++)
734 : : {
97 drowley@postgresql.o 735 [ - + ]: 10000 : CHECK_FOR_INTERRUPTS();
736 : :
307 michael@paquier.xyz 737 [ + + + - ]: 10000 : switch (pg_prng_uint32(&state) % 3)
738 : : {
739 : 3308 : case 0: /* add */
289 740 : 3308 : member = pg_prng_uint32(&state) % max_range + min_value;
741 [ + + ]: 3308 : if (!bms_is_member(member, bms))
742 : 3307 : members[num_members++] = member;
307 743 : 3308 : bms = bms_add_member(bms, member);
744 : 3308 : break;
745 : 3341 : case 1: /* delete */
289 746 [ + + ]: 3341 : if (num_members > 0)
747 : : {
748 : 3302 : int pos = pg_prng_uint32(&state) % num_members;
749 : :
750 : 3302 : member = members[pos];
751 [ - + ]: 3302 : if (!bms_is_member(member, bms))
48 peter@eisentraut.org 752 [ # # ]:UBC 0 : elog(ERROR, "expected %d to be a valid member, seed " UINT64_FORMAT,
753 : : member, seed);
754 : :
307 michael@paquier.xyz 755 :CBC 3302 : bms = bms_del_member(bms, member);
756 : :
757 : : /*
758 : : * Move the final array member at the position of the
759 : : * member just deleted, reducing the array size by one.
760 : : */
289 761 : 3302 : members[pos] = members[--num_members];
762 : : }
307 763 : 3341 : break;
764 : 3351 : case 2: /* test membership */
765 : : /* Verify that bitmap contains all members */
289 766 [ + + ]: 112720 : for (int i = 0; i < num_members; i++)
767 : : {
768 [ - + ]: 109369 : if (!bms_is_member(members[i], bms))
48 peter@eisentraut.org 769 [ # # ]:UBC 0 : elog(ERROR, "missing member %d, seed " UINT64_FORMAT,
770 : : members[i], seed);
771 : : }
307 michael@paquier.xyz 772 :CBC 3351 : break;
773 : : }
774 : 10000 : total_ops++;
775 : : }
776 : :
298 777 : 1 : bms_free(bms);
289 778 : 1 : pfree(members);
779 : :
307 780 : 1 : PG_RETURN_INT32(total_ops);
781 : : }
782 : :
783 : : /*
784 : : * Random testing for bms_offset_members(). Generates a random set and then
785 : : * picks a number to offset the members by. We then create another set, which
786 : : * is built by looping over the members of the random set and performing
787 : : * bms_add_member and adding on the offset to create a known good set to
788 : : * compare the result of bms_offset_members() to.
789 : : *
790 : : * Arguments:
791 : : * arg1: optional random seed. NULL means use a random seed.
792 : : * arg2: the number of operations to perform.
793 : : * arg3: the maximum bitmapset member number to use in the random set.
794 : : * arg4: the minimum bitmapset member number to use in the random set.
795 : : */
796 : : Datum
17 drowley@postgresql.o 797 :GNC 1 : test_random_offset_operations(PG_FUNCTION_ARGS)
798 : : {
799 : : pg_prng_state state;
800 : : int64 seed;
801 : : int num_ops;
802 : : int max_range;
803 : : int min_value;
804 : : int member;
805 : :
806 [ + - ]: 1 : if (PG_ARGISNULL(0))
807 : 1 : seed = GetCurrentTimestamp();
808 : : else
17 drowley@postgresql.o 809 :UNC 0 : seed = PG_GETARG_INT64(0);
810 : :
17 drowley@postgresql.o 811 :GNC 1 : num_ops = PG_GETARG_INT32(1);
812 : 1 : max_range = PG_GETARG_INT32(2);
813 : 1 : min_value = PG_GETARG_INT32(3);
814 : :
815 [ + - - + ]: 1 : if (PG_ARGISNULL(1) || num_ops <= 0)
17 drowley@postgresql.o 816 [ # # ]:UNC 0 : elog(ERROR, "invalid number of operations");
17 drowley@postgresql.o 817 [ + - - + ]:GNC 1 : if (PG_ARGISNULL(2) || max_range <= 0)
17 drowley@postgresql.o 818 [ # # ]:UNC 0 : elog(ERROR, "invalid maximum range");
17 drowley@postgresql.o 819 [ + - - + ]:GNC 1 : if (PG_ARGISNULL(3) || min_value < 0)
17 drowley@postgresql.o 820 [ # # ]:UNC 0 : elog(ERROR, "invalid minimum value");
821 : :
17 drowley@postgresql.o 822 :GNC 1 : pg_prng_seed(&state, (uint64) seed);
823 : :
824 [ + + ]: 1001 : for (int op = 0; op < num_ops; op++)
825 : : {
826 : 1000 : Bitmapset *random_bms = NULL;
827 : : Bitmapset *offset_bms1;
828 : 1000 : Bitmapset *offset_bms2 = NULL;
829 : : int offset;
830 : : int nmembers;
831 : :
832 [ - + ]: 1000 : CHECK_FOR_INTERRUPTS();
833 : :
834 : : /* Figure out a random offset and how many members to add */
835 : 1000 : offset = (pg_prng_uint32(&state) % max_range) - (pg_prng_uint32(&state) % max_range);
836 : 1000 : nmembers = pg_prng_uint32(&state) % max_range + min_value;
837 : :
838 [ + + ]: 522208 : for (int i = 0; i < nmembers; i++)
839 : : {
840 : 521208 : member = pg_prng_uint32(&state) % max_range + min_value;
841 : 521208 : random_bms = bms_add_member(random_bms, member);
842 : : }
843 : :
844 : : /* create a known-good set the old fashioned way */
845 : 1000 : offset_bms2 = NULL;
846 : 1000 : member = -1;
847 [ + + ]: 383866 : while ((member = bms_next_member(random_bms, member)) >= 0)
848 : : {
849 [ + + ]: 382866 : if (member + offset >= 0)
850 : 320902 : offset_bms2 = bms_add_member(offset_bms2, member + offset);
851 : : }
852 : :
853 : : /* do the offsetting */
854 : 1000 : offset_bms1 = bms_offset_members(random_bms, offset);
855 : :
856 : : /* check against the known-good set */
857 [ - + ]: 1000 : if (!bms_equal(offset_bms1, offset_bms2))
17 drowley@postgresql.o 858 [ # # ]:UNC 0 : elog(ERROR, "bms_offset_members failed with offset %d seed " INT64_FORMAT, offset, seed);
859 : :
860 : : /* Cleanup before the next loop */
17 drowley@postgresql.o 861 :GNC 1000 : bms_free(random_bms);
862 : 1000 : bms_free(offset_bms1);
863 : 1000 : bms_free(offset_bms2);
864 : : }
865 : :
866 : 1 : PG_RETURN_INT32(num_ops);
867 : : }
|