summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/path/costsize.c155
-rw-r--r--src/backend/optimizer/path/joinpath.c15
-rw-r--r--src/backend/optimizer/plan/createplan.c2
-rw-r--r--src/backend/optimizer/plan/planner.c1
-rw-r--r--src/backend/optimizer/prep/prepunion.c6
-rw-r--r--src/backend/optimizer/util/pathnode.c212
-rw-r--r--src/include/nodes/pathnodes.h2
-rw-r--r--src/include/optimizer/cost.h10
-rw-r--r--src/include/optimizer/pathnode.h12
-rw-r--r--src/test/isolation/specs/horizons.spec1
-rw-r--r--src/test/regress/expected/btree_index.out12
-rw-r--r--src/test/regress/expected/select_parallel.out8
12 files changed, 316 insertions, 120 deletions
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 79991b19807..e1523d15df1 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -50,6 +50,17 @@
* so beware of division-by-zero.) The LIMIT is applied as a top-level
* plan node.
*
+ * Each path stores the total number of disabled nodes that exist at or
+ * below that point in the plan tree. This is regarded as a component of
+ * the cost, and paths with fewer disabled nodes should be regarded as
+ * cheaper than those with more. Disabled nodes occur when the user sets
+ * a GUC like enable_seqscan=false. We can't necessarily respect such a
+ * setting in every part of the plan tree, but we want to respect in as many
+ * parts of the plan tree as possible. Simpler schemes like storing a Boolean
+ * here rather than a count fail to do that. We used to disable nodes by
+ * adding a large constant to the startup cost, but that distorted planning
+ * in other ways.
+ *
* 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
* total_cost) into. All the input data they need is passed as separate
@@ -301,9 +312,6 @@ cost_seqscan(Path *path, PlannerInfo *root,
else
path->rows = baserel->rows;
- if (!enable_seqscan)
- startup_cost += disable_cost;
-
/* fetch estimated page cost for tablespace containing table */
get_tablespace_page_costs(baserel->reltablespace,
NULL,
@@ -346,6 +354,7 @@ cost_seqscan(Path *path, PlannerInfo *root,
path->rows = clamp_row_est(path->rows / parallel_divisor);
}
+ path->disabled_nodes = enable_seqscan ? 0 : 1;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + cpu_run_cost + disk_run_cost;
}
@@ -418,6 +427,7 @@ cost_samplescan(Path *path, PlannerInfo *root,
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ path->disabled_nodes = 0;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -456,6 +466,7 @@ cost_gather(GatherPath *path, PlannerInfo *root,
startup_cost += parallel_setup_cost;
run_cost += parallel_tuple_cost * path->path.rows;
+ path->path.disabled_nodes = path->subpath->disabled_nodes;
path->path.startup_cost = startup_cost;
path->path.total_cost = (startup_cost + run_cost);
}
@@ -473,6 +484,7 @@ cost_gather(GatherPath *path, PlannerInfo *root,
void
cost_gather_merge(GatherMergePath *path, PlannerInfo *root,
RelOptInfo *rel, ParamPathInfo *param_info,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double *rows)
{
@@ -490,9 +502,6 @@ cost_gather_merge(GatherMergePath *path, PlannerInfo *root,
else
path->path.rows = rel->rows;
- if (!enable_gathermerge)
- startup_cost += disable_cost;
-
/*
* Add one to the number of workers to account for the leader. This might
* be overgenerous since the leader will do less work than other workers
@@ -523,6 +532,8 @@ cost_gather_merge(GatherMergePath *path, PlannerInfo *root,
startup_cost += parallel_setup_cost;
run_cost += parallel_tuple_cost * path->path.rows * 1.05;
+ path->path.disabled_nodes = input_disabled_nodes
+ + (enable_gathermerge ? 0 : 1);
path->path.startup_cost = startup_cost + input_startup_cost;
path->path.total_cost = (startup_cost + run_cost + input_total_cost);
}
@@ -603,9 +614,8 @@ cost_index(IndexPath *path, PlannerInfo *root, double loop_count,
path->indexclauses);
}
- if (!enable_indexscan)
- startup_cost += disable_cost;
/* we don't need to check enable_indexonlyscan; indxpath.c does that */
+ path->path.disabled_nodes = enable_indexscan ? 0 : 1;
/*
* Call index-access-method-specific code to estimate the processing cost
@@ -1038,9 +1048,6 @@ cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
else
path->rows = baserel->rows;
- if (!enable_bitmapscan)
- startup_cost += disable_cost;
-
pages_fetched = compute_bitmap_pages(root, baserel, bitmapqual,
loop_count, &indexTotalCost,
&tuples_fetched);
@@ -1102,6 +1109,7 @@ cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ path->disabled_nodes = enable_bitmapscan ? 0 : 1;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -1187,6 +1195,7 @@ cost_bitmap_and_node(BitmapAndPath *path, PlannerInfo *root)
}
path->bitmapselectivity = selec;
path->path.rows = 0; /* per above, not used */
+ path->path.disabled_nodes = 0;
path->path.startup_cost = totalCost;
path->path.total_cost = totalCost;
}
@@ -1261,6 +1270,7 @@ cost_tidscan(Path *path, PlannerInfo *root,
/* Should only be applied to base relations */
Assert(baserel->relid > 0);
Assert(baserel->rtekind == RTE_RELATION);
+ Assert(tidquals != NIL);
/* Mark the path with the correct row estimate */
if (param_info)
@@ -1275,6 +1285,14 @@ cost_tidscan(Path *path, PlannerInfo *root,
RestrictInfo *rinfo = lfirst_node(RestrictInfo, l);
Expr *qual = rinfo->clause;
+ /*
+ * We must use a TID scan for CurrentOfExpr; in any other case, we
+ * should be generating a TID scan only if enable_tidscan=true. Also,
+ * if CurrentOfExpr is the qual, there should be only one.
+ */
+ Assert(enable_tidscan || IsA(qual, CurrentOfExpr));
+ Assert(list_length(tidquals) == 1 || !IsA(qual, CurrentOfExpr));
+
if (IsA(qual, ScalarArrayOpExpr))
{
/* Each element of the array yields 1 tuple */
@@ -1322,6 +1340,12 @@ cost_tidscan(Path *path, PlannerInfo *root,
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ /*
+ * There are assertions above verifying that we only reach this function
+ * either when enable_tidscan=true or when the TID scan is the only legal
+ * path, so it's safe to set disabled_nodes to zero here.
+ */
+ path->disabled_nodes = 0;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -1414,6 +1438,9 @@ cost_tidrangescan(Path *path, PlannerInfo *root,
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ /* we should not generate this path type when enable_tidscan=false */
+ Assert(enable_tidscan);
+ path->disabled_nodes = 0;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -1466,6 +1493,7 @@ cost_subqueryscan(SubqueryScanPath *path, PlannerInfo *root,
* SubqueryScan node, plus cpu_tuple_cost to account for selection and
* projection overhead.
*/
+ path->path.disabled_nodes = path->subpath->disabled_nodes;
path->path.startup_cost = path->subpath->startup_cost;
path->path.total_cost = path->subpath->total_cost;
@@ -1556,6 +1584,7 @@ cost_functionscan(Path *path, PlannerInfo *root,
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ path->disabled_nodes = 0;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -1612,6 +1641,7 @@ cost_tablefuncscan(Path *path, PlannerInfo *root,
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ path->disabled_nodes = 0;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -1659,6 +1689,7 @@ cost_valuesscan(Path *path, PlannerInfo *root,
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ path->disabled_nodes = 0;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -1706,6 +1737,7 @@ cost_ctescan(Path *path, PlannerInfo *root,
startup_cost += path->pathtarget->cost.startup;
run_cost += path->pathtarget->cost.per_tuple * path->rows;
+ path->disabled_nodes = 0;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -1743,6 +1775,7 @@ cost_namedtuplestorescan(Path *path, PlannerInfo *root,
cpu_per_tuple += cpu_tuple_cost + qpqual_cost.per_tuple;
run_cost += cpu_per_tuple * baserel->tuples;
+ path->disabled_nodes = 0;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -1777,6 +1810,7 @@ cost_resultscan(Path *path, PlannerInfo *root,
cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
run_cost += cpu_per_tuple * baserel->tuples;
+ path->disabled_nodes = 0;
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -1816,6 +1850,7 @@ cost_recursive_union(Path *runion, Path *nrterm, Path *rterm)
*/
total_cost += cpu_tuple_cost * total_rows;
+ runion->disabled_nodes = nrterm->disabled_nodes + rterm->disabled_nodes;
runion->startup_cost = startup_cost;
runion->total_cost = total_cost;
runion->rows = total_rows;
@@ -1964,6 +1999,7 @@ cost_tuplesort(Cost *startup_cost, Cost *run_cost,
void
cost_incremental_sort(Path *path,
PlannerInfo *root, List *pathkeys, int presorted_keys,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples, int width, Cost comparison_cost, int sort_mem,
double limit_tuples)
@@ -2083,6 +2119,11 @@ cost_incremental_sort(Path *path,
run_cost += 2.0 * cpu_tuple_cost * input_groups;
path->rows = input_tuples;
+
+ /* should not generate these paths when enable_incremental_sort=false */
+ Assert(enable_incremental_sort);
+ path->disabled_nodes = input_disabled_nodes;
+
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -2101,7 +2142,8 @@ cost_incremental_sort(Path *path,
*/
void
cost_sort(Path *path, PlannerInfo *root,
- List *pathkeys, Cost input_cost, double tuples, int width,
+ List *pathkeys, int input_disabled_nodes,
+ Cost input_cost, double tuples, int width,
Cost comparison_cost, int sort_mem,
double limit_tuples)
@@ -2114,12 +2156,10 @@ cost_sort(Path *path, PlannerInfo *root,
comparison_cost, sort_mem,
limit_tuples);
- if (!enable_sort)
- startup_cost += disable_cost;
-
startup_cost += input_cost;
path->rows = tuples;
+ path->disabled_nodes = input_disabled_nodes + (enable_sort ? 0 : 1);
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -2211,6 +2251,7 @@ cost_append(AppendPath *apath)
{
ListCell *l;
+ apath->path.disabled_nodes = 0;
apath->path.startup_cost = 0;
apath->path.total_cost = 0;
apath->path.rows = 0;
@@ -2232,12 +2273,16 @@ cost_append(AppendPath *apath)
*/
apath->path.startup_cost = firstsubpath->startup_cost;
- /* Compute rows and costs as sums of subplan rows and costs. */
+ /*
+ * Compute rows, number of disabled nodes, and total cost as sums
+ * of underlying subplan values.
+ */
foreach(l, apath->subpaths)
{
Path *subpath = (Path *) lfirst(l);
apath->path.rows += subpath->rows;
+ apath->path.disabled_nodes += subpath->disabled_nodes;
apath->path.total_cost += subpath->total_cost;
}
}
@@ -2277,6 +2322,7 @@ cost_append(AppendPath *apath)
cost_sort(&sort_path,
NULL, /* doesn't currently need root */
pathkeys,
+ subpath->disabled_nodes,
subpath->total_cost,
subpath->rows,
subpath->pathtarget->width,
@@ -2287,6 +2333,7 @@ cost_append(AppendPath *apath)
}
apath->path.rows += subpath->rows;
+ apath->path.disabled_nodes += subpath->disabled_nodes;
apath->path.startup_cost += subpath->startup_cost;
apath->path.total_cost += subpath->total_cost;
}
@@ -2335,6 +2382,7 @@ cost_append(AppendPath *apath)
apath->path.total_cost += subpath->total_cost;
}
+ apath->path.disabled_nodes += subpath->disabled_nodes;
apath->path.rows = clamp_row_est(apath->path.rows);
i++;
@@ -2375,6 +2423,7 @@ cost_append(AppendPath *apath)
*
* 'pathkeys' is a list of sort keys
* 'n_streams' is the number of input streams
+ * 'input_disabled_nodes' is the sum of the input streams' disabled node counts
* 'input_startup_cost' is the sum of the input streams' startup costs
* 'input_total_cost' is the sum of the input streams' total costs
* 'tuples' is the number of tuples in all the streams
@@ -2382,6 +2431,7 @@ cost_append(AppendPath *apath)
void
cost_merge_append(Path *path, PlannerInfo *root,
List *pathkeys, int n_streams,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double tuples)
{
@@ -2412,6 +2462,7 @@ cost_merge_append(Path *path, PlannerInfo *root,
*/
run_cost += cpu_tuple_cost * APPEND_CPU_COST_MULTIPLIER * tuples;
+ path->disabled_nodes = input_disabled_nodes;
path->startup_cost = startup_cost + input_startup_cost;
path->total_cost = startup_cost + run_cost + input_total_cost;
}
@@ -2430,6 +2481,7 @@ cost_merge_append(Path *path, PlannerInfo *root,
*/
void
cost_material(Path *path,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double tuples, int width)
{
@@ -2467,6 +2519,7 @@ cost_material(Path *path,
run_cost += seq_page_cost * npages;
}
+ path->disabled_nodes = input_disabled_nodes + (enable_material ? 0 : 1);
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
@@ -2630,6 +2683,7 @@ cost_agg(Path *path, PlannerInfo *root,
AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
int numGroupCols, double numGroups,
List *quals,
+ int disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples, double input_width)
{
@@ -2685,10 +2739,7 @@ cost_agg(Path *path, PlannerInfo *root,
startup_cost = input_startup_cost;
total_cost = input_total_cost;
if (aggstrategy == AGG_MIXED && !enable_hashagg)
- {
- startup_cost += disable_cost;
- total_cost += disable_cost;
- }
+ ++disabled_nodes;
/* calcs phrased this way to match HASHED case, see note above */
total_cost += aggcosts->transCost.startup;
total_cost += aggcosts->transCost.per_tuple * input_tuples;
@@ -2703,7 +2754,7 @@ cost_agg(Path *path, PlannerInfo *root,
/* must be AGG_HASHED */
startup_cost = input_total_cost;
if (!enable_hashagg)
- startup_cost += disable_cost;
+ ++disabled_nodes;
startup_cost += aggcosts->transCost.startup;
startup_cost += aggcosts->transCost.per_tuple * input_tuples;
/* cost of computing hash value */
@@ -2812,6 +2863,7 @@ cost_agg(Path *path, PlannerInfo *root,
}
path->rows = output_tuples;
+ path->disabled_nodes = disabled_nodes;
path->startup_cost = startup_cost;
path->total_cost = total_cost;
}
@@ -3046,6 +3098,7 @@ get_windowclause_startup_tuples(PlannerInfo *root, WindowClause *wc,
void
cost_windowagg(Path *path, PlannerInfo *root,
List *windowFuncs, WindowClause *winclause,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples)
{
@@ -3111,6 +3164,7 @@ cost_windowagg(Path *path, PlannerInfo *root,
total_cost += cpu_tuple_cost * input_tuples;
path->rows = input_tuples;
+ path->disabled_nodes = input_disabled_nodes;
path->startup_cost = startup_cost;
path->total_cost = total_cost;
@@ -3142,6 +3196,7 @@ void
cost_group(Path *path, PlannerInfo *root,
int numGroupCols, double numGroups,
List *quals,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples)
{
@@ -3180,6 +3235,7 @@ cost_group(Path *path, PlannerInfo *root,
}
path->rows = output_tuples;
+ path->disabled_nodes = input_disabled_nodes;
path->startup_cost = startup_cost;
path->total_cost = total_cost;
}
@@ -3214,6 +3270,7 @@ initial_cost_nestloop(PlannerInfo *root, JoinCostWorkspace *workspace,
Path *outer_path, Path *inner_path,
JoinPathExtraData *extra)
{
+ int disabled_nodes;
Cost startup_cost = 0;
Cost run_cost = 0;
double outer_path_rows = outer_path->rows;
@@ -3222,6 +3279,11 @@ initial_cost_nestloop(PlannerInfo *root, JoinCostWorkspace *workspace,
Cost inner_run_cost;
Cost inner_rescan_run_cost;
+ /* Count up disabled nodes. */
+ disabled_nodes = enable_nestloop ? 0 : 1;
+ disabled_nodes += inner_path->disabled_nodes;
+ disabled_nodes += outer_path->disabled_nodes;
+
/* estimate costs to rescan the inner relation */
cost_rescan(root, inner_path,
&inner_rescan_start_cost,
@@ -3269,6 +3331,7 @@ initial_cost_nestloop(PlannerInfo *root, JoinCostWorkspace *workspace,
/* CPU costs left for later */
/* Public result fields */
+ workspace->disabled_nodes = disabled_nodes;
workspace->startup_cost = startup_cost;
workspace->total_cost = startup_cost + run_cost;
/* Save private data for final_cost_nestloop */
@@ -3298,6 +3361,9 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
QualCost restrict_qual_cost;
double ntuples;
+ /* Set the number of disabled nodes. */
+ path->jpath.path.disabled_nodes = workspace->disabled_nodes;
+
/* Protect some assumptions below that rowcounts aren't zero */
if (outer_path_rows <= 0)
outer_path_rows = 1;
@@ -3318,14 +3384,6 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path,
clamp_row_est(path->jpath.path.rows / parallel_divisor);
}
- /*
- * We could include disable_cost in the preliminary estimate, but that
- * would amount to optimizing for the case where the join method is
- * disabled, which doesn't seem like the way to bet.
- */
- if (!enable_nestloop)
- startup_cost += disable_cost;
-
/* cost of inner-relation source data (we already dealt with outer rel) */
if (path->jpath.jointype == JOIN_SEMI || path->jpath.jointype == JOIN_ANTI ||
@@ -3497,6 +3555,7 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
List *outersortkeys, List *innersortkeys,
JoinPathExtraData *extra)
{
+ int disabled_nodes;
Cost startup_cost = 0;
Cost run_cost = 0;
double outer_path_rows = outer_path->rows;
@@ -3617,6 +3676,8 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
Assert(outerstartsel <= outerendsel);
Assert(innerstartsel <= innerendsel);
+ disabled_nodes = enable_mergejoin ? 0 : 1;
+
/* cost of source data */
if (outersortkeys) /* do we need to sort outer? */
@@ -3624,12 +3685,14 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
cost_sort(&sort_path,
root,
outersortkeys,
+ outer_path->disabled_nodes,
outer_path->total_cost,
outer_path_rows,
outer_path->pathtarget->width,
0.0,
work_mem,
-1.0);
+ disabled_nodes += sort_path.disabled_nodes;
startup_cost += sort_path.startup_cost;
startup_cost += (sort_path.total_cost - sort_path.startup_cost)
* outerstartsel;
@@ -3638,6 +3701,7 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
}
else
{
+ disabled_nodes += outer_path->disabled_nodes;
startup_cost += outer_path->startup_cost;
startup_cost += (outer_path->total_cost - outer_path->startup_cost)
* outerstartsel;
@@ -3650,12 +3714,14 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
cost_sort(&sort_path,
root,
innersortkeys,
+ inner_path->disabled_nodes,
inner_path->total_cost,
inner_path_rows,
inner_path->pathtarget->width,
0.0,
work_mem,
-1.0);
+ disabled_nodes += sort_path.disabled_nodes;
startup_cost += sort_path.startup_cost;
startup_cost += (sort_path.total_cost - sort_path.startup_cost)
* innerstartsel;
@@ -3664,6 +3730,7 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
}
else
{
+ disabled_nodes += inner_path->disabled_nodes;
startup_cost += inner_path->startup_cost;
startup_cost += (inner_path->total_cost - inner_path->startup_cost)
* innerstartsel;
@@ -3682,6 +3749,7 @@ initial_cost_mergejoin(PlannerInfo *root, JoinCostWorkspace *workspace,
/* CPU costs left for later */
/* Public result fields */
+ workspace->disabled_nodes = disabled_nodes;
workspace->startup_cost = startup_cost;
workspace->total_cost = startup_cost + run_cost + inner_run_cost;
/* Save private data for final_cost_mergejoin */
@@ -3746,6 +3814,9 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
rescannedtuples;
double rescanratio;
+ /* Set the number of disabled nodes. */
+ path->jpath.path.disabled_nodes = workspace->disabled_nodes;
+
/* Protect some assumptions below that rowcounts aren't zero */
if (inner_path_rows <= 0)
inner_path_rows = 1;
@@ -3766,14 +3837,6 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
}
/*
- * We could include disable_cost in the preliminary estimate, but that
- * would amount to optimizing for the case where the join method is
- * disabled, which doesn't seem like the way to bet.
- */
- if (!enable_mergejoin)
- startup_cost += disable_cost;
-
- /*
* Compute cost of the mergequals and qpquals (other restriction clauses)
* separately.
*/
@@ -4056,6 +4119,7 @@ initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace,
JoinPathExtraData *extra,
bool parallel_hash)
{
+ int disabled_nodes;
Cost startup_cost = 0;
Cost run_cost = 0;
double outer_path_rows = outer_path->rows;
@@ -4067,6 +4131,11 @@ initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace,
int num_skew_mcvs;
size_t space_allowed; /* unused */
+ /* Count up disabled nodes. */
+ disabled_nodes = enable_hashjoin ? 0 : 1;
+ disabled_nodes += inner_path->disabled_nodes;
+ disabled_nodes += outer_path->disabled_nodes;
+
/* cost of source data */
startup_cost += outer_path->startup_cost;
run_cost += outer_path->total_cost - outer_path->startup_cost;
@@ -4136,6 +4205,7 @@ initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace,
/* CPU costs left for later */
/* Public result fields */
+ workspace->disabled_nodes = disabled_nodes;
workspace->startup_cost = startup_cost;
workspace->total_cost = startup_cost + run_cost;
/* Save private data for final_cost_hashjoin */
@@ -4180,6 +4250,9 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
Selectivity innermcvfreq;
ListCell *hcl;
+ /* Set the number of disabled nodes. */
+ path->jpath.path.disabled_nodes = workspace->disabled_nodes;
+
/* Mark the path with the correct row estimate */
if (path->jpath.path.param_info)
path->jpath.path.rows = path->jpath.path.param_info->ppi_rows;
@@ -4195,14 +4268,6 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path,
clamp_row_est(path->jpath.path.rows / parallel_divisor);
}
- /*
- * We could include disable_cost in the preliminary estimate, but that
- * would amount to optimizing for the case where the join method is
- * disabled, which doesn't seem like the way to bet.
- */
- if (!enable_hashjoin)
- startup_cost += disable_cost;
-
/* mark the path with estimated # of batches */
path->num_batches = numbatches;
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index e858f596005..b0e8c94dfc3 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -915,7 +915,7 @@ try_nestloop_path(PlannerInfo *root,
initial_cost_nestloop(root, &workspace, jointype,
outer_path, inner_path, extra);
- if (add_path_precheck(joinrel,
+ if (add_path_precheck(joinrel, workspace.disabled_nodes,
workspace.startup_cost, workspace.total_cost,
pathkeys, required_outer))
{
@@ -999,7 +999,8 @@ try_partial_nestloop_path(PlannerInfo *root,
*/
initial_cost_nestloop(root, &workspace, jointype,
outer_path, inner_path, extra);
- if (!add_partial_path_precheck(joinrel, workspace.total_cost, pathkeys))
+ if (!add_partial_path_precheck(joinrel, workspace.disabled_nodes,
+ workspace.total_cost, pathkeys))
return;
/* Might be good enough to be worth trying, so let's try it. */
@@ -1096,7 +1097,7 @@ try_mergejoin_path(PlannerInfo *root,
outersortkeys, innersortkeys,
extra);
- if (add_path_precheck(joinrel,
+ if (add_path_precheck(joinrel, workspace.disabled_nodes,
workspace.startup_cost, workspace.total_cost,
pathkeys, required_outer))
{
@@ -1168,7 +1169,8 @@ try_partial_mergejoin_path(PlannerInfo *root,
outersortkeys, innersortkeys,
extra);
- if (!add_partial_path_precheck(joinrel, workspace.total_cost, pathkeys))
+ if (!add_partial_path_precheck(joinrel, workspace.disabled_nodes,
+ workspace.total_cost, pathkeys))
return;
/* Might be good enough to be worth trying, so let's try it. */
@@ -1237,7 +1239,7 @@ try_hashjoin_path(PlannerInfo *root,
initial_cost_hashjoin(root, &workspace, jointype, hashclauses,
outer_path, inner_path, extra, false);
- if (add_path_precheck(joinrel,
+ if (add_path_precheck(joinrel, workspace.disabled_nodes,
workspace.startup_cost, workspace.total_cost,
NIL, required_outer))
{
@@ -1298,7 +1300,8 @@ try_partial_hashjoin_path(PlannerInfo *root,
*/
initial_cost_hashjoin(root, &workspace, jointype, hashclauses,
outer_path, inner_path, extra, parallel_hash);
- if (!add_partial_path_precheck(joinrel, workspace.total_cost, NIL))
+ if (!add_partial_path_precheck(joinrel, workspace.disabled_nodes,
+ workspace.total_cost, NIL))
return;
/* Might be good enough to be worth trying, so let's try it. */
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 28addc1129a..1960e59ef2f 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -5452,6 +5452,7 @@ label_sort_with_costsize(PlannerInfo *root, Sort *plan, double limit_tuples)
cost_sort(&sort_path, root, NIL,
lefttree->total_cost,
+ 0, /* a Plan contains no count of disabled nodes */
lefttree->plan_rows,
lefttree->plan_width,
0.0,
@@ -6546,6 +6547,7 @@ materialize_finished_plan(Plan *subplan)
/* Set cost data */
cost_material(&matpath,
+ 0, /* a Plan contains no count of disabled nodes */
subplan->startup_cost,
subplan->total_cost,
subplan->plan_rows,
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 948afd90948..b5827d39808 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -6748,6 +6748,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
/* Estimate the cost of seq scan + sort */
seqScanPath = create_seqscan_path(root, rel, NULL, 0);
cost_sort(&seqScanAndSortPath, root, NIL,
+ seqScanPath->disabled_nodes,
seqScanPath->total_cost, rel->tuples, rel->reltarget->width,
comparisonCost, maintenance_work_mem, -1.0);
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 1c69c6e97e8..a0baf6d4a18 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -1346,6 +1346,7 @@ choose_hashed_setop(PlannerInfo *root, List *groupClauses,
cost_agg(&hashed_p, root, AGG_HASHED, NULL,
numGroupCols, dNumGroups,
NIL,
+ input_path->disabled_nodes,
input_path->startup_cost, input_path->total_cost,
input_path->rows, input_path->pathtarget->width);
@@ -1353,14 +1354,17 @@ choose_hashed_setop(PlannerInfo *root, List *groupClauses,
* Now for the sorted case. Note that the input is *always* unsorted,
* since it was made by appending unrelated sub-relations together.
*/
+ sorted_p.disabled_nodes = input_path->disabled_nodes;
sorted_p.startup_cost = input_path->startup_cost;
sorted_p.total_cost = input_path->total_cost;
/* XXX cost_sort doesn't actually look at pathkeys, so just pass NIL */
- cost_sort(&sorted_p, root, NIL, sorted_p.total_cost,
+ cost_sort(&sorted_p, root, NIL, sorted_p.disabled_nodes,
+ sorted_p.total_cost,
input_path->rows, input_path->pathtarget->width,
0.0, work_mem, -1.0);
cost_group(&sorted_p, root, numGroupCols, dNumGroups,
NIL,
+ sorted_p.disabled_nodes,
sorted_p.startup_cost, sorted_p.total_cost,
input_path->rows);
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 54e042a8a59..fc97bf6ee26 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -68,6 +68,15 @@ static bool pathlist_is_reparameterizable_by_child(List *pathlist,
int
compare_path_costs(Path *path1, Path *path2, CostSelector criterion)
{
+ /* Number of disabled nodes, if different, trumps all else. */
+ if (unlikely(path1->disabled_nodes != path2->disabled_nodes))
+ {
+ if (path1->disabled_nodes < path2->disabled_nodes)
+ return -1;
+ else
+ return +1;
+ }
+
if (criterion == STARTUP_COST)
{
if (path1->startup_cost < path2->startup_cost)
@@ -118,6 +127,15 @@ compare_fractional_path_costs(Path *path1, Path *path2,
Cost cost1,
cost2;
+ /* Number of disabled nodes, if different, trumps all else. */
+ if (unlikely(path1->disabled_nodes != path2->disabled_nodes))
+ {
+ if (path1->disabled_nodes < path2->disabled_nodes)
+ return -1;
+ else
+ return +1;
+ }
+
if (fraction <= 0.0 || fraction >= 1.0)
return compare_path_costs(path1, path2, TOTAL_COST);
cost1 = path1->startup_cost +
@@ -166,6 +184,15 @@ compare_path_costs_fuzzily(Path *path1, Path *path2, double fuzz_factor)
#define CONSIDER_PATH_STARTUP_COST(p) \
((p)->param_info == NULL ? (p)->parent->consider_startup : (p)->parent->consider_param_startup)
+ /* Number of disabled nodes, if different, trumps all else. */
+ if (unlikely(path1->disabled_nodes != path2->disabled_nodes))
+ {
+ if (path1->disabled_nodes < path2->disabled_nodes)
+ return COSTS_BETTER1;
+ else
+ return COSTS_BETTER2;
+ }
+
/*
* Check total cost first since it's more likely to be different; many
* paths have zero startup cost.
@@ -362,15 +389,29 @@ set_cheapest(RelOptInfo *parent_rel)
* add_path
* Consider a potential implementation path for the specified parent rel,
* and add it to the rel's pathlist if it is worthy of consideration.
+ *
* A path is worthy if it has a better sort order (better pathkeys) or
- * cheaper cost (on either dimension), or generates fewer rows, than any
- * existing path that has the same or superset parameterization rels.
- * We also consider parallel-safe paths more worthy than others.
+ * cheaper cost (as defined below), or generates fewer rows, than any
+ * existing path that has the same or superset parameterization rels. We
+ * also consider parallel-safe paths more worthy than others.
+ *
+ * Cheaper cost can mean either a cheaper total cost or a cheaper startup
+ * cost; if one path is cheaper in one of these aspects and another is
+ * cheaper in the other, we keep both. However, when some path type is
+ * disabled (e.g. due to enable_seqscan=false), the number of times that
+ * a disabled path type is used is considered to be a higher-order
+ * component of the cost. Hence, if path A uses no disabled path type,
+ * and path B uses 1 or more disabled path types, A is cheaper, no matter
+ * what we estimate for the startup and total costs. The startup and total
+ * cost essentially act as a tiebreak when comparing paths that use equal
+ * numbers of disabled path nodes; but in practice this tiebreak is almost
+ * always used, since normally no path types are disabled.
*
- * We also remove from the rel's pathlist any old paths that are dominated
- * by new_path --- that is, new_path is cheaper, at least as well ordered,
- * generates no more rows, requires no outer rels not required by the old
- * path, and is no less parallel-safe.
+ * In addition to possibly adding new_path, we also remove from the rel's
+ * pathlist any old paths that are dominated by new_path --- that is,
+ * new_path is cheaper, at least as well ordered, generates no more rows,
+ * requires no outer rels not required by the old path, and is no less
+ * parallel-safe.
*
* In most cases, a path with a superset parameterization will generate
* fewer rows (since it has more join clauses to apply), so that those two
@@ -389,10 +430,10 @@ set_cheapest(RelOptInfo *parent_rel)
* parent_rel->consider_param_startup is true for a parameterized one.
* Again, this allows discarding useless paths sooner.
*
- * The pathlist is kept sorted by total_cost, with cheaper paths
- * at the front. Within this routine, that's simply a speed hack:
- * doing it that way makes it more likely that we will reject an inferior
- * path after a few comparisons, rather than many comparisons.
+ * The pathlist is kept sorted by disabled_nodes and then by total_cost,
+ * with cheaper paths at the front. Within this routine, that's simply a
+ * speed hack: doing it that way makes it more likely that we will reject
+ * an inferior path after a few comparisons, rather than many comparisons.
* However, add_path_precheck relies on this ordering to exit early
* when possible.
*
@@ -593,8 +634,13 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
}
else
{
- /* new belongs after this old path if it has cost >= old's */
- if (new_path->total_cost >= old_path->total_cost)
+ /*
+ * new belongs after this old path if it has more disabled nodes
+ * or if it has the same number of nodes but a greater total cost
+ */
+ if (new_path->disabled_nodes > old_path->disabled_nodes ||
+ (new_path->disabled_nodes == old_path->disabled_nodes &&
+ new_path->total_cost >= old_path->total_cost))
insert_at = foreach_current_index(p1) + 1;
}
@@ -639,7 +685,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
* so the required information has to be passed piecemeal.
*/
bool
-add_path_precheck(RelOptInfo *parent_rel,
+add_path_precheck(RelOptInfo *parent_rel, int disabled_nodes,
Cost startup_cost, Cost total_cost,
List *pathkeys, Relids required_outer)
{
@@ -659,6 +705,20 @@ add_path_precheck(RelOptInfo *parent_rel,
PathKeysComparison keyscmp;
/*
+ * Since the pathlist is sorted by disabled_nodes and then by
+ * total_cost, we can stop looking once we reach a path with more
+ * disabled nodes, or the same number of disabled nodes plus a
+ * total_cost larger than the new path's.
+ */
+ if (unlikely(old_path->disabled_nodes != disabled_nodes))
+ {
+ if (disabled_nodes < old_path->disabled_nodes)
+ break;
+ }
+ else if (total_cost <= old_path->total_cost * STD_FUZZ_FACTOR)
+ break;
+
+ /*
* We are looking for an old_path with the same parameterization (and
* by assumption the same rowcount) that dominates the new path on
* pathkeys as well as both cost metrics. If we find one, we can
@@ -666,39 +726,27 @@ add_path_precheck(RelOptInfo *parent_rel,
*
* Cost comparisons here should match compare_path_costs_fuzzily.
*/
- if (total_cost > old_path->total_cost * STD_FUZZ_FACTOR)
+ /* new path can win on startup cost only if consider_startup */
+ if (startup_cost > old_path->startup_cost * STD_FUZZ_FACTOR ||
+ !consider_startup)
{
- /* new path can win on startup cost only if consider_startup */
- if (startup_cost > old_path->startup_cost * STD_FUZZ_FACTOR ||
- !consider_startup)
+ /* new path loses on cost, so check pathkeys... */
+ List *old_path_pathkeys;
+
+ old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys;
+ keyscmp = compare_pathkeys(new_path_pathkeys,
+ old_path_pathkeys);
+ if (keyscmp == PATHKEYS_EQUAL ||
+ keyscmp == PATHKEYS_BETTER2)
{
- /* new path loses on cost, so check pathkeys... */
- List *old_path_pathkeys;
-
- old_path_pathkeys = old_path->param_info ? NIL : old_path->pathkeys;
- keyscmp = compare_pathkeys(new_path_pathkeys,
- old_path_pathkeys);
- if (keyscmp == PATHKEYS_EQUAL ||
- keyscmp == PATHKEYS_BETTER2)
+ /* new path does not win on pathkeys... */
+ if (bms_equal(required_outer, PATH_REQ_OUTER(old_path)))
{
- /* new path does not win on pathkeys... */
- if (bms_equal(required_outer, PATH_REQ_OUTER(old_path)))
- {
- /* Found an old path that dominates the new one */
- return false;
- }
+ /* Found an old path that dominates the new one */
+ return false;
}
}
}
- else
- {
- /*
- * Since the pathlist is sorted by total_cost, we can stop looking
- * once we reach a path with a total_cost larger than the new
- * path's.
- */
- break;
- }
}
return true;
@@ -734,7 +782,7 @@ add_path_precheck(RelOptInfo *parent_rel,
* produce the same number of rows. Neither do we need to consider startup
* costs: parallelism is only used for plans that will be run to completion.
* Therefore, this routine is much simpler than add_path: it needs to
- * consider only pathkeys and total cost.
+ * consider only disabled nodes, pathkeys and total cost.
*
* As with add_path, we pfree paths that are found to be dominated by
* another partial path; this requires that there be no other references to
@@ -775,7 +823,15 @@ add_partial_path(RelOptInfo *parent_rel, Path *new_path)
/* Unless pathkeys are incompatible, keep just one of the two paths. */
if (keyscmp != PATHKEYS_DIFFERENT)
{
- if (new_path->total_cost > old_path->total_cost * STD_FUZZ_FACTOR)
+ if (unlikely(new_path->disabled_nodes != old_path->disabled_nodes))
+ {
+ if (new_path->disabled_nodes > old_path->disabled_nodes)
+ accept_new = false;
+ else
+ remove_old = true;
+ }
+ else if (new_path->total_cost > old_path->total_cost
+ * STD_FUZZ_FACTOR)
{
/* New path costs more; keep it only if pathkeys are better. */
if (keyscmp != PATHKEYS_BETTER1)
@@ -862,8 +918,8 @@ add_partial_path(RelOptInfo *parent_rel, Path *new_path)
* is surely a loser.
*/
bool
-add_partial_path_precheck(RelOptInfo *parent_rel, Cost total_cost,
- List *pathkeys)
+add_partial_path_precheck(RelOptInfo *parent_rel, int disabled_nodes,
+ Cost total_cost, List *pathkeys)
{
ListCell *p1;
@@ -906,8 +962,8 @@ add_partial_path_precheck(RelOptInfo *parent_rel, Cost total_cost,
* partial path; the resulting plans, if run in parallel, will be run to
* completion.
*/
- if (!add_path_precheck(parent_rel, total_cost, total_cost, pathkeys,
- NULL))
+ if (!add_path_precheck(parent_rel, disabled_nodes, total_cost, total_cost,
+ pathkeys, NULL))
return false;
return true;
@@ -1419,6 +1475,7 @@ create_merge_append_path(PlannerInfo *root,
Relids required_outer)
{
MergeAppendPath *pathnode = makeNode(MergeAppendPath);
+ int input_disabled_nodes;
Cost input_startup_cost;
Cost input_total_cost;
ListCell *l;
@@ -1452,6 +1509,7 @@ create_merge_append_path(PlannerInfo *root,
* Add up the sizes and costs of the input paths.
*/
pathnode->path.rows = 0;
+ input_disabled_nodes = 0;
input_startup_cost = 0;
input_total_cost = 0;
foreach(l, subpaths)
@@ -1468,6 +1526,7 @@ create_merge_append_path(PlannerInfo *root,
if (pathkeys_contained_in(pathkeys, subpath->pathkeys))
{
/* Subpath is adequately ordered, we won't need to sort it */
+ input_disabled_nodes += subpath->disabled_nodes;
input_startup_cost += subpath->startup_cost;
input_total_cost += subpath->total_cost;
}
@@ -1479,12 +1538,14 @@ create_merge_append_path(PlannerInfo *root,
cost_sort(&sort_path,
root,
pathkeys,
+ subpath->disabled_nodes,
subpath->total_cost,
subpath->rows,
subpath->pathtarget->width,
0.0,
work_mem,
pathnode->limit_tuples);
+ input_disabled_nodes += sort_path.disabled_nodes;
input_startup_cost += sort_path.startup_cost;
input_total_cost += sort_path.total_cost;
}
@@ -1500,12 +1561,14 @@ create_merge_append_path(PlannerInfo *root,
((Path *) linitial(subpaths))->parallel_aware ==
pathnode->path.parallel_aware)
{
+ pathnode->path.disabled_nodes = input_disabled_nodes;
pathnode->path.startup_cost = input_startup_cost;
pathnode->path.total_cost = input_total_cost;
}
else
cost_merge_append(&pathnode->path, root,
pathkeys, list_length(subpaths),
+ input_disabled_nodes,
input_startup_cost, input_total_cost,
pathnode->path.rows);
@@ -1587,6 +1650,7 @@ create_material_path(RelOptInfo *rel, Path *subpath)
pathnode->subpath = subpath;
cost_material(&pathnode->path,
+ subpath->disabled_nodes,
subpath->startup_cost,
subpath->total_cost,
subpath->rows,
@@ -1633,6 +1697,10 @@ create_memoize_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
*/
pathnode->est_entries = 0;
+ /* we should not generate this path type when enable_memoize=false */
+ Assert(enable_memoize);
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
+
/*
* Add a small additional charge for caching the first entry. All the
* harder calculations for rescans are performed in cost_memoize_rescan().
@@ -1732,6 +1800,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
{
pathnode->umethod = UNIQUE_PATH_NOOP;
pathnode->path.rows = rel->rows;
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost = subpath->total_cost;
pathnode->path.pathkeys = subpath->pathkeys;
@@ -1770,6 +1839,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
{
pathnode->umethod = UNIQUE_PATH_NOOP;
pathnode->path.rows = rel->rows;
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost = subpath->total_cost;
pathnode->path.pathkeys = subpath->pathkeys;
@@ -1797,6 +1867,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
* Estimate cost for sort+unique implementation
*/
cost_sort(&sort_path, root, NIL,
+ subpath->disabled_nodes,
subpath->total_cost,
rel->rows,
subpath->pathtarget->width,
@@ -1834,6 +1905,7 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
AGG_HASHED, NULL,
numCols, pathnode->path.rows,
NIL,
+ subpath->disabled_nodes,
subpath->startup_cost,
subpath->total_cost,
rel->rows,
@@ -1842,7 +1914,9 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
if (sjinfo->semi_can_btree && sjinfo->semi_can_hash)
{
- if (agg_path.total_cost < sort_path.total_cost)
+ if (agg_path.disabled_nodes < sort_path.disabled_nodes ||
+ (agg_path.disabled_nodes == sort_path.disabled_nodes &&
+ agg_path.total_cost < sort_path.total_cost))
pathnode->umethod = UNIQUE_PATH_HASH;
else
pathnode->umethod = UNIQUE_PATH_SORT;
@@ -1860,11 +1934,13 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
if (pathnode->umethod == UNIQUE_PATH_HASH)
{
+ pathnode->path.disabled_nodes = agg_path.disabled_nodes;
pathnode->path.startup_cost = agg_path.startup_cost;
pathnode->path.total_cost = agg_path.total_cost;
}
else
{
+ pathnode->path.disabled_nodes = sort_path.disabled_nodes;
pathnode->path.startup_cost = sort_path.startup_cost;
pathnode->path.total_cost = sort_path.total_cost;
}
@@ -1888,6 +1964,7 @@ create_gather_merge_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
Relids required_outer, double *rows)
{
GatherMergePath *pathnode = makeNode(GatherMergePath);
+ int input_disabled_nodes = 0;
Cost input_startup_cost = 0;
Cost input_total_cost = 0;
@@ -1915,11 +1992,13 @@ create_gather_merge_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,
pathnode->path.pathkeys = pathkeys;
pathnode->path.pathtarget = target ? target : rel->reltarget;
+ input_disabled_nodes += subpath->disabled_nodes;
input_startup_cost += subpath->startup_cost;
input_total_cost += subpath->total_cost;
cost_gather_merge(pathnode, root, rel, pathnode->path.param_info,
- input_startup_cost, input_total_cost, rows);
+ input_disabled_nodes, input_startup_cost,
+ input_total_cost, rows);
return pathnode;
}
@@ -2227,7 +2306,8 @@ create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel,
ForeignPath *
create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
PathTarget *target,
- double rows, Cost startup_cost, Cost total_cost,
+ double rows, int disabled_nodes,
+ Cost startup_cost, Cost total_cost,
List *pathkeys,
Relids required_outer,
Path *fdw_outerpath,
@@ -2248,6 +2328,7 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
pathnode->path.parallel_safe = rel->consider_parallel;
pathnode->path.parallel_workers = 0;
pathnode->path.rows = rows;
+ pathnode->path.disabled_nodes = disabled_nodes;
pathnode->path.startup_cost = startup_cost;
pathnode->path.total_cost = total_cost;
pathnode->path.pathkeys = pathkeys;
@@ -2273,7 +2354,8 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
ForeignPath *
create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
PathTarget *target,
- double rows, Cost startup_cost, Cost total_cost,
+ double rows, int disabled_nodes,
+ Cost startup_cost, Cost total_cost,
List *pathkeys,
Relids required_outer,
Path *fdw_outerpath,
@@ -2300,6 +2382,7 @@ create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
pathnode->path.parallel_safe = rel->consider_parallel;
pathnode->path.parallel_workers = 0;
pathnode->path.rows = rows;
+ pathnode->path.disabled_nodes = disabled_nodes;
pathnode->path.startup_cost = startup_cost;
pathnode->path.total_cost = total_cost;
pathnode->path.pathkeys = pathkeys;
@@ -2325,7 +2408,8 @@ create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
ForeignPath *
create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel,
PathTarget *target,
- double rows, Cost startup_cost, Cost total_cost,
+ double rows, int disabled_nodes,
+ Cost startup_cost, Cost total_cost,
List *pathkeys,
Path *fdw_outerpath,
List *fdw_restrictinfo,
@@ -2347,6 +2431,7 @@ create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel,
pathnode->path.parallel_safe = rel->consider_parallel;
pathnode->path.parallel_workers = 0;
pathnode->path.rows = rows;
+ pathnode->path.disabled_nodes = disabled_nodes;
pathnode->path.startup_cost = startup_cost;
pathnode->path.total_cost = total_cost;
pathnode->path.pathkeys = pathkeys;
@@ -2734,6 +2819,7 @@ create_projection_path(PlannerInfo *root,
* Set cost of plan as subpath's cost, adjusted for tlist replacement.
*/
pathnode->path.rows = subpath->rows;
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
pathnode->path.startup_cost = subpath->startup_cost +
(target->cost.startup - oldtarget->cost.startup);
pathnode->path.total_cost = subpath->total_cost +
@@ -2750,6 +2836,7 @@ create_projection_path(PlannerInfo *root,
* evaluating the tlist. There is no qual to worry about.
*/
pathnode->path.rows = subpath->rows;
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
pathnode->path.startup_cost = subpath->startup_cost +
target->cost.startup;
pathnode->path.total_cost = subpath->total_cost +
@@ -2917,6 +3004,7 @@ create_set_projection_path(PlannerInfo *root,
* This is slightly bizarre maybe, but it's what 9.6 did; we may revisit
* this estimate later.
*/
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
pathnode->path.rows = subpath->rows * tlist_rows;
pathnode->path.startup_cost = subpath->startup_cost +
target->cost.startup;
@@ -2967,6 +3055,7 @@ create_incremental_sort_path(PlannerInfo *root,
cost_incremental_sort(&pathnode->path,
root, pathkeys, presorted_keys,
+ subpath->disabled_nodes,
subpath->startup_cost,
subpath->total_cost,
subpath->rows,
@@ -3013,6 +3102,7 @@ create_sort_path(PlannerInfo *root,
pathnode->subpath = subpath;
cost_sort(&pathnode->path, root, pathkeys,
+ subpath->disabled_nodes,
subpath->total_cost,
subpath->rows,
subpath->pathtarget->width,
@@ -3065,6 +3155,7 @@ create_group_path(PlannerInfo *root,
list_length(groupClause),
numGroups,
qual,
+ subpath->disabled_nodes,
subpath->startup_cost, subpath->total_cost,
subpath->rows);
@@ -3122,6 +3213,7 @@ create_upper_unique_path(PlannerInfo *root,
* all columns get compared at most of the tuples. (XXX probably this is
* an overestimate.)
*/
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost = subpath->total_cost +
cpu_operator_cost * subpath->rows * numCols;
@@ -3200,6 +3292,7 @@ create_agg_path(PlannerInfo *root,
aggstrategy, aggcosts,
list_length(groupClause), numGroups,
qual,
+ subpath->disabled_nodes,
subpath->startup_cost, subpath->total_cost,
subpath->rows, subpath->pathtarget->width);
@@ -3308,6 +3401,7 @@ create_groupingsets_path(PlannerInfo *root,
numGroupCols,
rollup->numGroups,
having_qual,
+ subpath->disabled_nodes,
subpath->startup_cost,
subpath->total_cost,
subpath->rows,
@@ -3333,7 +3427,7 @@ create_groupingsets_path(PlannerInfo *root,
numGroupCols,
rollup->numGroups,
having_qual,
- 0.0, 0.0,
+ 0, 0.0, 0.0,
subpath->rows,
subpath->pathtarget->width);
if (!rollup->is_hashed)
@@ -3342,7 +3436,7 @@ create_groupingsets_path(PlannerInfo *root,
else
{
/* Account for cost of sort, but don't charge input cost again */
- cost_sort(&sort_path, root, NIL,
+ cost_sort(&sort_path, root, NIL, 0,
0.0,
subpath->rows,
subpath->pathtarget->width,
@@ -3358,12 +3452,14 @@ create_groupingsets_path(PlannerInfo *root,
numGroupCols,
rollup->numGroups,
having_qual,
+ sort_path.disabled_nodes,
sort_path.startup_cost,
sort_path.total_cost,
sort_path.rows,
subpath->pathtarget->width);
}
+ pathnode->path.disabled_nodes += agg_path.disabled_nodes;
pathnode->path.total_cost += agg_path.total_cost;
pathnode->path.rows += agg_path.rows;
}
@@ -3395,6 +3491,7 @@ create_minmaxagg_path(PlannerInfo *root,
{
MinMaxAggPath *pathnode = makeNode(MinMaxAggPath);
Cost initplan_cost;
+ int initplan_disabled_nodes = 0;
ListCell *lc;
/* The topmost generated Plan node will be a Result */
@@ -3419,12 +3516,14 @@ create_minmaxagg_path(PlannerInfo *root,
{
MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
+ initplan_disabled_nodes += mminfo->path->disabled_nodes;
initplan_cost += mminfo->pathcost;
if (!mminfo->path->parallel_safe)
pathnode->path.parallel_safe = false;
}
/* add tlist eval cost for each output row, plus cpu_tuple_cost */
+ pathnode->path.disabled_nodes = initplan_disabled_nodes;
pathnode->path.startup_cost = initplan_cost + target->cost.startup;
pathnode->path.total_cost = initplan_cost + target->cost.startup +
target->cost.per_tuple + cpu_tuple_cost;
@@ -3517,6 +3616,7 @@ create_windowagg_path(PlannerInfo *root,
cost_windowagg(&pathnode->path, root,
windowFuncs,
winclause,
+ subpath->disabled_nodes,
subpath->startup_cost,
subpath->total_cost,
subpath->rows);
@@ -3584,6 +3684,7 @@ create_setop_path(PlannerInfo *root,
* Charge one cpu_operator_cost per comparison per input tuple. We assume
* all columns get compared at most of the tuples.
*/
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost = subpath->total_cost +
cpu_operator_cost * subpath->rows * list_length(distinctList);
@@ -3683,6 +3784,7 @@ create_lockrows_path(PlannerInfo *root, RelOptInfo *rel,
* possible refetches, but it's hard to say how much. For now, use
* cpu_tuple_cost per row.
*/
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost = subpath->total_cost +
cpu_tuple_cost * subpath->rows;
@@ -3759,6 +3861,7 @@ create_modifytable_path(PlannerInfo *root, RelOptInfo *rel,
* costs to change any higher-level planning choices. But we might want
* to make it look better sometime.
*/
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost = subpath->total_cost;
if (returningLists != NIL)
@@ -3835,6 +3938,7 @@ create_limit_path(PlannerInfo *root, RelOptInfo *rel,
subpath->parallel_safe;
pathnode->path.parallel_workers = subpath->parallel_workers;
pathnode->path.rows = subpath->rows;
+ pathnode->path.disabled_nodes = subpath->disabled_nodes;
pathnode->path.startup_cost = subpath->startup_cost;
pathnode->path.total_cost = subpath->total_cost;
pathnode->path.pathkeys = subpath->pathkeys;
diff --git a/src/include/nodes/pathnodes.h b/src/include/nodes/pathnodes.h
index 14ccfc1ac1c..540d021592e 100644
--- a/src/include/nodes/pathnodes.h
+++ b/src/include/nodes/pathnodes.h
@@ -1658,6 +1658,7 @@ typedef struct Path
/* estimated size/costs for path (see costsize.c for more info) */
Cardinality rows; /* estimated number of result tuples */
+ int disabled_nodes; /* count of disabled nodes */
Cost startup_cost; /* cost expended before fetching any tuples */
Cost total_cost; /* total cost (assuming all tuples fetched) */
@@ -3333,6 +3334,7 @@ typedef struct
typedef struct JoinCostWorkspace
{
/* Preliminary cost estimates --- must not be larger than final ones! */
+ int disabled_nodes;
Cost startup_cost; /* cost expended before fetching any tuples */
Cost total_cost; /* total cost (assuming all tuples fetched) */
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 57861bfb446..854a782944a 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -108,35 +108,42 @@ extern void cost_resultscan(Path *path, PlannerInfo *root,
RelOptInfo *baserel, ParamPathInfo *param_info);
extern void cost_recursive_union(Path *runion, Path *nrterm, Path *rterm);
extern void cost_sort(Path *path, PlannerInfo *root,
- List *pathkeys, Cost input_cost, double tuples, int width,
+ List *pathkeys, int disabled_nodes,
+ Cost input_cost, double tuples, int width,
Cost comparison_cost, int sort_mem,
double limit_tuples);
extern void cost_incremental_sort(Path *path,
PlannerInfo *root, List *pathkeys, int presorted_keys,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples, int width, Cost comparison_cost, int sort_mem,
double limit_tuples);
extern void cost_append(AppendPath *apath);
extern void cost_merge_append(Path *path, PlannerInfo *root,
List *pathkeys, int n_streams,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double tuples);
extern void cost_material(Path *path,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double tuples, int width);
extern void cost_agg(Path *path, PlannerInfo *root,
AggStrategy aggstrategy, const AggClauseCosts *aggcosts,
int numGroupCols, double numGroups,
List *quals,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples, double input_width);
extern void cost_windowagg(Path *path, PlannerInfo *root,
List *windowFuncs, WindowClause *winclause,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples);
extern void cost_group(Path *path, PlannerInfo *root,
int numGroupCols, double numGroups,
List *quals,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples);
extern void initial_cost_nestloop(PlannerInfo *root,
@@ -171,6 +178,7 @@ extern void cost_gather(GatherPath *path, PlannerInfo *root,
RelOptInfo *rel, ParamPathInfo *param_info, double *rows);
extern void cost_gather_merge(GatherMergePath *path, PlannerInfo *root,
RelOptInfo *rel, ParamPathInfo *param_info,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double *rows);
extern void cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan);
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index f00bd55f393..1035e6560c1 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -27,11 +27,12 @@ extern int compare_fractional_path_costs(Path *path1, Path *path2,
double fraction);
extern void set_cheapest(RelOptInfo *parent_rel);
extern void add_path(RelOptInfo *parent_rel, Path *new_path);
-extern bool add_path_precheck(RelOptInfo *parent_rel,
+extern bool add_path_precheck(RelOptInfo *parent_rel, int disabled_nodes,
Cost startup_cost, Cost total_cost,
List *pathkeys, Relids required_outer);
extern void add_partial_path(RelOptInfo *parent_rel, Path *new_path);
extern bool add_partial_path_precheck(RelOptInfo *parent_rel,
+ int disabled_nodes,
Cost total_cost, List *pathkeys);
extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel,
@@ -124,7 +125,8 @@ extern Path *create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel,
Relids required_outer);
extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
PathTarget *target,
- double rows, Cost startup_cost, Cost total_cost,
+ double rows, int disabled_nodes,
+ Cost startup_cost, Cost total_cost,
List *pathkeys,
Relids required_outer,
Path *fdw_outerpath,
@@ -132,7 +134,8 @@ extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
List *fdw_private);
extern ForeignPath *create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
PathTarget *target,
- double rows, Cost startup_cost, Cost total_cost,
+ double rows, int disabled_nodes,
+ Cost startup_cost, Cost total_cost,
List *pathkeys,
Relids required_outer,
Path *fdw_outerpath,
@@ -140,7 +143,8 @@ extern ForeignPath *create_foreign_join_path(PlannerInfo *root, RelOptInfo *rel,
List *fdw_private);
extern ForeignPath *create_foreign_upper_path(PlannerInfo *root, RelOptInfo *rel,
PathTarget *target,
- double rows, Cost startup_cost, Cost total_cost,
+ double rows, int disabled_nodes,
+ Cost startup_cost, Cost total_cost,
List *pathkeys,
Path *fdw_outerpath,
List *fdw_restrictinfo,
diff --git a/src/test/isolation/specs/horizons.spec b/src/test/isolation/specs/horizons.spec
index d5239ff2287..3f987f943df 100644
--- a/src/test/isolation/specs/horizons.spec
+++ b/src/test/isolation/specs/horizons.spec
@@ -40,7 +40,6 @@ session pruner
setup
{
SET enable_seqscan = false;
- SET enable_indexscan = false;
SET enable_bitmapscan = false;
}
diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out
index 510646cbce7..092233cc9d0 100644
--- a/src/test/regress/expected/btree_index.out
+++ b/src/test/regress/expected/btree_index.out
@@ -332,11 +332,13 @@ select proname from pg_proc where proname ilike '00%foo' order by 1;
explain (costs off)
select proname from pg_proc where proname ilike 'ri%foo' order by 1;
- QUERY PLAN
------------------------------------------------------------------
- Index Only Scan using pg_proc_proname_args_nsp_index on pg_proc
- Filter: (proname ~~* 'ri%foo'::text)
-(2 rows)
+ QUERY PLAN
+----------------------------------------------
+ Sort
+ Sort Key: proname
+ -> Seq Scan on pg_proc
+ Filter: (proname ~~* 'ri%foo'::text)
+(4 rows)
reset enable_seqscan;
reset enable_indexscan;
diff --git a/src/test/regress/expected/select_parallel.out b/src/test/regress/expected/select_parallel.out
index a61c138bd0a..bdd675319f2 100644
--- a/src/test/regress/expected/select_parallel.out
+++ b/src/test/regress/expected/select_parallel.out
@@ -538,15 +538,17 @@ explain (costs off)
------------------------------------------------------------
Aggregate
-> Nested Loop
- -> Seq Scan on tenk2
- Filter: (thousand = 0)
+ -> Gather
+ Workers Planned: 4
+ -> Parallel Seq Scan on tenk2
+ Filter: (thousand = 0)
-> Gather
Workers Planned: 4
-> Parallel Bitmap Heap Scan on tenk1
Recheck Cond: (hundred > 1)
-> Bitmap Index Scan on tenk1_hundred
Index Cond: (hundred > 1)
-(10 rows)
+(12 rows)
select count(*) from tenk1, tenk2 where tenk1.hundred > 1 and tenk2.thousand=0;
count