Commit 7d91b604 authored by Tom Lane's avatar Tom Lane

Fix handling of "Subplans Removed" field in EXPLAIN output.

Commit 499be013 added this field in a rather poorly-thought-through
manner, with the result being that rather than being a field of the
Append or MergeAppend plan node as intended (and as it seems to be,
in text format), it was actually an element of the "Plans" subgroup.
At least in JSON format, that's flat out invalid syntax, because
"Plans" is an array not an object.

While it's not hard to move the generation of the field so that it
appears where it's supposed to, this does result in a visible change
in field order in text format, in cases where a Append or MergeAppend
plan node has any InitPlans attached.  That's slightly annoying to
do in stable branches; but the alternative of continuing to emit
broken non-text formats seems worse.

Also, since the set of fields emitted is not supposed to be
data-dependent in non-text formats, make sure that "Subplans Removed"
appears in Append and MergeAppend nodes even when it's zero, in those
formats.  (The previous coding made it look like it could appear in
some other node types such as BitmapAnd, but we don't actually support
runtime pruning there, so don't emit it in those cases.)

Per bug #16171 from Mahadevan Ramachandran.  Fix by Daniel Gustafsson
and Tom Lane, reviewed by Hamid Akhtar.  Back-patch to v11 where this
code came in.

Discussion: https://postgr.es/m/16171-b72259ab75505fa2@postgresql.org
parent 177be9ed
...@@ -119,8 +119,9 @@ static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es); ...@@ -119,8 +119,9 @@ static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es); static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors, static void show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
ExplainState *es); ExplainState *es);
static void ExplainMemberNodes(PlanState **planstates, int nsubnodes, static void ExplainMemberNodes(PlanState **planstates, int nplans,
int nplans, List *ancestors, ExplainState *es); List *ancestors, ExplainState *es);
static void ExplainMissingMembers(int nplans, int nchildren, ExplainState *es);
static void ExplainSubPlans(List *plans, List *ancestors, static void ExplainSubPlans(List *plans, List *ancestors,
const char *relationship, ExplainState *es); const char *relationship, ExplainState *es);
static void ExplainCustomChildren(CustomScanState *css, static void ExplainCustomChildren(CustomScanState *css,
...@@ -1967,6 +1968,30 @@ ExplainNode(PlanState *planstate, List *ancestors, ...@@ -1967,6 +1968,30 @@ ExplainNode(PlanState *planstate, List *ancestors,
ExplainFlushWorkersState(es); ExplainFlushWorkersState(es);
es->workers_state = save_workers_state; es->workers_state = save_workers_state;
/*
* If partition pruning was done during executor initialization, the
* number of child plans we'll display below will be less than the number
* of subplans that was specified in the plan. To make this a bit less
* mysterious, emit an indication that this happened. Note that this
* field is emitted now because we want it to be a property of the parent
* node; it *cannot* be emitted within the Plans sub-node we'll open next.
*/
switch (nodeTag(plan))
{
case T_Append:
ExplainMissingMembers(((AppendState *) planstate)->as_nplans,
list_length(((Append *) plan)->appendplans),
es);
break;
case T_MergeAppend:
ExplainMissingMembers(((MergeAppendState *) planstate)->ms_nplans,
list_length(((MergeAppend *) plan)->mergeplans),
es);
break;
default:
break;
}
/* Get ready to display the child plans */ /* Get ready to display the child plans */
haschildren = planstate->initPlan || haschildren = planstate->initPlan ||
outerPlanState(planstate) || outerPlanState(planstate) ||
...@@ -2007,31 +2032,26 @@ ExplainNode(PlanState *planstate, List *ancestors, ...@@ -2007,31 +2032,26 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_ModifyTable: case T_ModifyTable:
ExplainMemberNodes(((ModifyTableState *) planstate)->mt_plans, ExplainMemberNodes(((ModifyTableState *) planstate)->mt_plans,
((ModifyTableState *) planstate)->mt_nplans, ((ModifyTableState *) planstate)->mt_nplans,
list_length(((ModifyTable *) plan)->plans),
ancestors, es); ancestors, es);
break; break;
case T_Append: case T_Append:
ExplainMemberNodes(((AppendState *) planstate)->appendplans, ExplainMemberNodes(((AppendState *) planstate)->appendplans,
((AppendState *) planstate)->as_nplans, ((AppendState *) planstate)->as_nplans,
list_length(((Append *) plan)->appendplans),
ancestors, es); ancestors, es);
break; break;
case T_MergeAppend: case T_MergeAppend:
ExplainMemberNodes(((MergeAppendState *) planstate)->mergeplans, ExplainMemberNodes(((MergeAppendState *) planstate)->mergeplans,
((MergeAppendState *) planstate)->ms_nplans, ((MergeAppendState *) planstate)->ms_nplans,
list_length(((MergeAppend *) plan)->mergeplans),
ancestors, es); ancestors, es);
break; break;
case T_BitmapAnd: case T_BitmapAnd:
ExplainMemberNodes(((BitmapAndState *) planstate)->bitmapplans, ExplainMemberNodes(((BitmapAndState *) planstate)->bitmapplans,
((BitmapAndState *) planstate)->nplans, ((BitmapAndState *) planstate)->nplans,
list_length(((BitmapAnd *) plan)->bitmapplans),
ancestors, es); ancestors, es);
break; break;
case T_BitmapOr: case T_BitmapOr:
ExplainMemberNodes(((BitmapOrState *) planstate)->bitmapplans, ExplainMemberNodes(((BitmapOrState *) planstate)->bitmapplans,
((BitmapOrState *) planstate)->nplans, ((BitmapOrState *) planstate)->nplans,
list_length(((BitmapOr *) plan)->bitmapplans),
ancestors, es); ancestors, es);
break; break;
case T_SubqueryScan: case T_SubqueryScan:
...@@ -3348,32 +3368,33 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors, ...@@ -3348,32 +3368,33 @@ show_modifytable_info(ModifyTableState *mtstate, List *ancestors,
* *
* The ancestors list should already contain the immediate parent of these * The ancestors list should already contain the immediate parent of these
* plans. * plans.
*
* nsubnodes indicates the number of items in the planstates array.
* nplans indicates the original number of subnodes in the Plan, some of these
* may have been pruned by the run-time pruning code.
*/ */
static void static void
ExplainMemberNodes(PlanState **planstates, int nsubnodes, int nplans, ExplainMemberNodes(PlanState **planstates, int nplans,
List *ancestors, ExplainState *es) List *ancestors, ExplainState *es)
{ {
int j; int j;
/* for (j = 0; j < nplans; j++)
* The number of subnodes being lower than the number of subplans that was
* specified in the plan means that some subnodes have been ignored per
* instruction for the partition pruning code during the executor
* initialization. To make this a bit less mysterious, we'll indicate
* here that this has happened.
*/
if (nsubnodes < nplans)
ExplainPropertyInteger("Subplans Removed", NULL, nplans - nsubnodes, es);
for (j = 0; j < nsubnodes; j++)
ExplainNode(planstates[j], ancestors, ExplainNode(planstates[j], ancestors,
"Member", NULL, es); "Member", NULL, es);
} }
/*
* Report about any pruned subnodes of an Append or MergeAppend node.
*
* nplans indicates the number of live subplans.
* nchildren indicates the original number of subnodes in the Plan;
* some of these may have been pruned by the run-time pruning code.
*/
static void
ExplainMissingMembers(int nplans, int nchildren, ExplainState *es)
{
if (nplans < nchildren || es->format != EXPLAIN_FORMAT_TEXT)
ExplainPropertyInteger("Subplans Removed", NULL,
nchildren - nplans, es);
}
/* /*
* Explain a list of SubPlans (or initPlans, which also use SubPlan nodes). * Explain a list of SubPlans (or initPlans, which also use SubPlan nodes).
* *
......
...@@ -1808,9 +1808,9 @@ explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2); ...@@ -1808,9 +1808,9 @@ explain (analyze, costs off, summary off, timing off) execute ab_q2 (2, 2);
QUERY PLAN QUERY PLAN
--------------------------------------------------------- ---------------------------------------------------------
Append (actual rows=0 loops=1) Append (actual rows=0 loops=1)
Subplans Removed: 6
InitPlan 1 (returns $0) InitPlan 1 (returns $0)
-> Result (actual rows=1 loops=1) -> Result (actual rows=1 loops=1)
Subplans Removed: 6
-> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1) -> Seq Scan on ab_a2_b1 ab_1 (actual rows=0 loops=1)
Filter: ((a >= $1) AND (a <= $2) AND (b < $0)) Filter: ((a >= $1) AND (a <= $2) AND (b < $0))
-> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
...@@ -1826,9 +1826,9 @@ explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2); ...@@ -1826,9 +1826,9 @@ explain (analyze, costs off, summary off, timing off) execute ab_q3 (2, 2);
QUERY PLAN QUERY PLAN
--------------------------------------------------------- ---------------------------------------------------------
Append (actual rows=0 loops=1) Append (actual rows=0 loops=1)
Subplans Removed: 6
InitPlan 1 (returns $0) InitPlan 1 (returns $0)
-> Result (actual rows=1 loops=1) -> Result (actual rows=1 loops=1)
Subplans Removed: 6
-> Seq Scan on ab_a1_b2 ab_1 (actual rows=0 loops=1) -> Seq Scan on ab_a1_b2 ab_1 (actual rows=0 loops=1)
Filter: ((b >= $1) AND (b <= $2) AND (a < $0)) Filter: ((b >= $1) AND (b <= $2) AND (a < $0))
-> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1) -> Seq Scan on ab_a2_b2 ab_2 (actual rows=0 loops=1)
...@@ -2396,9 +2396,9 @@ explain (analyze, costs off, summary off, timing off) execute ab_q6(1); ...@@ -2396,9 +2396,9 @@ explain (analyze, costs off, summary off, timing off) execute ab_q6(1);
QUERY PLAN QUERY PLAN
-------------------------------------------------- --------------------------------------------------
Append (actual rows=0 loops=1) Append (actual rows=0 loops=1)
Subplans Removed: 12
InitPlan 1 (returns $0) InitPlan 1 (returns $0)
-> Result (actual rows=1 loops=1) -> Result (actual rows=1 loops=1)
Subplans Removed: 12
-> Seq Scan on ab_a1_b1 ab_1 (never executed) -> Seq Scan on ab_a1_b1 ab_1 (never executed)
Filter: ((a = $1) AND (b = $0)) Filter: ((a = $1) AND (b = $0))
-> Seq Scan on ab_a1_b2 ab_2 (never executed) -> Seq Scan on ab_a1_b2 ab_2 (never executed)
...@@ -3032,9 +3032,9 @@ execute ps1(1); ...@@ -3032,9 +3032,9 @@ execute ps1(1);
QUERY PLAN QUERY PLAN
-------------------------------------------------------- --------------------------------------------------------
Append (actual rows=1 loops=1) Append (actual rows=1 loops=1)
Subplans Removed: 2
InitPlan 1 (returns $0) InitPlan 1 (returns $0)
-> Result (actual rows=1 loops=1) -> Result (actual rows=1 loops=1)
Subplans Removed: 2
-> Seq Scan on mc3p1 mc3p_1 (actual rows=1 loops=1) -> Seq Scan on mc3p1 mc3p_1 (actual rows=1 loops=1)
Filter: ((a = $1) AND (abs(b) < $0)) Filter: ((a = $1) AND (abs(b) < $0))
(6 rows) (6 rows)
...@@ -3047,9 +3047,9 @@ execute ps2(1); ...@@ -3047,9 +3047,9 @@ execute ps2(1);
QUERY PLAN QUERY PLAN
-------------------------------------------------------- --------------------------------------------------------
Append (actual rows=2 loops=1) Append (actual rows=2 loops=1)
Subplans Removed: 1
InitPlan 1 (returns $0) InitPlan 1 (returns $0)
-> Result (actual rows=1 loops=1) -> Result (actual rows=1 loops=1)
Subplans Removed: 1
-> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1) -> Seq Scan on mc3p0 mc3p_1 (actual rows=1 loops=1)
Filter: ((a <= $1) AND (abs(b) < $0)) Filter: ((a <= $1) AND (abs(b) < $0))
-> Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1) -> Seq Scan on mc3p1 mc3p_2 (actual rows=1 loops=1)
...@@ -3594,9 +3594,9 @@ explain (costs off) execute q (1, 1); ...@@ -3594,9 +3594,9 @@ explain (costs off) execute q (1, 1);
QUERY PLAN QUERY PLAN
--------------------------------------------------------------- ---------------------------------------------------------------
Append Append
Subplans Removed: 1
InitPlan 1 (returns $0) InitPlan 1 (returns $0)
-> Result -> Result
Subplans Removed: 1
-> Seq Scan on p1 p -> Seq Scan on p1 p
Filter: ((a = $1) AND (b = $2) AND (c = $0)) Filter: ((a = $1) AND (b = $2) AND (c = $0))
-> Seq Scan on q111 q1 -> Seq Scan on q111 q1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment