Avoid a couple of zero-divide scenarios in the planner.
cost_subplan() supposed that the given subplan must have plan_rows > 0, which as far as I can tell was true until recent refactoring of the code in createplan.c; but now that code allows the Result for a provably empty subquery to have plan_rows = 0. Rather than undo that change, put in a clamp to prevent zero divide. get_cheapest_fractional_path() likewise supposed that best_path->rows > 0. This assumption has been wrong for longer. It's actually harmless given IEEE float math, because a positive value divided by zero gives +Infinity and compare_fractional_path_costs() will do the right thing with that. Still, best not to assume that. final_cost_nestloop() also seems to have some risks in this area, so borrow the clamping logic already present in the mergejoin cost functions. Lastly, remove unnecessary clamp_row_est() in planner.c's calls to get_number_of_groups(). The only thing that function does with path_rows is pass it to estimate_num_groups() which already has an internal clamp, so we don't need the extra call; and if we did, the callers are arguably the wrong place for it anyway. First two items reported by Piotr Stefaniak, the others are products of my nosing around for similar problems. No back-patch since there's no evidence that problems arise in the back branches.
This commit is contained in:
parent
676265eb7b
commit
76281aa964
|
@ -45,9 +45,10 @@
|
|||
* (total_cost - startup_cost) * tuples_to_fetch / path->rows;
|
||||
* Note that a base relation's rows count (and, by extension, plan_rows for
|
||||
* plan nodes below the LIMIT node) are set without regard to any LIMIT, so
|
||||
* that this equation works properly. (Also, these routines guarantee not to
|
||||
* set the rows count to zero, so there will be no zero divide.) The LIMIT is
|
||||
* applied as a top-level plan node.
|
||||
* that this equation works properly. (Note: while path->rows is never zero
|
||||
* for ordinary relations, it is zero for paths for provably-empty relations,
|
||||
* so beware of division-by-zero.) The LIMIT is applied as a top-level
|
||||
* plan node.
|
||||
*
|
||||
* For largely historical reasons, most of the routines in this module use
|
||||
* the passed result Path only to store their results (rows, startup_cost and
|
||||
|
@ -1991,6 +1992,12 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
|
|||
QualCost restrict_qual_cost;
|
||||
double ntuples;
|
||||
|
||||
/* Protect some assumptions below that rowcounts aren't zero or NaN */
|
||||
if (outer_path_rows <= 0 || isnan(outer_path_rows))
|
||||
outer_path_rows = 1;
|
||||
if (inner_path_rows <= 0 || isnan(inner_path_rows))
|
||||
inner_path_rows = 1;
|
||||
|
||||
/* Mark the path with the correct row estimate */
|
||||
if (path->path.param_info)
|
||||
path->path.rows = path->path.param_info->ppi_rows;
|
||||
|
@ -3025,8 +3032,8 @@ cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan)
|
|||
|
||||
if (subplan->subLinkType == EXISTS_SUBLINK)
|
||||
{
|
||||
/* we only need to fetch 1 tuple */
|
||||
sp_cost.per_tuple += plan_run_cost / plan->plan_rows;
|
||||
/* we only need to fetch 1 tuple; clamp to avoid zero divide */
|
||||
sp_cost.per_tuple += plan_run_cost / clamp_row_est(plan->plan_rows);
|
||||
}
|
||||
else if (subplan->subLinkType == ALL_SUBLINK ||
|
||||
subplan->subLinkType == ANY_SUBLINK)
|
||||
|
|
|
@ -3346,12 +3346,10 @@ create_grouping_paths(PlannerInfo *root,
|
|||
}
|
||||
|
||||
/*
|
||||
* Estimate number of groups. Note: if cheapest_path is a dummy, it will
|
||||
* have zero rowcount estimate, which we don't want to use for fear of
|
||||
* divide-by-zero. Hence clamp.
|
||||
* Estimate number of groups.
|
||||
*/
|
||||
dNumGroups = get_number_of_groups(root,
|
||||
clamp_row_est(cheapest_path->rows),
|
||||
cheapest_path->rows,
|
||||
rollup_lists,
|
||||
rollup_groupclauses);
|
||||
|
||||
|
@ -3415,7 +3413,7 @@ create_grouping_paths(PlannerInfo *root,
|
|||
|
||||
/* Estimate number of partial groups. */
|
||||
dNumPartialGroups = get_number_of_groups(root,
|
||||
clamp_row_est(cheapest_partial_path->rows),
|
||||
cheapest_partial_path->rows,
|
||||
NIL,
|
||||
NIL);
|
||||
|
||||
|
@ -4840,8 +4838,8 @@ get_cheapest_fractional_path(RelOptInfo *rel, double tuple_fraction)
|
|||
if (tuple_fraction <= 0.0)
|
||||
return best_path;
|
||||
|
||||
/* Convert absolute # of tuples to a fraction; no need to clamp */
|
||||
if (tuple_fraction >= 1.0)
|
||||
/* Convert absolute # of tuples to a fraction; no need to clamp to 0..1 */
|
||||
if (tuple_fraction >= 1.0 && best_path->rows > 0)
|
||||
tuple_fraction /= best_path->rows;
|
||||
|
||||
foreach(l, rel->pathlist)
|
||||
|
|
Loading…
Reference in New Issue