I have been looking into what it would take to get rid of the
custom_read_write and custom_query_jumble for the RangeTblEntry node
type. This is one of the larger and more complex exceptions left.
(Similar considerations would also apply to the Constraint node type.)
Allegedly, only certain fields of RangeTblEntry are valid based on
rtekind. But exactly which ones seems to be documented and handled
inconsistently. It seems that over time, new RTE kinds have "borrowed"
fields that notionally belong to other RTE kinds, which is technically
not a problem but creates a bit of a mess when trying to understand all
this.
I have some WIP patches to accompany this discussion.
Let's start with the jumble function. I suppose that this was just
carried over from the pg_stat_statements-specific code without any
detailed review. For example, the "inh" field is documented to be valid
in all RTEs, but it's only jumbled for RTE_RELATION. The "lateral"
field isn't looked at at all. I wouldn't be surprised if there are more
cases like this.
In the first attached patch, I remove _jumbleRangeTblEntry() and instead
add per-field query_jumble_ignore annotations to approximately match the
behavior of the previous custom code. The pg_stat_statements test suite
has some coverage of this. I get rid of switch on rtekind; this should
be technically correct, since we do the equal and copy functions like
this also. So for example the "inh" field is now considered in each
case. But I left "lateral" alone. I suspect several of these new
query_jumble_ignore should actually be dropped because the code was
wrong before.
In the second patch, I'm removing the switch on rtekind from
range_table_mutator_impl(). This should be fine because all the
subroutines can handle unset/NULL fields. And it removes one more place
that needs to track knowledge about which fields are valid when.
In the third patch, I'm removing the custom read/write functions for
RangeTblEntry. Those functions wanted to have a few fields at the front
to make the dump more legible; I'm doing that now by moving the fields
up in the actual struct.
Not done here, but something we should do is restructure the
documentation of RangeTblEntry itself. I'm still thinking about the
best way to structure this, but I'm thinking more like noting for each
field when it's used, instead by block like it is now, which makes it
awkward if a new RTE wants to borrow some fields.
Now one could probably rightfully complain that having all these unused
fields dumped would make the RangeTblEntry serialization bigger. I'm
not sure who big of a problem that actually is, considering how many
often-unset fields other node types have. But it deserves some
consideration. I think the best way to work around that would be to
have a mode that omits fields that have their default value (zero).
This would be more generally useful; for example Query also has a bunch
of fields that are not often set. I think this would be pretty easy to
implement, for example like
#define WRITE_INT_FIELD(fldname) \
if (full_mode || node->fldname) \
appendStringInfo(str, " :" CppAsString(fldname) " %d",
node->fldname)
There is also the discussion over at [0] about larger redesigns of the
node serialization format. I'm also interested in that, but here I'm
mainly trying to remove more special cases to make that kind of work
easier in the future.
Any thoughts about the direction?
[0]:
https://www.postgresql.org/message-id/flat/CACxu%3DvL_SD%3DWJiFSJyyBuZAp_2v_XBqb1x9JBiqz52a_g9z3jA%40mail.gmail.comFrom ef08061a6d8f01e7db350b74eaa2d403df6079b9 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Fri, 1 Dec 2023 11:45:50 +0100
Subject: [PATCH v1 1/3] Remove custom _jumbleRangeTblEntry()
---
src/backend/nodes/queryjumblefuncs.c | 48 ----------------------------
src/include/nodes/parsenodes.h | 42 ++++++++++++------------
2 files changed, 21 insertions(+), 69 deletions(-)
diff --git a/src/backend/nodes/queryjumblefuncs.c
b/src/backend/nodes/queryjumblefuncs.c
index 281907a4d8..9094ea02d8 100644
--- a/src/backend/nodes/queryjumblefuncs.c
+++ b/src/backend/nodes/queryjumblefuncs.c
@@ -347,51 +347,3 @@ _jumbleA_Const(JumbleState *jstate, Node *node)
}
}
}
-
-static void
-_jumbleRangeTblEntry(JumbleState *jstate, Node *node)
-{
- RangeTblEntry *expr = (RangeTblEntry *) node;
-
- JUMBLE_FIELD(rtekind);
- switch (expr->rtekind)
- {
- case RTE_RELATION:
- JUMBLE_FIELD(relid);
- JUMBLE_NODE(tablesample);
- JUMBLE_FIELD(inh);
- break;
- case RTE_SUBQUERY:
- JUMBLE_NODE(subquery);
- break;
- case RTE_JOIN:
- JUMBLE_FIELD(jointype);
- break;
- case RTE_FUNCTION:
- JUMBLE_NODE(functions);
- break;
- case RTE_TABLEFUNC:
- JUMBLE_NODE(tablefunc);
- break;
- case RTE_VALUES:
- JUMBLE_NODE(values_lists);
- break;
- case RTE_CTE:
-
- /*
- * Depending on the CTE name here isn't ideal, but it's
the only
- * info we have to identify the referenced WITH item.
- */
- JUMBLE_STRING(ctename);
- JUMBLE_FIELD(ctelevelsup);
- break;
- case RTE_NAMEDTUPLESTORE:
- JUMBLE_STRING(enrname);
- break;
- case RTE_RESULT:
- break;
- default:
- elog(ERROR, "unrecognized RTE kind: %d", (int)
expr->rtekind);
- break;
- }
-}
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index e494309da8..a97f3cb16f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1018,7 +1018,7 @@ typedef enum RTEKind
typedef struct RangeTblEntry
{
- pg_node_attr(custom_read_write, custom_query_jumble)
+ pg_node_attr(custom_read_write)
NodeTag type;
@@ -1062,16 +1062,16 @@ typedef struct RangeTblEntry
* tables to be invalidated if the underlying table is altered.
*/
Oid relid; /* OID of the relation
*/
- char relkind; /* relation kind (see
pg_class.relkind) */
- int rellockmode; /* lock level that query
requires on the rel */
+ char relkind pg_node_attr(query_jumble_ignore);
/* relation kind (see pg_class.relkind) */
+ int rellockmode pg_node_attr(query_jumble_ignore);
/* lock level that query requires on the rel */
struct TableSampleClause *tablesample; /* sampling info, or NULL */
- Index perminfoindex;
+ Index perminfoindex pg_node_attr(query_jumble_ignore);
/*
* Fields valid for a subquery RTE (else NULL):
*/
Query *subquery; /* the sub-query */
- bool security_barrier; /* is from security_barrier
view? */
+ bool security_barrier pg_node_attr(query_jumble_ignore);
/* is from security_barrier view? */
/*
* Fields valid for a join RTE (else NULL/zero):
@@ -1117,17 +1117,17 @@ typedef struct RangeTblEntry
* joinleftcols/joinrighttcols.
*/
JoinType jointype; /* type of join */
- int joinmergedcols; /* number of merged (JOIN
USING) columns */
- List *joinaliasvars; /* list of alias-var expansions */
- List *joinleftcols; /* left-side input column numbers */
- List *joinrightcols; /* right-side input column numbers */
+ int joinmergedcols
pg_node_attr(query_jumble_ignore); /* number of merged (JOIN USING) columns */
+ List *joinaliasvars pg_node_attr(query_jumble_ignore); /* list
of alias-var expansions */
+ List *joinleftcols pg_node_attr(query_jumble_ignore); /*
left-side input column numbers */
+ List *joinrightcols pg_node_attr(query_jumble_ignore); /*
right-side input column numbers */
/*
* join_using_alias is an alias clause attached directly to JOIN/USING.
It
* is different from the alias field (below) in that it does not hide
the
* range variables of the tables being joined.
*/
- Alias *join_using_alias;
+ Alias *join_using_alias pg_node_attr(query_jumble_ignore);
/*
* Fields valid for a function RTE (else NIL/zero):
@@ -1138,7 +1138,7 @@ typedef struct RangeTblEntry
* expandRTE().
*/
List *functions; /* list of RangeTblFunction nodes */
- bool funcordinality; /* is this called WITH ORDINALITY? */
+ bool funcordinality pg_node_attr(query_jumble_ignore); /* is
this called WITH ORDINALITY? */
/*
* Fields valid for a TableFunc RTE (else NULL):
@@ -1155,7 +1155,7 @@ typedef struct RangeTblEntry
*/
char *ctename; /* name of the WITH list item */
Index ctelevelsup; /* number of query levels up */
- bool self_reference; /* is this a recursive self-reference?
*/
+ bool self_reference pg_node_attr(query_jumble_ignore); /* is
this a recursive self-reference? */
/*
* Fields valid for CTE, VALUES, ENR, and TableFunc RTEs (else NIL):
@@ -1175,25 +1175,25 @@ typedef struct RangeTblEntry
* all three lists (as well as an empty-string entry in eref). Testing
* for zero coltype is the standard way to detect a dropped column.
*/
- List *coltypes; /* OID list of column type OIDs */
- List *coltypmods; /* integer list of column typmods */
- List *colcollations; /* OID list of column collation OIDs */
+ List *coltypes pg_node_attr(query_jumble_ignore); /* OID
list of column type OIDs */
+ List *coltypmods pg_node_attr(query_jumble_ignore);
/* integer list of column typmods */
+ List *colcollations pg_node_attr(query_jumble_ignore); /* OID
list of column collation OIDs */
/*
* Fields valid for ENR RTEs (else NULL/zero):
*/
char *enrname; /* name of ephemeral named relation */
- Cardinality enrtuples; /* estimated or actual from caller */
+ Cardinality enrtuples pg_node_attr(query_jumble_ignore);
/* estimated or actual from caller */
/*
* Fields valid in all RTEs:
*/
- Alias *alias; /* user-written alias clause,
if any */
- Alias *eref; /* expanded reference names */
- bool lateral; /* subquery, function, or
values is LATERAL? */
+ Alias *alias pg_node_attr(query_jumble_ignore);
/* user-written alias clause, if any */
+ Alias *eref pg_node_attr(query_jumble_ignore);
/* expanded reference names */
+ bool lateral pg_node_attr(query_jumble_ignore);
/* subquery, function, or values is LATERAL? */
bool inh; /* inheritance requested? */
- bool inFromCl; /* present in FROM clause? */
- List *securityQuals; /* security barrier quals to apply, if
any */
+ bool inFromCl pg_node_attr(query_jumble_ignore);
/* present in FROM clause? */
+ List *securityQuals pg_node_attr(query_jumble_ignore); /*
security barrier quals to apply, if any */
} RangeTblEntry;
/*
--
2.43.0
From 4a5efa4057ba8eddad3fe62b9a789bd3a5e03db8 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Fri, 1 Dec 2023 11:54:26 +0100
Subject: [PATCH v1 2/3] Simplify range_table_mutator_impl()
---
src/backend/nodes/nodeFuncs.c | 61 +++++++++++++----------------------
1 file changed, 23 insertions(+), 38 deletions(-)
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index c03f4f23e2..ac195930af 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3688,47 +3688,32 @@ range_table_mutator_impl(List *rtable,
RangeTblEntry *newrte;
FLATCOPY(newrte, rte, RangeTblEntry);
- switch (rte->rtekind)
+
+ MUTATE(newrte->tablesample, rte->tablesample, TableSampleClause
*);
+
+ if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
+ MUTATE(newrte->subquery, rte->subquery, Query *);
+ else
{
- case RTE_RELATION:
- MUTATE(newrte->tablesample, rte->tablesample,
- TableSampleClause *);
- /* we don't bother to copy eref, aliases, etc;
OK? */
- break;
- case RTE_SUBQUERY:
- if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
- MUTATE(newrte->subquery, rte->subquery,
Query *);
- else
- {
- /* else, copy RT subqueries as-is */
- newrte->subquery =
copyObject(rte->subquery);
- }
- break;
- case RTE_JOIN:
- if (!(flags & QTW_IGNORE_JOINALIASES))
- MUTATE(newrte->joinaliasvars,
rte->joinaliasvars, List *);
- else
- {
- /* else, copy join aliases as-is */
- newrte->joinaliasvars =
copyObject(rte->joinaliasvars);
- }
- break;
- case RTE_FUNCTION:
- MUTATE(newrte->functions, rte->functions, List
*);
- break;
- case RTE_TABLEFUNC:
- MUTATE(newrte->tablefunc, rte->tablefunc,
TableFunc *);
- break;
- case RTE_VALUES:
- MUTATE(newrte->values_lists, rte->values_lists,
List *);
- break;
- case RTE_CTE:
- case RTE_NAMEDTUPLESTORE:
- case RTE_RESULT:
- /* nothing to do */
- break;
+ /* else, copy RT subqueries as-is */
+ newrte->subquery = copyObject(rte->subquery);
+ }
+
+ if (!(flags & QTW_IGNORE_JOINALIASES))
+ MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List
*);
+ else
+ {
+ /* else, copy join aliases as-is */
+ newrte->joinaliasvars = copyObject(rte->joinaliasvars);
}
+
+ MUTATE(newrte->functions, rte->functions, List *);
+ MUTATE(newrte->tablefunc, rte->tablefunc, TableFunc *);
+ MUTATE(newrte->values_lists, rte->values_lists, List *);
MUTATE(newrte->securityQuals, rte->securityQuals, List *);
+
+ /* we don't bother to copy eref, aliases, etc; OK? */
+
newrt = lappend(newrt, newrte);
}
return newrt;
--
2.43.0
From 0ecfd88afc56d564b1c4c60492c80ca716694ec6 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <pe...@eisentraut.org>
Date: Fri, 1 Dec 2023 12:12:31 +0100
Subject: [PATCH v1 3/3] Remove custom RangeTblEntry read/write
---
src/backend/nodes/outfuncs.c | 80 -----------------------------
src/backend/nodes/readfuncs.c | 92 ----------------------------------
src/include/nodes/parsenodes.h | 12 +++--
3 files changed, 8 insertions(+), 176 deletions(-)
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e66a99247e..ded3c59b76 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -489,86 +489,6 @@ _outExtensibleNode(StringInfo str, const ExtensibleNode
*node)
methods->nodeOut(str, node);
}
-static void
-_outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
-{
- WRITE_NODE_TYPE("RANGETBLENTRY");
-
- /* put alias + eref first to make dump more legible */
- WRITE_NODE_FIELD(alias);
- WRITE_NODE_FIELD(eref);
- WRITE_ENUM_FIELD(rtekind, RTEKind);
-
- switch (node->rtekind)
- {
- case RTE_RELATION:
- WRITE_OID_FIELD(relid);
- WRITE_CHAR_FIELD(relkind);
- WRITE_INT_FIELD(rellockmode);
- WRITE_NODE_FIELD(tablesample);
- WRITE_UINT_FIELD(perminfoindex);
- break;
- case RTE_SUBQUERY:
- WRITE_NODE_FIELD(subquery);
- WRITE_BOOL_FIELD(security_barrier);
- /* we re-use these RELATION fields, too: */
- WRITE_OID_FIELD(relid);
- WRITE_CHAR_FIELD(relkind);
- WRITE_INT_FIELD(rellockmode);
- WRITE_UINT_FIELD(perminfoindex);
- break;
- case RTE_JOIN:
- WRITE_ENUM_FIELD(jointype, JoinType);
- WRITE_INT_FIELD(joinmergedcols);
- WRITE_NODE_FIELD(joinaliasvars);
- WRITE_NODE_FIELD(joinleftcols);
- WRITE_NODE_FIELD(joinrightcols);
- WRITE_NODE_FIELD(join_using_alias);
- break;
- case RTE_FUNCTION:
- WRITE_NODE_FIELD(functions);
- WRITE_BOOL_FIELD(funcordinality);
- break;
- case RTE_TABLEFUNC:
- WRITE_NODE_FIELD(tablefunc);
- break;
- case RTE_VALUES:
- WRITE_NODE_FIELD(values_lists);
- WRITE_NODE_FIELD(coltypes);
- WRITE_NODE_FIELD(coltypmods);
- WRITE_NODE_FIELD(colcollations);
- break;
- case RTE_CTE:
- WRITE_STRING_FIELD(ctename);
- WRITE_UINT_FIELD(ctelevelsup);
- WRITE_BOOL_FIELD(self_reference);
- WRITE_NODE_FIELD(coltypes);
- WRITE_NODE_FIELD(coltypmods);
- WRITE_NODE_FIELD(colcollations);
- break;
- case RTE_NAMEDTUPLESTORE:
- WRITE_STRING_FIELD(enrname);
- WRITE_FLOAT_FIELD(enrtuples);
- WRITE_NODE_FIELD(coltypes);
- WRITE_NODE_FIELD(coltypmods);
- WRITE_NODE_FIELD(colcollations);
- /* we re-use these RELATION fields, too: */
- WRITE_OID_FIELD(relid);
- break;
- case RTE_RESULT:
- /* no extra fields */
- break;
- default:
- elog(ERROR, "unrecognized RTE kind: %d", (int)
node->rtekind);
- break;
- }
-
- WRITE_BOOL_FIELD(lateral);
- WRITE_BOOL_FIELD(inh);
- WRITE_BOOL_FIELD(inFromCl);
- WRITE_NODE_FIELD(securityQuals);
-}
-
static void
_outA_Expr(StringInfo str, const A_Expr *node)
{
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index cc2021c1f7..0c64e06c38 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -486,98 +486,6 @@ _readConstraint(void)
READ_DONE();
}
-static RangeTblEntry *
-_readRangeTblEntry(void)
-{
- READ_LOCALS(RangeTblEntry);
-
- /* put alias + eref first to make dump more legible */
- READ_NODE_FIELD(alias);
- READ_NODE_FIELD(eref);
- READ_ENUM_FIELD(rtekind, RTEKind);
-
- switch (local_node->rtekind)
- {
- case RTE_RELATION:
- READ_OID_FIELD(relid);
- READ_CHAR_FIELD(relkind);
- READ_INT_FIELD(rellockmode);
- READ_NODE_FIELD(tablesample);
- READ_UINT_FIELD(perminfoindex);
- break;
- case RTE_SUBQUERY:
- READ_NODE_FIELD(subquery);
- READ_BOOL_FIELD(security_barrier);
- /* we re-use these RELATION fields, too: */
- READ_OID_FIELD(relid);
- READ_CHAR_FIELD(relkind);
- READ_INT_FIELD(rellockmode);
- READ_UINT_FIELD(perminfoindex);
- break;
- case RTE_JOIN:
- READ_ENUM_FIELD(jointype, JoinType);
- READ_INT_FIELD(joinmergedcols);
- READ_NODE_FIELD(joinaliasvars);
- READ_NODE_FIELD(joinleftcols);
- READ_NODE_FIELD(joinrightcols);
- READ_NODE_FIELD(join_using_alias);
- break;
- case RTE_FUNCTION:
- READ_NODE_FIELD(functions);
- READ_BOOL_FIELD(funcordinality);
- break;
- case RTE_TABLEFUNC:
- READ_NODE_FIELD(tablefunc);
- /* The RTE must have a copy of the column type info, if
any */
- if (local_node->tablefunc)
- {
- TableFunc *tf = local_node->tablefunc;
-
- local_node->coltypes = tf->coltypes;
- local_node->coltypmods = tf->coltypmods;
- local_node->colcollations = tf->colcollations;
- }
- break;
- case RTE_VALUES:
- READ_NODE_FIELD(values_lists);
- READ_NODE_FIELD(coltypes);
- READ_NODE_FIELD(coltypmods);
- READ_NODE_FIELD(colcollations);
- break;
- case RTE_CTE:
- READ_STRING_FIELD(ctename);
- READ_UINT_FIELD(ctelevelsup);
- READ_BOOL_FIELD(self_reference);
- READ_NODE_FIELD(coltypes);
- READ_NODE_FIELD(coltypmods);
- READ_NODE_FIELD(colcollations);
- break;
- case RTE_NAMEDTUPLESTORE:
- READ_STRING_FIELD(enrname);
- READ_FLOAT_FIELD(enrtuples);
- READ_NODE_FIELD(coltypes);
- READ_NODE_FIELD(coltypmods);
- READ_NODE_FIELD(colcollations);
- /* we re-use these RELATION fields, too: */
- READ_OID_FIELD(relid);
- break;
- case RTE_RESULT:
- /* no extra fields */
- break;
- default:
- elog(ERROR, "unrecognized RTE kind: %d",
- (int) local_node->rtekind);
- break;
- }
-
- READ_BOOL_FIELD(lateral);
- READ_BOOL_FIELD(inh);
- READ_BOOL_FIELD(inFromCl);
- READ_NODE_FIELD(securityQuals);
-
- READ_DONE();
-}
-
static A_Expr *
_readA_Expr(void)
{
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index a97f3cb16f..d3b1af6531 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1018,8 +1018,6 @@ typedef enum RTEKind
typedef struct RangeTblEntry
{
- pg_node_attr(custom_read_write)
-
NodeTag type;
RTEKind rtekind; /* see above */
@@ -1030,6 +1028,14 @@ typedef struct RangeTblEntry
* code that is being actively worked on. FIXME someday.
*/
+ /*
+ * Fields valid in all RTEs:
+ *
+ * put alias + eref first to make dump more legible
+ */
+ Alias *alias pg_node_attr(query_jumble_ignore);
/* user-written alias clause, if any */
+ Alias *eref pg_node_attr(query_jumble_ignore);
/* expanded reference names */
+
/*
* Fields valid for a plain relation RTE (else zero):
*
@@ -1188,8 +1194,6 @@ typedef struct RangeTblEntry
/*
* Fields valid in all RTEs:
*/
- Alias *alias pg_node_attr(query_jumble_ignore);
/* user-written alias clause, if any */
- Alias *eref pg_node_attr(query_jumble_ignore);
/* expanded reference names */
bool lateral pg_node_attr(query_jumble_ignore);
/* subquery, function, or values is LATERAL? */
bool inh; /* inheritance requested? */
bool inFromCl pg_node_attr(query_jumble_ignore);
/* present in FROM clause? */
--
2.43.0