/**
* @file pgbitmap.c
* \code
* Author: Marc Munro
* Copyright (c) 2020 Marc Munro
* License: BSD
*
* \endcode
* @brief
* Define a bitmap type for postgres.
*
*/
#include "pgbitmap.h"
PG_MODULE_MAGIC;
/**
* The length of a 64-bit integer as a base64 string.
* This should really be 8 but the last char is always '=' so we can
* optimise it away.
*/
#define INT32SIZE_B64 7
/**
* The length of a 32-bit integer as a base64 string.
*/
#define INT64SIZE_B64 12
#ifdef USE_64_BIT
/**
* Array of bit positions for int64, indexed by bitno.
*/
static
uint64 bitmasks[64] = {
0x00000001, 0x00000002, 0x00000004, 0x00000008,
0x00000010, 0x00000020, 0x00000040, 0x00000080,
0x00000100, 0x00000200, 0x00000400, 0x00000800,
0x00001000, 0x00002000, 0x00004000, 0x00008000,
0x00010000, 0x00020000, 0x00040000, 0x00080000,
0x00100000, 0x00200000, 0x00400000, 0x00800000,
0x01000000, 0x02000000, 0x04000000, 0x08000000,
0x10000000, 0x20000000, 0x40000000, 0x80000000,
0x0000000100000000, 0x0000000200000000,
0x0000000400000000, 0x0000000800000000,
0x0000001000000000, 0x0000002000000000,
0x0000004000000000, 0x0000008000000000,
0x0000010000000000, 0x0000020000000000,
0x0000040000000000, 0x0000080000000000,
0x0000100000000000, 0x0000200000000000,
0x0000400000000000, 0x0000800000000000,
0x0001000000000000, 0x0002000000000000,
0x0004000000000000, 0x0008000000000000,
0x0010000000000000, 0x0020000000000000,
0x0040000000000000, 0x0080000000000000,
0x0100000000000000, 0x0200000000000000,
0x0400000000000000, 0x0800000000000000,
0x1000000000000000, 0x2000000000000000,
0x4000000000000000, 0x8000000000000000};
#else
/**
* Array of bit positions for int32, indexed by bitno.
*/
static
uint32 bitmasks[] = {0x00000001, 0x00000002, 0x00000004, 0x00000008,
0x00000010, 0x00000020, 0x00000040, 0x00000080,
0x00000100, 0x00000200, 0x00000400, 0x00000800,
0x00001000, 0x00002000, 0x00004000, 0x00008000,
0x00010000, 0x00020000, 0x00040000, 0x00080000,
0x00100000, 0x00200000, 0x00400000, 0x00800000,
0x01000000, 0x02000000, 0x04000000, 0x08000000,
0x10000000, 0x20000000, 0x40000000, 0x80000000};
#endif
/* BEGIN SECTION OF CODE COPIED FROM pgcrypto.c (via veil) */
static const char _base64[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const short b64lookup[128] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
};
static unsigned
b64_encode(const char *src, unsigned len, char *dst)
{
char *p,
*lend = dst + 76;
const char *s,
*end = src + len;
int pos = 2;
uint32 buf = 0;
s = src;
p = dst;
while (s < end)
{
buf |= (unsigned char) *s << (pos << 3);
pos--;
s++;
/* write it out */
if (pos < 0)
{
*p++ = _base64[(buf >> 18) & 0x3f];
*p++ = _base64[(buf >> 12) & 0x3f];
*p++ = _base64[(buf >> 6) & 0x3f];
*p++ = _base64[buf & 0x3f];
pos = 2;
buf = 0;
}
if (p >= lend)
{
*p++ = '\n';
lend = p + 76;
}
}
if (pos != 2)
{
*p++ = _base64[(buf >> 18) & 0x3f];
*p++ = _base64[(buf >> 12) & 0x3f];
*p++ = (pos == 0) ? _base64[(buf >> 6) & 0x3f] : '=';
*p++ = '=';
}
return p - dst;
}
static unsigned
b64_decode(const char *src, unsigned len, char *dst)
{
const char *srcend = src + len,
*s = src;
char *p = dst;
char c;
int b = 0;
uint32 buf = 0;
int pos = 0,
end = 0;
while (s < srcend)
{
c = *s++;
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
continue;
if (c == '=')
{
/* end sequence */
if (!end)
{
if (pos == 2)
end = 1;
else if (pos == 3)
end = 2;
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("unexpected \"=\"")));
}
b = 0;
}
else
{
b = -1;
if (c > 0 && c < 127)
b = b64lookup[(unsigned char) c];
if (b < 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid symbol")));
}
/* add it to buffer */
buf = (buf << 6) + b;
pos++;
if (pos == 4)
{
*p++ = (buf >> 16) & 255;
if (end == 0 || end > 1)
*p++ = (buf >> 8) & 255;
if (end == 0 || end > 2)
*p++ = buf & 255;
buf = 0;
pos = 0;
}
}
if (pos != 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("invalid end sequence")));
return p - dst;
}
/* END SECTION OF CODE COPIED FROM pgcrypto.c */
/*
* Low-level bit operation functions follow
**********************************************************************
*/
/**
* Predicate identifying whether the bitmap is empty. This takes a
* pretty simplistic view of what it means to be empty: any properly
* contructed non-empty bitmap will have bits set in at least the bitmin
* and bitmax positions,
*
* @param bitmap The ::Bitmap being scanned.
*
* @return True if the bit at bitmin is zero.
*/
static boolean
bitmapEmpty(Bitmap *bitmap)
{
return ((bitmap->bitset[0]) == 0);
}
/**
* Clear all bits in a ::Bitmap.
*
* @param bitmap The ::Bitmap within which all bits are to be cleared
*/
static void
clearBitmap(Bitmap *bitmap)
{
int32 elems = ARRAYELEMS(bitmap->bitmin, bitmap->bitmax);
int32 i;
for (i = 0; i < elems; i++) {
bitmap->bitset[i] = 0;
}
}
/**
* Return a new, possibly uninitialised, ::Bitmap.
*
* @param min The lowest value bit to be stored in the bitmap
* @param max The highest value bit to be stored in the bitmap
*
* @return New appropriately sized bitmap.
*/
static Bitmap *
newBitmap(int32 min, int32 max)
{
int32 elems = ARRAYELEMS(min, max);
int32 size = sizeof(Bitmap) + (sizeof(bm_int) * (elems + DBG_ELEMS));
Bitmap *bitmap = bitmap = palloc(size);
SET_VARSIZE(bitmap, size);
bitmap->bitmin = min;
bitmap->bitmax = max;
SETCANARY(bitmap);
return bitmap;
}
/**
* Test a bit within a ::Bitmap. If the bit is outside of the acceptable
* range return false.
*
* @param bitmap The ::Bitmap within which the bit is to be set.
* @param bit The bit to be tested.
*
* @return True if the bit is set, false otherwise.
*/
bool
bitmapTestbit(Bitmap *bitmap,
int32 bit)
{
if ((bit > bitmap->bitmax) ||
(bit < bitmap->bitmin))
{
return false;
}
else {
int32 relative_bit = bit - BITZERO(bitmap->bitmin);
int32 element = BITSET_ELEM(relative_bit);
return (bitmap->bitset[element] &
bitmasks[BITSET_BIT(relative_bit)]) != 0;
}
}
#ifdef BITMAP_DEBUG
static void
printBitmap(char *label, Bitmap *bitmap)
{
int i;
fprintf(stderr, "%s: %s<%d, %d>:", label,
CHKCANARY(bitmap)? "": "BROKEN BITMAP",
bitmap->bitmin, bitmap->bitmax);
for (i = bitmap->bitmin; i <= bitmap->bitmax; i++) {
fprintf(stderr, "%c", bitmapTestbit(bitmap, i)? '1': '0');
}
fprintf(stderr, ":\n");
}
static char *
bitmapString(Bitmap *bitmap)
{
static char result[200];
int i;
char *here;
i = sprintf(result, "%s<%d, %d>",
CHKCANARY(bitmap)? "": "BROKEN BITMAP:",
bitmap->bitmin, bitmap->bitmax);
here = result + i;
for (i = bitmap->bitmin; i <= bitmap->bitmax; i++) {
here += sprintf(here, "%c", bitmapTestbit(bitmap, i)? '1': '0');
}
return result;
}
#endif
/**
* Create a copy of a bitmap.
*
* @param bitmap The original bitmap
*
* @return Copy of bitmap.
*/
Bitmap *
bitmapCopy(Bitmap *bitmap)
{
Bitmap *result;
int32 i;
result = newBitmap(bitmap->bitmin, bitmap->bitmax);
for (i = ARRAYELEMS(bitmap->bitmin, bitmap->bitmax) - 1; i >= 0; i--) {
result->bitset[i] = bitmap->bitset[i];
}
return result;
}
/**
* Take an existing bitmap, and return a larger copy that will be able
* to store a specified bit.
*
* @param bitmap The original bitmap
* @param bit A new bit that the returned bitmap must be capable of
* storing.
*
* @return New bitmap with appropriate bitmin and bitmax values.
*/
static Bitmap *
extendBitmap(Bitmap *bitmap,
int32 bit)
{
Bitmap *result;
int32 orig_elems = ARRAYELEMS(bitmap->bitmin, bitmap->bitmax);
int32 new_elems;
int32 elems_offset;
int32 extra_elems;
int32 to_clear;
int32 i;
//elog(NOTICE, "Adding %d to %d->%d", bit, bitmap->bitmin, bitmap->bitmax);
// Allocate new bitmap (no need to clear it)
result = newBitmap(MIN(bit, bitmap->bitmin),
MAX(bit, bitmap->bitmax));
//elog(NOTICE, "New: %d->%d", result->bitmin, result->bitmax);
// Copy existing bitset into our new bitset.
elems_offset = BITSET_ELEM(bitmap->bitmin) - BITSET_ELEM(result->bitmin);
//elog(NOTICE, "orig_elems %d, offset %d", orig_elems, elems_offset);
for (i = 0; i < orig_elems; i++) {
//elog(NOTICE, "Copying %d into %d", i, i + elems_offset);
result->bitset[i + elems_offset] = bitmap->bitset[i];
}
// Clear any preceding elements
for (i = 0; i < elems_offset; i++) {
//elog(NOTICE, "Clearing %d", i);
result->bitset[i] = 0;
}
// Clear any following elements
new_elems = ARRAYELEMS(result->bitmin, result->bitmax);
extra_elems = new_elems - orig_elems;
to_clear = extra_elems - elems_offset;
//elog(NOTICE, "new_elems %d, extra_elems %d, to_clear %d",
// new_elems, extra_elems, to_clear);
for (i = new_elems - to_clear; i < new_elems; i++) {
//elog(NOTICE, "Clearing %d", i);
result->bitset[i] = 0;
}
return result;
}
/**
* Set a bit within a ::Bitmap.
*
* @param bitmap The ::Bitmap within which the bit is to be set.
* @param bit The bit to be set.
*
* @return Possibly new bitmap with the specified bit set.
*/
static void
doSetBit(Bitmap *bitmap,
int32 bit)
{
int32 relative_bit;
int32 element;
relative_bit = bit - BITZERO(bitmap->bitmin);
element = BITSET_ELEM(relative_bit);
bitmap->bitset[element] |= bitmasks[BITSET_BIT(relative_bit)];
/* The tests below handle the case where the bitmap was previously
* empty. */
if ((bit < bitmap->bitmin) || !bitmapTestbit(bitmap, bitmap->bitmin)) {
bitmap->bitmin = bit;
}
if ((bit > bitmap->bitmax) || !bitmapTestbit(bitmap, bitmap->bitmax)) {
bitmap->bitmax = bit;
}
}
/**
* Return a new ::Bitmap with bit set.
*
* @param bitmap The ::Bitmap within which the bit is to be set.
* @param bit The bit to be set.
*
* @return New bitmap with the specified bit set.
*/
static Bitmap *
bitmapSetbit(Bitmap *bitmap,
int32 bit)
{
Bitmap *result;
if ((bit > bitmap->bitmax) || (bit < bitmap->bitmin)) {
result = extendBitmap(bitmap, bit);
}
else {
result = bitmapCopy(bitmap);
}
doSetBit(result, bit);
return result;
}
/**
* Return the next set bit in the ::Bitmap.
*
* @param bitmap The ::Bitmap being scanned.
* @param inbit The starting bit from which to scan the bitmap
* @param found Boolean that will be set to true when a set bit has been
* found.
*
* @return The bit id of the found bit, or the inbit parameter if no set
* bits were found.
*/
static int32
bitmapNextBit(Bitmap *bitmap,
int32 inbit,
bool *found)
{
int32 bit = inbit;
while (bit <= bitmap->bitmax) {
if (bitmapTestbit(bitmap, bit)) {
*found = true;
return bit;
}
bit++;
}
*found = false;
return inbit;
}
static char *
serialise_bitmap(Bitmap *bitmap);
/**
* Take an existing ::Bitmap, and shrink it to be bounded by the elements
* containing the highest and lowest bits.
*
* @param bitmap The original ::Bitmap
*
* @return Bitmap with updated bitmin and bitmax values.
*/
static void
reduceBitmap(Bitmap *bitmap)
{
int32 bit = bitmap->bitmin;
bool found;
int32 first_elem;
int32 last_elem = BITSET_ELEM(bitmap->bitmax) - BITSET_ELEM(bitmap->bitmin);
int32 i;
bit = bitmapNextBit(bitmap, bit, &found);
if (!found) {
/* Bitmap is empty: do a quick exit. */
bitmap->bitmax = bitmap->bitmin;
return;
}
first_elem = BITSET_ELEM(bit) - BITSET_ELEM(bitmap->bitmin);
if (first_elem) {
/* The first elements have no bits, so we need to re-base
* the bitset array. */
for (i = first_elem; i <= last_elem; i++) {
bitmap->bitset[i - first_elem] = bitmap->bitset[i];
}
last_elem -= first_elem;
}
bitmap->bitmin = bit;
/* Now find last bit (we know that found is true at this point). */
while (found) {
bit += 1;
bit = bitmapNextBit(bitmap, bit, &found);
}
bitmap->bitmax = bit - 1; /* -1 undoes the last increment in above loop. */
}
/**
* Clear a bit within a ::Bitmap.
*
* @param bitmap The ::Bitmap within which the bit is to be cleared.
* @param bit The bit to be set.
*
* @return Possibly new bitmap with the specified bit set.
*/
static void
doClearBit(Bitmap *bitmap,
int32 bit)
{
int32 relative_bit = bit - BITZERO(bitmap->bitmin);
int32 element = BITSET_ELEM(relative_bit);
if ((bit <= bitmap->bitmax) && (bit >= bitmap->bitmin))
{
bitmap->bitset[element] &= ~(bitmasks[BITSET_BIT(relative_bit)]);
}
}
/**
* Clear a bit within a ::Bitmap.
*
* @param bitmap The ::Bitmap within which the bit is to be cleared.
* @param bit The bit to be cleared.
*
* @return The original bitmap, possibly shrunk, with the specified bit
* cleared.
*/
static Bitmap *
bitmapClearbit(Bitmap *bitmap,
int32 bit)
{
doClearBit(bitmap, bit);
if ((bit == bitmap->bitmin) || (bit == bitmap->bitmax)) {
reduceBitmap(bitmap);
}
return bitmap;
}
/**
* Ensure bitmin of bitmap is no less than parameter. This provides the
* means to quickly truncate a bitmap.
*
* @param bitmap The ::Bitmap to be modified
* @param bitmin The new minimum value
*
* @return A newly allocated bitmap which has no lower bits than bitmin
*/
static Bitmap *
bitmapSetMin(Bitmap *bitmap, int bitmin)
{
Bitmap *result = newBitmap(MAX(bitmap->bitmin, bitmin),
bitmap->bitmax);
int32 bitmap_elems = ARRAYELEMS(bitmap->bitmin, bitmap->bitmax);
int32 res_elems = ARRAYELEMS(result->bitmin, result->bitmax);
int32 lost_elems = bitmap_elems - res_elems;
int32 i;
/* Copy the bitset elements that we need. */
for (i = 0; i < res_elems; i++) {
result->bitset[i] = bitmap->bitset[i + lost_elems];
}
if (result->bitmin != bitmap->bitmin) {
/* Clear any bits below bitmin */
if (result->bitmin > BITZERO(result->bitmin)) {
for (i = BITSET_ELEM(result->bitmin - 1); i >= 0; i--) {
result->bitset[0] &= ~(bitmasks[BITSET_BIT(i)]);
}
}
/* Update bitmin to match the lowest bit that is actually set. */
/* If the bitmap is sparse, there may be entire words that are
* empty of bits. Let's deal with that situation first. */
lost_elems = 0;
for (i = 0; i < res_elems; i++) {
if (result->bitset[i] == 0) {
lost_elems++;
}
else {
break;
}
}
if (lost_elems) {
for (i = 0; i < (res_elems - lost_elems); i++) {
result->bitset[i] = result->bitset[i + lost_elems];
}
result->bitmin = BITZERO(result->bitmin + (lost_elems * ELEMBITS));
}
for (i = BITSET_ELEM(result->bitmin); i < ELEMBITS; i++) {
if (result->bitset[0] & bitmasks[BITSET_BIT(i)]) {
result->bitmin = BITZERO(result->bitmin) + i;
break;
}
}
}
return result;
}
/**
* Ensure bitmax of bitmap is no more than parameter
*
* @param bitmap The ::Bitmap to be modified
* @param bitmax The new minimum value
*
* @return A newly allocated bitmap which has no higher bits than bitmax
*/
static Bitmap *
bitmapSetMax(Bitmap *bitmap, int bitmax)
{
Bitmap *result = newBitmap(bitmap->bitmin,
MIN(bitmap->bitmax, bitmax));
int32 res_elems = ARRAYELEMS(result->bitmin, result->bitmax);
int32 last_elem;
int32 lost_elems;
int32 i;
/* Copy the bitset elements that we need. */
for (i = 0; i < res_elems; i++) {
result->bitset[i] = bitmap->bitset[i];
}
if (result->bitmax != bitmap->bitmax) {
last_elem = res_elems - 1;
/* Clear any bits above bitmax */
for (i = BITSET_BIT(result->bitmax) + 1; i < ELEMBITS; i++) {
result->bitset[last_elem] &= ~(bitmasks[BITSET_BIT(i)]);
}
/* Update bitmax to match the highest bit that is actually set. */
lost_elems = 0;
for (i = last_elem; i > 0; i--) {
if (result->bitset[i] == 0) {
// We found an empty word
lost_elems++;
}
else {
break;
}
}
if (lost_elems) {
result->bitmax = BITZERO(result->bitmax) -
(lost_elems * ELEMBITS) + ELEMBITS - 1;
}
last_elem -= lost_elems;
for (i = ELEMBITS - 1; i > 0; i--) {
if (result->bitset[last_elem] & bitmasks[BITSET_BIT(i)]) {
result->bitmax = BITZERO(result->bitmax) + i;
break;
}
}
}
return result;
}
/**
* Test for equality of 2 bitmaps
*
* @param bitmap1 The first ::Bitmap to be compared
* @param bitmap2 The second ::Bitmap to be compared
*
* @return True if the bitmaps are the same.
*/
static bool
bitmapEqual(Bitmap *bitmap1,
Bitmap *bitmap2)
{
int32 elems;
int32 i;
if ((bitmap1->bitmin == bitmap2->bitmin) &&
(bitmap1->bitmax == bitmap2->bitmax)) {
elems = ARRAYELEMS(bitmap1->bitmin, bitmap1->bitmax);
for (i = 0; i < elems; i++) {
if (bitmap1->bitset[i] != bitmap2->bitset[i]) {
return false;
}
}
return true;
}
else {
/* bitmin and bitmax do not agree but they could both be empty
* bitmaps, so check for that.
*/
return bitmapEmpty(bitmap1) && bitmapEmpty(bitmap2);
}
}
/**
* Create the union of two bitmaps.
*
* @param bitmap1 The first ::Bitmap to be unioned.
* @param bitmap2 The second ::Bitmap, to be unioned with bitmap1.
*
* @return A newly allocated bitmap which is the union
*/
static Bitmap *
bitmapUnion(Bitmap *bitmap1,
Bitmap *bitmap2)
{
Bitmap *result = newBitmap(MIN(bitmap1->bitmin, bitmap2->bitmin),
MAX(bitmap1->bitmax, bitmap2->bitmax));
int32 res_elems = ARRAYELEMS(result->bitmin, result->bitmax);
int32 bit_offset1 = BITZERO(result->bitmin) - BITZERO(bitmap1->bitmin);
int32 bit_offset2 = BITZERO(result->bitmin) - BITZERO(bitmap2->bitmin);
int32 elem_offset1 = BITSET_ELEM(bit_offset1);
int32 elem_offset2 = BITSET_ELEM(bit_offset2);
int32 elem_max1 = BITSET_ELEM((bitmap1->bitmax -
BITZERO(bitmap1->bitmin)));
int32 elem_max2 = BITSET_ELEM((bitmap2->bitmax -
BITZERO(bitmap2->bitmin)));
bm_int elem;
int32 to;
int32 from;
for (to = 0; to < res_elems; to++) {
elem = 0;
from = to + elem_offset1;
if ((from >= 0) && (from <= elem_max1)) {
elem = bitmap1->bitset[from];
}
from = to + elem_offset2;
if ((from >= 0) && (from <= elem_max2)) {
elem |= bitmap2->bitset[from];
}
result->bitset[to] = elem;
}
// This would normally be unncessary but it is possible that
// either of the source bitmaps are empty, so we do this to be
// safe.
reduceBitmap(result);
return result;
}
/**
* Create the intersection of two bitmaps.
*
* @param bitmap1 The first ::Bitmap to be intersected.
* @param bitmap2 The second ::Bitmap, to be intersected with bitmap1.
*
* @return A newly allocated bitmap which is the intersection
*/
static Bitmap *
bitmapIntersect(Bitmap *bitmap1,
Bitmap *bitmap2)
{
Bitmap *result = newBitmap(MAX(bitmap1->bitmin, bitmap2->bitmin),
MIN(bitmap1->bitmax, bitmap2->bitmax));
int32 res_elems = ARRAYELEMS(result->bitmin, result->bitmax);
int32 bit_offset1 = BITZERO(result->bitmin) - BITZERO(bitmap1->bitmin);
int32 bit_offset2 = BITZERO(result->bitmin) - BITZERO(bitmap2->bitmin);
int32 elem_offset1 = BITSET_ELEM(bit_offset1);
int32 elem_offset2 = BITSET_ELEM(bit_offset2);
int32 elem_max1 = BITSET_ELEM((bitmap1->bitmax -
BITZERO(bitmap1->bitmin)));
int32 elem_max2 = BITSET_ELEM((bitmap2->bitmax -
BITZERO(bitmap2->bitmin)));
bm_int elem;
int32 to;
int32 from;
for (to = 0; to < res_elems; to++) {
elem = 0;
from = to + elem_offset1;
if ((from >= 0) && (from <= elem_max1)) {
elem = bitmap1->bitset[from];
from = to + elem_offset2;
if ((from >= 0) && (from <= elem_max2)) {
elem &= bitmap2->bitset[from];
}
else {
elem = 0;
}
}
result->bitset[to] = elem;
}
reduceBitmap(result);
return result;
}
/**
* Create the subtraction of two bitmaps.
*
* @param bitmap1 The ::Bitmap from which bitmap2 will be subtracted
* @param bitmap2 The ::Bitmap to be subtracted from from bitmap1.
*
* @return A newly allocated bitmap which is the subtraction
*/
static Bitmap *
bitmapMinus(Bitmap *bitmap1,
Bitmap *bitmap2)
{
Bitmap *result = bitmapCopy(bitmap1);
bool found = !bitmapEmpty(bitmap2);
int32 bit = MAX(bitmap1->bitmin, bitmap2->bitmin);
/* Now clear any bits that match from bitmap2 */
while (found && (bit <= bitmap1->bitmax)) {
doClearBit(result, bit);
bit++;
bit = bitmapNextBit(bitmap2, bit, &found);
}
reduceBitmap(result);
return result;
}
/*
* Serialisation functions follow
**********************************************************************
*/
/**
* Serialise an int32 value as a base64 stream (truncated to save a
* byte) into *p_stream.
*
* @param p_stream Pointer into stream currently being written. This
* must be large enought to take the contents to be written. This
* pointer is updated to point to the next free slot in the stream after
* writing the int32 value, and is null terminated at that position.
* @param value The value to be written to the stream.
*/
static void
serialise_int32(char **p_stream, int32 value)
{
int32 len = b64_encode((char *) &value, sizeof(int32), *p_stream);
(*p_stream) += (len - 1); /* X: dumb optimisation saves a byte */
(**p_stream) = '\0';
}
/**
* De-serialise an int32 value from a base64 character stream.
*
* @param p_stream Pointer into the stream currently being read.
* must be large enought to take the contents to be written. This
* pointer is updated to point to the next free slot in the stream after
* reading the int32 value.
* @return the int32 value read from the stream
*/
static int32
deserialise_int32(char **p_stream)
{
int32 value;
char *endpos = (*p_stream) + INT32SIZE_B64;
char endchar = *endpos;
*endpos = '='; /* deal with dumb optimisation (X) above */
b64_decode(*p_stream, INT32SIZE_B64 + 1, (char *) &value);
*endpos = endchar;
(*p_stream) += INT32SIZE_B64;
return value;
}
/**
* Serialise a binary stream as a base64 stream into *p_stream.
*
* @param p_stream Pointer into stream currently being written. This
* pointer is updated to point to the next free slot in the stream after
* writing the contents of instream and is null terminated at that
* position.
*
* @param bytes The number of bytes to be written.
* @param instream The binary stream to be written.
*/
static void
serialise_stream(char **p_stream, int32 bytes, char *instream)
{
int32 len = b64_encode(instream, bytes, *p_stream);
(*p_stream)[len] = '\0';
(*p_stream) += len;
}
/**
* Return the length of a base64 encoded stream for a binary stream of
* ::bytes length.
*
* @param bytes The length of the input binary stream in bytes
* @return The length of the base64 character stream required to
* represent the input stream.
*/
static int
streamlen(int32 bytes)
{
return (4 * ((bytes + 2) / 3));
}
/**
* De-serialise a binary stream.
*
* @param p_stream Pointer into the stream currently being read.
* pointer is updated to point to the next free slot in the stream after
* reading the stream.
* @param bytes The number of bytes to be read
* @param outstream Pointer into the pre-allocated memory are into which
* the binary from p_stream is to be written.
*/
static void
deserialise_stream(char **p_stream, int32 bytes, char *outstream)
{
int32 len = streamlen(bytes);
b64_decode(*p_stream, len, outstream);
(*p_stream) += len;
}
/**
* Serialise a bitmap.
*
* @param bitmap The bitmap to be serialised.
*/
static char *
serialise_bitmap(Bitmap *bitmap)
{
int32 elems = ARRAYELEMS(bitmap->bitmin, bitmap->bitmax);
int32 stream_len = (INT32SIZE_B64 * 2) +
streamlen(sizeof(bm_int) * elems) + 1;
char *stream = palloc(stream_len * sizeof(char));
char *streamstart = stream;
serialise_int32(&stream, bitmap->bitmin);
serialise_int32(&stream, bitmap->bitmax);
serialise_stream(&stream, elems * sizeof(bm_int),
(char *) &(bitmap->bitset));
*stream = '\0';
/* Ensure bitmap is valid. */
if (bitmap->bitmin != bitmap->bitmax) {
if (! (bitmapTestbit(bitmap, bitmap->bitmin) &&
bitmapTestbit(bitmap, bitmap->bitmax)))
{
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("Corrupted bitmap"),
errdetail("one of bitmin or bitmax is not set.")));
}
}
return streamstart;
}
/**
* De-serialise a bitmap.
*
* @param charstream The serialised character string containing the bitmap.
*/
static Bitmap *
deserialise_bitmap(char *charstream)
{
Bitmap *bitmap;
char **p_stream = &charstream;
int32 elems;
int32 bitmin;
int32 bitmax;
if (strcmp(charstream, "[]") == 0) {
bitmap = newBitmap(0, 0);
clearBitmap(bitmap);
}
else {
bitmin = deserialise_int32(p_stream);
bitmax = deserialise_int32(p_stream);
bitmap = newBitmap(bitmin, bitmax);
elems = ARRAYELEMS(bitmin, bitmax);
deserialise_stream(p_stream, elems * sizeof(bitmap->bitset[0]),
(char *) &(bitmap->bitset[0]));
}
return bitmap;
}
/**
* Compare 2 bitmaps for indexing/sorting purposes
*
* @param bitmap1 The first ::Bitmap to be compared
* @param bitmap2 The second ::Bitmap to be compared
*
* @return < 0, 0, or > 0 like strcmp
*/
static int
bitmapCmp(Bitmap *bitmap1,
Bitmap *bitmap2)
{
if (bitmapEqual(bitmap1, bitmap2)) {
return 0;
}
else {
char *s1 = serialise_bitmap(bitmap1);
char *s2 = serialise_bitmap(bitmap2);
int result = strcmp(s1, s2);
pfree(s1);
pfree(s2);
return result;
}
}
/*
* Interface functions follow
**********************************************************************
*/
PG_FUNCTION_INFO_V1(bitmap_in);
/**
* bitmap_in(serialised_bitmap text) returns bitmap
* Create a Bitmap initialised from a (base64) text value.
*
* @param fcinfo Params as described_below
*
serialised_bitmap text
A base64 serialiastion of a
* bitmap value.
* @return Bitmap
the newly created bitmap
*/
Datum
bitmap_in(PG_FUNCTION_ARGS)
{
char *stream;
Bitmap *bitmap;
stream = PG_GETARG_CSTRING(0);
bitmap = deserialise_bitmap(stream);
PG_RETURN_BITMAP(bitmap);
}
PG_FUNCTION_INFO_V1(bitmap_out);
/**
* bitmap_out(bitmap bitmap) returns text
* Create a (base64) text representation of a bitmap.
*
* @param fcinfo Params as described_below
*
bitmap bitmap
A base64 serialiastion of a
* bitmap value.
* @return cstring
the newly serialised text stream.
*/
Datum
bitmap_out(PG_FUNCTION_ARGS)
{
char *result;
Bitmap *bitmap;
bitmap = PG_GETARG_BITMAP(0);
result = serialise_bitmap(bitmap);
PG_RETURN_CSTRING(result);
}
PG_FUNCTION_INFO_V1(bitmap_is_empty);
/**
* bitmap_is_empty(bitmap bitmap) returns boolean
* Predicate to identify whether a bitmap is empty or not.
*
* @param fcinfo Params as described_below
*
bitmap bitmap
The bitmap being examined.
* bitmap value.
* @return boolean
true, if the bitmap has no bits.
*/
Datum
bitmap_is_empty(PG_FUNCTION_ARGS)
{
Bitmap *bitmap;
bitmap = PG_GETARG_BITMAP(0);
PG_RETURN_BOOL(bitmapEmpty(bitmap));
}
PG_FUNCTION_INFO_V1(bitmap_bitmin);
/**
* bitmap_bitmin(bitmap bitmap) returns boolean
* Return the lowest bit set in the bitmap, or NULL if none are set.
* This relies on bitmap->bitmin always identifying the lowest numbered
* bit in the bitset, unless the bitmap is empty.
*
* @param fcinfo Params as described_below
*
bitmap bitmap
The bitmap being examined.
* @return integer
The lowest bit set in the bitmap or
* NULL if there are no bits set.
*/
Datum
bitmap_bitmin(PG_FUNCTION_ARGS)
{
Bitmap *bitmap = PG_GETARG_BITMAP(0);
int32 relative_bit = bitmap->bitmin - BITZERO(bitmap->bitmin);
if (bitmap->bitset[0] & bitmasks[relative_bit]) {
PG_RETURN_INT32(bitmap->bitmin);
}
if (bitmap->bitmin != bitmap->bitmax) {
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("Corrupted bitmap"),
errdetail("bitzeo is incorrect for bitmap (range %d..%d).",
bitmap->bitmin, bitmap->bitmax)));
}
PG_RETURN_NULL();
}
PG_FUNCTION_INFO_V1(bitmap_bitmax);
/**
* bitmap_bitmax(bitmap bitmap) returns boolean
* Return the highest bit set in the bitmap, or NULL if none are set.
* This relies on bitmap->bitmax always identifying the highest numbered
* bit in the bitset, unless the bitmap is empty.
*
* @param fcinfo Params as described_below
*
bitmap bitmap
The bitmap being examined.
* @return integer
The lowest bit set in the bitmap or
* NULL if there are no bits set.
*/
Datum
bitmap_bitmax(PG_FUNCTION_ARGS)
{
Bitmap *bitmap = PG_GETARG_BITMAP(0);
int32 relative_bit = bitmap->bitmax - BITZERO(bitmap->bitmin);
int32 elem = BITSET_ELEM(relative_bit);
if (bitmap->bitset[elem] & bitmasks[BITSET_BIT(relative_bit)]) {
PG_RETURN_INT32(bitmap->bitmax);
}
if (bitmap->bitmin != bitmap->bitmax) {
ereport(ERROR,
(errcode(ERRCODE_INTERNAL_ERROR),
errmsg("Corrupted bitmap"),
errdetail("bitmax is incorrect for bitmap (range %d..%d).",
bitmap->bitmin, bitmap->bitmax)));
}
PG_RETURN_NULL();
}
PG_FUNCTION_INFO_V1(bitmap_bits);
/**
* bitmap_bits(name text)
returns setof int4
* Return the set of all bits set in the specified Bitmap.
*
* @param fcinfo name text
The name of the bitmap.
* @return setof int4
The set of bits that are set in the
* bitmap.
*/
Datum
bitmap_bits(PG_FUNCTION_ARGS)
{
struct bitmap_bits_state {
Bitmap *bitmap;
int32 bit;
} *state;
FuncCallContext *funcctx;
MemoryContext oldcontext;
bool found;
Datum datum;
if (SRF_IS_FIRSTCALL())
{
funcctx = SRF_FIRSTCALL_INIT();
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
state = palloc(sizeof(struct bitmap_bits_state));
MemoryContextSwitchTo(oldcontext);
state->bitmap = PG_GETARG_BITMAP(0);
state->bit = state->bitmap->bitmin;
funcctx->user_fctx = state;
}
funcctx = SRF_PERCALL_SETUP();
state = funcctx->user_fctx;
state->bit = bitmapNextBit(state->bitmap, state->bit, &found);
if (found) {
datum = Int32GetDatum(state->bit);
state->bit++;
SRF_RETURN_NEXT(funcctx, datum);
}
else {
SRF_RETURN_DONE(funcctx);
}
}
PG_FUNCTION_INFO_V1(bitmap_new_empty);
/**
* bitmap_new_empty() returns bitmap;
* Create an empty Bitmap, for dealing with a named range of
* values.
*
* @param fcinfo Params as described_below
* @return Bitmap
the newly allocated bitmap
*/
Datum
bitmap_new_empty(PG_FUNCTION_ARGS)
{
Bitmap *bitmap;
bitmap = newBitmap(0, 0);
clearBitmap(bitmap);
PG_RETURN_BITMAP(bitmap);
}
PG_FUNCTION_INFO_V1(bitmap_new);
/**
* bitmap_new() returns bitmap;
* Create or re-initialise a Bitmap, for dealing with a named range of
* values.
*
* @param fcinfo Params as described_below
*
bitno int32
The first integer value to be recorded in
* the bitmap
* @return Bitmap
the newly allocated bitmap
*/
Datum
bitmap_new(PG_FUNCTION_ARGS)
{
Bitmap *bitmap;
int32 bit;
if (PG_ARGISNULL(0)) {
PG_RETURN_NULL();
}
bit = PG_GETARG_INT32(0);
bitmap = newBitmap(bit, bit);
clearBitmap(bitmap);
doSetBit(bitmap, bit);
PG_RETURN_BITMAP(bitmap);
}
PG_FUNCTION_INFO_V1(bitmap_setbit);
/**
* bitmap_setbit(bitmap bitmap, bit int4) returns bool
* Set the given bit in the bitmap, returning TRUE. This can be used as
* an aggregate function in which case the bitmap parameter will be null
* for the first call. In this case simply create a bitmap from the
* second argument.
*
* @param fcinfo Params as described_below
*
bitmap bitmap
The bitmap to be manipulated.
*
bitno int4
The bitnumber to be set.
* @return bitmap
The result.
*/
Datum
bitmap_setbit(PG_FUNCTION_ARGS)
{
Bitmap *bitmap;
int32 bitno;
Bitmap *result;
if (PG_ARGISNULL(0)) {
if (PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitno = PG_GETARG_INT32(1);
result = newBitmap(bitno, bitno);
clearBitmap(result);
doSetBit(result, bitno);
}
else {
bitno = PG_GETARG_INT32(1);
bitmap = PG_GETARG_BITMAP(0);
if (PG_ARGISNULL(1)) {
result = bitmapCopy(bitmap);
}
else {
result = bitmapSetbit(bitmap, bitno);
}
}
PG_RETURN_BITMAP(result);
}
PG_FUNCTION_INFO_V1(bitmap_testbit);
/**
* bitmap_testbit(bitmap bitmap, bit int4) returns bool
* Test the given bit in the bitmap, returning TRUE if it is 1.
*
* @param fcinfo Params as described_below
*
bitmap bitmap
The bitmap to be manipulated.
*
bitno int4
The bitnumber to be tested.
* @return bool
the truth value of the bit.
*/
Datum
bitmap_testbit(PG_FUNCTION_ARGS)
{
Bitmap *bitmap;
int32 bitno;
bool result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap = PG_GETARG_BITMAP(0);
bitno = PG_GETARG_INT32(1);
result = bitmapTestbit(bitmap, bitno);
PG_RETURN_BOOL(result);
}
PG_FUNCTION_INFO_V1(bitmap_setmin);
/**
* bitmap_setmin(bitmap bitmap, bitmin int4) returns bitmap
* Return a new bitmap having no bits less than bitmin
*
* @param fcinfo Params as described_below
*
bitmap bitmap
The bitmap to be manipulated.
*
bitmin int4
The new minimum bit.
* @return bitmap
The new bitmap.
*/
Datum
bitmap_setmin(PG_FUNCTION_ARGS)
{
Bitmap *bitmap;
int32 bitmin;
Bitmap *result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap = PG_GETARG_BITMAP(0);
bitmin = PG_GETARG_INT32(1);
result = bitmapCopy(bitmap);
result = bitmapSetMin(result, bitmin);
PG_RETURN_BITMAP(result);
}
PG_FUNCTION_INFO_V1(bitmap_setmax);
/**
* bitmap_setmax(bitmap bitmap, bitmax int4) returns bitmap
* Return a new bitmap having no bits greater than bitmax
*
* @param fcinfo Params as described_below
*
bitmap bitmap
The bitmap to be manipulated.
*
bitmax int4
The new maximum bit.
* @return bitmap
The new bitmap.
*/
Datum
bitmap_setmax(PG_FUNCTION_ARGS)
{
Bitmap *bitmap;
int32 bitmax;
Bitmap *result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap = PG_GETARG_BITMAP(0);
bitmax = PG_GETARG_INT32(1);
result = bitmapCopy(bitmap);
result = bitmapSetMax(result, bitmax);
PG_RETURN_BITMAP(result);
}
PG_FUNCTION_INFO_V1(bitmap_equal);
/**
* bitmap_equal(bitmap1 bitmap, bitmap2 bitmap) returns bool
* Return true if the bitmaps are equivalent,
*
* @param fcinfo Params as described_below
*
bitmap1 bitmap
The first bitmap
*
bitmap2 bitmap
The second bitmap
* @return bool
true if the bitmaps contain the same bits.
*/
Datum
bitmap_equal(PG_FUNCTION_ARGS)
{
Bitmap *bitmap1;
Bitmap *bitmap2;
bool result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap1 = PG_GETARG_BITMAP(0);
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapEqual(bitmap1, bitmap2);
PG_RETURN_BOOL(result);
}
PG_FUNCTION_INFO_V1(bitmap_nequal);
/**
* bitmap_nequal(bitmap1 bitmap, bitmap2 bitmap) returns bool
* Return true if the bitmaps are not equivalent,
*
* @param fcinfo Params as described_below
*
bitmap1 bitmap
The first bitmap
*
bitmap2 bitmap
The second bitmap
* @return bool
true unless the bitmaps contain the same bits.
*/
Datum
bitmap_nequal(PG_FUNCTION_ARGS)
{
Bitmap *bitmap1;
Bitmap *bitmap2;
bool result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap1 = PG_GETARG_BITMAP(0);
bitmap2 = PG_GETARG_BITMAP(1);
result = !bitmapEqual(bitmap1, bitmap2);
PG_RETURN_BOOL(result);
}
PG_FUNCTION_INFO_V1(bitmap_cmp);
/**
* bitmap_cmp(bitmap1 bitmap, bitmap2 bitmap) returns bool
* Return result of comparison of bitmap1's string representation with
* bitmap2's.
*
* @param fcinfo Params as described_below
*
bitmap1 bitmap
The first bitmap
*
bitmap2 bitmap
The second bitmap
* @return int4
result of comparison
*/
Datum
bitmap_cmp(PG_FUNCTION_ARGS)
{
Bitmap *bitmap1;
Bitmap *bitmap2;
int32 result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap1 = PG_GETARG_BITMAP(0);
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapCmp(bitmap1, bitmap2);
PG_RETURN_INT32(result);
}
PG_FUNCTION_INFO_V1(bitmap_lt);
/**
* bitmap_lt(bitmap1 bitmap, bitmap2 bitmap) returns bool
* Return true if bitmap1's string representation should be sorted
* earlier than bitmap2's.
*
* @param fcinfo Params as described_below
*
bitmap1 bitmap
The first bitmap
*
bitmap2 bitmap
The second bitmap
* @return bool
true unless the bitmaps contain the same bits.
*/
Datum
bitmap_lt(PG_FUNCTION_ARGS)
{
Bitmap *bitmap1;
Bitmap *bitmap2;
bool result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap1 = PG_GETARG_BITMAP(0);
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapCmp(bitmap1, bitmap2) < 0;
PG_RETURN_BOOL(result);
}
PG_FUNCTION_INFO_V1(bitmap_le);
/**
* bitmap_le(bitmap1 bitmap, bitmap2 bitmap) returns bool
* Return true if bitmap1's string representation should be sorted
* earlier than, or the same as, bitmap2's.
*
* @param fcinfo Params as described_below
*
bitmap1 bitmap
The first bitmap
*
bitmap2 bitmap
The second bitmap
* @return bool
true unless the bitmaps contain the same bits.
*/
Datum
bitmap_le(PG_FUNCTION_ARGS)
{
Bitmap *bitmap1;
Bitmap *bitmap2;
bool result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap1 = PG_GETARG_BITMAP(0);
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapCmp(bitmap1, bitmap2) <= 0;
PG_RETURN_BOOL(result);
}
PG_FUNCTION_INFO_V1(bitmap_gt);
/**
* bitmap_gt(bitmap1 bitmap, bitmap2 bitmap) returns bool
* Return true if bitmap1's string representation should be sorted
* later than bitmap2's.
*
* @param fcinfo Params as described_below
*
bitmap1 bitmap
The first bitmap
*
bitmap2 bitmap
The second bitmap
* @return bool
true unless the bitmaps contain the same bits.
*/
Datum
bitmap_gt(PG_FUNCTION_ARGS)
{
Bitmap *bitmap1;
Bitmap *bitmap2;
bool result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap1 = PG_GETARG_BITMAP(0);
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapCmp(bitmap1, bitmap2) > 0;
PG_RETURN_BOOL(result);
}
PG_FUNCTION_INFO_V1(bitmap_ge);
/**
* bitmap_ge(bitmap1 bitmap, bitmap2 bitmap) returns bool
* Return true if bitmap1's string representation should be sorted
* later than, or the same as, bitmap2's.
*
* @param fcinfo Params as described_below
*
bitmap1 bitmap
The first bitmap
*
bitmap2 bitmap
The second bitmap
* @return bool
true unless the bitmaps contain the same bits.
*/
Datum
bitmap_ge(PG_FUNCTION_ARGS)
{
Bitmap *bitmap1;
Bitmap *bitmap2;
bool result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap1 = PG_GETARG_BITMAP(0);
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapCmp(bitmap1, bitmap2) >= 0;
PG_RETURN_BOOL(result);
}
PG_FUNCTION_INFO_V1(bitmap_union);
/**
* bitmap_union(bitmap1 bitmap, bitmap2 bitmap) returns bitmap
* Return the union of 2 bitmaps. This can be used as an aggregate
* function in which case the bitmap1 parameter will be null for the
* first call. In this case simply return the second argument.
*
* @param fcinfo Params as described_below
*
bitmap1 bitmap
The first bitmap
*
bitmap2 bitmap
The second bitmap
* @return bitmap
the union of the two bitmaps.
*/
Datum
bitmap_union(PG_FUNCTION_ARGS)
{
Bitmap *bitmap1;
Bitmap *bitmap2;
Bitmap *result;
if (PG_ARGISNULL(0)) {
if (PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
else {
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapCopy(bitmap2);
}
}
else {
bitmap1 = PG_GETARG_BITMAP(0);
if (PG_ARGISNULL(1)) {
result = bitmapCopy(bitmap1);
}
else {
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapUnion(bitmap1, bitmap2);
}
}
PG_RETURN_BITMAP(result);
}
PG_FUNCTION_INFO_V1(bitmap_clearbit);
/**
* bitmap_clearbit(bitmap bitmap, bit int4) returns bool
* Clear the given bit in the bitmap, returning FALSE.
*
* @param fcinfo Params as described_below
*
bitmap bitmap
The bitmap to be manipulated.
*
bitno int4
The bitnumber to be set.
* @return bool
TRUE
*/
Datum
bitmap_clearbit(PG_FUNCTION_ARGS)
{
Bitmap *bitmap;
int32 bitno;
Bitmap *result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap = PG_GETARG_BITMAP(0);
bitno = PG_GETARG_INT32(1);
result = bitmapCopy(bitmap);
result = bitmapClearbit(result, bitno);
PG_RETURN_BITMAP(result);
}
PG_FUNCTION_INFO_V1(bitmap_intersection);
/**
* bitmap_intersection(bitmap1 bitmap, bitmap2 bitmap)
* returns bitmap
* Return the intersection of 2 bitmaps. This can be used as an
* aggregate function in which case the bitmap1 parameter will be null
* for the first call. In this case simply return the second argument.
*
* @param fcinfo Params as described_below
*
bitmap1 bitmap
The first bitmap
*
bitmap2 bitmap
The second bitmap
* @return bitmap
the intersection of the two bitmaps.
*/
Datum
bitmap_intersection(PG_FUNCTION_ARGS)
{
Bitmap *bitmap1;
Bitmap *bitmap2;
Bitmap *result;
if (PG_ARGISNULL(0)) {
if (PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapCopy(bitmap2);
}
else {
bitmap1 = PG_GETARG_BITMAP(0);
if (PG_ARGISNULL(1)) {
result = bitmapCopy(bitmap1);
}
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapIntersect(bitmap1, bitmap2);
}
PG_RETURN_BITMAP(result);
}
PG_FUNCTION_INFO_V1(bitmap_minus);
/**
* bitmap_minus(bitmap1 bitmap, bitmap2 bitmap)
* returns bitmap
* Return the bitmap1 with all bits from bitmap2 subtracted (cleared)
* from it.
*
* @param fcinfo Params as described_below
*
bitmap1 bitmap
The first bitmap
*
bitmap2 bitmap
The second bitmap
* @return bitmap
the subtraction of the two bitmaps.
*/
Datum
bitmap_minus(PG_FUNCTION_ARGS)
{
Bitmap *bitmap1;
Bitmap *bitmap2;
Bitmap *result;
if (PG_ARGISNULL(0) || PG_ARGISNULL(1)) {
PG_RETURN_NULL();
}
bitmap1 = PG_GETARG_BITMAP(0);
bitmap2 = PG_GETARG_BITMAP(1);
result = bitmapMinus(bitmap1, bitmap2);
PG_RETURN_BITMAP(result);
}