Change EXPLAIN output so that subplans and initplans (particularly CTEs)
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 5 Apr 2009 19:59:40 +0000 (19:59 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 5 Apr 2009 19:59:40 +0000 (19:59 +0000)
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
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/plan/subselect.c
src/backend/utils/adt/ruleutils.c
src/include/nodes/primnodes.h

index a8c07900c236892a27f19a442ff7af994e825989..b334e2bc5074bb03e1eb383af65e8067c9848d5e 100644 (file)
@@ -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, "    ->  ");
index 3a51aca78eefc38e9fcb61a1f728ed7bb9efcc07..c78747ebbe45c92b8d1911c252bd943ccc4f74b2 100644 (file)
@@ -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);
index 537bf68e19ef55747e9be4c7b1ee41bd7eeb8f6b..a6f78c6ae690d55245376e0eb21d78669007c97d 100644 (file)
@@ -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);
index 236ae60c8ab5ed4c07fa6b4b51d4577eb1377db1..88a4c3562c376b6b64da145489bd58eda3ec223d 100644 (file)
@@ -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);
index 389b8f24a8b33f6c0b9ad87f3976b5905bf25f23..3ac803ef7976ff73ba62eeb12aebf15e92172b58 100644 (file)
@@ -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;
 }
index a6dce9e275fb57b60ed4e3f4d46d7897b3b0fb62..63aecb0703d9c4969c6b4ac7f032528337158c86 100644 (file)
@@ -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:
index 068752808d35aefbb6faa1883532e37bdd52be31..bf3563bbbaa30e76e55ae6239cd8a11a7fceb553 100644 (file)
@@ -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 */