From 08fbb26856f36ca36b9842b6caf2ba32492e3720 Mon Sep 17 00:00:00 2001 From: Robert Haas Date: Fri, 21 Mar 2025 11:06:35 -0400 Subject: [PATCH] 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 bd70b6d9d5..5dc707d69e 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 215491ccfd..5dcd09e712 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -571,6 +571,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->firstResultRels = glob->firstResultRels; result->appendRelations = glob->appendRelations; diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 7f241cddb4..6f0d97f393 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -395,6 +395,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 e009e131bc..61ba04d014 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 782fb471b6..9df11cd394 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -120,6 +120,9 @@ typedef struct PlannedStmt */ List *subplans; + /* a list of SubPlanRTInfo objects */ + List *subrtinfos; + /* indices of subplans that require REWIND */ Bitmapset *rewindPlanIDs; @@ -1780,4 +1783,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; + 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 9ea573fae2..1c6a7252ee 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -4309,3 +4309,4 @@ zic_t ExplainExtensionOption ExplainOptionHandler overexplain_options +SubPlanRTInfo -- 2.39.5