aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/planagg.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/planagg.c')
-rw-r--r--src/backend/optimizer/plan/planagg.c69
1 files changed, 23 insertions, 46 deletions
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index 8634940efc1..48c4fee8923 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -47,7 +47,7 @@
#include "utils/lsyscache.h"
#include "utils/syscache.h"
-static bool find_minmax_aggs_walker(Node *node, List **context);
+static bool can_minmax_aggs(PlannerInfo *root, List **context);
static bool build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
Oid eqop, Oid sortop, bool nulls_first);
static void minmax_qp_callback(PlannerInfo *root, void *extra);
@@ -66,7 +66,8 @@ static Oid fetch_agg_sort_op(Oid aggfnoid);
* query_planner(), because we generate indexscan paths by cloning the
* planner's state and invoking query_planner() on a modified version of
* the query parsetree. Thus, all preprocessing needed before query_planner()
- * must already be done.
+ * must already be done. This relies on the list of aggregates in
+ * root->agginfos, so preprocess_aggrefs() must have been called already, too.
*/
void
preprocess_minmax_aggregates(PlannerInfo *root)
@@ -140,9 +141,7 @@ preprocess_minmax_aggregates(PlannerInfo *root)
* all are MIN/MAX aggregates. Stop as soon as we find one that isn't.
*/
aggs_list = NIL;
- if (find_minmax_aggs_walker((Node *) root->processed_tlist, &aggs_list))
- return;
- if (find_minmax_aggs_walker(parse->havingQual, &aggs_list))
+ if (!can_minmax_aggs(root, &aggs_list))
return;
/*
@@ -227,38 +226,33 @@ preprocess_minmax_aggregates(PlannerInfo *root)
}
/*
- * 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
+ * can_minmax_aggs
+ * Walk through all the aggregates in the query, and check
+ * if they are all MIN/MAX aggregates. 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.
+ * Returns false if a non-MIN/MAX aggregate is found, true otherwise.
*
* 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)
+can_minmax_aggs(PlannerInfo *root, List **context)
{
- if (node == NULL)
- return false;
- if (IsA(node, Aggref))
+ ListCell *lc;
+
+ foreach(lc, root->agginfos)
{
- Aggref *aggref = (Aggref *) node;
+ AggInfo *agginfo = (AggInfo *) lfirst(lc);
+ Aggref *aggref = agginfo->representative_aggref;
Oid aggsortop;
TargetEntry *curTarget;
MinMaxAggInfo *mminfo;
- ListCell *l;
Assert(aggref->agglevelsup == 0);
if (list_length(aggref->args) != 1)
- return true; /* it couldn't be MIN/MAX */
+ return false; /* it couldn't be MIN/MAX */
/*
* ORDER BY is usually irrelevant for MIN/MAX, but it can change the
@@ -274,7 +268,7 @@ find_minmax_aggs_walker(Node *node, List **context)
* quickly.
*/
if (aggref->aggorder != NIL)
- return true;
+ return false;
/* note: we do not care if DISTINCT is mentioned ... */
/*
@@ -283,30 +277,19 @@ find_minmax_aggs_walker(Node *node, List **context)
* now, just punt.
*/
if (aggref->aggfilter != NULL)
- return true;
+ return false;
aggsortop = fetch_agg_sort_op(aggref->aggfnoid);
if (!OidIsValid(aggsortop))
- return true; /* not a MIN/MAX aggregate */
+ return false; /* not a MIN/MAX aggregate */
curTarget = (TargetEntry *) linitial(aggref->args);
if (contain_mutable_functions((Node *) curTarget->expr))
- return true; /* not potentially indexable */
+ return false; /* not potentially indexable */
if (type_is_rowtype(exprType((Node *) curTarget->expr)))
- return true; /* IS NOT NULL would have weird semantics */
-
- /*
- * Check whether it's already in the list, and add it if not.
- */
- foreach(l, *context)
- {
- mminfo = (MinMaxAggInfo *) lfirst(l);
- if (mminfo->aggfnoid == aggref->aggfnoid &&
- equal(mminfo->target, curTarget->expr))
- return false;
- }
+ return false; /* IS NOT NULL would have weird semantics */
mminfo = makeNode(MinMaxAggInfo);
mminfo->aggfnoid = aggref->aggfnoid;
@@ -318,16 +301,8 @@ find_minmax_aggs_walker(Node *node, List **context)
mminfo->param = NULL;
*context = lappend(*context, mminfo);
-
- /*
- * We need not recurse into the argument, since it can't contain any
- * aggregates.
- */
- return false;
}
- Assert(!IsA(node, SubLink));
- return expression_tree_walker(node, find_minmax_aggs_walker,
- (void *) context);
+ return true;
}
/*
@@ -368,6 +343,8 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo,
subroot->plan_params = NIL;
subroot->outer_params = NULL;
subroot->init_plans = NIL;
+ subroot->agginfos = NIL;
+ subroot->aggtransinfos = NIL;
subroot->parse = parse = copyObject(root->parse);
IncrementVarSublevelsUp((Node *) parse, 1, 1);