summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/setrefs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/setrefs.c')
-rw-r--r--src/backend/optimizer/plan/setrefs.c251
1 files changed, 249 insertions, 2 deletions
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index aa2c3084fc8..16f572faf42 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -104,6 +104,8 @@ static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
static void set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset);
+static void set_combineagg_references(PlannerInfo *root, Plan *plan,
+ int rtoffset);
static void set_dummy_tlist_references(Plan *plan, int rtoffset);
static indexed_tlist *build_tlist_index(List *tlist);
static Var *search_indexed_tlist_for_var(Var *var,
@@ -117,6 +119,8 @@ static Var *search_indexed_tlist_for_sortgroupref(Node *node,
Index sortgroupref,
indexed_tlist *itlist,
Index newvarno);
+static Var *search_indexed_tlist_for_partial_aggref(Aggref *aggref,
+ indexed_tlist *itlist, Index newvarno);
static List *fix_join_expr(PlannerInfo *root,
List *clauses,
indexed_tlist *outer_itlist,
@@ -131,6 +135,13 @@ static Node *fix_upper_expr(PlannerInfo *root,
int rtoffset);
static Node *fix_upper_expr_mutator(Node *node,
fix_upper_expr_context *context);
+static Node *fix_combine_agg_expr(PlannerInfo *root,
+ Node *node,
+ indexed_tlist *subplan_itlist,
+ Index newvarno,
+ int rtoffset);
+static Node *fix_combine_agg_expr_mutator(Node *node,
+ fix_upper_expr_context *context);
static List *set_returning_clause_references(PlannerInfo *root,
List *rlist,
Plan *topplan,
@@ -667,8 +678,16 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
}
break;
case T_Agg:
- set_upper_references(root, plan, rtoffset);
- break;
+ {
+ Agg *aggplan = (Agg *) plan;
+
+ if (aggplan->combineStates)
+ set_combineagg_references(root, plan, rtoffset);
+ else
+ set_upper_references(root, plan, rtoffset);
+
+ break;
+ }
case T_Group:
set_upper_references(root, plan, rtoffset);
break;
@@ -1702,6 +1721,73 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset)
}
/*
+ * set_combineagg_references
+ * This serves the same function as set_upper_references(), but treats
+ * Aggrefs differently. Here we transform Aggref nodes args to suit the
+ * combine aggregate phase. This means that the Aggref->args are converted
+ * to reference the corresponding aggregate function in the subplan rather
+ * than simple Var(s), as would be the case for a non-combine aggregate
+ * node.
+ */
+static void
+set_combineagg_references(PlannerInfo *root, Plan *plan, int rtoffset)
+{
+ Plan *subplan = plan->lefttree;
+ indexed_tlist *subplan_itlist;
+ List *output_targetlist;
+ ListCell *l;
+
+ Assert(IsA(plan, Agg));
+ Assert(((Agg *) plan)->combineStates);
+
+ subplan_itlist = build_tlist_index(subplan->targetlist);
+
+ output_targetlist = NIL;
+
+ foreach(l, plan->targetlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(l);
+ Node *newexpr;
+
+ /* If it's a non-Var sort/group item, first try to match by sortref */
+ if (tle->ressortgroupref != 0 && !IsA(tle->expr, Var))
+ {
+ newexpr = (Node *)
+ search_indexed_tlist_for_sortgroupref((Node *) tle->expr,
+ tle->ressortgroupref,
+ subplan_itlist,
+ OUTER_VAR);
+ if (!newexpr)
+ newexpr = fix_combine_agg_expr(root,
+ (Node *) tle->expr,
+ subplan_itlist,
+ OUTER_VAR,
+ rtoffset);
+ }
+ else
+ newexpr = fix_combine_agg_expr(root,
+ (Node *) tle->expr,
+ subplan_itlist,
+ OUTER_VAR,
+ rtoffset);
+ tle = flatCopyTargetEntry(tle);
+ tle->expr = (Expr *) newexpr;
+ output_targetlist = lappend(output_targetlist, tle);
+ }
+
+ plan->targetlist = output_targetlist;
+
+ plan->qual = (List *)
+ fix_combine_agg_expr(root,
+ (Node *) plan->qual,
+ subplan_itlist,
+ OUTER_VAR,
+ rtoffset);
+
+ pfree(subplan_itlist);
+}
+
+/*
* set_dummy_tlist_references
* Replace the targetlist of an upper-level plan node with a simple
* list of OUTER_VAR references to its child.
@@ -1968,6 +2054,68 @@ search_indexed_tlist_for_sortgroupref(Node *node,
}
/*
+ * search_indexed_tlist_for_partial_aggref - find an Aggref in an indexed tlist
+ *
+ * Aggrefs for partial aggregates have their aggoutputtype adjusted to set it
+ * to the aggregate state's type. This means that a standard equal() comparison
+ * won't match when comparing an Aggref which is in partial mode with an Aggref
+ * which is not. Here we manually compare all of the fields apart from
+ * aggoutputtype.
+ */
+static Var *
+search_indexed_tlist_for_partial_aggref(Aggref *aggref, indexed_tlist *itlist,
+ Index newvarno)
+{
+ ListCell *lc;
+
+ foreach(lc, itlist->tlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ if (IsA(tle->expr, Aggref))
+ {
+ Aggref *tlistaggref = (Aggref *) tle->expr;
+ Var *newvar;
+
+ if (aggref->aggfnoid != tlistaggref->aggfnoid)
+ continue;
+ if (aggref->aggtype != tlistaggref->aggtype)
+ continue;
+ /* ignore aggoutputtype */
+ if (aggref->aggcollid != tlistaggref->aggcollid)
+ continue;
+ if (aggref->inputcollid != tlistaggref->inputcollid)
+ continue;
+ if (!equal(aggref->aggdirectargs, tlistaggref->aggdirectargs))
+ continue;
+ if (!equal(aggref->args, tlistaggref->args))
+ continue;
+ if (!equal(aggref->aggorder, tlistaggref->aggorder))
+ continue;
+ if (!equal(aggref->aggdistinct, tlistaggref->aggdistinct))
+ continue;
+ if (!equal(aggref->aggfilter, tlistaggref->aggfilter))
+ continue;
+ if (aggref->aggstar != tlistaggref->aggstar)
+ continue;
+ if (aggref->aggvariadic != tlistaggref->aggvariadic)
+ continue;
+ if (aggref->aggkind != tlistaggref->aggkind)
+ continue;
+ if (aggref->agglevelsup != tlistaggref->agglevelsup)
+ continue;
+
+ newvar = makeVarFromTargetEntry(newvarno, tle);
+ newvar->varnoold = 0; /* wasn't ever a plain Var */
+ newvar->varoattno = 0;
+
+ return newvar;
+ }
+ }
+ return NULL;
+}
+
+/*
* fix_join_expr
* Create a new set of targetlist entries or join qual clauses by
* changing the varno/varattno values of variables in the clauses
@@ -2238,6 +2386,105 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context)
}
/*
+ * fix_combine_agg_expr
+ * Like fix_upper_expr() but additionally adjusts the Aggref->args of
+ * Aggrefs so that they references the corresponding Aggref in the subplan.
+ */
+static Node *
+fix_combine_agg_expr(PlannerInfo *root,
+ Node *node,
+ indexed_tlist *subplan_itlist,
+ Index newvarno,
+ int rtoffset)
+{
+ fix_upper_expr_context context;
+
+ context.root = root;
+ context.subplan_itlist = subplan_itlist;
+ context.newvarno = newvarno;
+ context.rtoffset = rtoffset;
+ return fix_combine_agg_expr_mutator(node, &context);
+}
+
+static Node *
+fix_combine_agg_expr_mutator(Node *node, fix_upper_expr_context *context)
+{
+ Var *newvar;
+
+ if (node == NULL)
+ return NULL;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
+
+ newvar = search_indexed_tlist_for_var(var,
+ context->subplan_itlist,
+ context->newvarno,
+ context->rtoffset);
+ if (!newvar)
+ elog(ERROR, "variable not found in subplan target list");
+ return (Node *) newvar;
+ }
+ if (IsA(node, PlaceHolderVar))
+ {
+ PlaceHolderVar *phv = (PlaceHolderVar *) node;
+
+ /* See if the PlaceHolderVar has bubbled up from a lower plan node */
+ if (context->subplan_itlist->has_ph_vars)
+ {
+ newvar = search_indexed_tlist_for_non_var((Node *) phv,
+ context->subplan_itlist,
+ context->newvarno);
+ if (newvar)
+ return (Node *) newvar;
+ }
+ /* If not supplied by input plan, evaluate the contained expr */
+ return fix_upper_expr_mutator((Node *) phv->phexpr, context);
+ }
+ if (IsA(node, Param))
+ return fix_param_node(context->root, (Param *) node);
+ if (IsA(node, Aggref))
+ {
+ Aggref *aggref = (Aggref *) node;
+
+ newvar = search_indexed_tlist_for_partial_aggref(aggref,
+ context->subplan_itlist,
+ context->newvarno);
+ if (newvar)
+ {
+ Aggref *newaggref;
+ TargetEntry *newtle;
+
+ /*
+ * Now build a new TargetEntry for the Aggref's arguments which is
+ * a single Var which references the corresponding AggRef in the
+ * node below.
+ */
+ newtle = makeTargetEntry((Expr *) newvar, 1, NULL, false);
+ newaggref = (Aggref *) copyObject(aggref);
+ newaggref->args = list_make1(newtle);
+
+ return (Node *) newaggref;
+ }
+ else
+ elog(ERROR, "Aggref not found in subplan target list");
+ }
+ /* Try matching more complex expressions too, if tlist has any */
+ if (context->subplan_itlist->has_non_vars)
+ {
+ newvar = search_indexed_tlist_for_non_var(node,
+ context->subplan_itlist,
+ context->newvarno);
+ if (newvar)
+ return (Node *) newvar;
+ }
+ fix_expr_common(context->root, node);
+ return expression_tree_mutator(node,
+ fix_combine_agg_expr_mutator,
+ (void *) context);
+}
+
+/*
* set_returning_clause_references
* Perform setrefs.c's work on a RETURNING targetlist
*