aboutsummaryrefslogtreecommitdiff
path: root/src/backend/commands/explain.c
diff options
context:
space:
mode:
authorDavid Rowley <drowley@postgresql.org>2024-10-11 17:19:59 +1300
committerDavid Rowley <drowley@postgresql.org>2024-10-11 17:19:59 +1300
commit161320b4b960ee4fe918959be6529ae9b106ea5a (patch)
tree35fceff1c373c3e505d2d1180429fb1571e8bb73 /src/backend/commands/explain.c
parentc75c6f8d280a3364643fc51f9c9fdf2dee0213c2 (diff)
downloadpostgresql-161320b4b960ee4fe918959be6529ae9b106ea5a.tar.gz
postgresql-161320b4b960ee4fe918959be6529ae9b106ea5a.zip
Adjust EXPLAIN's output for disabled nodes
c01743aa4 added EXPLAIN output to display the plan node's disabled_node count whenever that count is above 0. Seemingly, there weren't many people who liked that output as each parent of a disabled node would also have a "Disabled Nodes" output due to the way disabled_nodes is accumulated towards the root plan node. It was often hard and sometimes impossible to figure out which nodes were disabled from looking at EXPLAIN. You might think it would be possible to manually add up the numbers from the "Disabled Nodes" output of a given node's children to figure out if that node has a higher disabled_nodes count than its children, but that wouldn't have worked for Append and Merge Append nodes if some disabled child nodes were run-time pruned during init plan. Those children are not displayed in EXPLAIN. Here we attempt to improve this output by only showing "Disabled: true" against only the nodes which are explicitly disabled themselves. That seems to be the output that's desired by the most people who voiced their opinion. This is done by summing up the disabled_nodes of the given node's children and checking if that number is less than the disabled_nodes of the current node. This commit also fixes a bug in make_sort() which was neglecting to set the Sort's disabled_nodes field. This should have copied what was done in cost_sort(), but it hadn't been updated. With the new output, the choice to not maintain that field properly was clearly wrong as the disabled-ness of the node was attributed to the Sort's parent instead. Reviewed-by: Laurenz Albe, Alena Rybakina Discussion: https://postgr.es/m/9e4ad616bebb103ec2084bf6f724cfc739e7fabb.camel@cybertec.at
Diffstat (limited to 'src/backend/commands/explain.c')
-rw-r--r--src/backend/commands/explain.c98
1 files changed, 95 insertions, 3 deletions
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index ee1bcb84e28..18a5af6b919 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1364,6 +1364,96 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
}
/*
+ * plan_is_disabled
+ * Checks if the given plan node type was disabled during query planning.
+ * This is evident by the disable_node field being higher than the sum of
+ * the disabled_node field from the plan's children.
+ */
+static bool
+plan_is_disabled(Plan *plan)
+{
+ int child_disabled_nodes;
+
+ /* The node is certainly not disabled if this is zero */
+ if (plan->disabled_nodes == 0)
+ return false;
+
+ child_disabled_nodes = 0;
+
+ /*
+ * Handle special nodes first. Children of BitmapOrs and BitmapAnds can't
+ * be disabled, so no need to handle those specifically.
+ */
+ if (IsA(plan, Append))
+ {
+ ListCell *lc;
+ Append *aplan = (Append *) plan;
+
+ /*
+ * Sum the Append childrens' disabled_nodes. This purposefully
+ * includes any run-time pruned children. Ignoring those could give
+ * us the incorrect number of disabled nodes.
+ */
+ foreach(lc, aplan->appendplans)
+ {
+ Plan *subplan = lfirst(lc);
+
+ child_disabled_nodes += subplan->disabled_nodes;
+ }
+ }
+ else if (IsA(plan, MergeAppend))
+ {
+ ListCell *lc;
+ MergeAppend *maplan = (MergeAppend *) plan;
+
+ /*
+ * Sum the MergeAppend childrens' disabled_nodes. This purposefully
+ * includes any run-time pruned children. Ignoring those could give
+ * us the incorrect number of disabled nodes.
+ */
+ foreach(lc, maplan->mergeplans)
+ {
+ Plan *subplan = lfirst(lc);
+
+ child_disabled_nodes += subplan->disabled_nodes;
+ }
+ }
+ else if (IsA(plan, SubqueryScan))
+ child_disabled_nodes += ((SubqueryScan *) plan)->subplan->disabled_nodes;
+ else if (IsA(plan, CustomScan))
+ {
+ ListCell *lc;
+ CustomScan *cplan = (CustomScan *) plan;
+
+ foreach(lc, cplan->custom_plans)
+ {
+ Plan *subplan = lfirst(lc);
+
+ child_disabled_nodes += subplan->disabled_nodes;
+ }
+ }
+ else
+ {
+ /*
+ * Else, sum up disabled_nodes from the plan's inner and outer side.
+ */
+ if (outerPlan(plan))
+ child_disabled_nodes += outerPlan(plan)->disabled_nodes;
+ if (innerPlan(plan))
+ child_disabled_nodes += innerPlan(plan)->disabled_nodes;
+ }
+
+ /*
+ * It's disabled if the plan's disable_nodes is higher than the sum of its
+ * child's plan disabled_nodes.
+ */
+ if (plan->disabled_nodes > child_disabled_nodes)
+ return true;
+
+ return false;
+}
+
+/*
* ExplainNode -
* Appends a description of a plan tree to es->str
*
@@ -1399,6 +1489,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
ExplainWorkersState *save_workers_state = es->workers_state;
int save_indent = es->indent;
bool haschildren;
+ bool isdisabled;
/*
* Prepare per-worker output buffers, if needed. We'll append the data in
@@ -1914,9 +2005,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
if (es->format == EXPLAIN_FORMAT_TEXT)
appendStringInfoChar(es->str, '\n');
- if (plan->disabled_nodes != 0)
- ExplainPropertyInteger("Disabled Nodes", NULL, plan->disabled_nodes,
- es);
+
+ isdisabled = plan_is_disabled(plan);
+ if (es->format != EXPLAIN_FORMAT_TEXT || isdisabled)
+ ExplainPropertyBool("Disabled", isdisabled, es);
/* prepare per-worker general execution details */
if (es->workers_state && es->verbose)