fix_infinite_row_estimates.patch
text/plain
Filename: fix_infinite_row_estimates.patch
Type: text/plain
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/optimizer/path/costsize.c | 22 | 24 |
| src/backend/optimizer/plan/createplan.c | 2 | 0 |
| src/backend/optimizer/util/pathnode.c | 6 | 0 |
| src/test/regress/expected/partition_join.out | 5 | 6 |
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index cd3716d494..edcae1f36a 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -45,10 +45,9 @@
* (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. (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.
+ * 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.
*
* 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
@@ -71,6 +70,7 @@
#include "postgres.h"
+#include <float.h>
#include <math.h>
#include "access/amapi.h"
@@ -189,11 +189,14 @@ double
clamp_row_est(double nrows)
{
/*
- * Force estimate to be at least one row, to make explain output look
- * better and to avoid possible divide-by-zero when interpolating costs.
- * Make it an integer, too.
+ * Avoid infinite and NaN row estimates. Costs derived from such values
+ * are going to be useless. Also estimate to be at least one row, to make
+ * explain output look better and to avoid possible divide-by-zero when
+ * interpolating costs. Make it an integer, too.
*/
- if (nrows <= 1.0)
+ if (isinf(nrows))
+ nrows = rint(DBL_MAX);
+ else if (nrows <= 1.0 || isnan(nrows))
nrows = 1.0;
else
nrows = rint(nrows);
@@ -2037,10 +2040,15 @@ cost_append(AppendPath *apath)
apath->path.startup_cost = 0;
apath->path.total_cost = 0;
- apath->path.rows = 0;
if (apath->subpaths == NIL)
+ {
+ /* Avoid 0 rows to prevent divide by 0 risks later in planning */
+ apath->path.rows = 1.0;
return;
+ }
+
+ apath->path.rows = 0;
if (!apath->path.parallel_aware)
{
@@ -2737,12 +2745,8 @@ 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;
-
+ Assert(outer_path_rows > 0);
+ Assert(inner_path_rows > 0);
/* Mark the path with the correct row estimate */
if (path->path.param_info)
path->path.rows = path->path.param_info->ppi_rows;
@@ -2952,12 +2956,8 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
innerendsel;
Path sort_path; /* dummy for result of cost_sort */
- /* 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;
-
+ Assert(outer_path_rows > 0);
+ Assert(inner_path_rows > 0);
/*
* A merge join will stop as soon as it exhausts either input stream
* (unless it's an outer join, in which case the outer side has to be
@@ -3185,9 +3185,7 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
rescannedtuples;
double rescanratio;
- /* Protect some assumptions below that rowcounts aren't zero or NaN */
- if (inner_path_rows <= 0 || isnan(inner_path_rows))
- inner_path_rows = 1;
+ Assert(inner_path_rows > 0);
/* Mark the path with the correct row estimate */
if (path->jpath.path.param_info)
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 3d7a4e373f..57f4d2d2b4 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1112,6 +1112,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path, int flags)
copy_generic_path_info(plan, (Path *) best_path);
+ plan->plan_rows = 0.0;
+
return plan;
}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 5281a2f998..02bec0e96f 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1321,6 +1321,9 @@ create_append_path(PlannerInfo *root,
if (rows >= 0)
pathnode->path.rows = rows;
+ /* Avoid infinite row estimates in extreme cases */
+ pathnode->path.rows = clamp_row_est(pathnode->path.rows);
+
return pathnode;
}
@@ -1465,6 +1468,9 @@ create_merge_append_path(PlannerInfo *root,
input_startup_cost, input_total_cost,
pathnode->path.rows);
+ /* Avoid infinite row estimates in extreme cases */
+ pathnode->path.rows = clamp_row_est(pathnode->path.rows);
+
return pathnode;
}
diff --git a/src/test/regress/expected/partition_join.out b/src/test/regress/expected/partition_join.out
index 0057f41caa..1155be8d35 100644
--- a/src/test/regress/expected/partition_join.out
+++ b/src/test/regress/expected/partition_join.out
@@ -1439,8 +1439,8 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
--------------------------------------------
Sort
Sort Key: a, t2.b
- -> Hash Left Join
- Hash Cond: (t2.b = a)
+ -> Nested Loop Left Join
+ Join Filter: (a = t2.b)
-> Append
-> Seq Scan on prt2_p1 t2_1
Filter: (a = 0)
@@ -1448,10 +1448,9 @@ SELECT t1.a, t1.c, t2.b, t2.c FROM (SELECT * FROM prt1 WHERE a = 1 AND a = 2) t1
Filter: (a = 0)
-> Seq Scan on prt2_p3 t2_3
Filter: (a = 0)
- -> Hash
- -> Result
- One-Time Filter: false
-(14 rows)
+ -> Result
+ One-Time Filter: false
+(13 rows)
--
-- tests for hash partitioned tables.