mirror of
https://github.com/postgres/postgres.git
synced 2025-11-19 13:42:17 +03:00
Treat number of disabled nodes in a path as a separate cost metric.
Previously, when a path type was disabled by e.g. enable_seqscan=false, we either avoided generating that path type in the first place, or more commonly, we added a large constant, called disable_cost, to the estimated startup cost of that path. This latter approach can distort planning. For instance, an extremely expensive non-disabled path could seem to be worse than a disabled path, especially if the full cost of that path node need not be paid (e.g. due to a Limit). Or, as in the regression test whose expected output changes with this commit, the addition of disable_cost can make two paths that would normally be distinguishible in cost seem to have fuzzily the same cost. To fix that, we now count the number of disabled path nodes and consider that a high-order component of both the startup cost and the total cost. Hence, the path list is now sorted by disabled_nodes and then by total_cost, instead of just by the latter, and likewise for the partial path list. It is important that this number is a count and not simply a Boolean; else, as soon as we're unable to respect disabled path types in all portions of the path, we stop trying to avoid them where we can. Because the path list is now sorted by the number of disabled nodes, the join prechecks must compute the count of disabled nodes during the initial cost phase instead of postponing it to final cost time. Counts of disabled nodes do not cross subquery levels; at present, there is no reason for them to do so, since the we do not postpone path selection across subquery boundaries (see make_subplan). Reviewed by Andres Freund, Heikki Linnakangas, and David Rowley. Discussion: http://postgr.es/m/CA+TgmoZ_+MS+o6NeGK2xyBv-xM+w1AfFVuHE4f_aq6ekHv7YSQ@mail.gmail.com
This commit is contained in:
@@ -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;
|
||||
@@ -3765,14 +3836,6 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *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_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;
|
||||
|
||||
|
||||
@@ -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. */
|
||||
|
||||
Reference in New Issue
Block a user