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 : :
225 michael@paquier.xyz 31 :GNC 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);
47 : 2 : PG_FUNCTION_INFO_V1(test_bms_is_empty);
48 : 2 : PG_FUNCTION_INFO_V1(test_bms_membership);
49 : 2 : PG_FUNCTION_INFO_V1(test_bms_singleton_member);
50 : 2 : PG_FUNCTION_INFO_V1(test_bms_get_singleton_member);
51 : 2 : PG_FUNCTION_INFO_V1(test_bms_next_member);
52 : 2 : PG_FUNCTION_INFO_V1(test_bms_prev_member);
53 : 2 : PG_FUNCTION_INFO_V1(test_bms_hash_value);
54 : 2 : PG_FUNCTION_INFO_V1(test_bms_overlap);
55 : 2 : PG_FUNCTION_INFO_V1(test_bms_overlap_list);
56 : 2 : PG_FUNCTION_INFO_V1(test_bms_nonempty_difference);
57 : 2 : PG_FUNCTION_INFO_V1(test_bms_member_index);
58 : 2 : PG_FUNCTION_INFO_V1(test_bms_add_range);
59 : 2 : PG_FUNCTION_INFO_V1(test_bms_add_members);
60 : 2 : PG_FUNCTION_INFO_V1(test_bms_int_members);
218 61 : 2 : PG_FUNCTION_INFO_V1(test_bms_del_members);
225 62 : 2 : PG_FUNCTION_INFO_V1(test_bms_replace_members);
63 : 2 : PG_FUNCTION_INFO_V1(test_bms_join);
64 : 2 : PG_FUNCTION_INFO_V1(test_bitmap_hash);
65 : 2 : PG_FUNCTION_INFO_V1(test_bitmap_match);
66 : :
67 : : /* Test utility functions */
68 : 2 : PG_FUNCTION_INFO_V1(test_random_operations);
69 : :
70 : : /* Convenient macros to test results */
71 : : #define EXPECT_TRUE(expr) \
72 : : do { \
73 : : if (!(expr)) \
74 : : elog(ERROR, \
75 : : "%s was unexpectedly false in file \"%s\" line %u", \
76 : : #expr, __FILE__, __LINE__); \
77 : : } while (0)
78 : :
79 : : #define EXPECT_NOT_NULL(expr) \
80 : : do { \
81 : : if ((expr) == NULL) \
82 : : elog(ERROR, \
83 : : "%s was unexpectedly true in file \"%s\" line %u", \
84 : : #expr, __FILE__, __LINE__); \
85 : : } while (0)
86 : :
87 : : /* Encode/Decode to/from TEXT and Bitmapset */
88 : : #define BITMAPSET_TO_TEXT(bms) cstring_to_text(nodeToString(bms))
89 : : #define TEXT_TO_BITMAPSET(str) ((Bitmapset *) stringToNode(text_to_cstring(str)))
90 : :
91 : : /*
92 : : * Helper macro to fetch text parameters as Bitmapsets. SQL-NULL means empty
93 : : * set.
94 : : */
95 : : #define PG_ARG_GETBITMAPSET(n) \
96 : : (PG_ARGISNULL(n) ? NULL : TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(n)))
97 : :
98 : : /*
99 : : * Helper macro to handle converting sets back to text, returning the
100 : : * resulting text representation of the set.
101 : : */
102 : : #define PG_RETURN_BITMAPSET_AS_TEXT(bms) \
103 : : PG_RETURN_TEXT_P(BITMAPSET_TO_TEXT(bms))
104 : :
105 : : /*
106 : : * Individual test functions for each bitmapset API function
107 : : *
108 : : * Primarily, we aim to keep these as close to simple wrapper functions as
109 : : * possible in order to publish the functions of bitmapset.c to the SQL layer
110 : : * with as little interference as possible. We opt to return SQL NULL in
111 : : * cases where the input given to the SQL function isn't valid to pass to the
112 : : * underlying bitmapset.c function. For example we cannot do much useful
113 : : * testing if someone calls test_bms_make_singleton(NULL) since
114 : : * bms_make_singleton() expects an integer argument.
115 : : *
116 : : * For function arguments which are to be converted to Bitmapsets, we accept
117 : : * SQL NULL as a valid argument to mean an empty set. Optionally callers may
118 : : * pass '(b)'.
119 : : *
120 : : * For the test functions which return a Bitmapset, these are converted back
121 : : * to text with result generated by nodeToString().
122 : : */
123 : :
124 : : Datum
125 : 7 : test_bms_add_member(PG_FUNCTION_ARGS)
126 : : {
127 : : Bitmapset *bms;
128 : : int member;
129 : :
130 [ + + ]: 7 : if (PG_ARGISNULL(1))
216 131 : 1 : PG_RETURN_NULL(); /* invalid input */
132 : :
133 [ + - ]: 6 : bms = PG_ARG_GETBITMAPSET(0);
225 134 : 6 : member = PG_GETARG_INT32(1);
135 : :
216 136 : 6 : bms = bms_add_member(bms, member);
137 : :
138 : 4 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
139 : : }
140 : :
141 : : Datum
225 142 : 3 : test_bms_add_members(PG_FUNCTION_ARGS)
143 : : {
216 144 [ + - ]: 3 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
145 [ + - ]: 3 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
146 : :
147 : : /* left input is recycled */
225 148 : 3 : bms1 = bms_add_members(bms1, bms2);
149 : :
216 150 : 3 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
151 : : }
152 : :
153 : : Datum
225 154 : 12 : test_bms_del_member(PG_FUNCTION_ARGS)
155 : : {
156 : : Bitmapset *bms;
157 : : int32 member;
158 : :
159 [ + + ]: 12 : if (PG_ARGISNULL(1))
216 160 : 1 : PG_RETURN_NULL(); /* invalid input */
161 : :
162 [ + - ]: 11 : bms = PG_ARG_GETBITMAPSET(0);
225 163 : 11 : member = PG_GETARG_INT32(1);
164 : :
216 165 : 11 : bms = bms_del_member(bms, member);
166 : :
167 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
168 : : }
169 : :
170 : : Datum
225 171 : 6 : test_bms_is_member(PG_FUNCTION_ARGS)
172 : : {
173 : : Bitmapset *bms;
174 : : int32 member;
175 : : bool result;
176 : :
177 [ + + ]: 6 : if (PG_ARGISNULL(1))
216 178 : 1 : PG_RETURN_NULL(); /* invalid input */
179 : :
180 [ + - ]: 5 : bms = PG_ARG_GETBITMAPSET(0);
225 181 : 5 : member = PG_GETARG_INT32(1);
182 : :
216 183 : 5 : result = bms_is_member(member, bms);
184 : :
225 185 : 4 : PG_RETURN_BOOL(result);
186 : : }
187 : :
188 : : Datum
189 : 3 : test_bms_num_members(PG_FUNCTION_ARGS)
190 : : {
216 191 [ + - ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
192 : : int result;
193 : :
225 194 : 3 : result = bms_num_members(bms);
195 : :
196 : 3 : PG_RETURN_INT32(result);
197 : : }
198 : :
199 : : Datum
200 : 4 : test_bms_make_singleton(PG_FUNCTION_ARGS)
201 : : {
202 : : Bitmapset *bms;
203 : : int32 member;
204 : :
205 : 4 : member = PG_GETARG_INT32(0);
206 : 4 : bms = bms_make_singleton(member);
207 : :
216 208 : 3 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
209 : : }
210 : :
211 : : Datum
225 212 : 2 : test_bms_copy(PG_FUNCTION_ARGS)
213 : : {
216 214 [ + + ]: 2 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
215 : : Bitmapset *copy_bms;
216 : :
225 217 : 2 : copy_bms = bms_copy(bms);
218 : :
216 219 : 2 : PG_RETURN_BITMAPSET_AS_TEXT(copy_bms);
220 : : }
221 : :
222 : : Datum
225 223 : 13 : test_bms_equal(PG_FUNCTION_ARGS)
224 : : {
216 225 [ + + ]: 13 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
226 [ + + ]: 13 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
227 : : bool result;
228 : :
225 229 : 13 : result = bms_equal(bms1, bms2);
230 : :
231 : 13 : PG_RETURN_BOOL(result);
232 : : }
233 : :
234 : : Datum
235 : 9 : test_bms_union(PG_FUNCTION_ARGS)
236 : : {
216 237 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
238 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
239 : : Bitmapset *result_bms;
240 : :
225 241 : 9 : result_bms = bms_union(bms1, bms2);
242 : :
216 243 : 9 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
244 : : }
245 : :
246 : : Datum
225 247 : 4 : test_bms_membership(PG_FUNCTION_ARGS)
248 : : {
216 249 [ + + ]: 4 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
250 : : BMS_Membership result;
251 : :
225 252 : 4 : result = bms_membership(bms);
253 : :
254 : 4 : PG_RETURN_INT32((int32) result);
255 : : }
256 : :
257 : : Datum
258 : 6 : test_bms_next_member(PG_FUNCTION_ARGS)
259 : : {
260 : : Bitmapset *bms;
261 : : int32 prevmember;
262 : : int result;
263 : :
216 264 [ + + ]: 6 : if (PG_ARGISNULL(1))
265 : 1 : PG_RETURN_NULL(); /* invalid input */
266 : :
267 [ + + ]: 5 : bms = PG_ARG_GETBITMAPSET(0);
225 268 : 5 : prevmember = PG_GETARG_INT32(1);
269 : :
216 270 : 5 : result = bms_next_member(bms, prevmember);
271 : :
225 272 : 5 : PG_RETURN_INT32(result);
273 : : }
274 : :
275 : : Datum
276 : 8 : test_bms_intersect(PG_FUNCTION_ARGS)
277 : : {
216 278 [ + + ]: 8 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
279 [ + + ]: 8 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
280 : : Bitmapset *result_bms;
281 : :
225 282 : 8 : result_bms = bms_intersect(bms1, bms2);
283 : :
216 284 : 8 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
285 : : }
286 : :
287 : : Datum
225 288 : 10 : test_bms_difference(PG_FUNCTION_ARGS)
289 : : {
216 290 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
291 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
292 : : Bitmapset *result_bms;
293 : :
225 294 : 10 : result_bms = bms_difference(bms1, bms2);
295 : :
216 296 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
297 : : }
298 : :
299 : : Datum
225 300 : 10 : test_bms_compare(PG_FUNCTION_ARGS)
301 : : {
216 302 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
303 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
304 : : int result;
305 : :
225 306 : 10 : result = bms_compare(bms1, bms2);
307 : :
308 : 10 : PG_RETURN_INT32(result);
309 : : }
310 : :
311 : : Datum
312 : 3 : test_bms_is_empty(PG_FUNCTION_ARGS)
313 : : {
216 314 [ + + ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
315 : : bool result;
316 : :
225 317 : 3 : result = bms_is_empty(bms);
318 : :
319 : 3 : PG_RETURN_BOOL(result);
320 : : }
321 : :
322 : : Datum
323 : 10 : test_bms_is_subset(PG_FUNCTION_ARGS)
324 : : {
216 325 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
326 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
327 : : bool result;
328 : :
225 329 : 10 : result = bms_is_subset(bms1, bms2);
330 : :
331 : 10 : PG_RETURN_BOOL(result);
332 : : }
333 : :
334 : : Datum
335 : 23 : test_bms_subset_compare(PG_FUNCTION_ARGS)
336 : : {
216 337 [ + + ]: 23 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
338 [ + + ]: 23 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
339 : : BMS_Comparison result;
340 : :
225 341 : 23 : result = bms_subset_compare(bms1, bms2);
342 : :
343 : 23 : PG_RETURN_INT32((int32) result);
344 : : }
345 : :
346 : : Datum
347 : 3 : test_bms_singleton_member(PG_FUNCTION_ARGS)
348 : : {
216 349 [ + - ]: 3 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
350 : : int result;
351 : :
225 352 : 3 : result = bms_singleton_member(bms);
353 : :
354 : 1 : PG_RETURN_INT32(result);
355 : : }
356 : :
357 : : Datum
358 : 4 : test_bms_get_singleton_member(PG_FUNCTION_ARGS)
359 : : {
216 360 [ + + ]: 4 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
361 : : int member;
362 : :
363 : : /*
364 : : * Keep this simple. Return -1 when we detect the set is not a singleton
365 : : * set, otherwise return the singleton member.
366 : : */
367 [ + + ]: 4 : if (!bms_get_singleton_member(bms, &member))
368 : 3 : member = -1;
369 : :
370 : 4 : PG_RETURN_INT32(member);
371 : : }
372 : :
373 : : Datum
225 374 : 7 : test_bms_prev_member(PG_FUNCTION_ARGS)
375 : : {
376 : : Bitmapset *bms;
377 : : int32 prevmember;
378 : : int result;
379 : :
216 380 [ + + ]: 7 : if (PG_ARGISNULL(1))
381 : 1 : PG_RETURN_NULL(); /* invalid input */
382 : :
383 [ + + ]: 6 : bms = PG_ARG_GETBITMAPSET(0);
225 384 : 6 : prevmember = PG_GETARG_INT32(1);
385 : :
386 : 6 : result = bms_prev_member(bms, prevmember);
387 : :
388 : 6 : PG_RETURN_INT32(result);
389 : : }
390 : :
391 : : Datum
392 : 6 : test_bms_overlap(PG_FUNCTION_ARGS)
393 : : {
216 394 [ + + ]: 6 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
395 [ + + ]: 6 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
396 : : bool result;
397 : :
225 398 : 6 : result = bms_overlap(bms1, bms2);
399 : :
400 : 6 : PG_RETURN_BOOL(result);
401 : : }
402 : :
403 : : Datum
404 : 11 : test_bms_overlap_list(PG_FUNCTION_ARGS)
405 : : {
406 : : Bitmapset *bms;
407 : : ArrayType *array;
408 : 11 : List *int_list = NIL;
409 : : bool result;
216 410 : 11 : Datum *elem_datums = NULL;
411 : 11 : bool *elem_nulls = NULL;
412 : : int elem_count;
413 : : int i;
414 : :
415 [ + + ]: 11 : bms = PG_ARG_GETBITMAPSET(0);
416 : :
417 [ + + ]: 11 : if (!PG_ARGISNULL(1))
418 : : {
419 : 9 : array = PG_GETARG_ARRAYTYPE_P(1);
420 : :
421 : 9 : deconstruct_array(array,
422 : : INT4OID, sizeof(int32), true, 'i',
423 : : &elem_datums, &elem_nulls, &elem_count);
424 : :
425 [ + + ]: 31 : for (i = 0; i < elem_count; i++)
426 : : {
427 [ + - ]: 22 : if (!elem_nulls[i])
428 : : {
429 : 22 : int32 member = DatumGetInt32(elem_datums[i]);
430 : :
431 : 22 : int_list = lappend_int(int_list, member);
432 : : }
433 : : }
434 : : }
435 : :
225 436 : 11 : result = bms_overlap_list(bms, int_list);
437 : :
438 : 9 : list_free(int_list);
439 : :
216 440 [ + + ]: 9 : if (elem_datums)
441 : 7 : pfree(elem_datums);
442 : :
443 [ + + ]: 9 : if (elem_nulls)
444 : 7 : pfree(elem_nulls);
445 : :
225 446 : 9 : PG_RETURN_BOOL(result);
447 : : }
448 : :
449 : : Datum
450 : 9 : test_bms_nonempty_difference(PG_FUNCTION_ARGS)
451 : : {
216 452 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
453 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
454 : : bool result;
455 : :
225 456 : 9 : result = bms_nonempty_difference(bms1, bms2);
457 : :
458 : 9 : PG_RETURN_BOOL(result);
459 : : }
460 : :
461 : : Datum
462 : 8 : test_bms_member_index(PG_FUNCTION_ARGS)
463 : : {
464 : : Bitmapset *bms;
465 : : int32 member;
466 : : int result;
467 : :
216 468 [ + + ]: 8 : if (PG_ARGISNULL(1))
469 : 1 : PG_RETURN_NULL(); /* invalid input */
470 : :
471 [ + + ]: 7 : bms = PG_ARG_GETBITMAPSET(0);
225 472 : 7 : member = PG_GETARG_INT32(1);
473 : :
474 : 7 : result = bms_member_index(bms, member);
475 : :
476 : 7 : PG_RETURN_INT32(result);
477 : : }
478 : :
479 : : Datum
480 : 25 : test_bms_add_range(PG_FUNCTION_ARGS)
481 : : {
482 : : Bitmapset *bms;
483 : : int32 lower,
484 : : upper;
485 : :
486 [ + + + + ]: 25 : if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
216 487 : 3 : PG_RETURN_NULL(); /* invalid input */
488 : :
489 [ + + ]: 22 : bms = PG_ARG_GETBITMAPSET(0);
225 490 : 22 : lower = PG_GETARG_INT32(1);
491 : 22 : upper = PG_GETARG_INT32(2);
492 : :
493 : 22 : bms = bms_add_range(bms, lower, upper);
494 : :
216 495 : 21 : PG_RETURN_BITMAPSET_AS_TEXT(bms);
496 : : }
497 : :
498 : : Datum
225 499 : 7 : test_bms_int_members(PG_FUNCTION_ARGS)
500 : : {
216 501 [ + + ]: 7 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
502 [ + + ]: 7 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
503 : :
504 : : /* left input gets recycled */
225 505 : 7 : bms1 = bms_int_members(bms1, bms2);
506 : :
216 507 : 7 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
508 : : }
509 : :
510 : : Datum
218 511 : 10 : test_bms_del_members(PG_FUNCTION_ARGS)
512 : : {
216 513 [ + + ]: 10 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
514 [ + + ]: 10 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
515 : :
516 : : /* left input gets recycled */
517 : 10 : bms1 = bms_del_members(bms1, bms2);
518 : :
519 : 10 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
520 : : }
521 : :
522 : : Datum
225 523 : 6 : test_bms_replace_members(PG_FUNCTION_ARGS)
524 : : {
216 525 [ + + ]: 6 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
526 [ + + ]: 6 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
527 : :
528 : : /* left input gets recycled */
529 : 6 : bms1 = bms_replace_members(bms1, bms2);
530 : :
531 : 6 : PG_RETURN_BITMAPSET_AS_TEXT(bms1);
532 : : }
533 : :
534 : : Datum
225 535 : 9 : test_bms_join(PG_FUNCTION_ARGS)
536 : : {
216 537 [ + + ]: 9 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
538 [ + + ]: 9 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
539 : : Bitmapset *result_bms;
540 : :
541 : : /* either input can be recycled */
225 542 : 9 : result_bms = bms_join(bms1, bms2);
543 : :
216 544 : 9 : PG_RETURN_BITMAPSET_AS_TEXT(result_bms);
545 : : }
546 : :
547 : : Datum
225 548 : 7 : test_bms_hash_value(PG_FUNCTION_ARGS)
549 : : {
216 550 [ + + ]: 7 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
551 : : uint32 hash_result;
552 : :
225 553 : 7 : hash_result = bms_hash_value(bms);
554 : :
555 : 7 : PG_RETURN_INT32(hash_result);
556 : : }
557 : :
558 : : Datum
559 : 7 : test_bitmap_hash(PG_FUNCTION_ARGS)
560 : : {
216 561 [ + + ]: 7 : Bitmapset *bms = PG_ARG_GETBITMAPSET(0);
562 : : uint32 hash_result;
563 : :
564 : : /* Call bitmap_hash */
565 : 7 : hash_result = bitmap_hash(&bms, sizeof(Bitmapset *));
566 : :
225 567 : 7 : PG_RETURN_INT32(hash_result);
568 : : }
569 : :
570 : : Datum
571 : 12 : test_bitmap_match(PG_FUNCTION_ARGS)
572 : : {
216 573 [ + + ]: 12 : Bitmapset *bms1 = PG_ARG_GETBITMAPSET(0);
574 [ + + ]: 12 : Bitmapset *bms2 = PG_ARG_GETBITMAPSET(1);
575 : : int match_result;
576 : :
577 : : /* Call bitmap_match with addresses of the Bitmapset pointers */
578 : 12 : match_result = bitmap_match(&bms1, &bms2, sizeof(Bitmapset *));
579 : :
225 580 : 12 : PG_RETURN_INT32(match_result);
581 : : }
582 : :
583 : : /*
584 : : * Contrary to all the other functions which are one-one mappings with the
585 : : * equivalent C functions, this stresses Bitmapsets in a random fashion for
586 : : * various operations.
587 : : *
588 : : * "min_value" is the minimal value used for the members, that will stand
589 : : * up to a range of "max_range". "num_ops" defines the number of time each
590 : : * operation is done. "seed" is a random seed used to calculate the member
591 : : * values. When "seed" is NULL, a random seed will be chosen automatically.
592 : : *
593 : : * The return value is the number of times all operations have been executed.
594 : : */
595 : : Datum
596 : 1 : test_random_operations(PG_FUNCTION_ARGS)
597 : : {
598 : 1 : Bitmapset *bms1 = NULL;
599 : 1 : Bitmapset *bms2 = NULL;
600 : 1 : Bitmapset *bms = NULL;
601 : 1 : Bitmapset *result = NULL;
602 : : pg_prng_state state;
603 : 1 : uint64 seed = GetCurrentTimestamp();
604 : : int num_ops;
605 : : int max_range;
606 : : int min_value;
607 : : int member;
608 : : int *members;
609 : 1 : int num_members = 0;
214 drowley@postgresql.o 610 : 1 : int total_ops = 0;
611 : :
15 612 [ - + ]: 1 : if (!PG_ARGISNULL(0))
15 drowley@postgresql.o 613 :UNC 0 : seed = PG_GETARG_INT64(0);
614 : :
214 drowley@postgresql.o 615 :GNC 1 : num_ops = PG_GETARG_INT32(1);
616 : 1 : max_range = PG_GETARG_INT32(2);
617 : 1 : min_value = PG_GETARG_INT32(3);
618 : :
15 619 [ + - - + ]: 1 : if (PG_ARGISNULL(1) || num_ops <= 0)
15 drowley@postgresql.o 620 [ # # ]:UNC 0 : elog(ERROR, "invalid number of operations");
15 drowley@postgresql.o 621 [ + - - + ]:GNC 1 : if (PG_ARGISNULL(2) || max_range <= 0)
15 drowley@postgresql.o 622 [ # # ]:UNC 0 : elog(ERROR, "invalid maximum range");
15 drowley@postgresql.o 623 [ + - - + ]:GNC 1 : if (PG_ARGISNULL(3) || min_value < 0)
15 drowley@postgresql.o 624 [ # # ]:UNC 0 : elog(ERROR, "invalid minimum value");
625 : :
225 michael@paquier.xyz 626 :GNC 1 : pg_prng_seed(&state, seed);
627 : :
628 : : /*
629 : : * There can be up to "num_ops" members added. This is very unlikely,
630 : : * still possible if all the operations hit the "0" case during phase 4
631 : : * where multiple operation types are mixed together.
632 : : */
147 633 : 1 : members = palloc_array(int, num_ops);
634 : :
635 : : /* Phase 1: Random insertions in first set */
225 636 [ + + ]: 5001 : for (int i = 0; i < num_ops / 2; i++)
637 : : {
15 drowley@postgresql.o 638 [ - + ]: 5000 : CHECK_FOR_INTERRUPTS();
639 : :
225 michael@paquier.xyz 640 : 5000 : member = pg_prng_uint32(&state) % max_range + min_value;
641 : :
642 [ + + ]: 5000 : if (!bms_is_member(member, bms1))
643 : 4874 : members[num_members++] = member;
207 644 : 5000 : bms1 = bms_add_member(bms1, member);
645 : : }
646 : :
647 : : /* Phase 2: Random insertions in second set */
225 648 [ + + ]: 2501 : for (int i = 0; i < num_ops / 4; i++)
649 : : {
15 drowley@postgresql.o 650 [ - + ]: 2500 : CHECK_FOR_INTERRUPTS();
651 : :
225 michael@paquier.xyz 652 : 2500 : member = pg_prng_uint32(&state) % max_range + min_value;
653 : :
207 654 [ + + ]: 2500 : if (!bms_is_member(member, bms2))
655 : 2460 : members[num_members++] = member;
225 656 : 2500 : bms2 = bms_add_member(bms2, member);
657 : : }
658 : :
659 : : /* Test union */
660 : 1 : result = bms_union(bms1, bms2);
661 [ - + - - ]: 1 : EXPECT_NOT_NULL(result);
662 : :
663 : : /* Verify union contains all members from first and second sets */
664 [ + + ]: 7335 : for (int i = 0; i < num_members; i++)
665 : : {
15 drowley@postgresql.o 666 [ - + ]: 7334 : CHECK_FOR_INTERRUPTS();
667 : :
225 michael@paquier.xyz 668 [ - + ]: 7334 : if (!bms_is_member(members[i], result))
15 drowley@postgresql.o 669 [ # # ]:UNC 0 : elog(ERROR, "union missing member %d, seed " INT64_FORMAT,
670 : : members[i], seed);
671 : : }
225 michael@paquier.xyz 672 :GNC 1 : bms_free(result);
673 : :
674 : : /*
675 : : * Test intersection, checking that all the members in the result are from
676 : : * both the first and second sets.
677 : : */
678 : 1 : result = bms_intersect(bms1, bms2);
679 [ + - ]: 1 : if (result != NULL)
680 : : {
681 : 1 : member = -1;
682 : :
683 [ + + ]: 130 : while ((member = bms_next_member(result, member)) >= 0)
684 : : {
15 drowley@postgresql.o 685 [ - + ]: 129 : CHECK_FOR_INTERRUPTS();
686 : :
225 michael@paquier.xyz 687 [ + - - + ]: 129 : if (!bms_is_member(member, bms1) || !bms_is_member(member, bms2))
15 drowley@postgresql.o 688 [ # # ]:UNC 0 : elog(ERROR, "intersection contains invalid member %d, seed " INT64_FORMAT,
689 : : member, seed);
690 : : }
225 michael@paquier.xyz 691 :GNC 1 : bms_free(result);
692 : : }
693 : :
694 : : /* Phase 3: Test range operations */
695 : 1 : result = NULL;
696 [ + + ]: 10001 : for (int i = 0; i < num_ops; i++)
697 : : {
698 : 10000 : int lower = pg_prng_uint32(&state) % 100;
699 : 10000 : int upper = lower + (pg_prng_uint32(&state) % 20);
700 : :
15 drowley@postgresql.o 701 [ - + ]: 10000 : CHECK_FOR_INTERRUPTS();
702 : :
225 michael@paquier.xyz 703 : 10000 : result = bms_add_range(result, lower, upper);
704 : : }
705 [ + - ]: 1 : if (result != NULL)
706 : : {
707 [ - + - - ]: 1 : EXPECT_TRUE(bms_num_members(result) > 0);
708 : 1 : bms_free(result);
709 : : }
710 : :
711 : 1 : bms_free(bms1);
712 : 1 : bms_free(bms2);
713 : :
714 : : /*
715 : : * Phase 4: mix of operations on a single set, cross-checking a bitmap
716 : : * with a secondary state, "members".
717 : : */
207 718 : 1 : num_members = 0;
719 : :
720 [ + + ]: 10001 : for (int op = 0; op < num_ops; op++)
721 : : {
15 drowley@postgresql.o 722 [ - + ]: 10000 : CHECK_FOR_INTERRUPTS();
723 : :
225 michael@paquier.xyz 724 [ + + + - ]: 10000 : switch (pg_prng_uint32(&state) % 3)
725 : : {
726 : 3281 : case 0: /* add */
207 727 : 3281 : member = pg_prng_uint32(&state) % max_range + min_value;
728 [ + - ]: 3281 : if (!bms_is_member(member, bms))
729 : 3281 : members[num_members++] = member;
225 730 : 3281 : bms = bms_add_member(bms, member);
731 : 3281 : break;
732 : 3369 : case 1: /* delete */
207 733 [ + + ]: 3369 : if (num_members > 0)
734 : : {
735 : 3265 : int pos = pg_prng_uint32(&state) % num_members;
736 : :
737 : 3265 : member = members[pos];
738 [ - + ]: 3265 : if (!bms_is_member(member, bms))
15 drowley@postgresql.o 739 [ # # ]:UNC 0 : elog(ERROR, "expected %d to be a valid member, seed " INT64_FORMAT,
740 : : member, seed);
741 : :
225 michael@paquier.xyz 742 :GNC 3265 : bms = bms_del_member(bms, member);
743 : :
744 : : /*
745 : : * Move the final array member at the position of the
746 : : * member just deleted, reducing the array size by one.
747 : : */
207 748 : 3265 : members[pos] = members[--num_members];
749 : : }
225 750 : 3369 : break;
751 : 3350 : case 2: /* test membership */
752 : : /* Verify that bitmap contains all members */
207 753 [ + + ]: 84260 : for (int i = 0; i < num_members; i++)
754 : : {
755 [ - + ]: 80910 : if (!bms_is_member(members[i], bms))
15 drowley@postgresql.o 756 [ # # ]:UNC 0 : elog(ERROR, "missing member %d, seed " INT64_FORMAT,
757 : : members[i], seed);
758 : : }
225 michael@paquier.xyz 759 :GNC 3350 : break;
760 : : }
761 : 10000 : total_ops++;
762 : : }
763 : :
216 764 : 1 : bms_free(bms);
207 765 : 1 : pfree(members);
766 : :
225 767 : 1 : PG_RETURN_INT32(total_ops);
768 : : }
|