diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b2a9a8088f61d855945e5ec57eb7298a056a091d..7c1e3d6bbfd29ae9c2f8bc916262d7c7c5776381 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -62,7 +62,7 @@ int			force_parallel_mode = FORCE_PARALLEL_OFF;
 /* Hook for plugins to get control in planner() */
 planner_hook_type planner_hook = NULL;
 
-/* Hook for plugins to get control before grouping_planner plans upper rels */
+/* Hook for plugins to get control when grouping_planner() plans upper rels */
 create_upper_paths_hook_type create_upper_paths_hook = NULL;
 
 
@@ -1772,20 +1772,20 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 		root->upper_targets[UPPERREL_GROUP_AGG] = grouping_target;
 
 		/*
-		 * Let extensions, particularly FDWs and CustomScan providers,
-		 * 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 hooks want to know should
-		 * be obtainable via "root".
+		 * 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 (create_upper_paths_hook)
-			(*create_upper_paths_hook) (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
@@ -1962,6 +1962,11 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
 		add_path(final_rel, path);
 	}
 
+	/* Let extensions possibly add some more paths */
+	if (create_upper_paths_hook)
+		(*create_upper_paths_hook) (root, UPPERREL_FINAL,
+									current_rel, final_rel);
+
 	/* Note: currently, we leave it to callers to do set_cheapest() */
 }
 
@@ -3724,6 +3729,11 @@ create_grouping_paths(PlannerInfo *root,
 				 errmsg("could not implement GROUP BY"),
 				 errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
 
+	/* Let extensions possibly add some more paths */
+	if (create_upper_paths_hook)
+		(*create_upper_paths_hook) (root, UPPERREL_GROUP_AGG,
+									input_rel, grouped_rel);
+
 	/* Now choose the best path(s) */
 	set_cheapest(grouped_rel);
 
@@ -3780,6 +3790,11 @@ create_window_paths(PlannerInfo *root,
 								   activeWindows);
 	}
 
+	/* Let extensions possibly add some more paths */
+	if (create_upper_paths_hook)
+		(*create_upper_paths_hook) (root, UPPERREL_WINDOW,
+									input_rel, window_rel);
+
 	/* Now choose the best path(s) */
 	set_cheapest(window_rel);
 
@@ -4056,6 +4071,11 @@ create_distinct_paths(PlannerInfo *root,
 				 errmsg("could not implement DISTINCT"),
 				 errdetail("Some of the datatypes only support hashing, while others only support sorting.")));
 
+	/* Let extensions possibly add some more paths */
+	if (create_upper_paths_hook)
+		(*create_upper_paths_hook) (root, UPPERREL_DISTINCT,
+									input_rel, distinct_rel);
+
 	/* Now choose the best path(s) */
 	set_cheapest(distinct_rel);
 
@@ -4117,6 +4137,11 @@ create_ordered_paths(PlannerInfo *root,
 		}
 	}
 
+	/* Let extensions possibly add some more paths */
+	if (create_upper_paths_hook)
+		(*create_upper_paths_hook) (root, UPPERREL_ORDERED,
+									input_rel, ordered_rel);
+
 	/*
 	 * No need to bother with set_cheapest here; grouping_planner does not
 	 * need us to do it.
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index a1ab4daf11a7d8e3361467d8837f6e0831ccbf5a..552b756b8b1b5da51445c02183a304889b439ee1 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -206,7 +206,12 @@ plan_set_operations(PlannerInfo *root)
 	/* Add only the final path to the SETOP upperrel. */
 	add_path(setop_rel, path);
 
-	/* Select cheapest path (pretty easy at the moment) */
+	/* Let extensions possibly add some more paths */
+	if (create_upper_paths_hook)
+		(*create_upper_paths_hook) (root, UPPERREL_SETOP,
+									NULL, setop_rel);
+
+	/* Select cheapest path */
 	set_cheapest(setop_rel);
 
 	return setop_rel;
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index a95e73fa93b4e36f83dd0c79b8b3b2160ec87729..4161bcf8d745221a6acec7f0f721b96ef07984c1 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -24,9 +24,11 @@ typedef PlannedStmt *(*planner_hook_type) (Query *parse,
 												  ParamListInfo boundParams);
 extern PGDLLIMPORT planner_hook_type planner_hook;
 
-/* Hook for plugins to get control before grouping_planner plans upper rels */
+/* Hook for plugins to get control when grouping_planner() plans upper rels */
 typedef void (*create_upper_paths_hook_type) (PlannerInfo *root,
-												  RelOptInfo *scan_join_rel);
+													 UpperRelationKind stage,
+													   RelOptInfo *input_rel,
+													 RelOptInfo *output_rel);
 extern PGDLLIMPORT create_upper_paths_hook_type create_upper_paths_hook;