diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/plan/planagg.c | 23 | ||||
-rw-r--r-- | src/backend/optimizer/util/tlist.c | 37 |
2 files changed, 59 insertions, 1 deletions
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c index f7ce9b12a92..88a6c99ea50 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -37,6 +37,7 @@ #include "optimizer/paths.h" #include "optimizer/planmain.h" #include "optimizer/subselect.h" +#include "optimizer/tlist.h" #include "parser/parsetree.h" #include "parser/parse_clause.h" #include "utils/lsyscache.h" @@ -521,7 +522,27 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *mminfo) */ plan = create_plan(subroot, mminfo->path); - plan->targetlist = subparse->targetList; + /* + * If the top-level plan node is one that cannot do expression evaluation + * and its existing target list isn't already what we need, we must insert + * a Result node to project the desired tlist. + */ + if (!is_projection_capable_plan(plan) && + !tlist_same_exprs(subparse->targetList, plan->targetlist)) + { + plan = (Plan *) make_result(subroot, + subparse->targetList, + NULL, + plan); + } + else + { + /* + * Otherwise, just replace the subplan's flat tlist with the desired + * tlist. + */ + plan->targetlist = subparse->targetList; + } plan = (Plan *) make_limit(plan, subparse->limitOffset, diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index b8149f7b461..c98c767f310 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -189,6 +189,43 @@ get_tlist_exprs(List *tlist, bool includeJunk) /* + * tlist_same_exprs + * Check whether two target lists contain the same expressions + * + * Note: this function is used to decide whether it's safe to jam a new tlist + * into a non-projection-capable plan node. Obviously we can't do that unless + * the node's tlist shows it already returns the column values we want. + * However, we can ignore the TargetEntry attributes resname, ressortgroupref, + * resorigtbl, resorigcol, and resjunk, because those are only labelings that + * don't affect the row values computed by the node. (Moreover, if we didn't + * ignore them, we'd frequently fail to make the desired optimization, since + * the planner tends to not bother to make resname etc. valid in intermediate + * plan nodes.) Note that on success, the caller must still jam the desired + * tlist into the plan node, else it won't have the desired labeling fields. + */ +bool +tlist_same_exprs(List *tlist1, List *tlist2) +{ + ListCell *lc1, + *lc2; + + if (list_length(tlist1) != list_length(tlist2)) + return false; /* not same length, so can't match */ + + forboth(lc1, tlist1, lc2, tlist2) + { + TargetEntry *tle1 = (TargetEntry *) lfirst(lc1); + TargetEntry *tle2 = (TargetEntry *) lfirst(lc2); + + if (!equal(tle1->expr, tle2->expr)) + return false; + } + + return true; +} + + +/* * Does tlist have same output datatypes as listed in colTypes? * * Resjunk columns are ignored if junkOK is true; otherwise presence of |