Per the discussion at [1], it seems like it'd be a good idea to make
Bitmapsets into full-fledged, tagged Nodes, so that we could do things
like print or copy lists of them without special-case logic. The
extra space for the NodeTag is basically free due to alignment
considerations, at least on 64-bit hardware.
Attached is a cleaned-up version of Amit's patch v24-0003 at [2].
I fixed the problems with not always tagging Bitmapsets, and changed
the outfuncs/readfuncs logic so that Bitmapsets still print exactly
as they did before (thus, this doesn't require a catversion bump).
As proof of concept, I removed the read_write_ignore labels from
RelOptInfo's unique_for_rels and non_unique_for_rels fields, and
got nice-looking debug printout:
:unique_for_rels ((b 1))
:non_unique_for_rels <>
I also removed some special-case code from indxpath.c because
list_member() can do the same thing now. (There might be other
places that can be simplified; I didn't look very hard.)
It'd be possible to make Bitmapset fields be (mostly) not special cases
in the copy/equal/out/read support. But I chose to leave that alone,
because it'd add a little runtime overhead for indirecting through the
generic support functions while not really saving any code space.
Barring objections, I'd like to go ahead and push this.
regards, tom lane
[1] https://www.postgresql.org/message-id/94353655-c177-1f55-7afb-b2090de33341%40enterprisedb.com
[2] https://www.postgresql.org/message-id/CA%2BHiwqEYCLRZ2Boq_uK0pjLn_9b8XL-LmwKj7HN5kJOivUkYLg%40mail.gmail.com
diff --git a/src/backend/nodes/Makefile b/src/backend/nodes/Makefile
index 7450e191ee..d7b4261b47 100644
--- a/src/backend/nodes/Makefile
+++ b/src/backend/nodes/Makefile
@@ -52,6 +52,7 @@ node_headers = \
commands/trigger.h \
executor/tuptable.h \
foreign/fdwapi.h \
+ nodes/bitmapset.h \
nodes/extensible.h \
nodes/lockoptions.h \
nodes/replnodes.h \
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 0a6c30e4eb..b7b274aeff 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -194,6 +194,7 @@ bms_make_singleton(int x)
wordnum = WORDNUM(x);
bitnum = BITNUM(x);
result = (Bitmapset *) palloc0(BITMAPSET_SIZE(wordnum + 1));
+ result->type = T_Bitmapset;
result->nwords = wordnum + 1;
result->words[wordnum] = ((bitmapword) 1 << bitnum);
return result;
@@ -852,6 +853,7 @@ bms_add_range(Bitmapset *a, int lower, int upper)
if (a == NULL)
{
a = (Bitmapset *) palloc0(BITMAPSET_SIZE(uwordnum + 1));
+ a->type = T_Bitmapset;
a->nwords = uwordnum + 1;
}
else if (uwordnum >= a->nwords)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index e76fda8eba..84f5e2e6ad 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -160,6 +160,12 @@ _copyExtensibleNode(const ExtensibleNode *from)
return newnode;
}
+static Bitmapset *
+_copyBitmapset(const Bitmapset *from)
+{
+ return bms_copy(from);
+}
+
/*
* copyObjectImpl -- implementation of copyObject(); see nodes/nodes.h
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 0373aa30fe..b2f07da62e 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -145,6 +145,12 @@ _equalA_Const(const A_Const *a, const A_Const *b)
return true;
}
+static bool
+_equalBitmapset(const Bitmapset *a, const Bitmapset *b)
+{
+ return bms_equal(a, b);
+}
+
/*
* Lists are handled specially
*/
diff --git a/src/backend/nodes/gen_node_support.pl b/src/backend/nodes/gen_node_support.pl
index 81b8c184a9..d3f25299de 100644
--- a/src/backend/nodes/gen_node_support.pl
+++ b/src/backend/nodes/gen_node_support.pl
@@ -65,6 +65,7 @@ my @all_input_files = qw(
commands/trigger.h
executor/tuptable.h
foreign/fdwapi.h
+ nodes/bitmapset.h
nodes/extensible.h
nodes/lockoptions.h
nodes/replnodes.h
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 64c65f060b..f05e72f0dc 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -314,6 +314,9 @@ _outList(StringInfo str, const List *node)
* converts a bitmap set of integers
*
* Note: the output format is "(b int int ...)", similar to an integer List.
+ *
+ * We export this function for use by extensions that define extensible nodes.
+ * That's somewhat historical, though, because calling outNode() will work.
*/
void
outBitmapset(StringInfo str, const Bitmapset *bms)
@@ -844,6 +847,8 @@ outNode(StringInfo str, const void *obj)
_outString(str, (String *) obj);
else if (IsA(obj, BitString))
_outBitString(str, (BitString *) obj);
+ else if (IsA(obj, Bitmapset))
+ outBitmapset(str, (Bitmapset *) obj);
else
{
appendStringInfoChar(str, '{');
diff --git a/src/backend/nodes/read.c b/src/backend/nodes/read.c
index fe84f140ee..74d9d754d3 100644
--- a/src/backend/nodes/read.c
+++ b/src/backend/nodes/read.c
@@ -22,6 +22,7 @@
#include <ctype.h>
#include "common/string.h"
+#include "nodes/bitmapset.h"
#include "nodes/pg_list.h"
#include "nodes/readfuncs.h"
#include "nodes/value.h"
@@ -347,6 +348,7 @@ nodeRead(const char *token, int tok_len)
* Could be an integer list: (i int int ...)
* or an OID list: (o int int ...)
* or an XID list: (x int int ...)
+ * or a bitmapset: (b int int ...)
* or a list of nodes/values: (node node ...)
*----------
*/
@@ -372,6 +374,7 @@ nodeRead(const char *token, int tok_len)
tok_len, token);
l = lappend_int(l, val);
}
+ result = (Node *) l;
}
else if (tok_len == 1 && token[0] == 'o')
{
@@ -392,6 +395,7 @@ nodeRead(const char *token, int tok_len)
tok_len, token);
l = lappend_oid(l, val);
}
+ result = (Node *) l;
}
else if (tok_len == 1 && token[0] == 'x')
{
@@ -412,6 +416,30 @@ nodeRead(const char *token, int tok_len)
tok_len, token);
l = lappend_xid(l, val);
}
+ result = (Node *) l;
+ }
+ else if (tok_len == 1 && token[0] == 'b')
+ {
+ /* Bitmapset -- see also _readBitmapset() */
+ Bitmapset *bms = NULL;
+
+ for (;;)
+ {
+ int val;
+ char *endptr;
+
+ token = pg_strtok(&tok_len);
+ if (token == NULL)
+ elog(ERROR, "unterminated Bitmapset structure");
+ if (tok_len == 1 && token[0] == ')')
+ break;
+ val = (int) strtol(token, &endptr, 10);
+ if (endptr != token + tok_len)
+ elog(ERROR, "unrecognized integer: \"%.*s\"",
+ tok_len, token);
+ bms = bms_add_member(bms, val);
+ }
+ result = (Node *) bms;
}
else
{
@@ -426,8 +454,8 @@ nodeRead(const char *token, int tok_len)
if (token == NULL)
elog(ERROR, "unterminated List structure");
}
+ result = (Node *) l;
}
- result = (Node *) l;
break;
}
case RIGHT_PAREN:
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index b4ff855f7c..23776367c5 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -194,6 +194,10 @@ nullable_string(const char *token, int length)
/*
* _readBitmapset
+ *
+ * Note: this code is used in contexts where we know that a Bitmapset
+ * is expected. There is equivalent code in nodeRead() that can read a
+ * Bitmapset when we come across one in other contexts.
*/
static Bitmapset *
_readBitmapset(void)
@@ -234,7 +238,8 @@ _readBitmapset(void)
}
/*
- * for use by extensions which define extensible nodes
+ * We export this function for use by extensions that define extensible nodes.
+ * That's somewhat historical, though, because calling nodeRead() will work.
*/
Bitmapset *
readBitmapset(void)
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 77f3f81bcb..914bfd90bc 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -99,7 +99,6 @@ static void get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
List **considered_relids);
static bool eclass_already_used(EquivalenceClass *parent_ec, Relids oldrelids,
List *indexjoinclauses);
-static bool bms_equal_any(Relids relids, List *relids_list);
static void get_index_paths(PlannerInfo *root, RelOptInfo *rel,
IndexOptInfo *index, IndexClauseSet *clauses,
List **bitindexpaths);
@@ -370,8 +369,8 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
Path *path = (Path *) lfirst(lc);
Relids required_outer = PATH_REQ_OUTER(path);
- if (!bms_equal_any(required_outer, all_path_outers))
- all_path_outers = lappend(all_path_outers, required_outer);
+ all_path_outers = list_append_unique(all_path_outers,
+ required_outer);
}
/* Now, for each distinct parameterization set ... */
@@ -517,7 +516,7 @@ consider_index_join_outer_rels(PlannerInfo *root, RelOptInfo *rel,
int num_considered_relids;
/* If we already tried its relids set, no need to do so again */
- if (bms_equal_any(clause_relids, *considered_relids))
+ if (list_member(*considered_relids, clause_relids))
continue;
/*
@@ -612,7 +611,7 @@ get_join_index_paths(PlannerInfo *root, RelOptInfo *rel,
int indexcol;
/* If we already considered this relids set, don't repeat the work */
- if (bms_equal_any(relids, *considered_relids))
+ if (list_member(*considered_relids, relids))
return;
/* Identify indexclauses usable with this relids set */
@@ -694,25 +693,6 @@ eclass_already_used(EquivalenceClass *parent_ec, Relids oldrelids,
return false;
}
-/*
- * bms_equal_any
- * True if relids is bms_equal to any member of relids_list
- *
- * Perhaps this should be in bitmapset.c someday.
- */
-static bool
-bms_equal_any(Relids relids, List *relids_list)
-{
- ListCell *lc;
-
- foreach(lc, relids_list)
- {
- if (bms_equal(relids, (Relids) lfirst(lc)))
- return true;
- }
- return false;
-}
-
/*
* get_index_paths
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index 75b5ce1a8e..2792281658 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -20,6 +20,8 @@
#ifndef BITMAPSET_H
#define BITMAPSET_H
+#include "nodes/nodes.h"
+
/*
* Forward decl to save including pg_list.h
*/
@@ -48,6 +50,9 @@ typedef int32 signedbitmapword; /* must be the matching signed type */
typedef struct Bitmapset
{
+ pg_node_attr(custom_copy_equal, special_read_write)
+
+ NodeTag type;
int nwords; /* number of words in array */
bitmapword words[FLEXIBLE_ARRAY_MEMBER]; /* really [nwords] */
} Bitmapset;
diff --git a/src/include/nodes/meson.build b/src/include/nodes/meson.build
index b7df232081..e63881086e 100644
--- a/src/include/nodes/meson.build
+++ b/src/include/nodes/meson.build
@@ -13,6 +13,7 @@ node_support_input_i = [
'commands/trigger.h',
'executor/tuptable.h',
'foreign/fdwapi.h',
+ 'nodes/bitmapset.h',
'nodes/extensible.h',
'nodes/lockoptions.h',
'nodes/replnodes.h',
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 09342d128d..a544b313d3 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -911,13 +911,11 @@ typedef struct RelOptInfo
/*
* cache space for remembering if we have proven this relation unique
- *
- * can't print unique_for_rels/non_unique_for_rels; BMSes aren't Nodes
*/
/* known unique for these other relid set(s) */
- List *unique_for_rels pg_node_attr(read_write_ignore);
+ List *unique_for_rels;
/* known not unique for these set(s) */
- List *non_unique_for_rels pg_node_attr(read_write_ignore);
+ List *non_unique_for_rels;
/*
* used by various scans and joins: