mirror of
https://github.com/postgres/postgres.git
synced 2025-10-03 00:03:21 -04:00
This commit expands the set of tests added by 00c3d87a5cab, to bring the coverage of bitmapset.c close to 100% by addressing a lot of corner cases (most of these relate to word counts and reallocations). Some of the functions of this module also have their own idea of the result to return depending on the input values given. These are specific to the module, still let's add more coverage for all of them. Some comments are made more consistent in the tests, while on it. Author: Greg Burd <greg@burd.me> Reviewed-by: Michael Paquier <michael@paquier.xyz> Discussion: https://postgr.es/m/aNR-gsGmLnMaNT5i@paquier.xyz
1039 lines
20 KiB
C
1039 lines
20 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* test_bitmapset.c
|
|
* Test the Bitmapset data structure.
|
|
*
|
|
* This module tests the Bitmapset implementation in PostgreSQL, covering
|
|
* all public API functions.
|
|
*
|
|
* Copyright (c) 2025, PostgreSQL Global Development Group
|
|
*
|
|
* IDENTIFICATION
|
|
* src/test/modules/test_bitmapset/test_bitmapset.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include <stddef.h>
|
|
#include "catalog/pg_type.h"
|
|
#include "common/pg_prng.h"
|
|
#include "utils/array.h"
|
|
#include "fmgr.h"
|
|
#include "nodes/bitmapset.h"
|
|
#include "nodes/nodes.h"
|
|
#include "nodes/pg_list.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/timestamp.h"
|
|
#include "varatt.h"
|
|
|
|
PG_MODULE_MAGIC;
|
|
|
|
/* Bitmapset API functions in order of appearance in bitmapset.c */
|
|
PG_FUNCTION_INFO_V1(test_bms_make_singleton);
|
|
PG_FUNCTION_INFO_V1(test_bms_add_member);
|
|
PG_FUNCTION_INFO_V1(test_bms_del_member);
|
|
PG_FUNCTION_INFO_V1(test_bms_is_member);
|
|
PG_FUNCTION_INFO_V1(test_bms_num_members);
|
|
PG_FUNCTION_INFO_V1(test_bms_copy);
|
|
PG_FUNCTION_INFO_V1(test_bms_equal);
|
|
PG_FUNCTION_INFO_V1(test_bms_compare);
|
|
PG_FUNCTION_INFO_V1(test_bms_is_subset);
|
|
PG_FUNCTION_INFO_V1(test_bms_subset_compare);
|
|
PG_FUNCTION_INFO_V1(test_bms_union);
|
|
PG_FUNCTION_INFO_V1(test_bms_intersect);
|
|
PG_FUNCTION_INFO_V1(test_bms_difference);
|
|
PG_FUNCTION_INFO_V1(test_bms_is_empty);
|
|
PG_FUNCTION_INFO_V1(test_bms_membership);
|
|
PG_FUNCTION_INFO_V1(test_bms_singleton_member);
|
|
PG_FUNCTION_INFO_V1(test_bms_get_singleton_member);
|
|
PG_FUNCTION_INFO_V1(test_bms_next_member);
|
|
PG_FUNCTION_INFO_V1(test_bms_prev_member);
|
|
PG_FUNCTION_INFO_V1(test_bms_hash_value);
|
|
PG_FUNCTION_INFO_V1(test_bms_overlap);
|
|
PG_FUNCTION_INFO_V1(test_bms_overlap_list);
|
|
PG_FUNCTION_INFO_V1(test_bms_nonempty_difference);
|
|
PG_FUNCTION_INFO_V1(test_bms_member_index);
|
|
PG_FUNCTION_INFO_V1(test_bms_add_range);
|
|
PG_FUNCTION_INFO_V1(test_bms_add_members);
|
|
PG_FUNCTION_INFO_V1(test_bms_int_members);
|
|
PG_FUNCTION_INFO_V1(test_bms_del_members);
|
|
PG_FUNCTION_INFO_V1(test_bms_replace_members);
|
|
PG_FUNCTION_INFO_V1(test_bms_join);
|
|
PG_FUNCTION_INFO_V1(test_bitmap_hash);
|
|
PG_FUNCTION_INFO_V1(test_bitmap_match);
|
|
|
|
/* Test utility functions */
|
|
PG_FUNCTION_INFO_V1(test_random_operations);
|
|
|
|
/* Convenient macros to test results */
|
|
#define EXPECT_TRUE(expr) \
|
|
do { \
|
|
if (!(expr)) \
|
|
elog(ERROR, \
|
|
"%s was unexpectedly false in file \"%s\" line %u", \
|
|
#expr, __FILE__, __LINE__); \
|
|
} while (0)
|
|
|
|
#define EXPECT_NOT_NULL(expr) \
|
|
do { \
|
|
if ((expr) == NULL) \
|
|
elog(ERROR, \
|
|
"%s was unexpectedly true in file \"%s\" line %u", \
|
|
#expr, __FILE__, __LINE__); \
|
|
} while (0)
|
|
|
|
/* Encode/Decode to/from TEXT and Bitmapset */
|
|
#define BITMAPSET_TO_TEXT(bms) cstring_to_text(nodeToString(bms))
|
|
#define TEXT_TO_BITMAPSET(str) ((Bitmapset *) stringToNode(text_to_cstring(str)))
|
|
|
|
/*
|
|
* Individual test functions for each bitmapset API function
|
|
*/
|
|
|
|
Datum
|
|
test_bms_add_member(PG_FUNCTION_ARGS)
|
|
{
|
|
int member;
|
|
Bitmapset *bms = NULL;
|
|
text *result;
|
|
|
|
if (PG_ARGISNULL(1))
|
|
PG_RETURN_NULL();
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
member = PG_GETARG_INT32(1);
|
|
bms = bms_add_member(bms, member);
|
|
result = BITMAPSET_TO_TEXT(bms);
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_add_members(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
text *result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
/* IMPORTANT: bms_add_members modifies/frees the first argument */
|
|
bms1 = bms_add_members(bms1, bms2);
|
|
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
result = BITMAPSET_TO_TEXT(bms1);
|
|
bms_free(bms1);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_del_member(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
int32 member;
|
|
text *result;
|
|
|
|
if (PG_ARGISNULL(1))
|
|
PG_RETURN_NULL();
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
member = PG_GETARG_INT32(1);
|
|
bms = bms_del_member(bms, member);
|
|
|
|
if (bms_is_empty(bms))
|
|
PG_RETURN_NULL();
|
|
|
|
result = BITMAPSET_TO_TEXT(bms);
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_is_member(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
int32 member;
|
|
bool result;
|
|
|
|
if (PG_ARGISNULL(1))
|
|
PG_RETURN_BOOL(false);
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
member = PG_GETARG_INT32(1);
|
|
result = bms_is_member(member, bms);
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_num_members(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
int result = 0;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
result = bms_num_members(bms);
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_INT32(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_make_singleton(PG_FUNCTION_ARGS)
|
|
{
|
|
int32 member;
|
|
Bitmapset *bms;
|
|
text *result;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL();
|
|
|
|
member = PG_GETARG_INT32(0);
|
|
bms = bms_make_singleton(member);
|
|
|
|
result = BITMAPSET_TO_TEXT(bms);
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_copy(PG_FUNCTION_ARGS)
|
|
{
|
|
text *bms_data;
|
|
Bitmapset *bms = NULL;
|
|
Bitmapset *copy_bms;
|
|
text *result;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_NULL();
|
|
|
|
bms_data = PG_GETARG_TEXT_PP(0);
|
|
bms = TEXT_TO_BITMAPSET(bms_data);
|
|
copy_bms = bms_copy(bms);
|
|
result = BITMAPSET_TO_TEXT(copy_bms);
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
if (copy_bms)
|
|
bms_free(copy_bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_equal(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
bool result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
result = bms_equal(bms1, bms2);
|
|
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_union(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
Bitmapset *result_bms;
|
|
text *result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
result_bms = bms_union(bms1, bms2);
|
|
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
if (result_bms == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
result = BITMAPSET_TO_TEXT(result_bms);
|
|
bms_free(result_bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_membership(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
BMS_Membership result;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_INT32(BMS_EMPTY_SET);
|
|
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
result = bms_membership(bms);
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_INT32((int32) result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_next_member(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
int32 prevmember;
|
|
int result;
|
|
|
|
if (PG_ARGISNULL(0) || PG_ARGISNULL(1))
|
|
PG_RETURN_INT32(-2);
|
|
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
prevmember = PG_GETARG_INT32(1);
|
|
result = bms_next_member(bms, prevmember);
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_INT32(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_intersect(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
Bitmapset *result_bms;
|
|
text *result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
result_bms = bms_intersect(bms1, bms2);
|
|
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
if (result_bms == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
result = BITMAPSET_TO_TEXT(result_bms);
|
|
bms_free(result_bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_difference(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
Bitmapset *result_bms;
|
|
text *result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
result_bms = bms_difference(bms1, bms2);
|
|
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
if (result_bms == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
result = BITMAPSET_TO_TEXT(result_bms);
|
|
bms_free(result_bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_compare(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
int result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
result = bms_compare(bms1, bms2);
|
|
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
PG_RETURN_INT32(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_is_empty(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
bool result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
result = bms_is_empty(bms);
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_is_subset(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
bool result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
result = bms_is_subset(bms1, bms2);
|
|
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_subset_compare(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
BMS_Comparison result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
result = bms_subset_compare(bms1, bms2);
|
|
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
PG_RETURN_INT32((int32) result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_singleton_member(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
int result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
result = bms_singleton_member(bms);
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_INT32(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_get_singleton_member(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
int32 default_member = PG_GETARG_INT32(1);
|
|
bool success;
|
|
int member = -1;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_INT32(default_member);
|
|
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
/*
|
|
* bms_get_singleton_member returns bool and stores result in member
|
|
* pointer
|
|
*/
|
|
success = bms_get_singleton_member(bms, &member);
|
|
bms_free(bms);
|
|
|
|
if (success)
|
|
PG_RETURN_INT32(member);
|
|
|
|
PG_RETURN_INT32(default_member);
|
|
}
|
|
|
|
Datum
|
|
test_bms_prev_member(PG_FUNCTION_ARGS)
|
|
{
|
|
text *bms_data;
|
|
Bitmapset *bms = NULL;
|
|
int32 prevmember;
|
|
int result;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_INT32(-2);
|
|
|
|
bms_data = PG_GETARG_TEXT_PP(0);
|
|
prevmember = PG_GETARG_INT32(1);
|
|
|
|
if (VARSIZE_ANY_EXHDR(bms_data) == 0)
|
|
PG_RETURN_INT32(-2);
|
|
|
|
bms = TEXT_TO_BITMAPSET(bms_data);
|
|
result = bms_prev_member(bms, prevmember);
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_INT32(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_overlap(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
bool result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
result = bms_overlap(bms1, bms2);
|
|
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_overlap_list(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
ArrayType *array;
|
|
List *int_list = NIL;
|
|
bool result;
|
|
Datum *elem_datums;
|
|
bool *elem_nulls;
|
|
int elem_count;
|
|
int i;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_BOOL(false);
|
|
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (PG_ARGISNULL(1))
|
|
{
|
|
if (bms)
|
|
bms_free(bms);
|
|
PG_RETURN_BOOL(false);
|
|
}
|
|
|
|
array = PG_GETARG_ARRAYTYPE_P(1);
|
|
|
|
deconstruct_array(array,
|
|
INT4OID, sizeof(int32), true, 'i',
|
|
&elem_datums, &elem_nulls, &elem_count);
|
|
|
|
for (i = 0; i < elem_count; i++)
|
|
{
|
|
if (!elem_nulls[i])
|
|
{
|
|
int32 member = DatumGetInt32(elem_datums[i]);
|
|
|
|
int_list = lappend_int(int_list, member);
|
|
}
|
|
}
|
|
|
|
result = bms_overlap_list(bms, int_list);
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
list_free(int_list);
|
|
|
|
pfree(elem_datums);
|
|
pfree(elem_nulls);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_nonempty_difference(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
bool result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
result = bms_nonempty_difference(bms1, bms2);
|
|
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
PG_RETURN_BOOL(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_member_index(PG_FUNCTION_ARGS)
|
|
{
|
|
text *bms_data;
|
|
Bitmapset *bms = NULL;
|
|
int32 member;
|
|
int result;
|
|
|
|
if (PG_ARGISNULL(0))
|
|
PG_RETURN_INT32(-1);
|
|
|
|
bms_data = PG_GETARG_TEXT_PP(0);
|
|
member = PG_GETARG_INT32(1);
|
|
|
|
if (VARSIZE_ANY_EXHDR(bms_data) == 0)
|
|
PG_RETURN_INT32(-1);
|
|
|
|
bms = TEXT_TO_BITMAPSET(bms_data);
|
|
|
|
result = bms_member_index(bms, member);
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_INT32(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_add_range(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
int32 lower,
|
|
upper;
|
|
text *result;
|
|
|
|
if (PG_ARGISNULL(1) || PG_ARGISNULL(2))
|
|
PG_RETURN_NULL();
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
lower = PG_GETARG_INT32(1);
|
|
upper = PG_GETARG_INT32(2);
|
|
|
|
/* Check for invalid range */
|
|
if (upper < lower)
|
|
{
|
|
if (bms)
|
|
bms_free(bms);
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
bms = bms_add_range(bms, lower, upper);
|
|
|
|
result = BITMAPSET_TO_TEXT(bms);
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_int_members(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
text *result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
bms1 = bms_int_members(bms1, bms2);
|
|
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
if (bms1 == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
result = BITMAPSET_TO_TEXT(bms1);
|
|
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_del_members(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
Bitmapset *result_bms;
|
|
text *result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
/* IMPORTANT: bms_del_members modifies/frees the first argument */
|
|
result_bms = bms_del_members(bms1, bms2);
|
|
|
|
/* bms1 is now invalid, do not free it */
|
|
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
if (result_bms == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
result = BITMAPSET_TO_TEXT(result_bms);
|
|
bms_free(result_bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_replace_members(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
Bitmapset *result_bms;
|
|
text *result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
/* IMPORTANT: bms_replace_members modifies/frees the first argument */
|
|
result_bms = bms_replace_members(bms1, bms2);
|
|
|
|
/* bms1 is now invalid, do not free it */
|
|
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
if (result_bms == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
result = BITMAPSET_TO_TEXT(result_bms);
|
|
bms_free(result_bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_join(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
Bitmapset *result_bms;
|
|
text *result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
/* IMPORTANT: bms_join may recycle either input arguments */
|
|
result_bms = bms_join(bms1, bms2);
|
|
|
|
/* bms1 and bms2 may have been recycled! Do not free any of them. */
|
|
|
|
if (result_bms == NULL)
|
|
PG_RETURN_NULL();
|
|
|
|
result = BITMAPSET_TO_TEXT(result_bms);
|
|
bms_free(result_bms);
|
|
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
Datum
|
|
test_bms_hash_value(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
uint32 hash_result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
hash_result = bms_hash_value(bms);
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_INT32(hash_result);
|
|
}
|
|
|
|
Datum
|
|
test_bitmap_hash(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms = NULL;
|
|
Bitmapset *bms_ptr;
|
|
uint32 hash_result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
bms_ptr = bms;
|
|
|
|
/* Call bitmap_hash */
|
|
hash_result = bitmap_hash(&bms_ptr, sizeof(Bitmapset *));
|
|
|
|
/* Clean up */
|
|
if (!PG_ARGISNULL(0) && bms_ptr)
|
|
bms_free(bms_ptr);
|
|
|
|
PG_RETURN_INT32(hash_result);
|
|
}
|
|
|
|
Datum
|
|
test_bitmap_match(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL,
|
|
*bms2 = NULL;
|
|
Bitmapset *bms_ptr1,
|
|
*bms_ptr2;
|
|
int match_result;
|
|
|
|
if (!PG_ARGISNULL(0))
|
|
bms1 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(0));
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
bms2 = TEXT_TO_BITMAPSET(PG_GETARG_TEXT_PP(1));
|
|
|
|
/* Set up pointers to the Bitmapsets */
|
|
bms_ptr1 = bms1;
|
|
bms_ptr2 = bms2;
|
|
|
|
/* Call bitmap_match with addresses of the Bitmapset pointers */
|
|
match_result = bitmap_match(&bms_ptr1, &bms_ptr2, sizeof(Bitmapset *));
|
|
|
|
/* Clean up */
|
|
if (bms1)
|
|
bms_free(bms1);
|
|
if (bms2)
|
|
bms_free(bms2);
|
|
|
|
PG_RETURN_INT32(match_result);
|
|
}
|
|
|
|
/*
|
|
* Contrary to all the other functions which are one-one mappings with the
|
|
* equivalent C functions, this stresses Bitmapsets in a random fashion for
|
|
* various operations.
|
|
*
|
|
* "min_value" is the minimal value used for the members, that will stand
|
|
* up to a range of "max_range". "num_ops" defines the number of time each
|
|
* operation is done. "seed" is a random seed used to calculate the member
|
|
* values.
|
|
*
|
|
* The return value is the number of times all operations have been executed.
|
|
*/
|
|
Datum
|
|
test_random_operations(PG_FUNCTION_ARGS)
|
|
{
|
|
Bitmapset *bms1 = NULL;
|
|
Bitmapset *bms2 = NULL;
|
|
Bitmapset *bms = NULL;
|
|
Bitmapset *result = NULL;
|
|
pg_prng_state state;
|
|
uint64 seed = GetCurrentTimestamp();
|
|
int num_ops = 5000;
|
|
int total_ops = 0;
|
|
int max_range = 2000;
|
|
int min_value = 0;
|
|
int member;
|
|
int *members;
|
|
int num_members = 0;
|
|
|
|
if (!PG_ARGISNULL(0) && PG_GETARG_INT32(0) > 0)
|
|
seed = PG_GETARG_INT32(0);
|
|
|
|
if (!PG_ARGISNULL(1))
|
|
num_ops = PG_GETARG_INT32(1);
|
|
|
|
if (!PG_ARGISNULL(2))
|
|
max_range = PG_GETARG_INT32(2);
|
|
|
|
if (!PG_ARGISNULL(3))
|
|
min_value = PG_GETARG_INT32(3);
|
|
|
|
pg_prng_seed(&state, seed);
|
|
members = palloc(sizeof(int) * num_ops);
|
|
|
|
/* Phase 1: Random insertions */
|
|
for (int i = 0; i < num_ops / 2; i++)
|
|
{
|
|
member = pg_prng_uint32(&state) % max_range + min_value;
|
|
|
|
if (!bms_is_member(member, bms1))
|
|
{
|
|
members[num_members++] = member;
|
|
bms1 = bms_add_member(bms1, member);
|
|
}
|
|
}
|
|
|
|
/* Phase 2: Random set operations */
|
|
for (int i = 0; i < num_ops / 4; i++)
|
|
{
|
|
member = pg_prng_uint32(&state) % max_range + min_value;
|
|
|
|
bms2 = bms_add_member(bms2, member);
|
|
}
|
|
|
|
/* Test union */
|
|
result = bms_union(bms1, bms2);
|
|
EXPECT_NOT_NULL(result);
|
|
|
|
/* Verify union contains all members from first set */
|
|
for (int i = 0; i < num_members; i++)
|
|
{
|
|
if (!bms_is_member(members[i], result))
|
|
elog(ERROR, "union missing member %d", members[i]);
|
|
}
|
|
bms_free(result);
|
|
|
|
/* Test intersection */
|
|
result = bms_intersect(bms1, bms2);
|
|
if (result != NULL)
|
|
{
|
|
member = -1;
|
|
|
|
while ((member = bms_next_member(result, member)) >= 0)
|
|
{
|
|
if (!bms_is_member(member, bms1) || !bms_is_member(member, bms2))
|
|
elog(ERROR, "intersection contains invalid member %d", member);
|
|
}
|
|
bms_free(result);
|
|
}
|
|
|
|
/* Phase 3: Test range operations */
|
|
result = NULL;
|
|
for (int i = 0; i < num_ops; i++)
|
|
{
|
|
int lower = pg_prng_uint32(&state) % 100;
|
|
int upper = lower + (pg_prng_uint32(&state) % 20);
|
|
|
|
result = bms_add_range(result, lower, upper);
|
|
}
|
|
if (result != NULL)
|
|
{
|
|
EXPECT_TRUE(bms_num_members(result) > 0);
|
|
bms_free(result);
|
|
}
|
|
|
|
pfree(members);
|
|
bms_free(bms1);
|
|
bms_free(bms2);
|
|
|
|
for (int i = 0; i < num_ops; i++)
|
|
{
|
|
member = pg_prng_uint32(&state) % max_range + min_value;
|
|
switch (pg_prng_uint32(&state) % 3)
|
|
{
|
|
case 0: /* add */
|
|
bms = bms_add_member(bms, member);
|
|
break;
|
|
case 1: /* delete */
|
|
if (bms != NULL)
|
|
{
|
|
bms = bms_del_member(bms, member);
|
|
}
|
|
break;
|
|
case 2: /* test membership */
|
|
if (bms != NULL)
|
|
{
|
|
bms_is_member(member, bms);
|
|
}
|
|
break;
|
|
}
|
|
total_ops++;
|
|
}
|
|
|
|
if (bms)
|
|
bms_free(bms);
|
|
|
|
PG_RETURN_INT32(total_ops);
|
|
}
|