Respect disabled_nodes in fix_alternative_subplan.

When my commit e222534679 added the
concept of disabled_nodes, it failed to add a disabled_nodes field
to SubPlan. This is a regression: before that commit, when
fix_alternative_subplan compared the costs of two plans, the number
of disabled nodes affected the result, because it was just a
component of the total cost. After that commit, it no longer did,
making it possible for a disabled path to win on cost over one that
is not disabled. Fix that.

As usual for planner fixes that might destabilize plan choices,
no back-patch.

Discussion: https://postgr.es/m/CA+TgmoaK=4w7-qknUo3QhUJ53pXZq=c=KgZmRyD+k7ytqfmgSg@mail.gmail.com
Reviewed-by: Lukas Fittl <lukas@fittl.com>
This commit is contained in:
Robert Haas 2026-03-20 14:04:41 -04:00
parent 119e791e9c
commit 47c110f77e
3 changed files with 12 additions and 4 deletions

View file

@ -4761,6 +4761,7 @@ cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan)
sp_cost.per_tuple += plan->startup_cost;
}
subplan->disabled_nodes = plan->disabled_nodes;
subplan->startup_cost = sp_cost.startup;
subplan->per_call_cost = sp_cost.per_tuple;
}

View file

@ -2234,9 +2234,12 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
/*
* Compute the estimated cost of each subplan assuming num_exec
* executions, and keep the cheapest one. In event of exact equality of
* estimates, we prefer the later plan; this is a bit arbitrary, but in
* current usage it biases us to break ties against fast-start subplans.
* executions, and keep the cheapest one. If one subplan has more
* disabled nodes than another, choose the one with fewer disabled nodes
* regardless of cost; this parallels compare_path_costs. In event of
* exact equality of estimates, we prefer the later plan; this is a bit
* arbitrary, but in current usage it biases us to break ties against
* fast-start subplans.
*/
Assert(asplan->subplans != NIL);
@ -2246,7 +2249,10 @@ fix_alternative_subplan(PlannerInfo *root, AlternativeSubPlan *asplan,
Cost curcost;
curcost = curplan->startup_cost + num_exec * curplan->per_call_cost;
if (bestplan == NULL || curcost <= bestcost)
if (bestplan == NULL ||
curplan->disabled_nodes < bestplan->disabled_nodes ||
(curplan->disabled_nodes == bestplan->disabled_nodes &&
curcost <= bestcost))
{
bestplan = curplan;
bestcost = curcost;

View file

@ -1124,6 +1124,7 @@ typedef struct SubPlan
List *parParam; /* indices of input Params from parent plan */
List *args; /* exprs to pass as parParam values */
/* Estimated execution costs: */
int disabled_nodes; /* count of disabled nodes in the plan */
Cost startup_cost; /* one-time setup cost */
Cost per_call_cost; /* cost for each subplan evaluation */
} SubPlan;