aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/plan/createplan.c8
-rw-r--r--src/backend/optimizer/plan/planner.c17
-rw-r--r--src/backend/optimizer/util/tlist.c37
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