summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2012-07-21 17:45:07 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2012-07-21 17:45:07 -0400
commit31c7c642b6419b43eff903285e3da65e3f1901d6 (patch)
treef7c172f0de2d1692289f484e642f85ed01bb40c5 /src/backend/optimizer/plan
parented0af3324702685cce63aed0641b4cbb45816b50 (diff)
downloadpostgresql-31c7c642b6419b43eff903285e3da65e3f1901d6.tar.gz
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.
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r--src/backend/optimizer/plan/createplan.c29
-rw-r--r--src/backend/optimizer/plan/planagg.c6
-rw-r--r--src/backend/optimizer/plan/planner.c78
3 files changed, 69 insertions, 44 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 65ad1694b0..414406bb8a 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -30,6 +30,7 @@
#include "optimizer/placeholder.h"
#include "optimizer/plancat.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/predtest.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/subselect.h"
@@ -4126,8 +4127,8 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,
* anything for Aggref nodes; this is okay since they are really
* comparable to Vars.
*
- * See notes in grouping_planner about why only make_agg, make_windowagg
- * and make_group worry about tlist eval cost.
+ * See notes in add_tlist_costs_to_plan about why only make_agg,
+ * make_windowagg and make_group worry about tlist eval cost.
*/
if (qual)
{
@@ -4136,10 +4137,7 @@ make_agg(PlannerInfo *root, List *tlist, List *qual,
plan->total_cost += qual_cost.startup;
plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
}
- cost_qual_eval(&qual_cost, tlist, root);
- plan->startup_cost += qual_cost.startup;
- plan->total_cost += qual_cost.startup;
- plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
+ add_tlist_costs_to_plan(root, plan, tlist);
plan->qual = qual;
plan->targetlist = tlist;
@@ -4160,7 +4158,6 @@ make_windowagg(PlannerInfo *root, List *tlist,
WindowAgg *node = makeNode(WindowAgg);
Plan *plan = &node->plan;
Path windowagg_path; /* dummy for result of cost_windowagg */
- QualCost qual_cost;
node->winref = winref;
node->partNumCols = partNumCols;
@@ -4185,13 +4182,10 @@ make_windowagg(PlannerInfo *root, List *tlist,
/*
* We also need to account for the cost of evaluation of the tlist.
*
- * See notes in grouping_planner about why only make_agg, make_windowagg
- * and make_group worry about tlist eval cost.
+ * See notes in add_tlist_costs_to_plan about why only make_agg,
+ * make_windowagg and make_group worry about tlist eval cost.
*/
- cost_qual_eval(&qual_cost, tlist, root);
- plan->startup_cost += qual_cost.startup;
- plan->total_cost += qual_cost.startup;
- plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
+ add_tlist_costs_to_plan(root, plan, tlist);
plan->targetlist = tlist;
plan->lefttree = lefttree;
@@ -4242,8 +4236,8 @@ make_group(PlannerInfo *root,
* lower plan level and will only be copied by the Group node. Worth
* fixing?
*
- * See notes in grouping_planner about why only make_agg, make_windowagg
- * and make_group worry about tlist eval cost.
+ * See notes in add_tlist_costs_to_plan about why only make_agg,
+ * make_windowagg and make_group worry about tlist eval cost.
*/
if (qual)
{
@@ -4252,10 +4246,7 @@ make_group(PlannerInfo *root,
plan->total_cost += qual_cost.startup;
plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
}
- cost_qual_eval(&qual_cost, tlist, root);
- plan->startup_cost += qual_cost.startup;
- plan->total_cost += qual_cost.startup;
- plan->total_cost += qual_cost.per_tuple * plan->plan_rows;
+ add_tlist_costs_to_plan(root, plan, tlist);
plan->qual = qual;
plan->targetlist = tlist;
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index be52d16ff0..0bbca071fb 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -36,6 +36,7 @@
#include "optimizer/cost.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
#include "optimizer/subselect.h"
#include "parser/parsetree.h"
#include "parser/parse_clause.h"
@@ -205,7 +206,6 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
Path agg_p;
Plan *plan;
Node *hqual;
- QualCost tlist_cost;
ListCell *lc;
/* Nothing to do if preprocess_minmax_aggs rejected the query */
@@ -272,9 +272,7 @@ optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
plan = (Plan *) make_result(root, tlist, hqual, NULL);
/* Account for evaluation cost of the tlist (make_result did the rest) */
- cost_qual_eval(&tlist_cost, tlist, root);
- plan->startup_cost += tlist_cost.startup;
- plan->total_cost += tlist_cost.startup + tlist_cost.per_tuple;
+ add_tlist_costs_to_plan(root, plan, tlist);
return plan;
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index df76341c0a..31fe557072 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -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
{
@@ -1816,6 +1797,61 @@ grouping_planner(PlannerInfo *root, double tuple_fraction)
}
/*
+ * 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.
*