Improve node type forward reference
This is a small code simplification.
In primnodes.h, we have
typedef struct IntoClause
{
...
/* materialized view's SELECT query */
Node *viewQuery ...;
with the comment
/* (Although it's actually Query*, we declare
* it as Node* to avoid a forward reference.)
But we can do this better by using an incomplete struct, like
struct Query *viewQuery ...;
That way, everything has the correct type and fewer casts are required.
This technique is already used elsewhere in node type definitions.
The second patch just removes some more unnecessary casts around
copyObject() that I found while working on this.
Attachments:
0001-Improve-node-type-forward-reference.patchtext/plain; charset=UTF-8; name=0001-Improve-node-type-forward-reference.patchDownload
From df3f09a1d12c91595ad0702a17a2a7d884f5193f Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 14 Oct 2024 08:52:34 +0200
Subject: [PATCH 1/2] Improve node type forward reference
Instead of using Node *, we can use an incomplete struct. That way,
everything has the correct type and fewer casts are required. This
technique is already used elsewhere in node type definitions.
---
src/backend/commands/createas.c | 2 +-
src/backend/parser/analyze.c | 2 +-
src/include/nodes/primnodes.h | 5 ++---
3 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 0b629b1f79c..68ec122dbf9 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -133,7 +133,7 @@ create_ctas_internal(List *attrList, IntoClause *into)
if (is_matview)
{
/* StoreViewQuery scribbles on tree, so make a copy */
- Query *query = (Query *) copyObject(into->viewQuery);
+ Query *query = copyObject(into->viewQuery);
StoreViewQuery(intoRelationAddr.objectId, query, false);
CommandCounterIncrement();
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index e901203424d..5e5e0b1c5cf 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -3076,7 +3076,7 @@ transformCreateTableAsStmt(ParseState *pstate, CreateTableAsStmt *stmt)
* in the IntoClause because that's where intorel_startup() can
* conveniently get it from.
*/
- stmt->into->viewQuery = (Node *) copyObject(query);
+ stmt->into->viewQuery = copyObject(query);
}
/* represent the command as a utility Query */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index ea47652adb8..f2677b2da1d 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -152,8 +152,7 @@ typedef struct TableFunc
* For CREATE MATERIALIZED VIEW, viewQuery is the parsed-but-not-rewritten
* SELECT Query for the view; otherwise it's NULL. This is irrelevant in
* the query jumbling as CreateTableAsStmt already includes a reference to
- * its own Query, so ignore it. (Although it's actually Query*, we declare
- * it as Node* to avoid a forward reference.)
+ * its own Query, so ignore it.
*/
typedef struct IntoClause
{
@@ -166,7 +165,7 @@ typedef struct IntoClause
OnCommitAction onCommit; /* what do we do at COMMIT? */
char *tableSpaceName; /* table space to use, or NULL */
/* materialized view's SELECT query */
- Node *viewQuery pg_node_attr(query_jumble_ignore);
+ struct Query *viewQuery pg_node_attr(query_jumble_ignore);
bool skipData; /* true for WITH NO DATA */
} IntoClause;
--
2.47.0
0002-Fix-unnecessary-casts-of-copyObject-result.patchtext/plain; charset=UTF-8; name=0002-Fix-unnecessary-casts-of-copyObject-result.patchDownload
From 2cc8cde745140a17c35c17fe3ff10f0a4d5cd12a Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Mon, 14 Oct 2024 09:00:15 +0200
Subject: [PATCH 2/2] Fix unnecessary casts of copyObject() result
The result is already of the correct type, so these casts don't do
anything.
---
src/backend/commands/trigger.c | 2 +-
src/backend/nodes/nodeFuncs.c | 4 ++--
src/backend/rewrite/rewriteManip.c | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 3671e82535e..09356e46d16 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -1169,7 +1169,7 @@ CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString,
* Initialize our fabricated parse node by copying the original
* one, then resetting fields that we pass separately.
*/
- childStmt = (CreateTrigStmt *) copyObject(stmt);
+ childStmt = copyObject(stmt);
childStmt->funcname = NIL;
childStmt->whenClause = NULL;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 0d00e029f32..f76072228c9 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -2996,7 +2996,7 @@ expression_tree_mutator_impl(Node *node,
case T_SortGroupClause:
case T_CTESearchClause:
case T_MergeSupportFunc:
- return (Node *) copyObject(node);
+ return copyObject(node);
case T_WithCheckOption:
{
WithCheckOption *wco = (WithCheckOption *) node;
@@ -3604,7 +3604,7 @@ expression_tree_mutator_impl(Node *node,
break;
case T_PartitionPruneStepCombine:
/* no expression sub-nodes */
- return (Node *) copyObject(node);
+ return copyObject(node);
case T_JoinExpr:
{
JoinExpr *join = (JoinExpr *) node;
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index b20625fbd2b..8f90afb3269 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -1715,7 +1715,7 @@ ReplaceVarsFromTargetList_callback(Var *var,
break;
case REPLACEVARS_CHANGE_VARNO:
- var = (Var *) copyObject(var);
+ var = copyObject(var);
var->varno = rcon->nomatch_varno;
/* we leave the syntactic referent alone */
return (Node *) var;
--
2.47.0
On Mon, Oct 14, 2024 at 09:47:59AM +0200, Peter Eisentraut wrote:
But we can do this better by using an incomplete struct, like
struct Query *viewQuery ...;
That way, everything has the correct type and fewer casts are required. This
technique is already used elsewhere in node type definitions.
I noticed that the examples in parsenodes.h are for structs defined within
the same file. If the struct is defined in a separate file, I guess you
might need to include another header file wherever it is used, but that
doesn't seem too bad.
The second patch just removes some more unnecessary casts around
copyObject() that I found while working on this.
LGTM
--
nathan
On 14.10.24 23:28, Nathan Bossart wrote:
On Mon, Oct 14, 2024 at 09:47:59AM +0200, Peter Eisentraut wrote:
But we can do this better by using an incomplete struct, like
struct Query *viewQuery ...;
That way, everything has the correct type and fewer casts are required. This
technique is already used elsewhere in node type definitions.I noticed that the examples in parsenodes.h are for structs defined within
the same file. If the struct is defined in a separate file, I guess you
might need to include another header file wherever it is used, but that
doesn't seem too bad.
No, you can leave the struct incomplete. You only need to provide its
full definition (= include the other header file) if you actually want
to access the struct's fields.
Peter Eisentraut <peter@eisentraut.org> 于2024年10月15日周二 15:02写道:
On 14.10.24 23:28, Nathan Bossart wrote:
On Mon, Oct 14, 2024 at 09:47:59AM +0200, Peter Eisentraut wrote:
But we can do this better by using an incomplete struct, like
struct Query *viewQuery ...;
That way, everything has the correct type and fewer casts are required.
This
technique is already used elsewhere in node type definitions.
Agree. The attached patches LGTM.
I noticed that the examples in parsenodes.h are for structs defined
within
the same file. If the struct is defined in a separate file, I guess you
might need to include another header file wherever it is used, but that
doesn't seem too bad.No, you can leave the struct incomplete. You only need to provide its
full definition (= include the other header file) if you actually want
to access the struct's fields.
Yeah. There are many examples. For example as below:
in struct RelOptInfos,
/* use "struct FdwRoutine" to avoid including fdwapi.h here */
struct FdwRoutine *fdwroutine pg_node_attr(read_write_ignore);
--
Thanks,
Tender Wang
On Tue, Oct 15, 2024 at 09:02:48AM +0200, Peter Eisentraut wrote:
On 14.10.24 23:28, Nathan Bossart wrote:
On Mon, Oct 14, 2024 at 09:47:59AM +0200, Peter Eisentraut wrote:
But we can do this better by using an incomplete struct, like
struct Query *viewQuery ...;
That way, everything has the correct type and fewer casts are required. This
technique is already used elsewhere in node type definitions.I noticed that the examples in parsenodes.h are for structs defined within
the same file. If the struct is defined in a separate file, I guess you
might need to include another header file wherever it is used, but that
doesn't seem too bad.No, you can leave the struct incomplete. You only need to provide its full
definition (= include the other header file) if you actually want to access
the struct's fields.
That's what I figured. This one LGTM, too, then.
--
nathan
On 15.10.24 16:43, Nathan Bossart wrote:
On Tue, Oct 15, 2024 at 09:02:48AM +0200, Peter Eisentraut wrote:
On 14.10.24 23:28, Nathan Bossart wrote:
On Mon, Oct 14, 2024 at 09:47:59AM +0200, Peter Eisentraut wrote:
But we can do this better by using an incomplete struct, like
struct Query *viewQuery ...;
That way, everything has the correct type and fewer casts are required. This
technique is already used elsewhere in node type definitions.I noticed that the examples in parsenodes.h are for structs defined within
the same file. If the struct is defined in a separate file, I guess you
might need to include another header file wherever it is used, but that
doesn't seem too bad.No, you can leave the struct incomplete. You only need to provide its full
definition (= include the other header file) if you actually want to access
the struct's fields.That's what I figured. This one LGTM, too, then.
Committed, thanks.