Skip to content

Commit 101fd93

Browse files
committed
Add a GetForeignUpperPaths callback function for FDWs.
This is basically like the just-added create_upper_paths_hook, but control is funneled only to the FDW responsible for all the baserels of the current query; so providing such a callback is much less likely to add useless overhead than using the hook function is. The documentation is a bit sketchy. We'll likely want to improve it, and/or adjust the call conventions, when we get some experience with actually using this callback. Hopefully somebody will find time to experiment with it before 9.6 feature freeze.
1 parent be6de4c commit 101fd93

File tree

5 files changed

+67
-17
lines changed

5 files changed

+67
-17
lines changed

doc/src/sgml/fdwhandler.sgml

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ GetForeignPlan (PlannerInfo *root,
176176
access path. This is called at the end of query planning.
177177
The parameters are as for <function>GetForeignRelSize</>, plus
178178
the selected <structname>ForeignPath</> (previously produced by
179-
<function>GetForeignPaths</> or <function>GetForeignJoinPaths</>),
179+
<function>GetForeignPaths</>, <function>GetForeignJoinPaths</>,
180+
or <function>GetForeignUpperPaths</>),
180181
the target list to be emitted by the plan node,
181182
the restriction clauses to be enforced by the plan node,
182183
and the outer subplan of the <structname>ForeignScan</>,
@@ -344,6 +345,38 @@ GetForeignJoinPaths (PlannerInfo *root,
344345
</para>
345346
</sect2>
346347

348+
<sect2 id="fdw-callbacks-upper-planning">
349+
<title>FDW Routines For Planning Post-Scan/Join Processing</title>
350+
351+
<para>
352+
If an FDW supports performing remote post-scan/join processing, such as
353+
remote aggregation, it should provide this callback function:
354+
</para>
355+
356+
<para>
357+
<programlisting>
358+
void
359+
GetForeignUpperPaths (PlannerInfo *root,
360+
RelOptInfo *scan_join_rel);
361+
</programlisting>
362+
Create possible access paths for <firstterm>upper relation</> processing,
363+
which is the planner's term for all post-scan/join query processing, such
364+
as aggregation, window functions, sorting, and table updates. This
365+
optional function is called during query planning. Currently, it is
366+
called only if all base relation(s) involved in the query belong to the
367+
same FDW. This function should generate <structname>ForeignPath</>
368+
path(s) for the steps that the FDW knows how to perform remotely, and
369+
call <function>add_path</> to add these paths to the appropriate upper
370+
relation. As with <function>GetForeignJoinPaths</>, it is not necessary
371+
that this function succeed in creating any paths, since paths involving
372+
local processing are always possible.
373+
</para>
374+
375+
<para>
376+
See <xref linkend="fdw-planning"> for additional information.
377+
</para>
378+
</sect2>
379+
347380
<sect2 id="fdw-callbacks-update">
348381
<title>FDW Routines For Updating Foreign Tables</title>
349382

@@ -1160,7 +1193,8 @@ GetForeignServerByName(const char *name, bool missing_ok);
11601193
<para>
11611194
The FDW callback functions <function>GetForeignRelSize</>,
11621195
<function>GetForeignPaths</>, <function>GetForeignPlan</>,
1163-
<function>PlanForeignModify</>, and <function>GetForeignJoinPaths</>
1196+
<function>PlanForeignModify</>, <function>GetForeignJoinPaths</>,
1197+
and <function>GetForeignUpperPaths</>
11641198
must fit into the workings of the <productname>PostgreSQL</> planner.
11651199
Here are some notes about what they must do.
11661200
</para>
@@ -1322,7 +1356,7 @@ GetForeignServerByName(const char *name, bool missing_ok);
13221356
An FDW might additionally support direct execution of some plan actions
13231357
that are above the level of scans and joins, such as grouping or
13241358
aggregation. To offer such options, the FDW should generate paths
1325-
(probably ForeignPaths or CustomPaths) and insert them into the
1359+
and insert them into the
13261360
appropriate <firstterm>upper relation</>. For example, a path
13271361
representing remote aggregation should be inserted into the relation
13281362
obtained from <literal>fetch_upper_rel(root, UPPERREL_GROUP_AGG,
@@ -1332,6 +1366,9 @@ GetForeignServerByName(const char *name, bool missing_ok);
13321366
else there will be an error at plan time). If the remote-aggregation
13331367
path wins, which it usually would, it will be converted into a plan in
13341368
the usual way, by calling <function>GetForeignPlan</>.
1369+
Usually the most convenient place to generate such paths is in
1370+
the <function>GetForeignUpperPaths</> callback function, although
1371+
it can be done earlier if that seems appropriate.
13351372
</para>
13361373

13371374
<para>

src/backend/optimizer/README

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -916,7 +916,10 @@ remote aggregation into the UPPERREL_GROUP_AGG upperrel, if it notices
916916
that the query represents an aggregation that could be done entirely on
917917
the foreign server. That Path will then compete with Paths representing
918918
local aggregation on a regular scan of the foreign table, once the core
919-
planner reaches the point of considering aggregation.
919+
planner reaches the point of considering aggregation. (In practice,
920+
it will usually be more convenient for FDWs to detect such cases in a
921+
GetForeignUpperPaths callback; but that still represents injecting a
922+
Path before the core code has touched the corresponding upperrel.)
920923

921924

922925
Parallel Query and Partial Paths

src/backend/optimizer/plan/planner.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1752,13 +1752,17 @@ grouping_planner(PlannerInfo *root, bool inheritance_update,
17521752
root->upper_targets[UPPERREL_GROUP_AGG] = grouping_target;
17531753

17541754
/*
1755-
* Let extensions, particularly CustomScan providers, consider
1756-
* injecting extension Paths into the query's upperrels, where they
1757-
* will compete with the Paths we create below. We pass the final
1758-
* scan/join rel because that's not so easily findable from the
1759-
* PlannerInfo struct; anything else the hook wants to know should be
1760-
* obtainable via "root".
1755+
* Let extensions, particularly FDWs and CustomScan providers,
1756+
* consider injecting extension Paths into the query's upperrels,
1757+
* where they will compete with the Paths we create below. We pass
1758+
* the final scan/join rel because that's not so easily findable from
1759+
* the PlannerInfo struct; anything else the hooks want to know should
1760+
* be obtainable via "root".
17611761
*/
1762+
if (current_rel->fdwroutine &&
1763+
current_rel->fdwroutine->GetForeignUpperPaths)
1764+
current_rel->fdwroutine->GetForeignUpperPaths(root, current_rel);
1765+
17621766
if (create_upper_paths_hook)
17631767
(*create_upper_paths_hook) (root, current_rel);
17641768

src/backend/optimizer/util/pathnode.c

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1813,15 +1813,15 @@ create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel,
18131813

18141814
/*
18151815
* create_foreignscan_path
1816-
* Creates a path corresponding to a scan of a foreign table or
1817-
* a foreign join, returning the pathnode.
1816+
* Creates a path corresponding to a scan of a foreign table, foreign join,
1817+
* or foreign upper-relation processing, returning the pathnode.
18181818
*
18191819
* This function is never called from core Postgres; rather, it's expected
1820-
* to be called by the GetForeignPaths or GetForeignJoinPaths function of
1821-
* a foreign data wrapper. We make the FDW supply all fields of the path,
1822-
* since we do not have any way to calculate them in core. However, there
1823-
* is a sane default for the pathtarget (rel->reltarget), so we let a NULL
1824-
* for "target" select that.
1820+
* to be called by the GetForeignPaths, GetForeignJoinPaths, or
1821+
* GetForeignUpperPaths function of a foreign data wrapper. We make the FDW
1822+
* supply all fields of the path, since we do not have any way to calculate
1823+
* them in core. However, there is a usually-sane default for the pathtarget
1824+
* (rel->reltarget), so we let a NULL for "target" select that.
18251825
*/
18261826
ForeignPath *
18271827
create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,

src/include/foreign/fdwapi.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ typedef void (*GetForeignJoinPaths_function) (PlannerInfo *root,
5959
JoinType jointype,
6060
JoinPathExtraData *extra);
6161

62+
typedef void (*GetForeignUpperPaths_function) (PlannerInfo *root,
63+
RelOptInfo *scan_join_rel);
64+
6265
typedef void (*AddForeignUpdateTargets_function) (Query *parsetree,
6366
RangeTblEntry *target_rte,
6467
Relation target_relation);
@@ -166,6 +169,9 @@ typedef struct FdwRoutine
166169
/* Functions for remote-join planning */
167170
GetForeignJoinPaths_function GetForeignJoinPaths;
168171

172+
/* Functions for remote upper-relation (post scan/join) planning */
173+
GetForeignUpperPaths_function GetForeignUpperPaths;
174+
169175
/* Functions for updating foreign tables */
170176
AddForeignUpdateTargets_function AddForeignUpdateTargets;
171177
PlanForeignModify_function PlanForeignModify;

0 commit comments

Comments
 (0)