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