Commit 3ced8837 authored by Tom Lane's avatar Tom Lane

Simplify query_planner's API by having it return the top-level RelOptInfo.

Formerly, query_planner returned one or possibly two Paths for the topmost
join relation, so that grouping_planner didn't see the join RelOptInfo
(at least not directly; it didn't have any hesitation about examining
cheapest_path->parent, though).  However, correct selection of the Paths
involved a significant amount of coupling between query_planner and
grouping_planner, a problem which has gotten worse over time.  It seems
best to give up on this API choice and instead return the topmost
RelOptInfo explicitly.  Then grouping_planner can pull out the Paths it
wants from the rel's path list.  In this way we can remove all knowledge
of grouping behaviors from query_planner.

The only real benefit of the old way is that in the case of an empty
FROM clause, we never made any RelOptInfos at all, just a Path.  Now
we have to gin up a dummy RelOptInfo to represent the empty FROM clause.
That's not a very big deal though.

While at it, simplify query_planner's API a bit more by having the caller
set up root->tuple_fraction and root->limit_tuples, rather than passing
those values as separate parameters.  Since query_planner no longer does
anything with either value, requiring it to fill the PlannerInfo fields
seemed pretty arbitrary.

This patch just rearranges code; it doesn't (intentionally) change any
behaviors.  Followup patches will do more interesting things.
parent 841c29c8
......@@ -372,10 +372,10 @@ generated during the optimization process are marked with their sort order
It is also possible to avoid an explicit sort step to implement a user's
ORDER BY clause if the final path has the right ordering already, so the
sort ordering is of interest even at the top level. query_planner() will
sort ordering is of interest even at the top level. grouping_planner() will
look for the cheapest path with a sort order matching the desired order,
and grouping_planner() will compare its cost to the cost of using the
cheapest-overall path and doing an explicit sort.
then compare its cost to the cost of using the cheapest-overall path and
doing an explicit sort on that.
When we are generating paths for a particular RelOptInfo, we discard a path
if it is more expensive than another known path that has the same or better
......
......@@ -316,6 +316,7 @@ find_minmax_aggs_walker(Node *node, List **context)
Assert(aggref->agglevelsup == 0);
if (list_length(aggref->args) != 1)
return true; /* it couldn't be MIN/MAX */
/*
* ORDER BY is usually irrelevant for MIN/MAX, but it can change the
* outcome if the aggsortop's operator class recognizes non-identical
......@@ -329,6 +330,7 @@ find_minmax_aggs_walker(Node *node, List **context)
*/
if (aggref->aggorder != NIL)
return true;
/*
* We might implement the optimization when a FILTER clause is present
* by adding the filter to the quals of the generated subquery.
......@@ -399,9 +401,8 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
TargetEntry *tle;
NullTest *ntest;
SortGroupClause *sortcl;
Path *cheapest_path;
RelOptInfo *final_rel;
Path *sorted_path;
double dNumGroups;
Cost path_cost;
double path_fraction;
......@@ -470,25 +471,28 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
* Generate the best paths for this query, telling query_planner that we
* have LIMIT 1.
*/
query_planner(subroot, parse->targetList, 1.0, 1.0,
minmax_qp_callback, NULL,
&cheapest_path, &sorted_path, &dNumGroups);
subroot->tuple_fraction = 1.0;
subroot->limit_tuples = 1.0;
final_rel = query_planner(subroot, parse->targetList,
minmax_qp_callback, NULL);
/*
* Fail if no presorted path. However, if query_planner determines that
* the presorted path is also the cheapest, it will set sorted_path to
* NULL ... don't be fooled. (This is kind of a pain here, but it
* simplifies life for grouping_planner, so leave it be.)
* Get the best presorted path, that being the one that's cheapest for
* fetching just one row. If there's no such path, fail.
*/
if (final_rel->rows > 1.0)
path_fraction = 1.0 / final_rel->rows;
else
path_fraction = 1.0;
sorted_path =
get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist,
subroot->query_pathkeys,
NULL,
path_fraction);
if (!sorted_path)
{
if (cheapest_path &&
pathkeys_contained_in(subroot->sort_pathkeys,
cheapest_path->pathkeys))
sorted_path = cheapest_path;
else
return false;
}
return false;
/*
* Determine cost to get just the first row of the presorted path.
......@@ -496,11 +500,6 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
* Note: cost calculation here should match
* compare_fractional_path_costs().
*/
if (sorted_path->parent->rows > 1.0)
path_fraction = 1.0 / sorted_path->parent->rows;
else
path_fraction = 1.0;
path_cost = sorted_path->startup_cost +
path_fraction * (sorted_path->total_cost - sorted_path->startup_cost);
......
This diff is collapsed.
This diff is collapsed.
......@@ -1333,15 +1333,16 @@ is_simple_subquery(Query *subquery, RangeTblEntry *rte,
return false;
/*
* Hack: don't try to pull up a subquery with an empty jointree.
* query_planner() will correctly generate a Result plan for a jointree
* that's totally empty, but I don't think the right things happen if an
* empty FromExpr appears lower down in a jointree. It would pose a
* problem for the PlaceHolderVar mechanism too, since we'd have no way to
* identify where to evaluate a PHV coming out of the subquery. Not worth
* working hard on this, just to collapse SubqueryScan/Result into Result;
* especially since the SubqueryScan can often be optimized away by
* setrefs.c anyway.
* Don't pull up a subquery with an empty jointree. query_planner() will
* correctly generate a Result plan for a jointree that's totally empty,
* but we can't cope with an empty FromExpr appearing lower down in a
* jointree: we identify join rels via baserelid sets, so we couldn't
* distinguish a join containing such a FromExpr from one without it.
* This would for example break the PlaceHolderVar mechanism, since we'd
* have no way to identify where to evaluate a PHV coming out of the
* subquery. Not worth working hard on this, just to collapse
* SubqueryScan/Result into Result; especially since the SubqueryScan can
* often be optimized away by setrefs.c anyway.
*/
if (subquery->jointree->fromlist == NIL)
return false;
......
......@@ -676,6 +676,36 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
}
/*
* build_empty_join_rel
* Build a dummy join relation describing an empty set of base rels.
*
* This is used for queries with empty FROM clauses, such as "SELECT 2+2" or
* "INSERT INTO foo VALUES(...)". We don't try very hard to make the empty
* joinrel completely valid, since no real planning will be done with it ---
* we just need it to carry a simple Result path out of query_planner().
*/
RelOptInfo *
build_empty_join_rel(PlannerInfo *root)
{
RelOptInfo *joinrel;
/* The dummy join relation should be the only one ... */
Assert(root->join_rel_list == NIL);
joinrel = makeNode(RelOptInfo);
joinrel->reloptkind = RELOPT_JOINREL;
joinrel->relids = NULL; /* empty set */
joinrel->rows = 1; /* we produce one row for such cases */
joinrel->width = 0; /* it contains no Vars */
joinrel->rtekind = RTE_JOIN;
root->join_rel_list = lappend(root->join_rel_list, joinrel);
return joinrel;
}
/*
* find_childrel_appendrelinfo
* Get the AppendRelInfo associated with an appendrel child rel.
......
......@@ -142,6 +142,7 @@ extern RelOptInfo *build_join_rel(PlannerInfo *root,
RelOptInfo *inner_rel,
SpecialJoinInfo *sjinfo,
List **restrictlist_ptr);
extern RelOptInfo *build_empty_join_rel(PlannerInfo *root);
extern AppendRelInfo *find_childrel_appendrelinfo(PlannerInfo *root,
RelOptInfo *rel);
extern ParamPathInfo *get_baserel_parampathinfo(PlannerInfo *root,
......
......@@ -27,11 +27,8 @@ typedef void (*query_pathkeys_callback) (PlannerInfo *root, void *extra);
/*
* prototypes for plan/planmain.c
*/
extern void query_planner(PlannerInfo *root, List *tlist,
double tuple_fraction, double limit_tuples,
query_pathkeys_callback qp_callback, void *qp_extra,
Path **cheapest_path, Path **sorted_path,
double *num_groups);
extern RelOptInfo *query_planner(PlannerInfo *root, List *tlist,
query_pathkeys_callback qp_callback, void *qp_extra);
/*
* prototypes for plan/planagg.c
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment