*
* A bitmap set can represent any set of nonnegative integers, although
* it is mainly intended for sets where the maximum value is not large,
- * say at most a few hundred. By convention, a NULL pointer is always
- * accepted by all operations to represent the empty set. (But beware
- * that this is not the only representation of the empty set. Use
- * bms_is_empty() in preference to testing for NULL.)
+ * say at most a few hundred. By convention, we always represent the
+ * empty set by a NULL pointer.
*
*
* Copyright (c) 2003-2023, PostgreSQL Global Development Group
#error "invalid BITS_PER_BITMAPWORD"
#endif
+static bool bms_is_empty_internal(const Bitmapset *a);
+
/*
* bms_copy - make a palloc'd copy of a bitmapset
{
if (b == NULL)
return true;
- return bms_is_empty(b);
+ return false;
}
else if (b == NULL)
- return bms_is_empty(a);
+ return false;
/* Identify shorter and longer input */
if (a->nwords <= b->nwords)
{
/* Handle cases where either input is NULL */
if (a == NULL)
- return bms_is_empty(b) ? 0 : -1;
+ return (b == NULL) ? 0 : -1;
else if (b == NULL)
- return bms_is_empty(a) ? 0 : +1;
+ return +1;
/* Handle cases where one input is longer than the other */
shortlen = Min(a->nwords, b->nwords);
for (i = shortlen; i < a->nwords; i++)
resultlen = result->nwords;
for (i = 0; i < resultlen; i++)
result->words[i] &= other->words[i];
+ /* If we computed an empty result, we must return NULL */
+ if (bms_is_empty_internal(result))
+ {
+ pfree(result);
+ return NULL;
+ }
return result;
}
return NULL;
if (b == NULL)
return bms_copy(a);
+
+ /*
+ * In Postgres' usage, an empty result is a very common case, so it's
+ * worth optimizing for that by testing bms_nonempty_difference(). This
+ * saves us a palloc/pfree cycle compared to checking after-the-fact.
+ */
+ if (!bms_nonempty_difference(a, b))
+ return NULL;
+
/* Copy the left input */
result = bms_copy(a);
/* And remove b's bits from result */
shortlen = Min(a->nwords, b->nwords);
for (i = 0; i < shortlen; i++)
result->words[i] &= ~b->words[i];
+ /* Need not check for empty result, since we handled that case above */
return result;
}
if (a == NULL)
return true; /* empty set is a subset of anything */
if (b == NULL)
- return bms_is_empty(a);
+ return false;
/* Check common words */
shortlen = Min(a->nwords, b->nwords);
for (i = 0; i < shortlen; i++)
{
if (b == NULL)
return BMS_EQUAL;
- return bms_is_empty(b) ? BMS_EQUAL : BMS_SUBSET1;
+ return BMS_SUBSET1;
}
if (b == NULL)
- return bms_is_empty(a) ? BMS_EQUAL : BMS_SUBSET2;
+ return BMS_SUBSET2;
/* Check common words */
result = BMS_EQUAL; /* status so far */
shortlen = Min(a->nwords, b->nwords);
if (a == NULL)
return false;
if (b == NULL)
- return !bms_is_empty(a);
+ return true;
/* Check words in common */
shortlen = Min(a->nwords, b->nwords);
for (i = 0; i < shortlen; i++)
}
/*
- * bms_is_empty - is a set empty?
+ * bms_is_empty_internal - is a set empty?
*
- * This is even faster than bms_membership().
+ * This is now used only locally, to detect cases where a function has
+ * computed an empty set that we must now get rid of. Hence, we can
+ * assume the input isn't NULL.
*/
-bool
-bms_is_empty(const Bitmapset *a)
+static bool
+bms_is_empty_internal(const Bitmapset *a)
{
int nwords;
int wordnum;
- if (a == NULL)
- return true;
nwords = a->nwords;
for (wordnum = 0; wordnum < nwords; wordnum++)
{
bitnum = BITNUM(x);
if (wordnum < a->nwords)
a->words[wordnum] &= ~((bitmapword) 1 << bitnum);
+ /* If we computed an empty result, we must return NULL */
+ if (bms_is_empty_internal(a))
+ {
+ pfree(a);
+ return NULL;
+ }
return a;
}
a->words[i] &= b->words[i];
for (; i < a->nwords; i++)
a->words[i] = 0;
+ /* If we computed an empty result, we must return NULL */
+ if (bms_is_empty_internal(a))
+ {
+ pfree(a);
+ return NULL;
+ }
return a;
}
shortlen = Min(a->nwords, b->nwords);
for (i = 0; i < shortlen; i++)
a->words[i] &= ~b->words[i];
+ /* If we computed an empty result, we must return NULL */
+ if (bms_is_empty_internal(a))
+ {
+ pfree(a);
+ return NULL;
+ }
return a;
}
*
* A bitmap set can represent any set of nonnegative integers, although
* it is mainly intended for sets where the maximum value is not large,
- * say at most a few hundred. By convention, a NULL pointer is always
- * accepted by all operations to represent the empty set. (But beware
- * that this is not the only representation of the empty set. Use
- * bms_is_empty() in preference to testing for NULL.)
+ * say at most a few hundred. By convention, we always represent the
+ * empty set by a NULL pointer.
*
*
* Copyright (c) 2003-2023, PostgreSQL Global Development Group
/* optimized tests when we don't need to know exact membership count: */
extern BMS_Membership bms_membership(const Bitmapset *a);
-extern bool bms_is_empty(const Bitmapset *a);
+
+/* NULL is now the only allowed representation of an empty bitmapset */
+#define bms_is_empty(a) ((a) == NULL)
/* these routines recycle (modify or free) their non-const inputs: */