From 5a18e880fb53ee5b5a0be092b1cb796636dbb60c Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Sun, 19 Nov 2023 01:03:12 +0200 Subject: [PATCH 1/2] REALLOCATE_BITMAPSETS manual compile-time option This option forces each bitmapset modification to reallocate bitmapset. This is useful for debugging hangling pointers to bitmapset's. Discussion: https://postgr.es/m/CAMbWs4_wJthNtYBL+SsebpgF-5L2r5zFFk6xYbS0A78GKOTFHw@mail.gmail.com --- src/backend/nodes/bitmapset.c | 146 +++++++++++++++++++++++++++++---- src/include/pg_config_manual.h | 6 ++ 2 files changed, 138 insertions(+), 14 deletions(-) diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c index 704879f5660..8be3e3e2c5e 100644 --- a/src/backend/nodes/bitmapset.c +++ b/src/backend/nodes/bitmapset.c @@ -84,6 +84,7 @@ bms_copy(const Bitmapset *a) if (a == NULL) return NULL; + Assert(IsA(a, Bitmapset)); size = BITMAPSET_SIZE(a->nwords); result = (Bitmapset *) palloc(size); memcpy(result, a, size); @@ -98,8 +99,8 @@ bms_equal(const Bitmapset *a, const Bitmapset *b) { int i; - Assert(a == NULL || a->words[a->nwords - 1] != 0); - Assert(b == NULL || b->words[b->nwords - 1] != 0); + Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0)); + Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0)); /* Handle cases where either input is NULL */ if (a == NULL) @@ -139,8 +140,8 @@ bms_compare(const Bitmapset *a, const Bitmapset *b) { int i; - Assert(a == NULL || a->words[a->nwords - 1] != 0); - Assert(b == NULL || b->words[b->nwords - 1] != 0); + Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0)); + Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0)); /* Handle cases where either input is NULL */ if (a == NULL) @@ -215,6 +216,9 @@ bms_union(const Bitmapset *a, const Bitmapset *b) int otherlen; int i; + Assert(a == NULL || IsA(a, Bitmapset)); + Assert(b == NULL || IsA(b, Bitmapset)); + /* Handle cases where either input is NULL */ if (a == NULL) return bms_copy(b); @@ -253,9 +257,13 @@ bms_intersect(const Bitmapset *a, const Bitmapset *b) int resultlen; int i; + Assert(a == NULL || IsA(a, Bitmapset)); + Assert(b == NULL || IsA(b, Bitmapset)); + /* Handle cases where either input is NULL */ if (a == NULL || b == NULL) return NULL; + /* Identify shorter and longer input; copy the shorter one */ if (a->nwords <= b->nwords) { @@ -299,8 +307,8 @@ bms_difference(const Bitmapset *a, const Bitmapset *b) Bitmapset *result; int i; - Assert(a == NULL || a->words[a->nwords - 1] != 0); - Assert(b == NULL || b->words[b->nwords - 1] != 0); + Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0)); + Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0)); /* Handle cases where either input is NULL */ if (a == NULL) @@ -308,6 +316,8 @@ bms_difference(const Bitmapset *a, const Bitmapset *b) if (b == NULL) return bms_copy(a); + Assert(IsA(a, Bitmapset) && IsA(b, Bitmapset)); + /* * In Postgres' usage, an empty result is a very common case, so it's * worth optimizing for that by testing bms_nonempty_difference(). This @@ -364,8 +374,8 @@ bms_is_subset(const Bitmapset *a, const Bitmapset *b) { int i; - Assert(a == NULL || a->words[a->nwords - 1] != 0); - Assert(b == NULL || b->words[b->nwords - 1] != 0); + Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0)); + Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0)); /* Handle cases where either input is NULL */ if (a == NULL) @@ -373,6 +383,8 @@ bms_is_subset(const Bitmapset *a, const Bitmapset *b) if (b == NULL) return false; + Assert(IsA(a, Bitmapset) && IsA(b, Bitmapset)); + /* 'a' can't be a subset of 'b' if it contains more words */ if (a->nwords > b->nwords) return false; @@ -399,8 +411,8 @@ bms_subset_compare(const Bitmapset *a, const Bitmapset *b) int shortlen; int i; - Assert(a == NULL || a->words[a->nwords - 1] != 0); - Assert(b == NULL || b->words[b->nwords - 1] != 0); + Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0)); + Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0)); /* Handle cases where either input is NULL */ if (a == NULL) @@ -411,6 +423,9 @@ bms_subset_compare(const Bitmapset *a, const Bitmapset *b) } if (b == NULL) return BMS_SUBSET2; + + Assert(IsA(a, Bitmapset) && IsA(b, Bitmapset)); + /* Check common words */ result = BMS_EQUAL; /* status so far */ shortlen = Min(a->nwords, b->nwords); @@ -467,6 +482,9 @@ bms_is_member(int x, const Bitmapset *a) elog(ERROR, "negative bitmapset member not allowed"); if (a == NULL) return false; + + Assert(IsA(a, Bitmapset)); + wordnum = WORDNUM(x); bitnum = BITNUM(x); if (wordnum >= a->nwords) @@ -495,6 +513,8 @@ bms_member_index(Bitmapset *a, int x) if (!bms_is_member(x, a)) return -1; + Assert(IsA(a, Bitmapset)); + wordnum = WORDNUM(x); bitnum = BITNUM(x); @@ -529,6 +549,9 @@ bms_overlap(const Bitmapset *a, const Bitmapset *b) int shortlen; int i; + Assert(a == NULL || IsA(a, Bitmapset)); + Assert(b == NULL || IsA(b, Bitmapset)); + /* Handle cases where either input is NULL */ if (a == NULL || b == NULL) return false; @@ -553,6 +576,8 @@ bms_overlap_list(const Bitmapset *a, const List *b) int wordnum, bitnum; + Assert(a == NULL || IsA(a, Bitmapset)); + if (a == NULL || b == NIL) return false; @@ -582,8 +607,8 @@ bms_nonempty_difference(const Bitmapset *a, const Bitmapset *b) { int i; - Assert(a == NULL || a->words[a->nwords - 1] != 0); - Assert(b == NULL || b->words[b->nwords - 1] != 0); + Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0)); + Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0)); /* Handle cases where either input is NULL */ if (a == NULL) @@ -617,6 +642,9 @@ bms_singleton_member(const Bitmapset *a) if (a == NULL) elog(ERROR, "bitmapset is empty"); + + Assert(IsA(a, Bitmapset)); + nwords = a->nwords; wordnum = 0; do @@ -657,6 +685,7 @@ bms_get_singleton_member(const Bitmapset *a, int *member) if (a == NULL) return false; + Assert(IsA(a, Bitmapset)); nwords = a->nwords; wordnum = 0; do @@ -690,6 +719,7 @@ bms_num_members(const Bitmapset *a) if (a == NULL) return 0; + Assert(IsA(a, Bitmapset)); nwords = a->nwords; wordnum = 0; do @@ -717,6 +747,7 @@ bms_membership(const Bitmapset *a) if (a == NULL) return BMS_EMPTY_SET; + Assert(IsA(a, Bitmapset)); nwords = a->nwords; wordnum = 0; do @@ -759,6 +790,7 @@ bms_add_member(Bitmapset *a, int x) elog(ERROR, "negative bitmapset member not allowed"); if (a == NULL) return bms_make_singleton(x); + Assert(IsA(a, Bitmapset)); wordnum = WORDNUM(x); bitnum = BITNUM(x); @@ -767,8 +799,15 @@ bms_add_member(Bitmapset *a, int x) { int oldnwords = a->nwords; int i; +#ifdef REALLOCATE_BITMAPSETS + Bitmapset *tmp = a; + a = (Bitmapset *) palloc(BITMAPSET_SIZE(wordnum + 1)); + memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords)); + pfree(tmp); +#else a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(wordnum + 1)); +#endif a->nwords = wordnum + 1; /* zero out the enlarged portion */ i = oldnwords; @@ -777,6 +816,16 @@ bms_add_member(Bitmapset *a, int x) a->words[i] = 0; } while (++i < a->nwords); } +#ifdef REALLOCATE_BITMAPSETS + else + { + Bitmapset *tmp = a; + + a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords)); + memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords)); + pfree(tmp); + } +#endif a->words[wordnum] |= ((bitmapword) 1 << bitnum); return a; @@ -794,14 +843,24 @@ bms_del_member(Bitmapset *a, int x) { int wordnum, bitnum; +#ifdef REALLOCATE_BITMAPSETS + Bitmapset *tmp = a; +#endif if (x < 0) elog(ERROR, "negative bitmapset member not allowed"); if (a == NULL) return NULL; + Assert(IsA(a, Bitmapset)); wordnum = WORDNUM(x); bitnum = BITNUM(x); +#ifdef REALLOCATE_BITMAPSETS + a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords)); + memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords)); + pfree(tmp); +#endif + /* member can't exist. Return 'a' unmodified */ if (unlikely(wordnum >= a->nwords)) return a; @@ -839,6 +898,9 @@ bms_add_members(Bitmapset *a, const Bitmapset *b) int otherlen; int i; + Assert(a == NULL || IsA(a, Bitmapset)); + Assert(b == NULL || IsA(b, Bitmapset)); + /* Handle cases where either input is NULL */ if (a == NULL) return bms_copy(b); @@ -852,6 +914,13 @@ bms_add_members(Bitmapset *a, const Bitmapset *b) } else { +#ifdef REALLOCATE_BITMAPSETS + Bitmapset *tmp = a; + + a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords)); + memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords)); + pfree(tmp); +#endif result = a; other = b; } @@ -884,6 +953,8 @@ bms_add_range(Bitmapset *a, int lower, int upper) ushiftbits, wordnum; + Assert(a == NULL || IsA(a, Bitmapset)); + /* do nothing if nothing is called for, without further checking */ if (upper < lower) return a; @@ -902,9 +973,16 @@ bms_add_range(Bitmapset *a, int lower, int upper) { int oldnwords = a->nwords; int i; +#ifdef REALLOCATE_BITMAPSETS + Bitmapset *tmp = a; + a = (Bitmapset *) palloc(BITMAPSET_SIZE(uwordnum + 1)); + memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords)); + pfree(tmp); +#else /* ensure we have enough words to store the upper bit */ a = (Bitmapset *) repalloc(a, BITMAPSET_SIZE(uwordnum + 1)); +#endif a->nwords = uwordnum + 1; /* zero out the enlarged portion */ i = oldnwords; @@ -953,6 +1031,12 @@ bms_int_members(Bitmapset *a, const Bitmapset *b) int lastnonzero; int shortlen; int i; +#ifdef REALLOCATE_BITMAPSETS + Bitmapset *tmp = a; +#endif + + Assert(a == NULL || IsA(a, Bitmapset)); + Assert(b == NULL || IsA(b, Bitmapset)); /* Handle cases where either input is NULL */ if (a == NULL) @@ -962,6 +1046,13 @@ bms_int_members(Bitmapset *a, const Bitmapset *b) pfree(a); return NULL; } + +#ifdef REALLOCATE_BITMAPSETS + a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords)); + memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords)); + pfree(tmp); +#endif + /* Intersect b into a; we need never copy */ shortlen = Min(a->nwords, b->nwords); lastnonzero = -1; @@ -993,15 +1084,25 @@ Bitmapset * bms_del_members(Bitmapset *a, const Bitmapset *b) { int i; +#ifdef REALLOCATE_BITMAPSETS + Bitmapset *tmp = a; +#endif - Assert(a == NULL || a->words[a->nwords - 1] != 0); - Assert(b == NULL || b->words[b->nwords - 1] != 0); + Assert(a == NULL || (IsA(a, Bitmapset) && a->words[a->nwords - 1] != 0)); + Assert(b == NULL || (IsA(b, Bitmapset) && b->words[b->nwords - 1] != 0)); /* Handle cases where either input is NULL */ if (a == NULL) return NULL; if (b == NULL) return a; + +#ifdef REALLOCATE_BITMAPSETS + a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords)); + memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords)); + pfree(tmp); +#endif + /* Remove b's bits from a; we need never copy */ if (a->nwords > b->nwords) { @@ -1054,12 +1155,25 @@ bms_join(Bitmapset *a, Bitmapset *b) Bitmapset *other; int otherlen; int i; +#ifdef REALLOCATE_BITMAPSETS + Bitmapset *tmp = a; +#endif + + Assert(a == NULL || IsA(a, Bitmapset)); + Assert(b == NULL || IsA(b, Bitmapset)); /* Handle cases where either input is NULL */ if (a == NULL) return b; if (b == NULL) return a; + +#ifdef REALLOCATE_BITMAPSETS + a = (Bitmapset *) palloc(BITMAPSET_SIZE(tmp->nwords)); + memcpy(a, tmp, BITMAPSET_SIZE(tmp->nwords)); + pfree(tmp); +#endif + /* Identify shorter and longer input; use longer one as result */ if (a->nwords < b->nwords) { @@ -1109,6 +1223,8 @@ bms_next_member(const Bitmapset *a, int prevbit) int wordnum; bitmapword mask; + Assert(a == NULL || IsA(a, Bitmapset)); + if (a == NULL) return -2; nwords = a->nwords; @@ -1168,6 +1284,8 @@ bms_prev_member(const Bitmapset *a, int prevbit) int ushiftbits; bitmapword mask; + Assert(a == NULL || IsA(a, Bitmapset)); + /* * If set is NULL or if there are no more bits to the right then we've * nothing to do. diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h index 8a6e67a445d..16c383ba7f7 100644 --- a/src/include/pg_config_manual.h +++ b/src/include/pg_config_manual.h @@ -335,6 +335,12 @@ */ /* #define COPY_PARSE_PLAN_TREES */ +/* + * Define this to force Bitmapset reallocation on each modification. Helps + * to find hangling pointers to Bitmapset's. + */ +/* #define REALLOCATE_BITMAPSETS */ + /* * Define this to force all parse and plan trees to be passed through * outfuncs.c/readfuncs.c, to facilitate catching errors and omissions in -- 2.39.3 (Apple Git-145)