summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/postgres_fdw/postgres_fdw.c41
-rw-r--r--src/backend/optimizer/plan/createplan.c46
-rw-r--r--src/include/optimizer/planmain.h2
3 files changed, 65 insertions, 24 deletions
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index bf7c557da42..309f27ca856 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -1181,33 +1181,50 @@ postgresGetForeignPlan(PlannerInfo *root,
/*
* Ensure that the outer plan produces a tuple whose descriptor
- * matches our scan tuple slot. This is safe because all scans and
- * joins support projection, so we never need to insert a Result node.
- * Also, remove the local conditions from outer plan's quals, lest
- * they will be evaluated twice, once by the local plan and once by
- * the scan.
+ * matches our scan tuple slot. Also, remove the local conditions
+ * from outer plan's quals, lest they be evaluated twice, once by the
+ * local plan and once by the scan.
*/
if (outer_plan)
{
ListCell *lc;
- outer_plan->targetlist = fdw_scan_tlist;
-
+ /*
+ * First, update the plan's qual list if possible. In some cases
+ * the quals might be enforced below the topmost plan level, in
+ * which case we'll fail to remove them; it's not worth working
+ * harder than this.
+ */
foreach(lc, local_exprs)
{
- Join *join_plan = (Join *) outer_plan;
Node *qual = lfirst(lc);
outer_plan->qual = list_delete(outer_plan->qual, qual);
/*
* For an inner join the local conditions of foreign scan plan
- * can be part of the joinquals as well.
+ * can be part of the joinquals as well. (They might also be
+ * in the mergequals or hashquals, but we can't touch those
+ * without breaking the plan.)
*/
- if (join_plan->jointype == JOIN_INNER)
- join_plan->joinqual = list_delete(join_plan->joinqual,
- qual);
+ if (IsA(outer_plan, NestLoop) ||
+ IsA(outer_plan, MergeJoin) ||
+ IsA(outer_plan, HashJoin))
+ {
+ Join *join_plan = (Join *) outer_plan;
+
+ if (join_plan->jointype == JOIN_INNER)
+ join_plan->joinqual = list_delete(join_plan->joinqual,
+ qual);
+ }
}
+
+ /*
+ * Now fix the subplan's tlist --- this might result in inserting
+ * a Result node atop the plan tree.
+ */
+ outer_plan = change_plan_targetlist(outer_plan, fdw_scan_tlist,
+ best_path->path.parallel_safe);
}
}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index b79aa3fe390..9c16065c154 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1252,19 +1252,10 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path, int flags)
}
}
+ /* Use change_plan_targetlist in case we need to insert a Result node */
if (newitems || best_path->umethod == UNIQUE_PATH_SORT)
- {
- /*
- * 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) &&
- !tlist_same_exprs(newtlist, subplan->targetlist))
- subplan = inject_projection_plan(subplan, newtlist);
- else
- subplan->targetlist = newtlist;
- }
+ subplan = change_plan_targetlist(subplan, newtlist,
+ best_path->path.parallel_safe);
/*
* Build control information showing which subplan output columns are to
@@ -1506,6 +1497,37 @@ inject_projection_plan(Plan *subplan, List *tlist)
}
/*
+ * change_plan_targetlist
+ * Externally available wrapper for inject_projection_plan.
+ *
+ * This is meant for use by FDW plan-generation functions, which might
+ * want to adjust the tlist computed by some subplan tree. In general,
+ * a Result node is needed to compute the new tlist, but we can optimize
+ * some cases.
+ *
+ * In most cases, tlist_parallel_safe can just be passed as the parallel_safe
+ * flag of the FDW's own Path node. (It's not actually used in this branch.)
+ */
+Plan *
+change_plan_targetlist(Plan *subplan, List *tlist, bool tlist_parallel_safe)
+{
+ /*
+ * 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) &&
+ !tlist_same_exprs(tlist, subplan->targetlist))
+ subplan = inject_projection_plan(subplan, tlist);
+ else
+ {
+ /* Else we can just replace the plan node's tlist */
+ subplan->targetlist = tlist;
+ }
+ return subplan;
+}
+
+/*
* create_sort_plan
*
* Create a Sort plan for 'best_path' and (recursively) plans
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 4fbb6cc3e7e..b046ea1285b 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -52,6 +52,8 @@ extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
Index scanrelid, List *fdw_exprs, List *fdw_private,
List *fdw_scan_tlist, List *fdw_recheck_quals,
Plan *outer_plan);
+extern Plan *change_plan_targetlist(Plan *subplan, List *tlist,
+ bool tlist_parallel_safe);
extern Plan *materialize_finished_plan(Plan *subplan);
extern bool is_projection_capable_path(Path *path);
extern bool is_projection_capable_plan(Plan *plan);