From 7f0c31d9ae42816da3d4303182715eb70ba75417 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 5 Apr 2009 19:59:40 +0000 Subject: [PATCH] Change EXPLAIN output so that subplans and initplans (particularly CTEs) are individually labeled, rather than just grouped under an "InitPlan" or "SubPlan" heading. This in turn makes it possible for decompilation of a subplan reference to usefully identify which subplan it's referencing. I also made InitPlans identify which parameter symbol(s) they compute, so that references to those parameters elsewhere in the plan tree can be connected to the initplan that will be executed. Per a gripe from Robert Haas about EXPLAIN output of a WITH query being inadequate, plus some longstanding pet peeves of my own. --- src/backend/commands/explain.c | 12 ++++---- src/backend/nodes/copyfuncs.c | 1 + src/backend/nodes/equalfuncs.c | 1 + src/backend/nodes/outfuncs.c | 1 + src/backend/optimizer/plan/subselect.c | 39 ++++++++++++++++++++++++-- src/backend/utils/adt/ruleutils.c | 34 ++++++++++++++++++---- src/include/nodes/primnodes.h | 2 ++ 7 files changed, 75 insertions(+), 15 deletions(-) diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index a8c07900c2..b334e2bc50 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -951,14 +951,14 @@ explain_outNode(StringInfo str, { ListCell *lst; - for (i = 0; i < indent; i++) - appendStringInfo(str, " "); - appendStringInfo(str, " InitPlan\n"); foreach(lst, planstate->initPlan) { SubPlanState *sps = (SubPlanState *) lfirst(lst); SubPlan *sp = (SubPlan *) sps->xprstate.expr; + for (i = 0; i < indent; i++) + appendStringInfo(str, " "); + appendStringInfo(str, " %s\n", sp->plan_name); for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); @@ -1099,14 +1099,14 @@ explain_outNode(StringInfo str, { ListCell *lst; - for (i = 0; i < indent; i++) - appendStringInfo(str, " "); - appendStringInfo(str, " SubPlan\n"); foreach(lst, planstate->subPlan) { SubPlanState *sps = (SubPlanState *) lfirst(lst); SubPlan *sp = (SubPlan *) sps->xprstate.expr; + for (i = 0; i < indent; i++) + appendStringInfo(str, " "); + appendStringInfo(str, " %s\n", sp->plan_name); for (i = 0; i < indent; i++) appendStringInfo(str, " "); appendStringInfo(str, " -> "); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 3a51aca78e..c78747ebbe 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1116,6 +1116,7 @@ _copySubPlan(SubPlan *from) COPY_NODE_FIELD(testexpr); COPY_NODE_FIELD(paramIds); COPY_SCALAR_FIELD(plan_id); + COPY_STRING_FIELD(plan_name); COPY_SCALAR_FIELD(firstColType); COPY_SCALAR_FIELD(firstColTypmod); COPY_SCALAR_FIELD(useHashTable); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 537bf68e19..a6f78c6ae6 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -341,6 +341,7 @@ _equalSubPlan(SubPlan *a, SubPlan *b) COMPARE_NODE_FIELD(testexpr); COMPARE_NODE_FIELD(paramIds); COMPARE_SCALAR_FIELD(plan_id); + COMPARE_STRING_FIELD(plan_name); COMPARE_SCALAR_FIELD(firstColType); COMPARE_SCALAR_FIELD(firstColTypmod); COMPARE_SCALAR_FIELD(useHashTable); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 236ae60c8a..88a4c3562c 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -961,6 +961,7 @@ _outSubPlan(StringInfo str, SubPlan *node) WRITE_NODE_FIELD(testexpr); WRITE_NODE_FIELD(paramIds); WRITE_INT_FIELD(plan_id); + WRITE_STRING_FIELD(plan_name); WRITE_OID_FIELD(firstColType); WRITE_INT_FIELD(firstColTypmod); WRITE_BOOL_FIELD(useHashTable); diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 389b8f24a8..3ac803ef79 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -412,8 +412,8 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, int paramid; /* - * Initialize the SubPlan node. Note plan_id isn't set till further down, - * likewise the cost fields. + * Initialize the SubPlan node. Note plan_id, plan_name, and cost fields + * are set further down. */ splan = makeNode(SubPlan); splan->subLinkType = subLinkType; @@ -606,6 +606,30 @@ build_subplan(PlannerInfo *root, Plan *plan, List *rtable, root->glob->rewindPlanIDs = bms_add_member(root->glob->rewindPlanIDs, splan->plan_id); + /* Label the subplan for EXPLAIN purposes */ + if (isInitPlan) + { + ListCell *lc; + int offset; + + splan->plan_name = palloc(32 + 12 * list_length(splan->setParam)); + sprintf(splan->plan_name, "InitPlan %d (returns ", splan->plan_id); + offset = strlen(splan->plan_name); + foreach(lc, splan->setParam) + { + sprintf(splan->plan_name + offset, "$%d%s", + lfirst_int(lc), + lnext(lc) ? "," : ""); + offset += strlen(splan->plan_name + offset); + } + sprintf(splan->plan_name + offset, ")"); + } + else + { + splan->plan_name = palloc(32); + sprintf(splan->plan_name, "SubPlan %d", splan->plan_id); + } + /* Lastly, fill in the cost estimates for use later */ cost_subplan(root, splan, plan); @@ -875,7 +899,7 @@ SS_process_ctes(PlannerInfo *root) * Make a SubPlan node for it. This is just enough unlike * build_subplan that we can't share code. * - * Note plan_id isn't set till further down, likewise the cost fields. + * Note plan_id, plan_name, and cost fields are set further down. */ splan = makeNode(SubPlan); splan->subLinkType = CTE_SUBLINK; @@ -931,6 +955,10 @@ SS_process_ctes(PlannerInfo *root) root->cte_plan_ids = lappend_int(root->cte_plan_ids, splan->plan_id); + /* Label the subplan for EXPLAIN purposes */ + splan->plan_name = palloc(4 + strlen(cte->ctename) + 1); + sprintf(splan->plan_name, "CTE %s", cte->ctename); + /* Lastly, fill in the cost estimates for use later */ cost_subplan(root, splan, plan); } @@ -2134,5 +2162,10 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, prm = generate_new_param(root, resulttype, resulttypmod); node->setParam = list_make1_int(prm->paramid); + /* Label the subplan for EXPLAIN purposes */ + node->plan_name = palloc(64); + sprintf(node->plan_name, "InitPlan %d (returns $%d)", + node->plan_id, prm->paramid); + return prm; } diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index a6dce9e275..63aecb0703 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -4404,20 +4404,42 @@ get_rule_expr(Node *node, deparse_context *context, case T_SubPlan: { + SubPlan *subplan = (SubPlan *) node; + /* * We cannot see an already-planned subplan in rule deparsing, - * only while EXPLAINing a query plan. For now, just punt. + * only while EXPLAINing a query plan. We don't try to + * reconstruct the original SQL, just reference the subplan + * that appears elsewhere in EXPLAIN's result. */ - if (((SubPlan *) node)->useHashTable) - appendStringInfo(buf, "(hashed subplan)"); + if (subplan->useHashTable) + appendStringInfo(buf, "(hashed %s)", subplan->plan_name); else - appendStringInfo(buf, "(subplan)"); + appendStringInfo(buf, "(%s)", subplan->plan_name); } break; case T_AlternativeSubPlan: - /* As above, just punt */ - appendStringInfo(buf, "(alternative subplans)"); + { + AlternativeSubPlan *asplan = (AlternativeSubPlan *) node; + ListCell *lc; + + /* As above, this can only happen during EXPLAIN */ + appendStringInfo(buf, "(alternatives: "); + foreach(lc, asplan->subplans) + { + SubPlan *splan = (SubPlan *) lfirst(lc); + + Assert(IsA(splan, SubPlan)); + if (splan->useHashTable) + appendStringInfo(buf, "hashed %s", splan->plan_name); + else + appendStringInfo(buf, "%s", splan->plan_name); + if (lnext(lc)) + appendStringInfo(buf, " or "); + } + appendStringInfo(buf, ")"); + } break; case T_FieldSelect: diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 068752808d..bf3563bbba 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -502,6 +502,8 @@ typedef struct SubPlan List *paramIds; /* IDs of Params embedded in the above */ /* Identification of the Plan tree to use: */ int plan_id; /* Index (from 1) in PlannedStmt.subplans */ + /* Identification of the SubPlan for EXPLAIN and debugging purposes: */ + char *plan_name; /* A name assigned during planning */ /* Extra data useful for determining subplan's output type: */ Oid firstColType; /* Type of first column of subplan result */ int32 firstColTypmod; /* Typmod of first column of subplan result */ -- 2.39.5