create_foreignscan_path(root, baserel,
NULL, /* default pathtarget */
baserel->rows,
+ 0,
startup_cost,
total_cost,
NIL, /* no pathkeys */
List *pathkeys,
PgFdwPathExtraData *fpextra,
double *p_rows, int *p_width,
+ int *p_disabled_nodes,
Cost *p_startup_cost, Cost *p_total_cost);
static void get_remote_estimate(const char *sql,
PGconn *conn,
double retrieved_rows,
double width,
double limit_tuples,
+ int *disabled_nodes,
Cost *p_startup_cost,
Cost *p_run_cost);
static bool ec_member_matches_foreign(PlannerInfo *root, RelOptInfo *rel,
*/
estimate_path_cost_size(root, baserel, NIL, NIL, NULL,
&fpinfo->rows, &fpinfo->width,
+ &fpinfo->disabled_nodes,
&fpinfo->startup_cost, &fpinfo->total_cost);
/* Report estimated baserel size to planner. */
/* Fill in basically-bogus cost estimates for use later. */
estimate_path_cost_size(root, baserel, NIL, NIL, NULL,
&fpinfo->rows, &fpinfo->width,
+ &fpinfo->disabled_nodes,
&fpinfo->startup_cost, &fpinfo->total_cost);
}
path = create_foreignscan_path(root, baserel,
NULL, /* default pathtarget */
fpinfo->rows,
+ fpinfo->disabled_nodes,
fpinfo->startup_cost,
fpinfo->total_cost,
NIL, /* no pathkeys */
ParamPathInfo *param_info = (ParamPathInfo *) lfirst(lc);
double rows;
int width;
+ int disabled_nodes;
Cost startup_cost;
Cost total_cost;
/* Get a cost estimate from the remote */
estimate_path_cost_size(root, baserel,
param_info->ppi_clauses, NIL, NULL,
- &rows, &width,
+ &rows, &width, &disabled_nodes,
&startup_cost, &total_cost);
/*
path = create_foreignscan_path(root, baserel,
NULL, /* default pathtarget */
rows,
+ disabled_nodes,
startup_cost,
total_cost,
NIL, /* no pathkeys */
* final sort and the LIMIT restriction.
*
* The function returns the cost and size estimates in p_rows, p_width,
- * p_startup_cost and p_total_cost variables.
+ * p_disabled_nodes, p_startup_cost and p_total_cost variables.
*/
static void
estimate_path_cost_size(PlannerInfo *root,
List *pathkeys,
PgFdwPathExtraData *fpextra,
double *p_rows, int *p_width,
+ int *p_disabled_nodes,
Cost *p_startup_cost, Cost *p_total_cost)
{
PgFdwRelationInfo *fpinfo = (PgFdwRelationInfo *) foreignrel->fdw_private;
double rows;
double retrieved_rows;
int width;
+ int disabled_nodes = 0;
Cost startup_cost;
Cost total_cost;
adjust_foreign_grouping_path_cost(root, pathkeys,
retrieved_rows, width,
fpextra->limit_tuples,
+ &disabled_nodes,
&startup_cost, &run_cost);
}
else
/* Return results. */
*p_rows = rows;
*p_width = width;
+ *p_disabled_nodes = disabled_nodes;
*p_startup_cost = startup_cost;
*p_total_cost = total_cost;
}
double retrieved_rows,
double width,
double limit_tuples,
+ int *p_disabled_nodes,
Cost *p_startup_cost,
Cost *p_run_cost)
{
cost_sort(&sort_path,
root,
pathkeys,
+ 0,
*p_startup_cost + *p_run_cost,
retrieved_rows,
width,
{
double rows;
int width;
+ int disabled_nodes;
Cost startup_cost;
Cost total_cost;
List *useful_pathkeys = lfirst(lc);
Path *sorted_epq_path;
estimate_path_cost_size(root, rel, NIL, useful_pathkeys, NULL,
- &rows, &width, &startup_cost, &total_cost);
+ &rows, &width, &disabled_nodes,
+ &startup_cost, &total_cost);
/*
* The EPQ path must be at least as well sorted as the path itself, in
create_foreignscan_path(root, rel,
NULL,
rows,
+ disabled_nodes,
startup_cost,
total_cost,
useful_pathkeys,
create_foreign_join_path(root, rel,
NULL,
rows,
+ disabled_nodes,
startup_cost,
total_cost,
useful_pathkeys,
ForeignPath *joinpath;
double rows;
int width;
+ int disabled_nodes;
Cost startup_cost;
Cost total_cost;
Path *epq_path; /* Path to create plan to be executed when
/* Estimate costs for bare join relation */
estimate_path_cost_size(root, joinrel, NIL, NIL, NULL,
- &rows, &width, &startup_cost, &total_cost);
+ &rows, &width, &disabled_nodes,
+ &startup_cost, &total_cost);
/* Now update this information in the joinrel */
joinrel->rows = rows;
joinrel->reltarget->width = width;
fpinfo->rows = rows;
fpinfo->width = width;
+ fpinfo->disabled_nodes = disabled_nodes;
fpinfo->startup_cost = startup_cost;
fpinfo->total_cost = total_cost;
joinrel,
NULL, /* default pathtarget */
rows,
+ disabled_nodes,
startup_cost,
total_cost,
NIL, /* no pathkeys */
ForeignPath *grouppath;
double rows;
int width;
+ int disabled_nodes;
Cost startup_cost;
Cost total_cost;
/* Estimate the cost of push down */
estimate_path_cost_size(root, grouped_rel, NIL, NIL, NULL,
- &rows, &width, &startup_cost, &total_cost);
+ &rows, &width, &disabled_nodes,
+ &startup_cost, &total_cost);
/* Now update this information in the fpinfo */
fpinfo->rows = rows;
fpinfo->width = width;
+ fpinfo->disabled_nodes = disabled_nodes;
fpinfo->startup_cost = startup_cost;
fpinfo->total_cost = total_cost;
grouped_rel,
grouped_rel->reltarget,
rows,
+ disabled_nodes,
startup_cost,
total_cost,
NIL, /* no pathkeys */
PgFdwPathExtraData *fpextra;
double rows;
int width;
+ int disabled_nodes;
Cost startup_cost;
Cost total_cost;
List *fdw_private;
/* Estimate the costs of performing the final sort remotely */
estimate_path_cost_size(root, input_rel, NIL, root->sort_pathkeys, fpextra,
- &rows, &width, &startup_cost, &total_cost);
+ &rows, &width, &disabled_nodes,
+ &startup_cost, &total_cost);
/*
* Build the fdw_private list that will be used by postgresGetForeignPlan.
input_rel,
root->upper_targets[UPPERREL_ORDERED],
rows,
+ disabled_nodes,
startup_cost,
total_cost,
root->sort_pathkeys,
bool save_use_remote_estimate = false;
double rows;
int width;
+ int disabled_nodes;
Cost startup_cost;
Cost total_cost;
List *fdw_private;
path->parent,
path->pathtarget,
path->rows,
+ path->disabled_nodes,
path->startup_cost,
path->total_cost,
path->pathkeys,
ifpinfo->use_remote_estimate = false;
}
estimate_path_cost_size(root, input_rel, NIL, pathkeys, fpextra,
- &rows, &width, &startup_cost, &total_cost);
+ &rows, &width, &disabled_nodes,
+ &startup_cost, &total_cost);
if (!fpextra->has_final_sort)
ifpinfo->use_remote_estimate = save_use_remote_estimate;
input_rel,
root->upper_targets[UPPERREL_FINAL],
rows,
+ disabled_nodes,
startup_cost,
total_cost,
pathkeys,
/* Estimated size and cost for a scan, join, or grouping/aggregation. */
double rows;
int width;
+ int disabled_nodes;
Cost startup_cost;
Cost total_cost;
* 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
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,
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;
}
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;
}
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);
}
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)
{
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
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);
}
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
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);
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;
}
}
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;
}
/* 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)
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 */
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;
}
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;
}
* 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;
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
*/
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;
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)
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;
}
*/
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)
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;
}
{
ListCell *l;
+ apath->path.disabled_nodes = 0;
apath->path.startup_cost = 0;
apath->path.total_cost = 0;
apath->path.rows = 0;
*/
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;
}
}
cost_sort(&sort_path,
NULL, /* doesn't currently need root */
pathkeys,
+ subpath->disabled_nodes,
subpath->total_cost,
subpath->rows,
subpath->pathtarget->width,
}
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;
}
apath->path.total_cost += subpath->total_cost;
}
+ apath->path.disabled_nodes += subpath->disabled_nodes;
apath->path.rows = clamp_row_est(apath->path.rows);
i++;
*
* '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
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)
{
*/
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;
}
*/
void
cost_material(Path *path,
+ int input_disabled_nodes,
Cost input_startup_cost, Cost input_total_cost,
double tuples, int width)
{
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;
}
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)
{
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;
/* 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 */
}
path->rows = output_tuples;
+ path->disabled_nodes = disabled_nodes;
path->startup_cost = startup_cost;
path->total_cost = total_cost;
}
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)
{
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;
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)
{
}
path->rows = output_tuples;
+ path->disabled_nodes = input_disabled_nodes;
path->startup_cost = startup_cost;
path->total_cost = total_cost;
}
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;
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,
/* 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 */
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;
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 ||
List *outersortkeys, List *innersortkeys,
JoinPathExtraData *extra)
{
+ int disabled_nodes;
Cost startup_cost = 0;
Cost run_cost = 0;
double outer_path_rows = outer_path->rows;
Assert(outerstartsel <= outerendsel);
Assert(innerstartsel <= innerendsel);
+ disabled_nodes = enable_mergejoin ? 0 : 1;
+
/* cost of source data */
if (outersortkeys) /* do we need to sort outer? */
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;
}
else
{
+ disabled_nodes += outer_path->disabled_nodes;
startup_cost += outer_path->startup_cost;
startup_cost += (outer_path->total_cost - outer_path->startup_cost)
* outerstartsel;
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;
}
else
{
+ disabled_nodes += inner_path->disabled_nodes;
startup_cost += inner_path->startup_cost;
startup_cost += (inner_path->total_cost - inner_path->startup_cost)
* innerstartsel;
/* 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 */
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;
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.
JoinPathExtraData *extra,
bool parallel_hash)
{
+ int disabled_nodes;
Cost startup_cost = 0;
Cost run_cost = 0;
double outer_path_rows = outer_path->rows;
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;
/* 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 */
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;
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;
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))
{
*/
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. */
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))
{
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. */
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))
{
*/
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. */
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,
/* Set cost data */
cost_material(&matpath,
+ 0, /* a Plan contains no count of disabled nodes */
subplan->startup_cost,
subplan->total_cost,
subplan->plan_rows,
/* 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);
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);
* 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);
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)
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 +
#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.
* 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
* 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.
*
}
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;
}
* 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)
{
Path *old_path = (Path *) lfirst(p1);
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
*
* 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;
* 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
/* 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)
* 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;
* 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;
Relids required_outer)
{
MergeAppendPath *pathnode = makeNode(MergeAppendPath);
+ int input_disabled_nodes;
Cost input_startup_cost;
Cost input_total_cost;
ListCell *l;
* 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)
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;
}
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;
}
((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);
pathnode->subpath = subpath;
cost_material(&pathnode->path,
+ subpath->disabled_nodes,
subpath->startup_cost,
subpath->total_cost,
subpath->rows,
*/
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().
{
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;
{
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;
* Estimate cost for sort+unique implementation
*/
cost_sort(&sort_path, root, NIL,
+ subpath->disabled_nodes,
subpath->total_cost,
rel->rows,
subpath->pathtarget->width,
AGG_HASHED, NULL,
numCols, pathnode->path.rows,
NIL,
+ subpath->disabled_nodes,
subpath->startup_cost,
subpath->total_cost,
rel->rows,
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;
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;
}
Relids required_outer, double *rows)
{
GatherMergePath *pathnode = makeNode(GatherMergePath);
+ int input_disabled_nodes = 0;
Cost input_startup_cost = 0;
Cost input_total_cost = 0;
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;
}
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,
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;
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,
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;
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,
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;
* 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 +
* 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 +
* 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;
cost_incremental_sort(&pathnode->path,
root, pathkeys, presorted_keys,
+ subpath->disabled_nodes,
subpath->startup_cost,
subpath->total_cost,
subpath->rows,
pathnode->subpath = subpath;
cost_sort(&pathnode->path, root, pathkeys,
+ subpath->disabled_nodes,
subpath->total_cost,
subpath->rows,
subpath->pathtarget->width,
list_length(groupClause),
numGroups,
qual,
+ subpath->disabled_nodes,
subpath->startup_cost, subpath->total_cost,
subpath->rows);
* 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;
aggstrategy, aggcosts,
list_length(groupClause), numGroups,
qual,
+ subpath->disabled_nodes,
subpath->startup_cost, subpath->total_cost,
subpath->rows, subpath->pathtarget->width);
numGroupCols,
rollup->numGroups,
having_qual,
+ subpath->disabled_nodes,
subpath->startup_cost,
subpath->total_cost,
subpath->rows,
numGroupCols,
rollup->numGroups,
having_qual,
- 0.0, 0.0,
+ 0, 0.0, 0.0,
subpath->rows,
subpath->pathtarget->width);
if (!rollup->is_hashed)
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,
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;
}
{
MinMaxAggPath *pathnode = makeNode(MinMaxAggPath);
Cost initplan_cost;
+ int initplan_disabled_nodes = 0;
ListCell *lc;
/* The topmost generated Plan node will be a Result */
{
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;
cost_windowagg(&pathnode->path, root,
windowFuncs,
winclause,
+ subpath->disabled_nodes,
subpath->startup_cost,
subpath->total_cost,
subpath->rows);
* 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);
* 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;
* 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)
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;
/* 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) */
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) */
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,
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);
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,
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,
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,
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,
setup
{
SET enable_seqscan = false;
- SET enable_indexscan = false;
SET enable_bitmapscan = false;
}
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;
------------------------------------------------------------
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