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 : #include "varatt.h"
30 :
31 2 : PG_MODULE_MAGIC;
32 :
33 : /* Bitmapset API functions in order of appearance in bitmapset.c */
34 4 : PG_FUNCTION_INFO_V1(test_bms_make_singleton);
35 4 : PG_FUNCTION_INFO_V1(test_bms_add_member);
36 4 : PG_FUNCTION_INFO_V1(test_bms_del_member);
37 4 : PG_FUNCTION_INFO_V1(test_bms_is_member);
38 4 : PG_FUNCTION_INFO_V1(test_bms_num_members);
39 4 : PG_FUNCTION_INFO_V1(test_bms_copy);
40 4 : PG_FUNCTION_INFO_V1(test_bms_equal);
41 4 : PG_FUNCTION_INFO_V1(test_bms_compare);
42 4 : PG_FUNCTION_INFO_V1(test_bms_is_subset);
43 4 : PG_FUNCTION_INFO_V1(test_bms_subset_compare);
44 4 : PG_FUNCTION_INFO_V1(test_bms_union);
45 4 : PG_FUNCTION_INFO_V1(test_bms_intersect);
46 4 : PG_FUNCTION_INFO_V1(test_bms_difference);
47 4 : PG_FUNCTION_INFO_V1(test_bms_is_empty);
48 4 : PG_FUNCTION_INFO_V1(test_bms_membership);
49 4 : PG_FUNCTION_INFO_V1(test_bms_singleton_member);
50 4 : PG_FUNCTION_INFO_V1(test_bms_get_singleton_member);
51 4 : PG_FUNCTION_INFO_V1(test_bms_next_member);
52 4 : PG_FUNCTION_INFO_V1(test_bms_prev_member);
53 4 : PG_FUNCTION_INFO_V1(test_bms_hash_value);
54 4 : PG_FUNCTION_INFO_V1(test_bms_overlap);
55 4 : PG_FUNCTION_INFO_V1(test_bms_overlap_list);
56 4 : PG_FUNCTION_INFO_V1(test_bms_nonempty_difference);
57 4 : PG_FUNCTION_INFO_V1(test_bms_member_index);
58 4 : PG_FUNCTION_INFO_V1(test_bms_add_range);
59 4 : PG_FUNCTION_INFO_V1(test_bms_add_members);
60 4 : PG_FUNCTION_INFO_V1(test_bms_int_members);
61 4 : PG_FUNCTION_INFO_V1(test_bms_replace_members);
62 4 : PG_FUNCTION_INFO_V1(test_bms_join);
63 4 : PG_FUNCTION_INFO_V1(test_bitmap_hash);
64 4 : PG_FUNCTION_INFO_V1(test_bitmap_match);
65 :
66 : /* Test utility functions */
67 4 : 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 : * Individual test functions for each bitmapset API function
92 : */
93 :
94 : Datum
95 14 : test_bms_add_member(PG_FUNCTION_ARGS)
96 : {
97 : int member;
98 14 : Bitmapset *bms = NULL;
99 : text *result;
100 :
101 14 : if (PG_ARGISNULL(1))
102 0 : PG_RETURN_NULL();
103 :
104 14 : if (!PG_ARGISNULL(0))
105 14 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
106 :
107 14 : member = PG_GETARG_INT32(1);
108 14 : bms = bms_add_member(bms, member);
109 10 : result = BITMAPSET_TO_TEXT(bms);
110 :
111 10 : if (bms)
112 10 : bms_free(bms);
113 :
114 10 : if (result == NULL)
115 0 : PG_RETURN_NULL();
116 :
117 10 : PG_RETURN_TEXT_P(result);
118 : }
119 :
120 : Datum
121 6 : test_bms_add_members(PG_FUNCTION_ARGS)
122 : {
123 6 : Bitmapset *bms1 = NULL,
124 6 : *bms2 = NULL;
125 : text *result;
126 :
127 6 : if (!PG_ARGISNULL(0))
128 6 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
129 :
130 6 : if (!PG_ARGISNULL(1))
131 6 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
132 :
133 : /* IMPORTANT: bms_add_members modifies/frees the first argument */
134 6 : bms1 = bms_add_members(bms1, bms2);
135 :
136 6 : if (bms2)
137 6 : bms_free(bms2);
138 :
139 6 : if (bms1 == NULL)
140 0 : PG_RETURN_NULL();
141 :
142 6 : result = BITMAPSET_TO_TEXT(bms1);
143 6 : bms_free(bms1);
144 :
145 6 : PG_RETURN_TEXT_P(result);
146 : }
147 :
148 : Datum
149 16 : test_bms_del_member(PG_FUNCTION_ARGS)
150 : {
151 16 : Bitmapset *bms = NULL;
152 : int32 member;
153 : text *result;
154 :
155 16 : if (PG_ARGISNULL(1))
156 0 : PG_RETURN_NULL();
157 :
158 16 : if (!PG_ARGISNULL(0))
159 16 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
160 :
161 16 : member = PG_GETARG_INT32(1);
162 16 : bms = bms_del_member(bms, member);
163 :
164 14 : if (bms == NULL || bms_is_empty(bms))
165 : {
166 4 : if (bms)
167 0 : bms_free(bms);
168 4 : PG_RETURN_NULL();
169 : }
170 :
171 10 : result = BITMAPSET_TO_TEXT(bms);
172 10 : bms_free(bms);
173 :
174 10 : PG_RETURN_TEXT_P(result);
175 : }
176 :
177 : Datum
178 10 : test_bms_is_member(PG_FUNCTION_ARGS)
179 : {
180 10 : Bitmapset *bms = NULL;
181 : int32 member;
182 : bool result;
183 :
184 10 : if (PG_ARGISNULL(1))
185 0 : PG_RETURN_BOOL(false);
186 :
187 10 : if (!PG_ARGISNULL(0))
188 10 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
189 :
190 10 : member = PG_GETARG_INT32(1);
191 10 : result = bms_is_member(member, bms);
192 :
193 8 : if (bms)
194 6 : bms_free(bms);
195 :
196 8 : PG_RETURN_BOOL(result);
197 : }
198 :
199 : Datum
200 6 : test_bms_num_members(PG_FUNCTION_ARGS)
201 : {
202 6 : Bitmapset *bms = NULL;
203 6 : int result = 0;
204 :
205 6 : if (!PG_ARGISNULL(0))
206 6 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
207 :
208 6 : result = bms_num_members(bms);
209 :
210 6 : if (bms)
211 4 : bms_free(bms);
212 :
213 6 : PG_RETURN_INT32(result);
214 : }
215 :
216 : Datum
217 8 : test_bms_make_singleton(PG_FUNCTION_ARGS)
218 : {
219 : int32 member;
220 : Bitmapset *bms;
221 : text *result;
222 :
223 8 : if (PG_ARGISNULL(0))
224 0 : PG_RETURN_NULL();
225 :
226 8 : member = PG_GETARG_INT32(0);
227 8 : bms = bms_make_singleton(member);
228 :
229 6 : result = BITMAPSET_TO_TEXT(bms);
230 6 : bms_free(bms);
231 :
232 6 : PG_RETURN_TEXT_P(result);
233 : }
234 :
235 : Datum
236 4 : test_bms_copy(PG_FUNCTION_ARGS)
237 : {
238 : text *bms_data;
239 4 : Bitmapset *bms = NULL;
240 : Bitmapset *copy_bms;
241 : text *result;
242 :
243 4 : if (PG_ARGISNULL(0))
244 2 : PG_RETURN_NULL();
245 :
246 2 : bms_data = PG_GETARG_TEXT_PP(0);
247 2 : bms = TEXT_TO_BITMAPSET(bms_data);
248 2 : copy_bms = bms_copy(bms);
249 2 : result = BITMAPSET_TO_TEXT(copy_bms);
250 :
251 2 : if (bms)
252 2 : bms_free(bms);
253 2 : if (copy_bms)
254 2 : bms_free(copy_bms);
255 :
256 2 : PG_RETURN_TEXT_P(result);
257 : }
258 :
259 : Datum
260 16 : test_bms_equal(PG_FUNCTION_ARGS)
261 : {
262 16 : Bitmapset *bms1 = NULL,
263 16 : *bms2 = NULL;
264 : bool result;
265 :
266 16 : if (!PG_ARGISNULL(0))
267 16 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
268 :
269 16 : if (!PG_ARGISNULL(1))
270 16 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
271 :
272 16 : result = bms_equal(bms1, bms2);
273 :
274 16 : if (bms1)
275 10 : bms_free(bms1);
276 16 : if (bms2)
277 10 : bms_free(bms2);
278 :
279 16 : PG_RETURN_BOOL(result);
280 : }
281 :
282 : Datum
283 8 : test_bms_union(PG_FUNCTION_ARGS)
284 : {
285 8 : Bitmapset *bms1 = NULL,
286 8 : *bms2 = NULL;
287 : Bitmapset *result_bms;
288 : text *result;
289 :
290 8 : if (!PG_ARGISNULL(0))
291 8 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
292 :
293 8 : if (!PG_ARGISNULL(1))
294 8 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
295 :
296 8 : result_bms = bms_union(bms1, bms2);
297 :
298 8 : if (bms1)
299 6 : bms_free(bms1);
300 8 : if (bms2)
301 4 : bms_free(bms2);
302 :
303 8 : if (result_bms == NULL)
304 2 : PG_RETURN_NULL();
305 :
306 6 : result = BITMAPSET_TO_TEXT(result_bms);
307 6 : bms_free(result_bms);
308 :
309 6 : PG_RETURN_TEXT_P(result);
310 : }
311 :
312 : Datum
313 6 : test_bms_membership(PG_FUNCTION_ARGS)
314 : {
315 6 : Bitmapset *bms = NULL;
316 : BMS_Membership result;
317 :
318 6 : if (PG_ARGISNULL(0))
319 0 : PG_RETURN_INT32(BMS_EMPTY_SET);
320 :
321 6 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
322 6 : result = bms_membership(bms);
323 :
324 6 : if (bms)
325 4 : bms_free(bms);
326 :
327 6 : PG_RETURN_INT32((int32) result);
328 : }
329 :
330 : Datum
331 8 : test_bms_next_member(PG_FUNCTION_ARGS)
332 : {
333 8 : Bitmapset *bms = NULL;
334 : int32 prevmember;
335 : int result;
336 :
337 8 : if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
338 0 : PG_RETURN_INT32(-2);
339 :
340 8 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
341 8 : prevmember = PG_GETARG_INT32(1);
342 8 : result = bms_next_member(bms, prevmember);
343 :
344 8 : if (bms)
345 6 : bms_free(bms);
346 :
347 8 : PG_RETURN_INT32(result);
348 : }
349 :
350 : Datum
351 6 : test_bms_intersect(PG_FUNCTION_ARGS)
352 : {
353 6 : Bitmapset *bms1 = NULL,
354 6 : *bms2 = NULL;
355 : Bitmapset *result_bms;
356 : text *result;
357 :
358 6 : if (!PG_ARGISNULL(0))
359 6 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
360 :
361 6 : if (!PG_ARGISNULL(1))
362 6 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
363 :
364 6 : result_bms = bms_intersect(bms1, bms2);
365 :
366 6 : if (bms1)
367 6 : bms_free(bms1);
368 6 : if (bms2)
369 4 : bms_free(bms2);
370 :
371 6 : if (result_bms == NULL)
372 4 : PG_RETURN_NULL();
373 :
374 2 : result = BITMAPSET_TO_TEXT(result_bms);
375 2 : bms_free(result_bms);
376 :
377 2 : PG_RETURN_TEXT_P(result);
378 : }
379 :
380 : Datum
381 10 : test_bms_difference(PG_FUNCTION_ARGS)
382 : {
383 10 : Bitmapset *bms1 = NULL,
384 10 : *bms2 = NULL;
385 : Bitmapset *result_bms;
386 : text *result;
387 :
388 10 : if (!PG_ARGISNULL(0))
389 10 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
390 :
391 10 : if (!PG_ARGISNULL(1))
392 10 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
393 :
394 10 : result_bms = bms_difference(bms1, bms2);
395 :
396 10 : if (bms1)
397 10 : bms_free(bms1);
398 10 : if (bms2)
399 10 : bms_free(bms2);
400 :
401 10 : if (result_bms == NULL)
402 4 : PG_RETURN_NULL();
403 :
404 6 : result = BITMAPSET_TO_TEXT(result_bms);
405 6 : bms_free(result_bms);
406 :
407 6 : PG_RETURN_TEXT_P(result);
408 : }
409 :
410 : Datum
411 14 : test_bms_compare(PG_FUNCTION_ARGS)
412 : {
413 14 : Bitmapset *bms1 = NULL,
414 14 : *bms2 = NULL;
415 : int result;
416 :
417 14 : if (!PG_ARGISNULL(0))
418 14 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
419 :
420 14 : if (!PG_ARGISNULL(1))
421 14 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
422 :
423 14 : result = bms_compare(bms1, bms2);
424 :
425 14 : if (bms1)
426 10 : bms_free(bms1);
427 14 : if (bms2)
428 10 : bms_free(bms2);
429 :
430 14 : PG_RETURN_INT32(result);
431 : }
432 :
433 : Datum
434 6 : test_bms_is_empty(PG_FUNCTION_ARGS)
435 : {
436 6 : Bitmapset *bms = NULL;
437 : bool result;
438 :
439 6 : if (!PG_ARGISNULL(0))
440 4 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
441 :
442 6 : result = bms_is_empty(bms);
443 :
444 6 : if (bms)
445 2 : bms_free(bms);
446 :
447 6 : PG_RETURN_BOOL(result);
448 : }
449 :
450 : Datum
451 10 : test_bms_is_subset(PG_FUNCTION_ARGS)
452 : {
453 10 : Bitmapset *bms1 = NULL,
454 10 : *bms2 = NULL;
455 : bool result;
456 :
457 10 : if (!PG_ARGISNULL(0))
458 10 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
459 :
460 10 : if (!PG_ARGISNULL(1))
461 10 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
462 :
463 10 : result = bms_is_subset(bms1, bms2);
464 :
465 10 : if (bms1)
466 8 : bms_free(bms1);
467 10 : if (bms2)
468 10 : bms_free(bms2);
469 :
470 10 : PG_RETURN_BOOL(result);
471 : }
472 :
473 : Datum
474 14 : test_bms_subset_compare(PG_FUNCTION_ARGS)
475 : {
476 14 : Bitmapset *bms1 = NULL,
477 14 : *bms2 = NULL;
478 : BMS_Comparison result;
479 :
480 14 : if (!PG_ARGISNULL(0))
481 10 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
482 :
483 14 : if (!PG_ARGISNULL(1))
484 10 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
485 :
486 14 : result = bms_subset_compare(bms1, bms2);
487 :
488 14 : if (bms1)
489 10 : bms_free(bms1);
490 14 : if (bms2)
491 10 : bms_free(bms2);
492 :
493 14 : PG_RETURN_INT32((int32) result);
494 : }
495 :
496 : Datum
497 4 : test_bms_singleton_member(PG_FUNCTION_ARGS)
498 : {
499 4 : Bitmapset *bms = NULL;
500 : int result;
501 :
502 4 : if (!PG_ARGISNULL(0))
503 4 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
504 :
505 4 : result = bms_singleton_member(bms);
506 :
507 2 : if (bms)
508 2 : bms_free(bms);
509 :
510 2 : PG_RETURN_INT32(result);
511 : }
512 :
513 : Datum
514 4 : test_bms_get_singleton_member(PG_FUNCTION_ARGS)
515 : {
516 4 : Bitmapset *bms = NULL;
517 4 : int32 default_member = PG_GETARG_INT32(1);
518 : int member;
519 : bool success;
520 :
521 4 : if (PG_ARGISNULL(0))
522 0 : PG_RETURN_INT32(default_member);
523 :
524 4 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
525 :
526 : /*
527 : * bms_get_singleton_member returns bool and stores result in member
528 : * pointer
529 : */
530 4 : success = bms_get_singleton_member(bms, &member);
531 4 : bms_free(bms);
532 :
533 4 : if (success)
534 2 : PG_RETURN_INT32(member);
535 : else
536 2 : PG_RETURN_INT32(default_member);
537 : }
538 :
539 : Datum
540 8 : test_bms_prev_member(PG_FUNCTION_ARGS)
541 : {
542 : text *bms_data;
543 8 : Bitmapset *bms = NULL;
544 : int32 prevmember;
545 : int result;
546 :
547 8 : if (PG_ARGISNULL(0))
548 0 : PG_RETURN_INT32(-2);
549 :
550 8 : bms_data = PG_GETARG_TEXT_PP(0);
551 8 : prevmember = PG_GETARG_INT32(1);
552 :
553 8 : if (VARSIZE_ANY_EXHDR(bms_data) == 0)
554 0 : PG_RETURN_INT32(-2);
555 :
556 8 : bms = TEXT_TO_BITMAPSET(bms_data);
557 8 : result = bms_prev_member(bms, prevmember);
558 8 : bms_free(bms);
559 :
560 8 : PG_RETURN_INT32(result);
561 : }
562 :
563 : Datum
564 6 : test_bms_overlap(PG_FUNCTION_ARGS)
565 : {
566 6 : Bitmapset *bms1 = NULL,
567 6 : *bms2 = NULL;
568 : bool result;
569 :
570 6 : if (!PG_ARGISNULL(0))
571 6 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
572 :
573 6 : if (!PG_ARGISNULL(1))
574 6 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
575 :
576 6 : result = bms_overlap(bms1, bms2);
577 :
578 6 : if (bms1)
579 4 : bms_free(bms1);
580 6 : if (bms2)
581 6 : bms_free(bms2);
582 :
583 6 : PG_RETURN_BOOL(result);
584 : }
585 :
586 : Datum
587 12 : test_bms_overlap_list(PG_FUNCTION_ARGS)
588 : {
589 12 : Bitmapset *bms = NULL;
590 : ArrayType *array;
591 12 : List *int_list = NIL;
592 : bool result;
593 : Datum *elem_datums;
594 : bool *elem_nulls;
595 : int elem_count;
596 : int i;
597 :
598 12 : if (PG_ARGISNULL(0))
599 0 : PG_RETURN_BOOL(false);
600 :
601 12 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
602 :
603 12 : if (PG_ARGISNULL(1))
604 : {
605 0 : if (bms)
606 0 : bms_free(bms);
607 0 : PG_RETURN_BOOL(false);
608 : }
609 :
610 12 : array = PG_GETARG_ARRAYTYPE_P(1);
611 :
612 12 : if (ARR_ELEMTYPE(array) != INT4OID)
613 0 : ereport(ERROR,
614 : (errcode(ERRCODE_DATATYPE_MISMATCH),
615 : errmsg("integer array expected")));
616 :
617 12 : deconstruct_array(array,
618 : INT4OID, sizeof(int32), true, 'i',
619 : &elem_datums, &elem_nulls, &elem_count);
620 :
621 40 : for (i = 0; i < elem_count; i++)
622 : {
623 28 : if (!elem_nulls[i])
624 : {
625 28 : int32 member = DatumGetInt32(elem_datums[i]);
626 :
627 28 : int_list = lappend_int(int_list, member);
628 : }
629 : }
630 :
631 12 : result = bms_overlap_list(bms, int_list);
632 :
633 12 : if (bms)
634 12 : bms_free(bms);
635 :
636 12 : list_free(int_list);
637 :
638 12 : pfree(elem_datums);
639 12 : pfree(elem_nulls);
640 :
641 12 : PG_RETURN_BOOL(result);
642 : }
643 :
644 : Datum
645 10 : test_bms_nonempty_difference(PG_FUNCTION_ARGS)
646 : {
647 10 : Bitmapset *bms1 = NULL,
648 10 : *bms2 = NULL;
649 : bool result;
650 :
651 10 : if (!PG_ARGISNULL(0))
652 8 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
653 :
654 10 : if (!PG_ARGISNULL(1))
655 8 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
656 :
657 10 : result = bms_nonempty_difference(bms1, bms2);
658 :
659 10 : if (bms1)
660 8 : bms_free(bms1);
661 10 : if (bms2)
662 8 : bms_free(bms2);
663 :
664 10 : PG_RETURN_BOOL(result);
665 : }
666 :
667 : Datum
668 8 : test_bms_member_index(PG_FUNCTION_ARGS)
669 : {
670 : text *bms_data;
671 8 : Bitmapset *bms = NULL;
672 : int32 member;
673 : int result;
674 :
675 8 : if (PG_ARGISNULL(0))
676 2 : PG_RETURN_INT32(-1);
677 :
678 6 : bms_data = PG_GETARG_TEXT_PP(0);
679 6 : member = PG_GETARG_INT32(1);
680 :
681 6 : if (VARSIZE_ANY_EXHDR(bms_data) == 0)
682 0 : PG_RETURN_INT32(-1);
683 :
684 6 : bms = TEXT_TO_BITMAPSET(bms_data);
685 :
686 6 : result = bms_member_index(bms, member);
687 6 : bms_free(bms);
688 :
689 6 : PG_RETURN_INT32(result);
690 : }
691 :
692 : Datum
693 36 : test_bms_add_range(PG_FUNCTION_ARGS)
694 : {
695 36 : Bitmapset *bms = NULL;
696 : int32 lower,
697 : upper;
698 : text *result;
699 :
700 36 : if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
701 0 : PG_RETURN_NULL();
702 :
703 36 : if (!PG_ARGISNULL(0))
704 32 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
705 :
706 36 : lower = PG_GETARG_INT32(1);
707 36 : upper = PG_GETARG_INT32(2);
708 :
709 : /* Check for invalid range */
710 36 : if (upper < lower)
711 : {
712 0 : if (bms)
713 0 : bms_free(bms);
714 0 : PG_RETURN_NULL();
715 : }
716 :
717 36 : bms = bms_add_range(bms, lower, upper);
718 :
719 34 : result = BITMAPSET_TO_TEXT(bms);
720 34 : if (bms)
721 34 : bms_free(bms);
722 :
723 34 : PG_RETURN_TEXT_P(result);
724 : }
725 :
726 : Datum
727 8 : test_bms_int_members(PG_FUNCTION_ARGS)
728 : {
729 8 : Bitmapset *bms1 = NULL,
730 8 : *bms2 = NULL;
731 : text *result;
732 :
733 8 : if (!PG_ARGISNULL(0))
734 8 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
735 :
736 8 : if (!PG_ARGISNULL(1))
737 8 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
738 :
739 8 : bms1 = bms_int_members(bms1, bms2);
740 :
741 8 : if (bms2)
742 6 : bms_free(bms2);
743 :
744 8 : if (bms1 == NULL)
745 4 : PG_RETURN_NULL();
746 :
747 4 : result = BITMAPSET_TO_TEXT(bms1);
748 :
749 4 : if (bms1)
750 4 : bms_free(bms1);
751 :
752 4 : PG_RETURN_TEXT_P(result);
753 : }
754 :
755 : Datum
756 10 : test_bms_replace_members(PG_FUNCTION_ARGS)
757 : {
758 10 : Bitmapset *bms1 = NULL,
759 10 : *bms2 = NULL;
760 : Bitmapset *result_bms;
761 : text *result;
762 :
763 10 : if (!PG_ARGISNULL(0))
764 8 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
765 :
766 10 : if (!PG_ARGISNULL(1))
767 8 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
768 :
769 : /* IMPORTANT: bms_replace_members modifies/frees the first argument */
770 10 : result_bms = bms_replace_members(bms1, bms2);
771 :
772 : /* bms1 is now invalid, do not free it */
773 :
774 10 : if (bms2)
775 8 : bms_free(bms2);
776 :
777 10 : if (result_bms == NULL)
778 2 : PG_RETURN_NULL();
779 :
780 8 : result = BITMAPSET_TO_TEXT(result_bms);
781 8 : bms_free(result_bms);
782 :
783 8 : PG_RETURN_TEXT_P(result);
784 : }
785 :
786 : Datum
787 8 : test_bms_join(PG_FUNCTION_ARGS)
788 : {
789 8 : Bitmapset *bms1 = NULL,
790 8 : *bms2 = NULL;
791 : Bitmapset *result_bms;
792 : text *result;
793 :
794 8 : if (!PG_ARGISNULL(0))
795 6 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
796 :
797 8 : if (!PG_ARGISNULL(1))
798 6 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
799 :
800 : /* IMPORTANT: bms_join may recycle either input arguments */
801 8 : result_bms = bms_join(bms1, bms2);
802 :
803 : /* bms1 and bms2 may have been recycled! Do not free any of them. */
804 :
805 8 : if (result_bms == NULL)
806 0 : PG_RETURN_NULL();
807 :
808 8 : result = BITMAPSET_TO_TEXT(result_bms);
809 8 : bms_free(result_bms);
810 :
811 8 : PG_RETURN_TEXT_P(result);
812 : }
813 :
814 : Datum
815 12 : test_bms_hash_value(PG_FUNCTION_ARGS)
816 : {
817 12 : Bitmapset *bms = NULL;
818 : uint32 hash_result;
819 :
820 12 : if (!PG_ARGISNULL(0))
821 12 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
822 :
823 12 : hash_result = bms_hash_value(bms);
824 :
825 12 : if (bms)
826 10 : bms_free(bms);
827 :
828 12 : PG_RETURN_INT32(hash_result);
829 : }
830 :
831 : Datum
832 12 : test_bitmap_hash(PG_FUNCTION_ARGS)
833 : {
834 12 : Bitmapset *bms = NULL;
835 : Bitmapset *bms_ptr;
836 : uint32 hash_result;
837 :
838 12 : if (!PG_ARGISNULL(0))
839 12 : bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
840 :
841 12 : bms_ptr = bms;
842 :
843 : /* Call bitmap_hash */
844 12 : hash_result = bitmap_hash(&bms_ptr, sizeof(Bitmapset *));
845 :
846 : /* Clean up */
847 12 : if (!PG_ARGISNULL(0) && bms_ptr)
848 10 : bms_free(bms_ptr);
849 :
850 12 : PG_RETURN_INT32(hash_result);
851 : }
852 :
853 : Datum
854 18 : test_bitmap_match(PG_FUNCTION_ARGS)
855 : {
856 18 : Bitmapset *bms1 = NULL,
857 18 : *bms2 = NULL;
858 : Bitmapset *bms_ptr1,
859 : *bms_ptr2;
860 : int match_result;
861 :
862 18 : if (!PG_ARGISNULL(0))
863 18 : bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
864 :
865 18 : if (!PG_ARGISNULL(1))
866 18 : bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
867 :
868 : /* Set up pointers to the Bitmapsets */
869 18 : bms_ptr1 = bms1;
870 18 : bms_ptr2 = bms2;
871 :
872 : /* Call bitmap_match with addresses of the Bitmapset pointers */
873 18 : match_result = bitmap_match(&bms_ptr1, &bms_ptr2, sizeof(Bitmapset *));
874 :
875 : /* Clean up */
876 18 : if (bms1)
877 12 : bms_free(bms1);
878 18 : if (bms2)
879 12 : bms_free(bms2);
880 :
881 18 : PG_RETURN_INT32(match_result);
882 : }
883 :
884 : /*
885 : * Contrary to all the other functions which are one-one mappings with the
886 : * equivalent C functions, this stresses Bitmapsets in a random fashion for
887 : * various operations.
888 : *
889 : * "min_value" is the minimal value used for the members, that will stand
890 : * up to a range of "max_range". "num_ops" defines the number of time each
891 : * operation is done. "seed" is a random seed used to calculate the member
892 : * values.
893 : *
894 : * The return value is the number of times all operations have been executed.
895 : */
896 : Datum
897 2 : test_random_operations(PG_FUNCTION_ARGS)
898 : {
899 2 : Bitmapset *bms1 = NULL;
900 2 : Bitmapset *bms2 = NULL;
901 2 : Bitmapset *bms = NULL;
902 2 : Bitmapset *result = NULL;
903 : pg_prng_state state;
904 2 : uint64 seed = GetCurrentTimestamp();
905 2 : int num_ops = 5000;
906 2 : int total_ops = 0;
907 2 : int max_range = 2000;
908 2 : int min_value = 0;
909 : int member;
910 : int *members;
911 2 : int num_members = 0;
912 :
913 2 : if (!PG_ARGISNULL(0) && PG_GETARG_INT32(0) > 0)
914 0 : seed = PG_GETARG_INT32(0);
915 :
916 2 : if (!PG_ARGISNULL(1))
917 2 : num_ops = PG_GETARG_INT32(1);
918 :
919 2 : if (!PG_ARGISNULL(2))
920 2 : max_range = PG_GETARG_INT32(2);
921 :
922 2 : if (!PG_ARGISNULL(3))
923 2 : min_value = PG_GETARG_INT32(3);
924 :
925 2 : pg_prng_seed(&state, seed);
926 2 : members = palloc(sizeof(int) * num_ops);
927 :
928 : /* Phase 1: Random insertions */
929 10002 : for (int i = 0; i < num_ops / 2; i++)
930 : {
931 10000 : member = pg_prng_uint32(&state) % max_range + min_value;
932 :
933 10000 : if (!bms_is_member(member, bms1))
934 : {
935 9710 : members[num_members++] = member;
936 9710 : bms1 = bms_add_member(bms1, member);
937 : }
938 : }
939 :
940 : /* Phase 2: Random set operations */
941 5002 : for (int i = 0; i < num_ops / 4; i++)
942 : {
943 5000 : member = pg_prng_uint32(&state) % max_range + min_value;
944 :
945 5000 : bms2 = bms_add_member(bms2, member);
946 : }
947 :
948 : /* Test union */
949 2 : result = bms_union(bms1, bms2);
950 2 : EXPECT_NOT_NULL(result);
951 :
952 : /* Verify union contains all members from first set */
953 9712 : for (int i = 0; i < num_members; i++)
954 : {
955 9710 : if (!bms_is_member(members[i], result))
956 0 : elog(ERROR, "union missing member %d", members[i]);
957 : }
958 2 : bms_free(result);
959 :
960 : /* Test intersection */
961 2 : result = bms_intersect(bms1, bms2);
962 2 : if (result != NULL)
963 : {
964 2 : member = -1;
965 :
966 328 : while ((member = bms_next_member(result, member)) >= 0)
967 : {
968 326 : if (!bms_is_member(member, bms1) || !bms_is_member(member, bms2))
969 0 : elog(ERROR, "intersection contains invalid member %d", member);
970 : }
971 2 : bms_free(result);
972 : }
973 :
974 : /* Phase 3: Test range operations */
975 2 : result = NULL;
976 20002 : for (int i = 0; i < num_ops; i++)
977 : {
978 20000 : int lower = pg_prng_uint32(&state) % 100;
979 20000 : int upper = lower + (pg_prng_uint32(&state) % 20);
980 :
981 20000 : result = bms_add_range(result, lower, upper);
982 : }
983 2 : if (result != NULL)
984 : {
985 2 : EXPECT_TRUE(bms_num_members(result) > 0);
986 2 : bms_free(result);
987 : }
988 :
989 2 : pfree(members);
990 2 : bms_free(bms1);
991 2 : bms_free(bms2);
992 :
993 20002 : for (int i = 0; i < num_ops; i++)
994 : {
995 20000 : member = pg_prng_uint32(&state) % max_range + min_value;
996 20000 : switch (pg_prng_uint32(&state) % 3)
997 : {
998 6766 : case 0: /* add */
999 6766 : bms = bms_add_member(bms, member);
1000 6766 : break;
1001 6704 : case 1: /* delete */
1002 6704 : if (bms != NULL)
1003 : {
1004 6700 : bms = bms_del_member(bms, member);
1005 : }
1006 6704 : break;
1007 6530 : case 2: /* test membership */
1008 6530 : if (bms != NULL)
1009 : {
1010 6530 : bms_is_member(member, bms);
1011 : }
1012 6530 : break;
1013 : }
1014 20000 : total_ops++;
1015 : }
1016 :
1017 2 : if (bms)
1018 2 : bms_free(bms);
1019 :
1020 2 : PG_RETURN_INT32(total_ops);
1021 : }
|