2005-04-12 01:06:57 +02:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* planagg.c
|
|
|
|
* Special planning for aggregate queries.
|
|
|
|
*
|
2010-11-04 17:01:17 +01:00
|
|
|
* This module tries to replace MIN/MAX aggregate functions by subqueries
|
|
|
|
* of the form
|
2011-03-22 05:34:31 +01:00
|
|
|
* (SELECT col FROM tab
|
|
|
|
* WHERE col IS NOT NULL AND existing-quals
|
|
|
|
* ORDER BY col ASC/DESC
|
|
|
|
* LIMIT 1)
|
2010-11-04 17:01:17 +01:00
|
|
|
* Given a suitable index on tab.col, this can be much faster than the
|
2011-04-10 17:42:00 +02:00
|
|
|
* generic scan-all-the-rows aggregation plan. We can handle multiple
|
2010-11-04 17:01:17 +01:00
|
|
|
* MIN/MAX aggregates by generating multiple subqueries, and their
|
2011-04-10 17:42:00 +02:00
|
|
|
* orderings can be different. However, if the query contains any
|
2010-11-04 17:01:17 +01:00
|
|
|
* non-optimizable aggregates, there's no point since we'll have to
|
|
|
|
* scan all the rows anyway.
|
|
|
|
*
|
|
|
|
*
|
2013-01-01 23:15:01 +01:00
|
|
|
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
|
2005-04-12 01:06:57 +02:00
|
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* IDENTIFICATION
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/backend/optimizer/plan/planagg.c
|
2005-04-12 01:06:57 +02:00
|
|
|
*
|
|
|
|
*-------------------------------------------------------------------------
|
|
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
|
2012-08-30 22:15:44 +02:00
|
|
|
#include "access/htup_details.h"
|
2005-04-12 01:06:57 +02:00
|
|
|
#include "catalog/pg_aggregate.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
|
|
#include "nodes/makefuncs.h"
|
2008-08-26 00:42:34 +02:00
|
|
|
#include "nodes/nodeFuncs.h"
|
2005-04-12 01:06:57 +02:00
|
|
|
#include "optimizer/clauses.h"
|
|
|
|
#include "optimizer/cost.h"
|
|
|
|
#include "optimizer/paths.h"
|
|
|
|
#include "optimizer/planmain.h"
|
2012-07-21 23:45:07 +02:00
|
|
|
#include "optimizer/planner.h"
|
2005-04-12 01:06:57 +02:00
|
|
|
#include "optimizer/subselect.h"
|
2006-07-11 19:26:59 +02:00
|
|
|
#include "parser/parsetree.h"
|
2011-03-22 05:34:31 +01:00
|
|
|
#include "parser/parse_clause.h"
|
2005-04-12 01:06:57 +02:00
|
|
|
#include "utils/lsyscache.h"
|
|
|
|
#include "utils/syscache.h"
|
|
|
|
|
|
|
|
|
|
|
|
static bool find_minmax_aggs_walker(Node *node, List **context);
|
2011-03-22 05:34:31 +01:00
|
|
|
static bool build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
|
|
|
|
Oid eqop, Oid sortop, bool nulls_first);
|
Postpone creation of pathkeys lists to fix bug #8049.
This patch gets rid of the concept of, and infrastructure for,
non-canonical PathKeys; we now only ever create canonical pathkey lists.
The need for non-canonical pathkeys came from the desire to have
grouping_planner initialize query_pathkeys and related pathkey lists before
calling query_planner. However, since query_planner didn't actually *do*
anything with those lists before they'd been made canonical, we can get rid
of the whole mess by just not creating the lists at all until the point
where we formerly canonicalized them.
There are several ways in which we could implement that without making
query_planner itself deal with grouping/sorting features (which are
supposed to be the province of grouping_planner). I chose to add a
callback function to query_planner's API; other alternatives would have
required adding more fields to PlannerInfo, which while not bad in itself
would create an ABI break for planner-related plugins in the 9.2 release
series. This still breaks ABI for anything that calls query_planner
directly, but it seems somewhat unlikely that there are any such plugins.
I had originally conceived of this change as merely a step on the way to
fixing bug #8049 from Teun Hoogendoorn; but it turns out that this fixes
that bug all by itself, as per the added regression test. The reason is
that now get_eclass_for_sort_expr is adding the ORDER BY expression at the
end of EquivalenceClass creation not the start, and so anything that is in
a multi-member EquivalenceClass has already been created with correct
em_nullable_relids. I am suspicious that there are related scenarios in
which we still need to teach get_eclass_for_sort_expr to compute correct
nullable_relids, but am not eager to risk destabilizing either 9.2 or 9.3
to fix bugs that are only hypothetical. So for the moment, do this and
stop here.
Back-patch to 9.2 but not to earlier branches, since they don't exhibit
this bug for lack of join-clause-movement logic that depends on
em_nullable_relids being correct. (We might have to revisit that choice
if any related bugs turn up.) In 9.2, don't change the signature of
make_pathkeys_for_sortclauses nor remove canonicalize_pathkeys, so as
not to risk more plugin breakage than we have to.
2013-04-29 20:49:01 +02:00
|
|
|
static void minmax_qp_callback(PlannerInfo *root, void *extra);
|
2011-03-22 05:34:31 +01:00
|
|
|
static void make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *mminfo);
|
|
|
|
static Node *replace_aggs_with_params_mutator(Node *node, PlannerInfo *root);
|
2005-04-12 01:06:57 +02:00
|
|
|
static Oid fetch_agg_sort_op(Oid aggfnoid);
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
2010-11-04 17:01:17 +01:00
|
|
|
* preprocess_minmax_aggregates - preprocess MIN/MAX aggregates
|
2005-04-12 01:06:57 +02:00
|
|
|
*
|
2010-11-04 17:01:17 +01:00
|
|
|
* Check to see whether the query contains MIN/MAX aggregate functions that
|
|
|
|
* might be optimizable via indexscans. If it does, and all the aggregates
|
|
|
|
* are potentially optimizable, then set up root->minmax_aggs with a list of
|
|
|
|
* these aggregates.
|
2005-04-12 01:06:57 +02:00
|
|
|
*
|
2010-11-04 17:01:17 +01:00
|
|
|
* Note: we are passed the preprocessed targetlist separately, because it's
|
|
|
|
* not necessarily equal to root->parse->targetList.
|
2005-04-12 01:06:57 +02:00
|
|
|
*/
|
2010-11-04 17:01:17 +01:00
|
|
|
void
|
|
|
|
preprocess_minmax_aggregates(PlannerInfo *root, List *tlist)
|
2005-04-12 01:06:57 +02:00
|
|
|
{
|
2005-06-06 00:32:58 +02:00
|
|
|
Query *parse = root->parse;
|
2007-02-06 07:50:26 +01:00
|
|
|
FromExpr *jtnode;
|
2005-04-12 01:06:57 +02:00
|
|
|
RangeTblRef *rtr;
|
|
|
|
RangeTblEntry *rte;
|
|
|
|
List *aggs_list;
|
2010-11-04 17:01:17 +01:00
|
|
|
ListCell *lc;
|
|
|
|
|
|
|
|
/* minmax_aggs list should be empty at this point */
|
|
|
|
Assert(root->minmax_aggs == NIL);
|
2005-04-12 01:06:57 +02:00
|
|
|
|
|
|
|
/* Nothing to do if query has no aggregates */
|
2005-06-06 00:32:58 +02:00
|
|
|
if (!parse->hasAggs)
|
2010-11-04 17:01:17 +01:00
|
|
|
return;
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
Assert(!parse->setOperations); /* shouldn't get here if a setop */
|
|
|
|
Assert(parse->rowMarks == NIL); /* nor if FOR UPDATE */
|
2005-04-12 01:06:57 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Reject unoptimizable cases.
|
|
|
|
*
|
2008-12-28 19:54:01 +01:00
|
|
|
* We don't handle GROUP BY or windowing, because our current
|
2009-06-11 16:49:15 +02:00
|
|
|
* implementations of grouping require looking at all the rows anyway, and
|
2011-04-10 17:42:00 +02:00
|
|
|
* so there's not much point in optimizing MIN/MAX. (Note: relaxing this
|
|
|
|
* would likely require some restructuring in grouping_planner(), since it
|
|
|
|
* performs assorted processing related to these features between calling
|
|
|
|
* preprocess_minmax_aggregates and optimize_minmax_aggregates.)
|
2005-04-12 01:06:57 +02:00
|
|
|
*/
|
2008-12-28 19:54:01 +01:00
|
|
|
if (parse->groupClause || parse->hasWindowFuncs)
|
2010-11-04 17:01:17 +01:00
|
|
|
return;
|
2005-04-12 01:06:57 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We also restrict the query to reference exactly one table, since join
|
|
|
|
* conditions can't be handled reasonably. (We could perhaps handle a
|
|
|
|
* query containing cartesian-product joins, but it hardly seems worth the
|
Revisit handling of UNION ALL subqueries with non-Var output columns.
In commit 57664ed25e5dea117158a2e663c29e60b3546e1c I tried to fix a bug
reported by Teodor Sigaev by making non-simple-Var output columns distinct
(by wrapping their expressions with dummy PlaceHolderVar nodes). This did
not work too well. Commit b28ffd0fcc583c1811e5295279e7d4366c3cae6c fixed
some ensuing problems with matching to child indexes, but per a recent
report from Claus Stadler, constraint exclusion of UNION ALL subqueries was
still broken, because constant-simplification didn't handle the injected
PlaceHolderVars well either. On reflection, the original patch was quite
misguided: there is no reason to expect that EquivalenceClass child members
will be distinct. So instead of trying to make them so, we should ensure
that we can cope with the situation when they're not.
Accordingly, this patch reverts the code changes in the above-mentioned
commits (though the regression test cases they added stay). Instead, I've
added assorted defenses to make sure that duplicate EC child members don't
cause any problems. Teodor's original problem ("MergeAppend child's
targetlist doesn't match MergeAppend") is addressed more directly by
revising prepare_sort_from_pathkeys to let the parent MergeAppend's sort
list guide creation of each child's sort list.
In passing, get rid of add_sort_column; as far as I can tell, testing for
duplicate sort keys at this stage is dead code. Certainly it doesn't
trigger often enough to be worth expending cycles on in ordinary queries.
And keeping the test would've greatly complicated the new logic in
prepare_sort_from_pathkeys, because comparing pathkey list entries against
a previous output array requires that we not skip any entries in the list.
Back-patch to 9.1, like the previous patches. The only known issue in
this area that wasn't caused by the ill-advised previous patches was the
MergeAppend planning failure, which of course is not relevant before 9.1.
It's possible that we need some of the new defenses against duplicate child
EC entries in older branches, but until there's some clear evidence of that
I'm going to refrain from back-patching further.
2012-03-16 18:11:12 +01:00
|
|
|
* trouble.) However, the single table could be buried in several levels
|
|
|
|
* of FromExpr due to subqueries. Note the "single" table could be an
|
|
|
|
* inheritance parent, too, including the case of a UNION ALL subquery
|
|
|
|
* that's been flattened to an appendrel.
|
2005-04-12 01:06:57 +02:00
|
|
|
*/
|
2007-02-06 07:50:26 +01:00
|
|
|
jtnode = parse->jointree;
|
|
|
|
while (IsA(jtnode, FromExpr))
|
|
|
|
{
|
|
|
|
if (list_length(jtnode->fromlist) != 1)
|
2010-11-04 17:01:17 +01:00
|
|
|
return;
|
2007-02-06 07:50:26 +01:00
|
|
|
jtnode = linitial(jtnode->fromlist);
|
|
|
|
}
|
|
|
|
if (!IsA(jtnode, RangeTblRef))
|
2010-11-04 17:01:17 +01:00
|
|
|
return;
|
2007-02-06 07:50:26 +01:00
|
|
|
rtr = (RangeTblRef *) jtnode;
|
2007-04-21 23:01:45 +02:00
|
|
|
rte = planner_rt_fetch(rtr->rtindex, root);
|
Revisit handling of UNION ALL subqueries with non-Var output columns.
In commit 57664ed25e5dea117158a2e663c29e60b3546e1c I tried to fix a bug
reported by Teodor Sigaev by making non-simple-Var output columns distinct
(by wrapping their expressions with dummy PlaceHolderVar nodes). This did
not work too well. Commit b28ffd0fcc583c1811e5295279e7d4366c3cae6c fixed
some ensuing problems with matching to child indexes, but per a recent
report from Claus Stadler, constraint exclusion of UNION ALL subqueries was
still broken, because constant-simplification didn't handle the injected
PlaceHolderVars well either. On reflection, the original patch was quite
misguided: there is no reason to expect that EquivalenceClass child members
will be distinct. So instead of trying to make them so, we should ensure
that we can cope with the situation when they're not.
Accordingly, this patch reverts the code changes in the above-mentioned
commits (though the regression test cases they added stay). Instead, I've
added assorted defenses to make sure that duplicate EC child members don't
cause any problems. Teodor's original problem ("MergeAppend child's
targetlist doesn't match MergeAppend") is addressed more directly by
revising prepare_sort_from_pathkeys to let the parent MergeAppend's sort
list guide creation of each child's sort list.
In passing, get rid of add_sort_column; as far as I can tell, testing for
duplicate sort keys at this stage is dead code. Certainly it doesn't
trigger often enough to be worth expending cycles on in ordinary queries.
And keeping the test would've greatly complicated the new logic in
prepare_sort_from_pathkeys, because comparing pathkey list entries against
a previous output array requires that we not skip any entries in the list.
Back-patch to 9.1, like the previous patches. The only known issue in
this area that wasn't caused by the ill-advised previous patches was the
MergeAppend planning failure, which of course is not relevant before 9.1.
It's possible that we need some of the new defenses against duplicate child
EC entries in older branches, but until there's some clear evidence of that
I'm going to refrain from back-patching further.
2012-03-16 18:11:12 +01:00
|
|
|
if (rte->rtekind == RTE_RELATION)
|
2012-06-10 21:20:04 +02:00
|
|
|
/* ordinary relation, ok */ ;
|
Revisit handling of UNION ALL subqueries with non-Var output columns.
In commit 57664ed25e5dea117158a2e663c29e60b3546e1c I tried to fix a bug
reported by Teodor Sigaev by making non-simple-Var output columns distinct
(by wrapping their expressions with dummy PlaceHolderVar nodes). This did
not work too well. Commit b28ffd0fcc583c1811e5295279e7d4366c3cae6c fixed
some ensuing problems with matching to child indexes, but per a recent
report from Claus Stadler, constraint exclusion of UNION ALL subqueries was
still broken, because constant-simplification didn't handle the injected
PlaceHolderVars well either. On reflection, the original patch was quite
misguided: there is no reason to expect that EquivalenceClass child members
will be distinct. So instead of trying to make them so, we should ensure
that we can cope with the situation when they're not.
Accordingly, this patch reverts the code changes in the above-mentioned
commits (though the regression test cases they added stay). Instead, I've
added assorted defenses to make sure that duplicate EC child members don't
cause any problems. Teodor's original problem ("MergeAppend child's
targetlist doesn't match MergeAppend") is addressed more directly by
revising prepare_sort_from_pathkeys to let the parent MergeAppend's sort
list guide creation of each child's sort list.
In passing, get rid of add_sort_column; as far as I can tell, testing for
duplicate sort keys at this stage is dead code. Certainly it doesn't
trigger often enough to be worth expending cycles on in ordinary queries.
And keeping the test would've greatly complicated the new logic in
prepare_sort_from_pathkeys, because comparing pathkey list entries against
a previous output array requires that we not skip any entries in the list.
Back-patch to 9.1, like the previous patches. The only known issue in
this area that wasn't caused by the ill-advised previous patches was the
MergeAppend planning failure, which of course is not relevant before 9.1.
It's possible that we need some of the new defenses against duplicate child
EC entries in older branches, but until there's some clear evidence of that
I'm going to refrain from back-patching further.
2012-03-16 18:11:12 +01:00
|
|
|
else if (rte->rtekind == RTE_SUBQUERY && rte->inh)
|
2012-06-10 21:20:04 +02:00
|
|
|
/* flattened UNION ALL subquery, ok */ ;
|
Revisit handling of UNION ALL subqueries with non-Var output columns.
In commit 57664ed25e5dea117158a2e663c29e60b3546e1c I tried to fix a bug
reported by Teodor Sigaev by making non-simple-Var output columns distinct
(by wrapping their expressions with dummy PlaceHolderVar nodes). This did
not work too well. Commit b28ffd0fcc583c1811e5295279e7d4366c3cae6c fixed
some ensuing problems with matching to child indexes, but per a recent
report from Claus Stadler, constraint exclusion of UNION ALL subqueries was
still broken, because constant-simplification didn't handle the injected
PlaceHolderVars well either. On reflection, the original patch was quite
misguided: there is no reason to expect that EquivalenceClass child members
will be distinct. So instead of trying to make them so, we should ensure
that we can cope with the situation when they're not.
Accordingly, this patch reverts the code changes in the above-mentioned
commits (though the regression test cases they added stay). Instead, I've
added assorted defenses to make sure that duplicate EC child members don't
cause any problems. Teodor's original problem ("MergeAppend child's
targetlist doesn't match MergeAppend") is addressed more directly by
revising prepare_sort_from_pathkeys to let the parent MergeAppend's sort
list guide creation of each child's sort list.
In passing, get rid of add_sort_column; as far as I can tell, testing for
duplicate sort keys at this stage is dead code. Certainly it doesn't
trigger often enough to be worth expending cycles on in ordinary queries.
And keeping the test would've greatly complicated the new logic in
prepare_sort_from_pathkeys, because comparing pathkey list entries against
a previous output array requires that we not skip any entries in the list.
Back-patch to 9.1, like the previous patches. The only known issue in
this area that wasn't caused by the ill-advised previous patches was the
MergeAppend planning failure, which of course is not relevant before 9.1.
It's possible that we need some of the new defenses against duplicate child
EC entries in older branches, but until there's some clear evidence of that
I'm going to refrain from back-patching further.
2012-03-16 18:11:12 +01:00
|
|
|
else
|
2010-11-04 17:01:17 +01:00
|
|
|
return;
|
2005-04-12 01:06:57 +02:00
|
|
|
|
|
|
|
/*
|
2010-11-04 17:01:17 +01:00
|
|
|
* Scan the tlist and HAVING qual to find all the aggregates and verify
|
2011-04-10 17:42:00 +02:00
|
|
|
* all are MIN/MAX aggregates. Stop as soon as we find one that isn't.
|
2005-04-12 01:06:57 +02:00
|
|
|
*/
|
|
|
|
aggs_list = NIL;
|
|
|
|
if (find_minmax_aggs_walker((Node *) tlist, &aggs_list))
|
2010-11-04 17:01:17 +01:00
|
|
|
return;
|
2005-06-06 00:32:58 +02:00
|
|
|
if (find_minmax_aggs_walker(parse->havingQual, &aggs_list))
|
2010-11-04 17:01:17 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, there is at least the possibility of performing the optimization.
|
2011-03-22 05:34:31 +01:00
|
|
|
* Build an access path for each aggregate. (We must do this now because
|
|
|
|
* we need to call query_planner with a pristine copy of the current query
|
|
|
|
* tree; it'll be too late when optimize_minmax_aggregates gets called.)
|
|
|
|
* If any of the aggregates prove to be non-indexable, give up; there is
|
|
|
|
* no point in optimizing just some of them.
|
2010-11-04 17:01:17 +01:00
|
|
|
*/
|
|
|
|
foreach(lc, aggs_list)
|
|
|
|
{
|
|
|
|
MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
|
2011-03-22 05:34:31 +01:00
|
|
|
Oid eqop;
|
|
|
|
bool reverse;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We'll need the equality operator that goes with the aggregate's
|
|
|
|
* ordering operator.
|
|
|
|
*/
|
|
|
|
eqop = get_equality_op_for_ordering_op(mminfo->aggsortop, &reverse);
|
2011-04-10 17:42:00 +02:00
|
|
|
if (!OidIsValid(eqop)) /* shouldn't happen */
|
2011-03-22 05:34:31 +01:00
|
|
|
elog(ERROR, "could not find equality operator for ordering operator %u",
|
|
|
|
mminfo->aggsortop);
|
2010-11-04 17:01:17 +01:00
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
/*
|
|
|
|
* We can use either an ordering that gives NULLS FIRST or one that
|
|
|
|
* gives NULLS LAST; furthermore there's unlikely to be much
|
|
|
|
* performance difference between them, so it doesn't seem worth
|
2011-04-10 17:42:00 +02:00
|
|
|
* costing out both ways if we get a hit on the first one. NULLS
|
2011-03-22 05:34:31 +01:00
|
|
|
* FIRST is more likely to be available if the operator is a
|
|
|
|
* reverse-sort operator, so try that first if reverse.
|
|
|
|
*/
|
|
|
|
if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, reverse))
|
|
|
|
continue;
|
|
|
|
if (build_minmax_path(root, mminfo, eqop, mminfo->aggsortop, !reverse))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* No indexable path for this aggregate, so fail */
|
|
|
|
return;
|
2010-11-04 17:01:17 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We're done until path generation is complete. Save info for later.
|
2011-04-10 17:42:00 +02:00
|
|
|
* (Setting root->minmax_aggs non-NIL signals we succeeded in making index
|
|
|
|
* access paths for all the aggregates.)
|
2010-11-04 17:01:17 +01:00
|
|
|
*/
|
|
|
|
root->minmax_aggs = aggs_list;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* optimize_minmax_aggregates - check for optimizing MIN/MAX via indexes
|
|
|
|
*
|
2011-03-22 05:34:31 +01:00
|
|
|
* Check to see whether using the aggregate indexscans is cheaper than the
|
|
|
|
* generic aggregate method. If so, generate and return a Plan that does it
|
2010-11-04 17:01:17 +01:00
|
|
|
* that way. Otherwise, return NULL.
|
|
|
|
*
|
2011-03-22 05:34:31 +01:00
|
|
|
* Note: it seems likely that the generic method will never be cheaper
|
|
|
|
* in practice, except maybe for tiny tables where it'd hardly matter.
|
|
|
|
* Should we skip even trying to build the standard plan, if
|
|
|
|
* preprocess_minmax_aggregates succeeds?
|
|
|
|
*
|
2011-04-24 22:55:20 +02:00
|
|
|
* We are passed the preprocessed tlist, as well as the estimated costs for
|
|
|
|
* doing the aggregates the regular way, and the best path devised for
|
2010-11-04 17:01:17 +01:00
|
|
|
* computing the input of a standard Agg node.
|
|
|
|
*/
|
|
|
|
Plan *
|
2011-04-24 22:55:20 +02:00
|
|
|
optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
|
|
|
|
const AggClauseCosts *aggcosts, Path *best_path)
|
2010-11-04 17:01:17 +01:00
|
|
|
{
|
|
|
|
Query *parse = root->parse;
|
|
|
|
Cost total_cost;
|
|
|
|
Path agg_p;
|
|
|
|
Plan *plan;
|
|
|
|
Node *hqual;
|
2011-03-22 05:34:31 +01:00
|
|
|
ListCell *lc;
|
2010-11-04 17:01:17 +01:00
|
|
|
|
|
|
|
/* Nothing to do if preprocess_minmax_aggs rejected the query */
|
|
|
|
if (root->minmax_aggs == NIL)
|
2005-04-12 01:06:57 +02:00
|
|
|
return NULL;
|
|
|
|
|
2010-11-04 17:01:17 +01:00
|
|
|
/*
|
2011-03-22 05:34:31 +01:00
|
|
|
* Now we have enough info to compare costs against the generic aggregate
|
|
|
|
* implementation.
|
|
|
|
*
|
|
|
|
* Note that we don't include evaluation cost of the tlist here; this is
|
|
|
|
* OK since it isn't included in best_path's cost either, and should be
|
|
|
|
* the same in either case.
|
2010-11-04 17:01:17 +01:00
|
|
|
*/
|
2005-04-12 01:06:57 +02:00
|
|
|
total_cost = 0;
|
2010-11-04 17:01:17 +01:00
|
|
|
foreach(lc, root->minmax_aggs)
|
2005-04-12 01:06:57 +02:00
|
|
|
{
|
2010-11-04 17:01:17 +01:00
|
|
|
MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
total_cost += mminfo->pathcost;
|
2005-04-12 01:06:57 +02:00
|
|
|
}
|
|
|
|
|
2011-04-24 22:55:20 +02:00
|
|
|
cost_agg(&agg_p, root, AGG_PLAIN, aggcosts,
|
2005-04-12 01:06:57 +02:00
|
|
|
0, 0,
|
|
|
|
best_path->startup_cost, best_path->total_cost,
|
|
|
|
best_path->parent->rows);
|
|
|
|
|
|
|
|
if (total_cost > agg_p.total_cost)
|
|
|
|
return NULL; /* too expensive */
|
|
|
|
|
|
|
|
/*
|
Revise the planner's handling of "pseudoconstant" WHERE clauses, that is
clauses containing no variables and no volatile functions. Such a clause
can be used as a one-time qual in a gating Result plan node, to suppress
plan execution entirely when it is false. Even when the clause is true,
putting it in a gating node wins by avoiding repeated evaluation of the
clause. In previous PG releases, query_planner() would do this for
pseudoconstant clauses appearing at the top level of the jointree, but
there was no ability to generate a gating Result deeper in the plan tree.
To fix it, get rid of the special case in query_planner(), and instead
process pseudoconstant clauses through the normal RestrictInfo qual
distribution mechanism. When a pseudoconstant clause is found attached to
a path node in create_plan(), pull it out and generate a gating Result at
that point. This requires special-casing pseudoconstants in selectivity
estimation and cost_qual_eval, but on the whole it's pretty clean.
It probably even makes the planner a bit faster than before for the normal
case of no pseudoconstants, since removing pull_constant_clauses saves one
useless traversal of the qual tree. Per gripe from Phil Frost.
2006-07-01 20:38:33 +02:00
|
|
|
* OK, we are going to generate an optimized plan.
|
2010-11-04 17:01:17 +01:00
|
|
|
*
|
|
|
|
* First, generate a subplan and output Param node for each agg.
|
2005-04-12 01:06:57 +02:00
|
|
|
*/
|
2011-03-22 05:34:31 +01:00
|
|
|
foreach(lc, root->minmax_aggs)
|
2005-04-12 01:06:57 +02:00
|
|
|
{
|
2011-03-22 05:34:31 +01:00
|
|
|
MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
|
|
|
|
|
|
|
|
make_agg_subplan(root, mminfo);
|
2005-04-12 01:06:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Modify the targetlist and HAVING qual to reference subquery outputs
|
|
|
|
*/
|
2011-03-22 05:34:31 +01:00
|
|
|
tlist = (List *) replace_aggs_with_params_mutator((Node *) tlist, root);
|
|
|
|
hqual = replace_aggs_with_params_mutator(parse->havingQual, root);
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2008-03-31 18:59:26 +02:00
|
|
|
/*
|
2009-06-11 16:49:15 +02:00
|
|
|
* We have to replace Aggrefs with Params in equivalence classes too, else
|
2012-11-26 18:57:17 +01:00
|
|
|
* ORDER BY or DISTINCT on an optimized aggregate will fail. We don't
|
|
|
|
* need to process child eclass members though, since they aren't of
|
2013-05-29 22:58:43 +02:00
|
|
|
* interest anymore --- and replace_aggs_with_params_mutator isn't able to
|
|
|
|
* handle Aggrefs containing translated child Vars, anyway.
|
2008-03-31 18:59:26 +02:00
|
|
|
*
|
2009-06-11 16:49:15 +02:00
|
|
|
* Note: at some point it might become necessary to mutate other data
|
|
|
|
* structures too, such as the query's sortClause or distinctClause. Right
|
|
|
|
* now, those won't be examined after this point.
|
2008-03-31 18:59:26 +02:00
|
|
|
*/
|
|
|
|
mutate_eclass_expressions(root,
|
|
|
|
replace_aggs_with_params_mutator,
|
2012-11-26 18:57:17 +01:00
|
|
|
(void *) root,
|
|
|
|
false);
|
2008-03-31 18:59:26 +02:00
|
|
|
|
2005-04-12 01:06:57 +02:00
|
|
|
/*
|
|
|
|
* Generate the output plan --- basically just a Result
|
|
|
|
*/
|
2007-02-22 23:00:26 +01:00
|
|
|
plan = (Plan *) make_result(root, tlist, hqual, NULL);
|
2005-04-12 01:06:57 +02:00
|
|
|
|
|
|
|
/* Account for evaluation cost of the tlist (make_result did the rest) */
|
2012-07-21 23:45:07 +02:00
|
|
|
add_tlist_costs_to_plan(root, plan, tlist);
|
2005-04-12 01:06:57 +02:00
|
|
|
|
|
|
|
return plan;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* find_minmax_aggs_walker
|
|
|
|
* Recursively scan the Aggref nodes in an expression tree, and check
|
|
|
|
* that each one is a MIN/MAX aggregate. If so, build a list of the
|
|
|
|
* distinct aggregate calls in the tree.
|
|
|
|
*
|
|
|
|
* Returns TRUE if a non-MIN/MAX aggregate is found, FALSE otherwise.
|
|
|
|
* (This seemingly-backward definition is used because expression_tree_walker
|
|
|
|
* aborts the scan on TRUE return, which is what we want.)
|
|
|
|
*
|
|
|
|
* Found aggregates are added to the list at *context; it's up to the caller
|
|
|
|
* to initialize the list to NIL.
|
|
|
|
*
|
|
|
|
* This does not descend into subqueries, and so should be used only after
|
|
|
|
* reduction of sublinks to subplans. There mustn't be outer-aggregate
|
|
|
|
* references either.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
find_minmax_aggs_walker(Node *node, List **context)
|
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return false;
|
|
|
|
if (IsA(node, Aggref))
|
|
|
|
{
|
|
|
|
Aggref *aggref = (Aggref *) node;
|
|
|
|
Oid aggsortop;
|
2009-12-15 18:57:48 +01:00
|
|
|
TargetEntry *curTarget;
|
2010-11-04 17:01:17 +01:00
|
|
|
MinMaxAggInfo *mminfo;
|
2005-04-12 01:06:57 +02:00
|
|
|
ListCell *l;
|
|
|
|
|
|
|
|
Assert(aggref->agglevelsup == 0);
|
2013-07-17 02:14:37 +02:00
|
|
|
if (list_length(aggref->args) != 1)
|
2006-07-27 21:52:07 +02:00
|
|
|
return true; /* it couldn't be MIN/MAX */
|
2013-07-17 02:14:37 +02:00
|
|
|
/*
|
|
|
|
* ORDER BY is usually irrelevant for MIN/MAX, but it can change the
|
|
|
|
* outcome if the aggsortop's operator class recognizes non-identical
|
|
|
|
* values as equal. For example, 4.0 and 4.00 are equal according to
|
|
|
|
* numeric_ops, yet distinguishable. If MIN() receives more than one
|
|
|
|
* value equal to 4.0 and no value less than 4.0, it is unspecified
|
|
|
|
* which of those equal values MIN() returns. An ORDER BY expression
|
|
|
|
* that differs for each of those equal values of the argument
|
|
|
|
* expression makes the result predictable once again. This is a
|
|
|
|
* niche requirement, and we do not implement it with subquery paths.
|
|
|
|
*/
|
|
|
|
if (aggref->aggorder != NIL)
|
|
|
|
return true;
|
2013-07-17 02:15:36 +02:00
|
|
|
/*
|
|
|
|
* We might implement the optimization when a FILTER clause is present
|
|
|
|
* by adding the filter to the quals of the generated subquery.
|
|
|
|
*/
|
|
|
|
if (aggref->aggfilter != NULL)
|
|
|
|
return true;
|
2005-04-12 01:06:57 +02:00
|
|
|
/* note: we do not care if DISTINCT is mentioned ... */
|
|
|
|
|
|
|
|
aggsortop = fetch_agg_sort_op(aggref->aggfnoid);
|
|
|
|
if (!OidIsValid(aggsortop))
|
|
|
|
return true; /* not a MIN/MAX aggregate */
|
|
|
|
|
2013-07-17 02:14:37 +02:00
|
|
|
curTarget = (TargetEntry *) linitial(aggref->args);
|
|
|
|
|
2010-11-04 17:01:17 +01:00
|
|
|
if (contain_mutable_functions((Node *) curTarget->expr))
|
|
|
|
return true; /* not potentially indexable */
|
|
|
|
|
|
|
|
if (type_is_rowtype(exprType((Node *) curTarget->expr)))
|
|
|
|
return true; /* IS NOT NULL would have weird semantics */
|
|
|
|
|
2005-04-12 01:06:57 +02:00
|
|
|
/*
|
|
|
|
* Check whether it's already in the list, and add it if not.
|
|
|
|
*/
|
|
|
|
foreach(l, *context)
|
|
|
|
{
|
2010-11-04 17:01:17 +01:00
|
|
|
mminfo = (MinMaxAggInfo *) lfirst(l);
|
|
|
|
if (mminfo->aggfnoid == aggref->aggfnoid &&
|
|
|
|
equal(mminfo->target, curTarget->expr))
|
2005-04-12 01:06:57 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-11-04 17:01:17 +01:00
|
|
|
mminfo = makeNode(MinMaxAggInfo);
|
|
|
|
mminfo->aggfnoid = aggref->aggfnoid;
|
|
|
|
mminfo->aggsortop = aggsortop;
|
|
|
|
mminfo->target = curTarget->expr;
|
2011-04-10 17:42:00 +02:00
|
|
|
mminfo->subroot = NULL; /* don't compute path yet */
|
2011-03-22 05:34:31 +01:00
|
|
|
mminfo->path = NULL;
|
|
|
|
mminfo->pathcost = 0;
|
|
|
|
mminfo->param = NULL;
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2010-11-04 17:01:17 +01:00
|
|
|
*context = lappend(*context, mminfo);
|
2005-04-12 01:06:57 +02:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* We need not recurse into the argument, since it can't contain any
|
|
|
|
* aggregates.
|
2005-04-12 01:06:57 +02:00
|
|
|
*/
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
Assert(!IsA(node, SubLink));
|
|
|
|
return expression_tree_walker(node, find_minmax_aggs_walker,
|
|
|
|
(void *) context);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2011-03-22 05:34:31 +01:00
|
|
|
* build_minmax_path
|
|
|
|
* Given a MIN/MAX aggregate, try to build an indexscan Path it can be
|
2010-11-04 17:01:17 +01:00
|
|
|
* optimized with.
|
2005-04-12 01:06:57 +02:00
|
|
|
*
|
2011-03-22 05:34:31 +01:00
|
|
|
* If successful, stash the best path in *mminfo and return TRUE.
|
|
|
|
* Otherwise, return FALSE.
|
2005-04-12 01:06:57 +02:00
|
|
|
*/
|
2011-03-22 05:34:31 +01:00
|
|
|
static bool
|
|
|
|
build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
|
|
|
|
Oid eqop, Oid sortop, bool nulls_first)
|
2005-04-12 01:06:57 +02:00
|
|
|
{
|
2011-03-22 05:34:31 +01:00
|
|
|
PlannerInfo *subroot;
|
|
|
|
Query *parse;
|
|
|
|
TargetEntry *tle;
|
|
|
|
NullTest *ntest;
|
|
|
|
SortGroupClause *sortcl;
|
|
|
|
Path *cheapest_path;
|
|
|
|
Path *sorted_path;
|
|
|
|
double dNumGroups;
|
|
|
|
Cost path_cost;
|
2010-11-04 17:01:17 +01:00
|
|
|
double path_fraction;
|
2007-10-13 02:58:03 +02:00
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
/*----------
|
|
|
|
* Generate modified query of the form
|
|
|
|
* (SELECT col FROM tab
|
|
|
|
* WHERE col IS NOT NULL AND existing-quals
|
|
|
|
* ORDER BY col ASC/DESC
|
|
|
|
* LIMIT 1)
|
|
|
|
*----------
|
2007-10-13 02:58:03 +02:00
|
|
|
*/
|
2011-03-22 05:34:31 +01:00
|
|
|
subroot = (PlannerInfo *) palloc(sizeof(PlannerInfo));
|
|
|
|
memcpy(subroot, root, sizeof(PlannerInfo));
|
|
|
|
subroot->parse = parse = (Query *) copyObject(root->parse);
|
|
|
|
/* make sure subroot planning won't change root->init_plans contents */
|
|
|
|
subroot->init_plans = list_copy(root->init_plans);
|
2012-08-27 04:48:55 +02:00
|
|
|
/* There shouldn't be any OJ or LATERAL info to translate, as yet */
|
2011-03-22 05:34:31 +01:00
|
|
|
Assert(subroot->join_info_list == NIL);
|
2012-08-27 04:48:55 +02:00
|
|
|
Assert(subroot->lateral_info_list == NIL);
|
2011-03-22 05:34:31 +01:00
|
|
|
/* and we haven't created PlaceHolderInfos, either */
|
|
|
|
Assert(subroot->placeholder_list == NIL);
|
|
|
|
|
|
|
|
/* single tlist entry that is the aggregate target */
|
|
|
|
tle = makeTargetEntry(copyObject(mminfo->target),
|
|
|
|
(AttrNumber) 1,
|
|
|
|
pstrdup("agg_target"),
|
|
|
|
false);
|
|
|
|
parse->targetList = list_make1(tle);
|
|
|
|
|
|
|
|
/* No HAVING, no DISTINCT, no aggregates anymore */
|
|
|
|
parse->havingQual = NULL;
|
|
|
|
subroot->hasHavingQual = false;
|
|
|
|
parse->distinctClause = NIL;
|
|
|
|
parse->hasDistinctOn = false;
|
|
|
|
parse->hasAggs = false;
|
|
|
|
|
|
|
|
/* Build "target IS NOT NULL" expression */
|
|
|
|
ntest = makeNode(NullTest);
|
|
|
|
ntest->nulltesttype = IS_NOT_NULL;
|
|
|
|
ntest->arg = copyObject(mminfo->target);
|
|
|
|
/* we checked it wasn't a rowtype in find_minmax_aggs_walker */
|
|
|
|
ntest->argisrow = false;
|
|
|
|
|
|
|
|
/* User might have had that in WHERE already */
|
|
|
|
if (!list_member((List *) parse->jointree->quals, ntest))
|
|
|
|
parse->jointree->quals = (Node *)
|
|
|
|
lcons(ntest, (List *) parse->jointree->quals);
|
|
|
|
|
|
|
|
/* Build suitable ORDER BY clause */
|
|
|
|
sortcl = makeNode(SortGroupClause);
|
|
|
|
sortcl->tleSortGroupRef = assignSortGroupRef(tle, parse->targetList);
|
|
|
|
sortcl->eqop = eqop;
|
|
|
|
sortcl->sortop = sortop;
|
|
|
|
sortcl->nulls_first = nulls_first;
|
2011-04-10 17:42:00 +02:00
|
|
|
sortcl->hashable = false; /* no need to make this accurate */
|
2011-03-22 05:34:31 +01:00
|
|
|
parse->sortClause = list_make1(sortcl);
|
|
|
|
|
|
|
|
/* set up expressions for LIMIT 1 */
|
|
|
|
parse->limitOffset = NULL;
|
2011-03-26 01:10:42 +01:00
|
|
|
parse->limitCount = (Node *) makeConst(INT8OID, -1, InvalidOid,
|
|
|
|
sizeof(int64),
|
2011-03-22 05:34:31 +01:00
|
|
|
Int64GetDatum(1), false,
|
|
|
|
FLOAT8PASSBYVAL);
|
2007-10-13 02:58:03 +02:00
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
/*
|
2011-04-10 17:42:00 +02:00
|
|
|
* Generate the best paths for this query, telling query_planner that we
|
|
|
|
* have LIMIT 1.
|
2011-03-22 05:34:31 +01:00
|
|
|
*/
|
|
|
|
query_planner(subroot, parse->targetList, 1.0, 1.0,
|
Postpone creation of pathkeys lists to fix bug #8049.
This patch gets rid of the concept of, and infrastructure for,
non-canonical PathKeys; we now only ever create canonical pathkey lists.
The need for non-canonical pathkeys came from the desire to have
grouping_planner initialize query_pathkeys and related pathkey lists before
calling query_planner. However, since query_planner didn't actually *do*
anything with those lists before they'd been made canonical, we can get rid
of the whole mess by just not creating the lists at all until the point
where we formerly canonicalized them.
There are several ways in which we could implement that without making
query_planner itself deal with grouping/sorting features (which are
supposed to be the province of grouping_planner). I chose to add a
callback function to query_planner's API; other alternatives would have
required adding more fields to PlannerInfo, which while not bad in itself
would create an ABI break for planner-related plugins in the 9.2 release
series. This still breaks ABI for anything that calls query_planner
directly, but it seems somewhat unlikely that there are any such plugins.
I had originally conceived of this change as merely a step on the way to
fixing bug #8049 from Teun Hoogendoorn; but it turns out that this fixes
that bug all by itself, as per the added regression test. The reason is
that now get_eclass_for_sort_expr is adding the ORDER BY expression at the
end of EquivalenceClass creation not the start, and so anything that is in
a multi-member EquivalenceClass has already been created with correct
em_nullable_relids. I am suspicious that there are related scenarios in
which we still need to teach get_eclass_for_sort_expr to compute correct
nullable_relids, but am not eager to risk destabilizing either 9.2 or 9.3
to fix bugs that are only hypothetical. So for the moment, do this and
stop here.
Back-patch to 9.2 but not to earlier branches, since they don't exhibit
this bug for lack of join-clause-movement logic that depends on
em_nullable_relids being correct. (We might have to revisit that choice
if any related bugs turn up.) In 9.2, don't change the signature of
make_pathkeys_for_sortclauses nor remove canonicalize_pathkeys, so as
not to risk more plugin breakage than we have to.
2013-04-29 20:49:01 +02:00
|
|
|
minmax_qp_callback, NULL,
|
2011-03-22 05:34:31 +01:00
|
|
|
&cheapest_path, &sorted_path, &dNumGroups);
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
/*
|
|
|
|
* 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.)
|
|
|
|
*/
|
|
|
|
if (!sorted_path)
|
|
|
|
{
|
|
|
|
if (cheapest_path &&
|
|
|
|
pathkeys_contained_in(subroot->sort_pathkeys,
|
|
|
|
cheapest_path->pathkeys))
|
|
|
|
sorted_path = cheapest_path;
|
|
|
|
else
|
|
|
|
return false;
|
2005-04-12 01:06:57 +02:00
|
|
|
}
|
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
/*
|
|
|
|
* Determine cost to get just the first row of the presorted path.
|
|
|
|
*
|
|
|
|
* 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;
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
path_cost = sorted_path->startup_cost +
|
|
|
|
path_fraction * (sorted_path->total_cost - sorted_path->startup_cost);
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
/* Save state for further processing */
|
|
|
|
mminfo->subroot = subroot;
|
|
|
|
mminfo->path = sorted_path;
|
|
|
|
mminfo->pathcost = path_cost;
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
return true;
|
2005-04-12 01:06:57 +02:00
|
|
|
}
|
|
|
|
|
Postpone creation of pathkeys lists to fix bug #8049.
This patch gets rid of the concept of, and infrastructure for,
non-canonical PathKeys; we now only ever create canonical pathkey lists.
The need for non-canonical pathkeys came from the desire to have
grouping_planner initialize query_pathkeys and related pathkey lists before
calling query_planner. However, since query_planner didn't actually *do*
anything with those lists before they'd been made canonical, we can get rid
of the whole mess by just not creating the lists at all until the point
where we formerly canonicalized them.
There are several ways in which we could implement that without making
query_planner itself deal with grouping/sorting features (which are
supposed to be the province of grouping_planner). I chose to add a
callback function to query_planner's API; other alternatives would have
required adding more fields to PlannerInfo, which while not bad in itself
would create an ABI break for planner-related plugins in the 9.2 release
series. This still breaks ABI for anything that calls query_planner
directly, but it seems somewhat unlikely that there are any such plugins.
I had originally conceived of this change as merely a step on the way to
fixing bug #8049 from Teun Hoogendoorn; but it turns out that this fixes
that bug all by itself, as per the added regression test. The reason is
that now get_eclass_for_sort_expr is adding the ORDER BY expression at the
end of EquivalenceClass creation not the start, and so anything that is in
a multi-member EquivalenceClass has already been created with correct
em_nullable_relids. I am suspicious that there are related scenarios in
which we still need to teach get_eclass_for_sort_expr to compute correct
nullable_relids, but am not eager to risk destabilizing either 9.2 or 9.3
to fix bugs that are only hypothetical. So for the moment, do this and
stop here.
Back-patch to 9.2 but not to earlier branches, since they don't exhibit
this bug for lack of join-clause-movement logic that depends on
em_nullable_relids being correct. (We might have to revisit that choice
if any related bugs turn up.) In 9.2, don't change the signature of
make_pathkeys_for_sortclauses nor remove canonicalize_pathkeys, so as
not to risk more plugin breakage than we have to.
2013-04-29 20:49:01 +02:00
|
|
|
/*
|
|
|
|
* Compute query_pathkeys and other pathkeys during plan generation
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
minmax_qp_callback(PlannerInfo *root, void *extra)
|
|
|
|
{
|
|
|
|
root->group_pathkeys = NIL;
|
|
|
|
root->window_pathkeys = NIL;
|
|
|
|
root->distinct_pathkeys = NIL;
|
|
|
|
|
|
|
|
root->sort_pathkeys =
|
|
|
|
make_pathkeys_for_sortclauses(root,
|
|
|
|
root->parse->sortClause,
|
|
|
|
root->parse->targetList);
|
|
|
|
|
|
|
|
root->query_pathkeys = root->sort_pathkeys;
|
|
|
|
}
|
|
|
|
|
2005-04-12 01:06:57 +02:00
|
|
|
/*
|
|
|
|
* Construct a suitable plan for a converted aggregate query
|
|
|
|
*/
|
|
|
|
static void
|
2011-03-22 05:34:31 +01:00
|
|
|
make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *mminfo)
|
2005-04-12 01:06:57 +02:00
|
|
|
{
|
2011-03-22 05:34:31 +01:00
|
|
|
PlannerInfo *subroot = mminfo->subroot;
|
|
|
|
Query *subparse = subroot->parse;
|
2005-04-12 01:06:57 +02:00
|
|
|
Plan *plan;
|
|
|
|
|
2010-11-04 17:01:17 +01:00
|
|
|
/*
|
|
|
|
* Generate the plan for the subquery. We already have a Path, but we have
|
|
|
|
* to convert it to a Plan and attach a LIMIT node above it.
|
|
|
|
*/
|
2011-03-22 05:34:31 +01:00
|
|
|
plan = create_plan(subroot, mminfo->path);
|
Revise the planner's handling of "pseudoconstant" WHERE clauses, that is
clauses containing no variables and no volatile functions. Such a clause
can be used as a one-time qual in a gating Result plan node, to suppress
plan execution entirely when it is false. Even when the clause is true,
putting it in a gating node wins by avoiding repeated evaluation of the
clause. In previous PG releases, query_planner() would do this for
pseudoconstant clauses appearing at the top level of the jointree, but
there was no ability to generate a gating Result deeper in the plan tree.
To fix it, get rid of the special case in query_planner(), and instead
process pseudoconstant clauses through the normal RestrictInfo qual
distribution mechanism. When a pseudoconstant clause is found attached to
a path node in create_plan(), pull it out and generate a gating Result at
that point. This requires special-casing pseudoconstants in selectivity
estimation and cost_qual_eval, but on the whole it's pretty clean.
It probably even makes the planner a bit faster than before for the normal
case of no pseudoconstants, since removing pull_constant_clauses saves one
useless traversal of the qual tree. Per gripe from Phil Frost.
2006-07-01 20:38:33 +02:00
|
|
|
|
2010-11-04 17:01:17 +01:00
|
|
|
plan->targetlist = subparse->targetList;
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
plan = (Plan *) make_limit(plan,
|
2005-06-06 00:32:58 +02:00
|
|
|
subparse->limitOffset,
|
2005-08-18 19:51:12 +02:00
|
|
|
subparse->limitCount,
|
|
|
|
0, 1);
|
2005-04-12 01:06:57 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Convert the plan into an InitPlan, and make a Param for its result.
|
|
|
|
*/
|
2011-03-22 05:34:31 +01:00
|
|
|
mminfo->param =
|
|
|
|
SS_make_initplan_from_plan(subroot, plan,
|
|
|
|
exprType((Node *) mminfo->target),
|
|
|
|
-1,
|
|
|
|
exprCollation((Node *) mminfo->target));
|
2007-02-19 08:03:34 +01:00
|
|
|
|
Fix mis-calculation of extParam/allParam sets for plan nodes, as seen in
bug #4290. The fundamental bug is that masking extParam by outer_params,
as finalize_plan had been doing, caused us to lose the information that
an initPlan depended on the output of a sibling initPlan. On reflection
the best thing to do seemed to be not to try to adjust outer_params for
this case but get rid of it entirely. The only thing it was really doing
for us was to filter out param IDs associated with SubPlan nodes, and that
can be done (with greater accuracy) while processing individual SubPlan
nodes in finalize_primnode. This approach was vindicated by the discovery
that the masking method was hiding a second bug: SS_finalize_plan failed to
remove extParam bits for initPlan output params that were referenced in the
main plan tree (it only got rid of those referenced by other initPlans).
It's not clear that this caused any real problems, given the limited use
of extParam by the executor, but it's certainly not what was intended.
I originally thought that there was also a problem with needing to include
indirect dependencies on external params in initPlans' param sets, but it
turns out that the executor handles this correctly so long as the depended-on
initPlan is earlier in the initPlans list than the one using its output.
That seems a bit of a fragile assumption, but it is true at the moment,
so I just documented it in some code comments rather than making what would
be rather invasive changes to remove the assumption.
Back-patch to 8.1. Previous versions don't have the case of initPlans
referring to other initPlans' outputs, so while the existing logic is still
questionable for them, there are not any known bugs to be fixed. So I'll
refrain from changing them for now.
2008-07-10 03:17:29 +02:00
|
|
|
/*
|
2011-04-10 17:42:00 +02:00
|
|
|
* Make sure the initplan gets into the outer PlannerInfo, along with any
|
|
|
|
* other initplans generated by the sub-planning run. We had to include
|
|
|
|
* the outer PlannerInfo's pre-existing initplans into the inner one's
|
|
|
|
* init_plans list earlier, so make sure we don't put back any duplicate
|
|
|
|
* entries.
|
Fix mis-calculation of extParam/allParam sets for plan nodes, as seen in
bug #4290. The fundamental bug is that masking extParam by outer_params,
as finalize_plan had been doing, caused us to lose the information that
an initPlan depended on the output of a sibling initPlan. On reflection
the best thing to do seemed to be not to try to adjust outer_params for
this case but get rid of it entirely. The only thing it was really doing
for us was to filter out param IDs associated with SubPlan nodes, and that
can be done (with greater accuracy) while processing individual SubPlan
nodes in finalize_primnode. This approach was vindicated by the discovery
that the masking method was hiding a second bug: SS_finalize_plan failed to
remove extParam bits for initPlan output params that were referenced in the
main plan tree (it only got rid of those referenced by other initPlans).
It's not clear that this caused any real problems, given the limited use
of extParam by the executor, but it's certainly not what was intended.
I originally thought that there was also a problem with needing to include
indirect dependencies on external params in initPlans' param sets, but it
turns out that the executor handles this correctly so long as the depended-on
initPlan is earlier in the initPlans list than the one using its output.
That seems a bit of a fragile assumption, but it is true at the moment,
so I just documented it in some code comments rather than making what would
be rather invasive changes to remove the assumption.
Back-patch to 8.1. Previous versions don't have the case of initPlans
referring to other initPlans' outputs, so while the existing logic is still
questionable for them, there are not any known bugs to be fixed. So I'll
refrain from changing them for now.
2008-07-10 03:17:29 +02:00
|
|
|
*/
|
2011-03-22 05:34:31 +01:00
|
|
|
root->init_plans = list_concat_unique_ptr(root->init_plans,
|
|
|
|
subroot->init_plans);
|
2010-05-10 18:25:46 +02:00
|
|
|
}
|
|
|
|
|
2005-04-12 01:06:57 +02:00
|
|
|
/*
|
|
|
|
* Replace original aggregate calls with subplan output Params
|
|
|
|
*/
|
|
|
|
static Node *
|
2011-03-22 05:34:31 +01:00
|
|
|
replace_aggs_with_params_mutator(Node *node, PlannerInfo *root)
|
2005-04-12 01:06:57 +02:00
|
|
|
{
|
|
|
|
if (node == NULL)
|
|
|
|
return NULL;
|
|
|
|
if (IsA(node, Aggref))
|
|
|
|
{
|
|
|
|
Aggref *aggref = (Aggref *) node;
|
2009-12-15 18:57:48 +01:00
|
|
|
TargetEntry *curTarget = (TargetEntry *) linitial(aggref->args);
|
2011-03-22 05:34:31 +01:00
|
|
|
ListCell *lc;
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
foreach(lc, root->minmax_aggs)
|
2005-04-12 01:06:57 +02:00
|
|
|
{
|
2011-03-22 05:34:31 +01:00
|
|
|
MinMaxAggInfo *mminfo = (MinMaxAggInfo *) lfirst(lc);
|
2005-04-12 01:06:57 +02:00
|
|
|
|
2011-03-22 05:34:31 +01:00
|
|
|
if (mminfo->aggfnoid == aggref->aggfnoid &&
|
|
|
|
equal(mminfo->target, curTarget->expr))
|
|
|
|
return (Node *) mminfo->param;
|
2005-04-12 01:06:57 +02:00
|
|
|
}
|
2011-03-22 05:34:31 +01:00
|
|
|
elog(ERROR, "failed to re-find MinMaxAggInfo record");
|
2005-04-12 01:06:57 +02:00
|
|
|
}
|
|
|
|
Assert(!IsA(node, SubLink));
|
|
|
|
return expression_tree_mutator(node, replace_aggs_with_params_mutator,
|
2011-03-22 05:34:31 +01:00
|
|
|
(void *) root);
|
2005-04-12 01:06:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the OID of the sort operator, if any, associated with an aggregate.
|
|
|
|
* Returns InvalidOid if there is no such operator.
|
|
|
|
*/
|
|
|
|
static Oid
|
|
|
|
fetch_agg_sort_op(Oid aggfnoid)
|
|
|
|
{
|
|
|
|
HeapTuple aggTuple;
|
|
|
|
Form_pg_aggregate aggform;
|
|
|
|
Oid aggsortop;
|
|
|
|
|
|
|
|
/* fetch aggregate entry from pg_aggregate */
|
2010-02-14 19:42:19 +01:00
|
|
|
aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggfnoid));
|
2005-04-12 01:06:57 +02:00
|
|
|
if (!HeapTupleIsValid(aggTuple))
|
|
|
|
return InvalidOid;
|
|
|
|
aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
|
|
|
|
aggsortop = aggform->aggsortop;
|
|
|
|
ReleaseSysCache(aggTuple);
|
|
|
|
|
|
|
|
return aggsortop;
|
|
|
|
}
|