diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 5c40168ce85..455eef64af4 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -357,7 +357,9 @@ GetForeignJoinPaths (PlannerInfo *root,
void
GetForeignUpperPaths (PlannerInfo *root,
- RelOptInfo *scan_join_rel);
+ UpperRelationKind stage,
+ RelOptInfo *input_rel,
+ RelOptInfo *output_rel);
Create possible access paths for upper relation> processing,
which is the planner's term for all post-scan/join query processing, such
@@ -365,11 +367,24 @@ GetForeignUpperPaths (PlannerInfo *root,
optional function is called during query planning. Currently, it is
called only if all base relation(s) involved in the query belong to the
same FDW. This function should generate ForeignPath>
- path(s) for the steps that the FDW knows how to perform remotely, and
- call add_path> to add these paths to the appropriate upper
- relation. As with GetForeignJoinPaths>, it is not necessary
- that this function succeed in creating any paths, since paths involving
- local processing are always possible.
+ path(s) for any post-scan/join processing that the FDW knows how to
+ perform remotely, and call add_path> to add these paths to
+ the indicated upper relation. As with GetForeignJoinPaths>,
+ it is not necessary that this function succeed in creating any paths,
+ since paths involving local processing are always possible.
+
+
+
+ The stage> parameter identifies which post-scan/join step is
+ currently being considered. output_rel> is the upper relation
+ that should receive paths representing computation of this step,
+ and input_rel> is the relation representing the input to this
+ step. (Note that ForeignPath> paths added
+ to output_rel> would typically not have any direct dependency
+ on paths of the input_rel>, since their processing is expected
+ to be done externally. However, examining paths previously generated for
+ the previous processing step can be useful to avoid redundant planning
+ work.)
@@ -1530,20 +1545,20 @@ GetForeignServerByName(const char *name, bool missing_ok);
An FDW might additionally support direct execution of some plan actions
that are above the level of scans and joins, such as grouping or
- aggregation. To offer such options, the FDW should generate paths
- and insert them into the
- appropriate upper relation>. For example, a path
- representing remote aggregation should be inserted into the relation
- obtained from fetch_upper_rel(root, UPPERREL_GROUP_AGG,
- NULL)>, using add_path>. This path will be compared on a
- cost basis with local aggregation performed by reading a simple scan path
- for the foreign relation (note that such a path must also be supplied,
- else there will be an error at plan time). If the remote-aggregation
- path wins, which it usually would, it will be converted into a plan in
- the usual way, by calling GetForeignPlan>.
- Usually the most convenient place to generate such paths is in
- the GetForeignUpperPaths> callback function, although
- it can be done earlier if that seems appropriate.
+ aggregation. To offer such options, the FDW should generate paths and
+ insert them into the appropriate upper relation>. For
+ example, a path representing remote aggregation should be inserted into
+ the UPPERREL_GROUP_AGG> relation, using add_path>.
+ This path will be compared on a cost basis with local aggregation
+ performed by reading a simple scan path for the foreign relation (note
+ that such a path must also be supplied, else there will be an error at
+ plan time). If the remote-aggregation path wins, which it usually would,
+ it will be converted into a plan in the usual way, by
+ calling GetForeignPlan>. The recommended place to generate
+ such paths is in the GetForeignUpperPaths>
+ callback function, which is called for each upper relation (i.e., each
+ post-scan/join processing step), if all the base relations of the query
+ come from the same FDW.
diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README
index 799e0ebe810..775bcc3b73b 100644
--- a/src/backend/optimizer/README
+++ b/src/backend/optimizer/README
@@ -919,19 +919,6 @@ The result of subquery_planner() is always returned as a set of Paths
stored in the UPPERREL_FINAL rel with NULL relids. The other types of
upperrels are created only if needed for the particular query.
-The upper-relation infrastructure is designed so that things will work
-properly if a particular upper relation is created and Paths are added
-to it sooner than would normally happen. This allows, for example,
-for an FDW's GetForeignPaths function to insert a Path representing
-remote aggregation into the UPPERREL_GROUP_AGG upperrel, if it notices
-that the query represents an aggregation that could be done entirely on
-the foreign server. That Path will then compete with Paths representing
-local aggregation on a regular scan of the foreign table, once the core
-planner reaches the point of considering aggregation. (In practice,
-it will usually be more convenient for FDWs to detect such cases in a
-GetForeignUpperPaths callback; but that still represents injecting a
-Path before the core code has touched the corresponding upperrel.)
-
Parallel Query and Partial Paths
--------------------------------
diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c
index cefec7bdf10..805aae7ee7a 100644
--- a/src/backend/optimizer/plan/planagg.c
+++ b/src/backend/optimizer/plan/planagg.c
@@ -205,7 +205,10 @@ preprocess_minmax_aggregates(PlannerInfo *root, List *tlist)
* will likely always win, but we need not assume that here.)
*
* Note: grouping_planner won't have created this upperrel yet, but it's
- * fine for us to create it first.
+ * fine for us to create it first. We will not have inserted the correct
+ * consider_parallel value in it, but MinMaxAggPath paths are currently
+ * never parallel-safe anyway, so that doesn't matter. Likewise, it
+ * doesn't matter that we haven't filled FDW-related fields in the rel.
*/
grouped_rel = fetch_upper_rel(root, UPPERREL_GROUP_AGG, NULL);
add_path(grouped_rel, (Path *)
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 747a14d71c3..0ece4f2aace 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1291,6 +1291,12 @@ inheritance_planner(PlannerInfo *root)
/* Result path must go into outer query's FINAL upperrel */
final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
+ /*
+ * We don't currently worry about setting final_rel's consider_parallel
+ * flag in this case, nor about allowing FDWs or create_upper_paths_hook
+ * to get control here.
+ */
+
/*
* If we managed to exclude every child rel, return a dummy plan; it
* doesn't even need a ModifyTable node.
@@ -1788,21 +1794,6 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
root->upper_targets[UPPERREL_WINDOW] = sort_input_target;
root->upper_targets[UPPERREL_GROUP_AGG] = grouping_target;
- /*
- * If there is an FDW that's responsible for the final scan/join rel,
- * let it consider injecting extension Paths into the query's
- * upperrels, where they will compete with the Paths we create below.
- * We pass the final scan/join rel because that's not so easily
- * findable from the PlannerInfo struct; anything else the FDW wants
- * to know should be obtainable via "root".
- *
- * Note: CustomScan providers, as well as FDWs that don't want to use
- * this hook, can use the create_upper_paths_hook; see below.
- */
- if (current_rel->fdwroutine &&
- current_rel->fdwroutine->GetForeignUpperPaths)
- current_rel->fdwroutine->GetForeignUpperPaths(root, current_rel);
-
/*
* If we have grouping and/or aggregation, consider ways to implement
* that. We build a new upperrel representing the output of this
@@ -1891,9 +1882,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
}
/*
- * Now we are prepared to build the final-output upperrel. Insert all
- * surviving paths, with LockRows, Limit, and/or ModifyTable steps added
- * if needed.
+ * Now we are prepared to build the final-output upperrel.
*/
final_rel = fetch_upper_rel(root, UPPERREL_FINAL, NULL);
@@ -1910,7 +1899,15 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
final_rel->consider_parallel = true;
/*
- * Generate paths for the final rel.
+ * If the current_rel belongs to a single FDW, so does the final_rel.
+ */
+ final_rel->serverid = current_rel->serverid;
+ final_rel->umid = current_rel->umid;
+ final_rel->fdwroutine = current_rel->fdwroutine;
+
+ /*
+ * Generate paths for the final_rel. Insert all surviving paths, with
+ * LockRows, Limit, and/or ModifyTable steps added if needed.
*/
foreach(lc, current_rel->pathlist)
{
@@ -1994,6 +1991,15 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
add_path(final_rel, path);
}
+ /*
+ * If there is an FDW that's responsible for all baserels of the query,
+ * let it consider adding ForeignPaths.
+ */
+ if (final_rel->fdwroutine &&
+ final_rel->fdwroutine->GetForeignUpperPaths)
+ final_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_FINAL,
+ current_rel, final_rel);
+
/* Let extensions possibly add some more paths */
if (create_upper_paths_hook)
(*create_upper_paths_hook) (root, UPPERREL_FINAL,
@@ -3268,6 +3274,13 @@ create_grouping_paths(PlannerInfo *root,
!has_parallel_hazard((Node *) parse->havingQual, false))
grouped_rel->consider_parallel = true;
+ /*
+ * If the input rel belongs to a single FDW, so does the grouped rel.
+ */
+ grouped_rel->serverid = input_rel->serverid;
+ grouped_rel->umid = input_rel->umid;
+ grouped_rel->fdwroutine = input_rel->fdwroutine;
+
/*
* Check for degenerate grouping.
*/
@@ -3770,6 +3783,15 @@ create_grouping_paths(PlannerInfo *root,
errmsg("could not implement GROUP BY"),
errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
+ /*
+ * If there is an FDW that's responsible for all baserels of the query,
+ * let it consider adding ForeignPaths.
+ */
+ if (grouped_rel->fdwroutine &&
+ grouped_rel->fdwroutine->GetForeignUpperPaths)
+ grouped_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_GROUP_AGG,
+ input_rel, grouped_rel);
+
/* Let extensions possibly add some more paths */
if (create_upper_paths_hook)
(*create_upper_paths_hook) (root, UPPERREL_GROUP_AGG,
@@ -3820,6 +3842,13 @@ create_window_paths(PlannerInfo *root,
!has_parallel_hazard((Node *) activeWindows, false))
window_rel->consider_parallel = true;
+ /*
+ * If the input rel belongs to a single FDW, so does the window rel.
+ */
+ window_rel->serverid = input_rel->serverid;
+ window_rel->umid = input_rel->umid;
+ window_rel->fdwroutine = input_rel->fdwroutine;
+
/*
* Consider computing window functions starting from the existing
* cheapest-total path (which will likely require a sort) as well as any
@@ -3841,6 +3870,15 @@ create_window_paths(PlannerInfo *root,
activeWindows);
}
+ /*
+ * If there is an FDW that's responsible for all baserels of the query,
+ * let it consider adding ForeignPaths.
+ */
+ if (window_rel->fdwroutine &&
+ window_rel->fdwroutine->GetForeignUpperPaths)
+ window_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_WINDOW,
+ input_rel, window_rel);
+
/* Let extensions possibly add some more paths */
if (create_upper_paths_hook)
(*create_upper_paths_hook) (root, UPPERREL_WINDOW,
@@ -3984,6 +4022,13 @@ create_distinct_paths(PlannerInfo *root,
*/
distinct_rel->consider_parallel = input_rel->consider_parallel;
+ /*
+ * If the input rel belongs to a single FDW, so does the distinct_rel.
+ */
+ distinct_rel->serverid = input_rel->serverid;
+ distinct_rel->umid = input_rel->umid;
+ distinct_rel->fdwroutine = input_rel->fdwroutine;
+
/* Estimate number of distinct rows there will be */
if (parse->groupClause || parse->groupingSets || parse->hasAggs ||
root->hasHavingQual)
@@ -4129,6 +4174,15 @@ create_distinct_paths(PlannerInfo *root,
errmsg("could not implement DISTINCT"),
errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
+ /*
+ * If there is an FDW that's responsible for all baserels of the query,
+ * let it consider adding ForeignPaths.
+ */
+ if (distinct_rel->fdwroutine &&
+ distinct_rel->fdwroutine->GetForeignUpperPaths)
+ distinct_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_DISTINCT,
+ input_rel, distinct_rel);
+
/* Let extensions possibly add some more paths */
if (create_upper_paths_hook)
(*create_upper_paths_hook) (root, UPPERREL_DISTINCT,
@@ -4176,6 +4230,13 @@ create_ordered_paths(PlannerInfo *root,
!has_parallel_hazard((Node *) target->exprs, false))
ordered_rel->consider_parallel = true;
+ /*
+ * If the input rel belongs to a single FDW, so does the ordered_rel.
+ */
+ ordered_rel->serverid = input_rel->serverid;
+ ordered_rel->umid = input_rel->umid;
+ ordered_rel->fdwroutine = input_rel->fdwroutine;
+
foreach(lc, input_rel->pathlist)
{
Path *path = (Path *) lfirst(lc);
@@ -4204,6 +4265,15 @@ create_ordered_paths(PlannerInfo *root,
}
}
+ /*
+ * If there is an FDW that's responsible for all baserels of the query,
+ * let it consider adding ForeignPaths.
+ */
+ if (ordered_rel->fdwroutine &&
+ ordered_rel->fdwroutine->GetForeignUpperPaths)
+ ordered_rel->fdwroutine->GetForeignUpperPaths(root, UPPERREL_ORDERED,
+ input_rel, ordered_rel);
+
/* Let extensions possibly add some more paths */
if (create_upper_paths_hook)
(*create_upper_paths_hook) (root, UPPERREL_ORDERED,
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index ca01238c7f2..b7147832e0f 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -174,6 +174,10 @@ plan_set_operations(PlannerInfo *root)
*/
setop_rel = fetch_upper_rel(root, UPPERREL_SETOP, NULL);
+ /*
+ * We don't currently worry about setting setop_rel's consider_parallel
+ * flag, nor about allowing FDWs to contribute paths to it.
+ */
/*
* If the topmost node is a recursive union, it needs special processing.
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index d5c1df26987..e1b0d0da7df 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -60,7 +60,9 @@ typedef void (*GetForeignJoinPaths_function) (PlannerInfo *root,
JoinPathExtraData *extra);
typedef void (*GetForeignUpperPaths_function) (PlannerInfo *root,
- RelOptInfo *scan_join_rel);
+ UpperRelationKind stage,
+ RelOptInfo *input_rel,
+ RelOptInfo *output_rel);
typedef void (*AddForeignUpdateTargets_function) (Query *parsetree,
RangeTblEntry *target_rte,