mirror of
https://github.com/postgres/postgres.git
synced 2025-07-11 10:01:57 +03:00
Account for SRFs in targetlists in planner rowcount estimates.
We made use of the ROWS estimate for set-returning functions used in FROM, but not for those used in SELECT targetlists; which is a bit of an oversight considering there are common usages that require the latter approach. Improve that. (I had initially thought it might be worth folding this into cost_qual_eval, but after investigation concluded that that wouldn't be very helpful, so just do it separately.) Per complaint from David Johnston. Back-patch to 9.2, but not further, for fear of destabilizing plan choices in existing releases.
This commit is contained in:
@ -1045,7 +1045,6 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
||||
double sub_limit_tuples;
|
||||
AttrNumber *groupColIdx = NULL;
|
||||
bool need_tlist_eval = true;
|
||||
QualCost tlist_cost;
|
||||
Path *cheapest_path;
|
||||
Path *sorted_path;
|
||||
Path *best_path;
|
||||
@ -1355,27 +1354,9 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
||||
|
||||
/*
|
||||
* Also, account for the cost of evaluation of the sub_tlist.
|
||||
*
|
||||
* Up to now, we have only been dealing with "flat" tlists,
|
||||
* containing just Vars. So their evaluation cost is zero
|
||||
* according to the model used by cost_qual_eval() (or if you
|
||||
* prefer, the cost is factored into cpu_tuple_cost). Thus we
|
||||
* can avoid accounting for tlist cost throughout
|
||||
* query_planner() and subroutines. But now we've inserted a
|
||||
* tlist that might contain actual operators, sub-selects, etc
|
||||
* --- so we'd better account for its cost.
|
||||
*
|
||||
* Below this point, any tlist eval cost for added-on nodes
|
||||
* should be accounted for as we create those nodes.
|
||||
* Presently, of the node types we can add on, only Agg,
|
||||
* WindowAgg, and Group project new tlists (the rest just copy
|
||||
* their input tuples) --- so make_agg(), make_windowagg() and
|
||||
* make_group() are responsible for computing the added cost.
|
||||
* See comments for add_tlist_costs_to_plan() for more info.
|
||||
*/
|
||||
cost_qual_eval(&tlist_cost, sub_tlist, root);
|
||||
result_plan->startup_cost += tlist_cost.startup;
|
||||
result_plan->total_cost += tlist_cost.startup +
|
||||
tlist_cost.per_tuple * result_plan->plan_rows;
|
||||
add_tlist_costs_to_plan(root, result_plan, sub_tlist);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1815,6 +1796,61 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
|
||||
return result_plan;
|
||||
}
|
||||
|
||||
/*
|
||||
* add_tlist_costs_to_plan
|
||||
*
|
||||
* Estimate the execution costs associated with evaluating the targetlist
|
||||
* expressions, and add them to the cost estimates for the Plan node.
|
||||
*
|
||||
* If the tlist contains set-returning functions, also inflate the Plan's cost
|
||||
* and plan_rows estimates accordingly. (Hence, this must be called *after*
|
||||
* any logic that uses plan_rows to, eg, estimate qual evaluation costs.)
|
||||
*
|
||||
* Note: during initial stages of planning, we mostly consider plan nodes with
|
||||
* "flat" tlists, containing just Vars. So their evaluation cost is zero
|
||||
* according to the model used by cost_qual_eval() (or if you prefer, the cost
|
||||
* is factored into cpu_tuple_cost). Thus we can avoid accounting for tlist
|
||||
* cost throughout query_planner() and subroutines. But once we apply a
|
||||
* tlist that might contain actual operators, sub-selects, etc, we'd better
|
||||
* account for its cost. Any set-returning functions in the tlist must also
|
||||
* affect the estimated rowcount.
|
||||
*
|
||||
* Once grouping_planner() has applied a general tlist to the topmost
|
||||
* scan/join plan node, any tlist eval cost for added-on nodes should be
|
||||
* accounted for as we create those nodes. Presently, of the node types we
|
||||
* can add on later, only Agg, WindowAgg, and Group project new tlists (the
|
||||
* rest just copy their input tuples) --- so make_agg(), make_windowagg() and
|
||||
* make_group() are responsible for calling this function to account for their
|
||||
* tlist costs.
|
||||
*/
|
||||
void
|
||||
add_tlist_costs_to_plan(PlannerInfo *root, Plan *plan, List *tlist)
|
||||
{
|
||||
QualCost tlist_cost;
|
||||
double tlist_rows;
|
||||
|
||||
cost_qual_eval(&tlist_cost, tlist, root);
|
||||
plan->startup_cost += tlist_cost.startup;
|
||||
plan->total_cost += tlist_cost.startup +
|
||||
tlist_cost.per_tuple * plan->plan_rows;
|
||||
|
||||
tlist_rows = tlist_returns_set_rows(tlist);
|
||||
if (tlist_rows > 1)
|
||||
{
|
||||
/*
|
||||
* We assume that execution costs of the tlist proper were all
|
||||
* accounted for by cost_qual_eval. However, it still seems
|
||||
* appropriate to charge something more for the executor's general
|
||||
* costs of processing the added tuples. The cost is probably less
|
||||
* than cpu_tuple_cost, though, so we arbitrarily use half of that.
|
||||
*/
|
||||
plan->total_cost += plan->plan_rows * (tlist_rows - 1) *
|
||||
cpu_tuple_cost / 2;
|
||||
|
||||
plan->plan_rows *= tlist_rows;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect whether a plan node is a "dummy" plan created when a relation
|
||||
* is deemed not to need scanning due to constraint exclusion.
|
||||
|
Reference in New Issue
Block a user