diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 8 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 17 | ||||
-rw-r--r-- | src/backend/optimizer/util/tlist.c | 37 |
3 files changed, 53 insertions, 9 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index d668128ccb1..65a2e38fc33 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -937,10 +937,12 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path) if (newitems || best_path->umethod == UNIQUE_PATH_SORT) { /* - * If the top plan node can't do projections, we need to add a Result - * node to help it along. + * If the top plan node can't do projections and its existing target + * list isn't already what we need, we need to add a Result node to + * help it along. */ - if (!is_projection_capable_plan(subplan)) + if (!is_projection_capable_plan(subplan) && + !tlist_same_exprs(newtlist, subplan->targetlist)) subplan = (Plan *) make_result(root, newtlist, NULL, subplan); else subplan->targetlist = newtlist; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 0847e787c39..670776b032b 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -1379,10 +1379,12 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) { /* * If the top-level plan node is one that cannot do expression - * evaluation, we must insert a Result node to project the + * 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(result_plan)) + if (!is_projection_capable_plan(result_plan) && + !tlist_same_exprs(sub_tlist, result_plan->targetlist)) { result_plan = (Plan *) make_result(root, sub_tlist, @@ -1542,10 +1544,13 @@ grouping_planner(PlannerInfo *root, double tuple_fraction) * If the top-level plan node is one that cannot do expression * evaluation, we must insert a Result node to project the desired * tlist. (In some cases this might not really be required, but - * it's not worth trying to avoid it.) Note that on second and - * subsequent passes through the following loop, the top-level - * node will be a WindowAgg which we know can project; so we only - * need to check once. + * it's not worth trying to avoid it. In particular, think not to + * skip adding the Result if the initial window_tlist matches the + * top-level plan node's output, because we might change the tlist + * inside the following loop.) Note that on second and subsequent + * passes through the following loop, the top-level node will be a + * WindowAgg which we know can project; so we only need to check + * once. */ if (!is_projection_capable_plan(result_plan)) { diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 9728ac8333b..5dc4b835fa3 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -159,6 +159,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 |