Skip to content

Commit c40ba5f

Browse files
committed
Fix rowcount estimate for SubqueryScan that's under a Gather.
SubqueryScan was always getting labeled with a rowcount estimate appropriate for non-parallel cases. However, nodes that are underneath a Gather should be treated as processing only one worker's share of the rows, whether the particular node is explicitly parallel-aware or not. Most non-scan-level node types get this right automatically because they base their rowcount estimate on that of their input sub-Path(s). But SubqueryScan didn't do that, instead using the whole-relation rowcount estimate as if it were a non-parallel-aware scan node. If there is a parallel-aware node below the SubqueryScan, this is wrong, and it results in inflating the cost estimates for nodes above the SubqueryScan, which can cause us to not choose a parallel plan, or choose a silly one --- as indeed is visible in the one regression test whose results change with this patch. (Although that plan tree appears to contain no SubqueryScans, there were some in it before setrefs.c deleted them.) To fix, use path->subpath->rows not baserel->tuples as the number of input tuples we'll process. This requires estimating the quals' selectivity afresh, which is slightly annoying; but it shouldn't really add much cost thanks to the caching done in RestrictInfo. This is pretty clearly a bug fix, but I'll refrain from back-patching as people might not appreciate plan choices changing in stable branches. The fact that it took us this long to identify the bug suggests that it's not a major problem. Per report from bucoo, though this is not his proposed patch. Discussion: https://postgr.es/m/202204121457159307248@sohu.com
1 parent dc2be6e commit c40ba5f

File tree

2 files changed

+22
-10
lines changed

2 files changed

+22
-10
lines changed

src/backend/optimizer/path/costsize.c

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1395,18 +1395,32 @@ cost_subqueryscan(SubqueryScanPath *path, PlannerInfo *root,
13951395
{
13961396
Cost startup_cost;
13971397
Cost run_cost;
1398+
List *qpquals;
13981399
QualCost qpqual_cost;
13991400
Cost cpu_per_tuple;
14001401

14011402
/* Should only be applied to base relations that are subqueries */
14021403
Assert(baserel->relid > 0);
14031404
Assert(baserel->rtekind == RTE_SUBQUERY);
14041405

1405-
/* Mark the path with the correct row estimate */
1406+
/*
1407+
* We compute the rowcount estimate as the subplan's estimate times the
1408+
* selectivity of relevant restriction clauses. In simple cases this will
1409+
* come out the same as baserel->rows; but when dealing with parallelized
1410+
* paths we must do it like this to get the right answer.
1411+
*/
14061412
if (param_info)
1407-
path->path.rows = param_info->ppi_rows;
1413+
qpquals = list_concat_copy(param_info->ppi_clauses,
1414+
baserel->baserestrictinfo);
14081415
else
1409-
path->path.rows = baserel->rows;
1416+
qpquals = baserel->baserestrictinfo;
1417+
1418+
path->path.rows = clamp_row_est(path->subpath->rows *
1419+
clauselist_selectivity(root,
1420+
qpquals,
1421+
0,
1422+
JOIN_INNER,
1423+
NULL));
14101424

14111425
/*
14121426
* Cost of path is cost of evaluating the subplan, plus cost of evaluating
@@ -1421,7 +1435,7 @@ cost_subqueryscan(SubqueryScanPath *path, PlannerInfo *root,
14211435

14221436
startup_cost = qpqual_cost.startup;
14231437
cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
1424-
run_cost = cpu_per_tuple * baserel->tuples;
1438+
run_cost = cpu_per_tuple * path->subpath->rows;
14251439

14261440
/* tlist eval costs are paid per output row, not per tuple scanned */
14271441
startup_cost += path->path.pathtarget->cost.startup;

src/test/regress/expected/incremental_sort.out

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,14 +1487,12 @@ explain (costs off) select * from t union select * from t order by 1,3;
14871487
-> Unique
14881488
-> Sort
14891489
Sort Key: t.a, t.b, t.c
1490-
-> Append
1491-
-> Gather
1492-
Workers Planned: 2
1490+
-> Gather
1491+
Workers Planned: 2
1492+
-> Parallel Append
14931493
-> Parallel Seq Scan on t
1494-
-> Gather
1495-
Workers Planned: 2
14961494
-> Parallel Seq Scan on t t_1
1497-
(13 rows)
1495+
(11 rows)
14981496

14991497
-- Full sort, not just incremental sort can be pushed below a gather merge path
15001498
-- by generate_useful_gather_paths.

0 commit comments

Comments
 (0)