v9-0002-Store-information-about-range-table-flattening-in.patch
application/octet-stream
Filename: v9-0002-Store-information-about-range-table-flattening-in.patch
Type: application/octet-stream
Part: 0
Message:
Re: plan shape work
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: format-patch
Series: patch v9-0002
Subject: Store information about range-table flattening in the final plan.
| File | + | − |
|---|---|---|
| contrib/pg_overexplain/pg_overexplain.c | 36 | 0 |
| src/backend/optimizer/plan/planner.c | 1 | 0 |
| src/backend/optimizer/plan/setrefs.c | 20 | 0 |
| src/include/nodes/pathnodes.h | 3 | 0 |
| src/include/nodes/plannodes.h | 17 | 0 |
| src/tools/pgindent/typedefs.list | 1 | 0 |
From 9039237030f03be9484ec6d734421aeea8ef4e2b Mon Sep 17 00:00:00 2001
From: Robert Haas <rhaas@postgresql.org>
Date: Wed, 24 Sep 2025 10:06:59 -0400
Subject: [PATCH v9 2/4] Store information about range-table flattening in the
final plan.
During planning, there is one range table per subquery; at the end if
planning, those separate range tables are flattened into a single
range table. Prior to this change, it was impractical for code
examining the final plan to understand which parts of the flattened
range table came from which subquery's range table.
If the only consumer of the final plan is the executor, that is
completely fine. However, if some code wants to examine the final
plan, or what happens when we execute it, and extract information from
it that be used in future planning cycles, it's inconvenient. So,
this commit remembers in the final plan which part of the final range
table came from which subquery's range table.
Additionally, this commit teaches pg_overexplain'e RANGE_TABLE option
to display the subquery name for each range table entry.
---
contrib/pg_overexplain/pg_overexplain.c | 36 +++++++++++++++++++++++++
src/backend/optimizer/plan/planner.c | 1 +
src/backend/optimizer/plan/setrefs.c | 20 ++++++++++++++
src/include/nodes/pathnodes.h | 3 +++
src/include/nodes/plannodes.h | 17 ++++++++++++
src/tools/pgindent/typedefs.list | 1 +
6 files changed, 78 insertions(+)
diff --git a/contrib/pg_overexplain/pg_overexplain.c b/contrib/pg_overexplain/pg_overexplain.c
index bd70b6d9d5e..5dc707d69e3 100644
--- a/contrib/pg_overexplain/pg_overexplain.c
+++ b/contrib/pg_overexplain/pg_overexplain.c
@@ -395,6 +395,8 @@ static void
overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
{
Index rti;
+ ListCell *lc_subrtinfo = list_head(plannedstmt->subrtinfos);
+ SubPlanRTInfo *rtinfo = NULL;
/* Open group, one entry per RangeTblEntry */
ExplainOpenGroup("Range Table", "Range Table", false, es);
@@ -405,6 +407,18 @@ overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
RangeTblEntry *rte = rt_fetch(rti, plannedstmt->rtable);
char *kind = NULL;
char *relkind;
+ SubPlanRTInfo *next_rtinfo;
+
+ /* Advance to next SubRTInfo, if it's time. */
+ if (lc_subrtinfo != NULL)
+ {
+ next_rtinfo = lfirst(lc_subrtinfo);
+ if (rti > next_rtinfo->rtoffset)
+ {
+ rtinfo = next_rtinfo;
+ lc_subrtinfo = lnext(plannedstmt->subrtinfos, lc_subrtinfo);
+ }
+ }
/* NULL entries are possible; skip them */
if (rte == NULL)
@@ -469,6 +483,28 @@ overexplain_range_table(PlannedStmt *plannedstmt, ExplainState *es)
ExplainPropertyBool("In From Clause", rte->inFromCl, es);
}
+ /*
+ * Indicate which subplan is the origin of which RTE. Note dummy
+ * subplans. Here again, we crunch more onto one line in text format.
+ */
+ if (rtinfo != NULL)
+ {
+ if (es->format == EXPLAIN_FORMAT_TEXT)
+ {
+ if (!rtinfo->dummy)
+ ExplainPropertyText("Subplan", rtinfo->plan_name, es);
+ else
+ ExplainPropertyText("Subplan",
+ psprintf("%s (dummy)",
+ rtinfo->plan_name), es);
+ }
+ else
+ {
+ ExplainPropertyText("Subplan", rtinfo->plan_name, es);
+ ExplainPropertyBool("Subplan Is Dummy", rtinfo->dummy, es);
+ }
+ }
+
/* rte->alias is optional; rte->eref is requested */
if (rte->alias != NULL)
overexplain_alias("Alias", rte->alias, es);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index fafc5627fc7..d47c2915b3a 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -579,6 +579,7 @@ standard_planner(Query *parse, const char *query_string, int cursorOptions,
result->unprunableRelids = bms_difference(glob->allRelids,
glob->prunableRelids);
result->permInfos = glob->finalrteperminfos;
+ result->subrtinfos = glob->subrtinfos;
result->resultRelations = glob->resultRelations;
result->appendRelations = glob->appendRelations;
result->subplans = glob->subplans;
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 6950eff2c5b..eef43792aeb 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -399,6 +399,26 @@ add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing)
Index rti;
ListCell *lc;
+ /*
+ * Record enough information to make it possible for code that looks at
+ * the final range table to understand how it was constructed. (If
+ * finalrtable is still NIL, then this is the very topmost PlannerInfo,
+ * which will always have plan_name == NULL and rtoffset == 0; we omit the
+ * degenerate list entry.)
+ */
+ if (root->glob->finalrtable != NIL)
+ {
+ SubPlanRTInfo *rtinfo = makeNode(SubPlanRTInfo);
+
+ rtinfo->plan_name = root->plan_name;
+ rtinfo->rtoffset = list_length(root->glob->finalrtable);
+
+ /* When recursing = true, it's an unplanned or dummy subquery. */
+ rtinfo->dummy = recursing;
+
+ root->glob->subrtinfos = lappend(root->glob->subrtinfos, rtinfo);
+ }
+
/*
* Add the query's own RTEs to the flattened rangetable.
*
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 6bf51fcefb9..e14cbd383fc 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -135,6 +135,9 @@ typedef struct PlannerGlobal
/* "flat" list of RTEPermissionInfos */
List *finalrteperminfos;
+ /* list of SubPlanRTInfo nodes */
+ List *subrtinfos;
+
/* "flat" list of PlanRowMarks */
List *finalrowmarks;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 3d196f5078e..a54f42524b0 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -131,6 +131,9 @@ typedef struct PlannedStmt
*/
List *subplans;
+ /* a list of SubPlanRTInfo objects */
+ List *subrtinfos;
+
/* indices of subplans that require REWIND */
Bitmapset *rewindPlanIDs;
@@ -1814,4 +1817,18 @@ typedef enum MonotonicFunction
MONOTONICFUNC_BOTH = MONOTONICFUNC_INCREASING | MONOTONICFUNC_DECREASING,
} MonotonicFunction;
+/*
+ * SubPlanRTInfo
+ *
+ * Information about which range table entries came from which subquery
+ * planning cycles.
+ */
+typedef struct SubPlanRTInfo
+{
+ NodeTag type;
+ const char *plan_name;
+ Index rtoffset;
+ bool dummy;
+} SubPlanRTInfo;
+
#endif /* PLANNODES_H */
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 3c80d49b67e..993688870db 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -4343,3 +4343,4 @@ yyscan_t
z_stream
z_streamp
zic_t
+SubPlanRTInfo
--
2.39.5 (Apple Git-154)