Commit 1a8a4e5c authored by Tom Lane's avatar Tom Lane

Code review for foreign/custom join pushdown patch.

Commit e7cb7ee1 included some design
decisions that seem pretty questionable to me, and there was quite a lot
of stuff not to like about the documentation and comments.  Clean up
as follows:

* Consider foreign joins only between foreign tables on the same server,
rather than between any two foreign tables with the same underlying FDW
handler function.  In most if not all cases, the FDW would simply have had
to apply the same-server restriction itself (far more expensively, both for
lack of caching and because it would be repeated for each combination of
input sub-joins), or else risk nasty bugs.  Anyone who's really intent on
doing something outside this restriction can always use the
set_join_pathlist_hook.

* Rename fdw_ps_tlist/custom_ps_tlist to fdw_scan_tlist/custom_scan_tlist
to better reflect what they're for, and allow these custom scan tlists
to be used even for base relations.

* Change make_foreignscan() API to include passing the fdw_scan_tlist
value, since the FDW is required to set that.  Backwards compatibility
doesn't seem like an adequate reason to expect FDWs to set it in some
ad-hoc extra step, and anyway existing FDWs can just pass NIL.

* Change the API of path-generating subroutines of add_paths_to_joinrel,
and in particular that of GetForeignJoinPaths and set_join_pathlist_hook,
so that various less-used parameters are passed in a struct rather than
as separate parameter-list entries.  The objective here is to reduce the
probability that future additions to those parameter lists will result in
source-level API breaks for users of these hooks.  It's possible that this
is even a small win for the core code, since most CPU architectures can't
pass more than half a dozen parameters efficiently anyway.  I kept root,
joinrel, outerrel, innerrel, and jointype as separate parameters to reduce
code churn in joinpath.c --- in particular, putting jointype into the
struct would have been problematic because of the subroutines' habit of
changing their local copies of that variable.

* Avoid ad-hocery in ExecAssignScanProjectionInfo.  It was probably all
right for it to know about IndexOnlyScan, but if the list is to grow
we should refactor the knowledge out to the callers.

* Restore nodeForeignscan.c's previous use of the relcache to avoid
extra GetFdwRoutine lookups for base-relation scans.

* Lots of cleanup of documentation and missed comments.  Re-order some
code additions into more logical places.
parent c594c750
...@@ -561,7 +561,8 @@ fileGetForeignPlan(PlannerInfo *root, ...@@ -561,7 +561,8 @@ fileGetForeignPlan(PlannerInfo *root,
scan_clauses, scan_clauses,
scan_relid, scan_relid,
NIL, /* no expressions to evaluate */ NIL, /* no expressions to evaluate */
best_path->fdw_private); best_path->fdw_private,
NIL /* no custom tlist */ );
} }
/* /*
......
...@@ -872,7 +872,8 @@ postgresGetForeignPlan(PlannerInfo *root, ...@@ -872,7 +872,8 @@ postgresGetForeignPlan(PlannerInfo *root,
local_exprs, local_exprs,
scan_relid, scan_relid,
params_list, params_list,
fdw_private); fdw_private,
NIL /* no custom tlist */ );
} }
/* /*
......
This diff is collapsed.
...@@ -175,8 +175,11 @@ GetForeignPlan (PlannerInfo *root, ...@@ -175,8 +175,11 @@ GetForeignPlan (PlannerInfo *root,
access path. This is called at the end of query planning. access path. This is called at the end of query planning.
The parameters are as for <function>GetForeignRelSize</>, plus The parameters are as for <function>GetForeignRelSize</>, plus
the selected <structname>ForeignPath</> (previously produced by the selected <structname>ForeignPath</> (previously produced by
<function>GetForeignPaths</>), the target list to be emitted by the <function>GetForeignPaths</> or <function>GetForeignJoinPaths</>),
plan node, and the restriction clauses to be enforced by the plan node. the target list to be emitted by the plan node,
and the restriction clauses to be enforced by the plan node.
(If the path is for a join rather than a base
relation, <literal>foreigntableid</> is <literal>InvalidOid</>.)
</para> </para>
<para> <para>
...@@ -235,9 +238,12 @@ IterateForeignScan (ForeignScanState *node); ...@@ -235,9 +238,12 @@ IterateForeignScan (ForeignScanState *node);
</para> </para>
<para> <para>
The rows returned must match the column signature of the foreign table The rows returned must match the <structfield>fdw_scan_tlist</> target
being scanned. If you choose to optimize away fetching columns that list if one was supplied, otherwise they must match the rowtype of the
are not needed, you should insert nulls in those column positions. foreign table being scanned. If you choose to optimize away fetching
columns that are not needed, you should insert nulls in those column
positions, or else generate a <structfield>fdw_scan_tlist</> list with
those columns omitted.
</para> </para>
<para> <para>
...@@ -275,6 +281,67 @@ EndForeignScan (ForeignScanState *node); ...@@ -275,6 +281,67 @@ EndForeignScan (ForeignScanState *node);
</sect2> </sect2>
<sect2 id="fdw-callbacks-join-scan">
<title>FDW Routines For Scanning Foreign Joins</title>
<para>
If an FDW supports performing foreign joins remotely (rather than
by fetching both tables' data and doing the join locally), it should
provide this callback function:
</para>
<para>
<programlisting>
void
GetForeignJoinPaths (PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
JoinType jointype,
JoinPathExtraData *extra);
</programlisting>
Create possible access paths for a join of two (or more) foreign tables
that all belong to the same foreign server. This optional
function is called during query planning. As
with <function>GetForeignPaths</>, this function should
generate <structname>ForeignPath</> path(s) for the
supplied <literal>joinrel</>, and call <function>add_path</> to add these
paths to the set of paths considered for the join. But unlike
<function>GetForeignPaths</>, it is not necessary that this function
succeed in creating at least one path, since paths involving local
joining are always possible.
</para>
<para>
Note that this function will be invoked repeatedly for the same join
relation, with different combinations of inner and outer relations; it is
the responsibility of the FDW to minimize duplicated work.
</para>
<para>
If a <structname>ForeignPath</> path is chosen for the join, it will
represent the entire join process; paths generated for the component
tables and subsidiary joins will not be used. Subsequent processing of
the join path proceeds much as it does for a path scanning a single
foreign table. One difference is that the <structfield>scanrelid</> of
the resulting <structname>ForeignScan</> plan node should be set to zero,
since there is no single relation that it represents; instead,
the <structfield>fs_relids</> field of the <structname>ForeignScan</>
node represents the set of relations that were joined. (The latter field
is set up automatically by the core planner code, and need not be filled
by the FDW.) Another difference is that, because the column list for a
remote join cannot be found from the system catalogs, the FDW must
fill <structfield>fdw_scan_tlist</> with an appropriate list
of <structfield>TargetEntry</> nodes, representing the set of columns
it will supply at runtime in the tuples it returns.
</para>
<para>
See <xref linkend="fdw-planning"> for additional information.
</para>
</sect2>
<sect2 id="fdw-callbacks-update"> <sect2 id="fdw-callbacks-update">
<title>FDW Routines For Updating Foreign Tables</title> <title>FDW Routines For Updating Foreign Tables</title>
...@@ -598,42 +665,6 @@ IsForeignRelUpdatable (Relation rel); ...@@ -598,42 +665,6 @@ IsForeignRelUpdatable (Relation rel);
</sect2> </sect2>
<sect2>
<title>FDW Routines For Remote Joins</title>
<para>
<programlisting>
void
GetForeignJoinPaths(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
JoinType jointype,
SpecialJoinInfo *sjinfo,
SemiAntiJoinFactors *semifactors,
Relids param_source_rels,
Relids extra_lateral_rels);
</programlisting>
Create possible access paths for a join of two foreign tables managed
by the same foreign data wrapper.
This optional function is called during query planning.
</para>
<para>
This function the FDW to add <structname>ForeignScan</> paths for the
supplied <literal>joinrel</>. Typically, the FDW will send the whole
join to the remote server as a single query, as performing the join
remotely rather than locally is typically much more efficient.
</para>
<para>
Since we cannot construct the slot descriptor for a remote join from
the catalogs, the FDW should set the <structfield>scanrelid</> of the
<structname>ForeignScan</> to zero and <structfield>fdw_ps_tlist</>
to an appropriate list of <structfield>TargetEntry</> nodes.
Junk entries will be ignored, but can be present for the benefit of
deparsing performed by <command>EXPLAIN</>.
</para>
</sect2>
<sect2 id="fdw-callbacks-explain"> <sect2 id="fdw-callbacks-explain">
<title>FDW Routines for <command>EXPLAIN</></title> <title>FDW Routines for <command>EXPLAIN</></title>
...@@ -904,10 +935,10 @@ GetForeignServerByName(const char *name, bool missing_ok); ...@@ -904,10 +935,10 @@ GetForeignServerByName(const char *name, bool missing_ok);
<para> <para>
The FDW callback functions <function>GetForeignRelSize</>, The FDW callback functions <function>GetForeignRelSize</>,
<function>GetForeignPaths</>, <function>GetForeignPlan</>, and <function>GetForeignPaths</>, <function>GetForeignPlan</>,
<function>PlanForeignModify</> must fit into the workings of the <function>PlanForeignModify</>, and <function>GetForeignJoinPaths</>
<productname>PostgreSQL</> planner. Here are some notes about what must fit into the workings of the <productname>PostgreSQL</> planner.
they must do. Here are some notes about what they must do.
</para> </para>
<para> <para>
...@@ -934,7 +965,7 @@ GetForeignServerByName(const char *name, bool missing_ok); ...@@ -934,7 +965,7 @@ GetForeignServerByName(const char *name, bool missing_ok);
<literal>baserel-&gt;fdw_private</> is a <type>void</> pointer that is <literal>baserel-&gt;fdw_private</> is a <type>void</> pointer that is
available for FDW planning functions to store information relevant to available for FDW planning functions to store information relevant to
the particular foreign table. The core planner does not touch it except the particular foreign table. The core planner does not touch it except
to initialize it to NULL when the <literal>baserel</> node is created. to initialize it to NULL when the <literal>RelOptInfo</> node is created.
It is useful for passing information forward from It is useful for passing information forward from
<function>GetForeignRelSize</> to <function>GetForeignPaths</> and/or <function>GetForeignRelSize</> to <function>GetForeignPaths</> and/or
<function>GetForeignPaths</> to <function>GetForeignPlan</>, thereby <function>GetForeignPaths</> to <function>GetForeignPlan</>, thereby
...@@ -1002,6 +1033,23 @@ GetForeignServerByName(const char *name, bool missing_ok); ...@@ -1002,6 +1033,23 @@ GetForeignServerByName(const char *name, bool missing_ok);
evaluation of the <structfield>fdw_exprs</> expression tree. evaluation of the <structfield>fdw_exprs</> expression tree.
</para> </para>
<para>
Another <structname>ForeignScan</> field that can be filled by FDWs
is <structfield>fdw_scan_tlist</>, which describes the tuples returned by
the FDW for this plan node. For simple foreign table scans this can be
set to <literal>NIL</>, implying that the returned tuples have the
rowtype declared for the foreign table. A non-NIL value must be a
targetlist (list of <structname>TargetEntry</>s) containing Vars and/or
expressions representing the returned columns. This might be used, for
example, to show that the FDW has omitted some columns that it noticed
won't be needed for the query. Also, if the FDW can compute expressions
used by the query more cheaply than can be done locally, it could add
those expressions to <structfield>fdw_scan_tlist</>. Note that join
plans (created from paths made by <function>GetForeignJoinPaths</>) must
always supply <structfield>fdw_scan_tlist</> to describe the set of
columns they will return.
</para>
<para> <para>
The FDW should always construct at least one path that depends only on The FDW should always construct at least one path that depends only on
the table's restriction clauses. In join queries, it might also choose the table's restriction clauses. In join queries, it might also choose
...@@ -1019,6 +1067,18 @@ GetForeignServerByName(const char *name, bool missing_ok); ...@@ -1019,6 +1067,18 @@ GetForeignServerByName(const char *name, bool missing_ok);
same as for an ordinary restriction clause. same as for an ordinary restriction clause.
</para> </para>
<para>
If an FDW supports remote joins, <function>GetForeignJoinPaths</> should
produce <structname>ForeignPath</>s for potential remote joins in much
the same way as <function>GetForeignPaths</> works for base tables.
Information about the intended join can be passed forward
to <function>GetForeignPlan</> in the same ways described above.
However, <structfield>baserestrictinfo</> is not relevant for join
relations; instead, the relevant join clauses for a particular join are
passed to <function>GetForeignJoinPaths</> as a separate parameter
(<literal>extra-&gt;restrictlist</>).
</para>
<para> <para>
When planning an <command>UPDATE</> or <command>DELETE</>, When planning an <command>UPDATE</> or <command>DELETE</>,
<function>PlanForeignModify</> can look up the <structname>RelOptInfo</> <function>PlanForeignModify</> can look up the <structname>RelOptInfo</>
......
...@@ -736,11 +736,11 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used) ...@@ -736,11 +736,11 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
break; break;
case T_ForeignScan: case T_ForeignScan:
*rels_used = bms_add_members(*rels_used, *rels_used = bms_add_members(*rels_used,
((ForeignScan *) plan)->fdw_relids); ((ForeignScan *) plan)->fs_relids);
break; break;
case T_CustomScan: case T_CustomScan:
*rels_used = bms_add_members(*rels_used, *rels_used = bms_add_members(*rels_used,
((CustomScan *) plan)->custom_relids); ((CustomScan *) plan)->custom_relids);
break; break;
case T_ModifyTable: case T_ModifyTable:
*rels_used = bms_add_member(*rels_used, *rels_used = bms_add_member(*rels_used,
......
...@@ -246,19 +246,18 @@ void ...@@ -246,19 +246,18 @@ void
ExecAssignScanProjectionInfo(ScanState *node) ExecAssignScanProjectionInfo(ScanState *node)
{ {
Scan *scan = (Scan *) node->ps.plan; Scan *scan = (Scan *) node->ps.plan;
Index varno;
/* Vars in an index-only scan's tlist should be INDEX_VAR */ ExecAssignScanProjectionInfoWithVarno(node, scan->scanrelid);
if (IsA(scan, IndexOnlyScan)) }
varno = INDEX_VAR;
/* Also foreign or custom scan on pseudo relation should be INDEX_VAR */ /*
else if (scan->scanrelid == 0) * ExecAssignScanProjectionInfoWithVarno
{ * As above, but caller can specify varno expected in Vars in the tlist.
Assert(IsA(scan, ForeignScan) || IsA(scan, CustomScan)); */
varno = INDEX_VAR; void
} ExecAssignScanProjectionInfoWithVarno(ScanState *node, Index varno)
else {
varno = scan->scanrelid; Scan *scan = (Scan *) node->ps.plan;
if (tlist_matches_tupdesc(&node->ps, if (tlist_matches_tupdesc(&node->ps,
scan->plan.targetlist, scan->plan.targetlist,
......
...@@ -22,13 +22,24 @@ ...@@ -22,13 +22,24 @@
CustomScanState * CustomScanState *
ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
{ {
CustomScanState *css; CustomScanState *css;
Index scan_relid = cscan->scan.scanrelid; Relation scan_rel = NULL;
Index scanrelid = cscan->scan.scanrelid;
Index tlistvarno;
/* populate a CustomScanState according to the CustomScan */ /*
* Allocate the CustomScanState object. We let the custom scan provider
* do the palloc, in case it wants to make a larger object that embeds
* CustomScanState as the first field. It must set the node tag and the
* methods field correctly at this time. Other standard fields should be
* set to zero.
*/
css = (CustomScanState *) cscan->methods->CreateCustomScanState(cscan); css = (CustomScanState *) cscan->methods->CreateCustomScanState(cscan);
Assert(IsA(css, CustomScanState)); Assert(IsA(css, CustomScanState));
/* ensure flags is filled correctly */
css->flags = cscan->flags;
/* fill up fields of ScanState */ /* fill up fields of ScanState */
css->ss.ps.plan = &cscan->scan.plan; css->ss.ps.plan = &cscan->scan.plan;
css->ss.ps.state = estate; css->ss.ps.state = estate;
...@@ -36,6 +47,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) ...@@ -36,6 +47,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
/* create expression context for node */ /* create expression context for node */
ExecAssignExprContext(estate, &css->ss.ps); ExecAssignExprContext(estate, &css->ss.ps);
css->ss.ps.ps_TupFromTlist = false;
/* initialize child expressions */ /* initialize child expressions */
css->ss.ps.targetlist = (List *) css->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) cscan->scan.plan.targetlist, ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
...@@ -49,32 +62,40 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) ...@@ -49,32 +62,40 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
ExecInitResultTupleSlot(estate, &css->ss.ps); ExecInitResultTupleSlot(estate, &css->ss.ps);
/* /*
* open the base relation and acquire an appropriate lock on it; * open the base relation, if any, and acquire an appropriate lock on it
* also, get and assign the scan type
*/ */
if (scan_relid > 0) if (scanrelid > 0)
{ {
Relation scan_rel; scan_rel = ExecOpenScanRelation(estate, scanrelid, eflags);
scan_rel = ExecOpenScanRelation(estate, scan_relid, eflags);
css->ss.ss_currentRelation = scan_rel; css->ss.ss_currentRelation = scan_rel;
css->ss.ss_currentScanDesc = NULL; /* set by provider */
ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel));
} }
else
/*
* Determine the scan tuple type. If the custom scan provider provided a
* targetlist describing the scan tuples, use that; else use base
* relation's rowtype.
*/
if (cscan->custom_scan_tlist != NIL || scan_rel == NULL)
{ {
TupleDesc ps_tupdesc; TupleDesc scan_tupdesc;
ps_tupdesc = ExecCleanTypeFromTL(cscan->custom_ps_tlist, false); scan_tupdesc = ExecTypeFromTL(cscan->custom_scan_tlist, false);
ExecAssignScanType(&css->ss, ps_tupdesc); ExecAssignScanType(&css->ss, scan_tupdesc);
/* Node's targetlist will contain Vars with varno = INDEX_VAR */
tlistvarno = INDEX_VAR;
}
else
{
ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel));
/* Node's targetlist will contain Vars with varno = scanrelid */
tlistvarno = scanrelid;
} }
css->ss.ps.ps_TupFromTlist = false;
/* /*
* Initialize result tuple type and projection info. * Initialize result tuple type and projection info.
*/ */
ExecAssignResultTypeFromTL(&css->ss.ps); ExecAssignResultTypeFromTL(&css->ss.ps);
ExecAssignScanProjectionInfo(&css->ss); ExecAssignScanProjectionInfoWithVarno(&css->ss, tlistvarno);
/* /*
* The callback of custom-scan provider applies the final initialization * The callback of custom-scan provider applies the final initialization
......
...@@ -102,7 +102,9 @@ ForeignScanState * ...@@ -102,7 +102,9 @@ ForeignScanState *
ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
{ {
ForeignScanState *scanstate; ForeignScanState *scanstate;
Relation currentRelation = NULL;
Index scanrelid = node->scan.scanrelid; Index scanrelid = node->scan.scanrelid;
Index tlistvarno;
FdwRoutine *fdwroutine; FdwRoutine *fdwroutine;
/* check for unsupported flags */ /* check for unsupported flags */
...@@ -141,40 +143,55 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) ...@@ -141,40 +143,55 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
ExecInitScanTupleSlot(estate, &scanstate->ss); ExecInitScanTupleSlot(estate, &scanstate->ss);
/* /*
* open the base relation and acquire an appropriate lock on it; * open the base relation, if any, and acquire an appropriate lock on it;
* also, get and assign the scan type * also acquire function pointers from the FDW's handler
*/ */
if (scanrelid > 0) if (scanrelid > 0)
{ {
Relation currentRelation;
currentRelation = ExecOpenScanRelation(estate, scanrelid, eflags); currentRelation = ExecOpenScanRelation(estate, scanrelid, eflags);
scanstate->ss.ss_currentRelation = currentRelation; scanstate->ss.ss_currentRelation = currentRelation;
ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation)); fdwroutine = GetFdwRoutineForRelation(currentRelation, true);
} }
else else
{ {
TupleDesc ps_tupdesc; /* We can't use the relcache, so get fdwroutine the hard way */
fdwroutine = GetFdwRoutineByServerId(node->fs_server);
}
ps_tupdesc = ExecCleanTypeFromTL(node->fdw_ps_tlist, false); /*
ExecAssignScanType(&scanstate->ss, ps_tupdesc); * Determine the scan tuple type. If the FDW provided a targetlist
* describing the scan tuples, use that; else use base relation's rowtype.
*/
if (node->fdw_scan_tlist != NIL || currentRelation == NULL)
{
TupleDesc scan_tupdesc;
scan_tupdesc = ExecTypeFromTL(node->fdw_scan_tlist, false);
ExecAssignScanType(&scanstate->ss, scan_tupdesc);
/* Node's targetlist will contain Vars with varno = INDEX_VAR */
tlistvarno = INDEX_VAR;
}
else
{
ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
/* Node's targetlist will contain Vars with varno = scanrelid */
tlistvarno = scanrelid;
} }
/* /*
* Initialize result tuple type and projection info. * Initialize result tuple type and projection info.
*/ */
ExecAssignResultTypeFromTL(&scanstate->ss.ps); ExecAssignResultTypeFromTL(&scanstate->ss.ps);
ExecAssignScanProjectionInfo(&scanstate->ss); ExecAssignScanProjectionInfoWithVarno(&scanstate->ss, tlistvarno);
/* /*
* Acquire function pointers from the FDW's handler, and init fdw_state. * Initialize FDW-related state.
*/ */
fdwroutine = GetFdwRoutine(node->fdw_handler);
scanstate->fdwroutine = fdwroutine; scanstate->fdwroutine = fdwroutine;
scanstate->fdw_state = NULL; scanstate->fdw_state = NULL;
/* /*
* Tell the FDW to initiate the scan. * Tell the FDW to initialize the scan.
*/ */
fdwroutine->BeginForeignScan(scanstate, eflags); fdwroutine->BeginForeignScan(scanstate, eflags);
......
...@@ -442,10 +442,12 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) ...@@ -442,10 +442,12 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
ExecAssignScanType(&indexstate->ss, tupDesc); ExecAssignScanType(&indexstate->ss, tupDesc);
/* /*
* Initialize result tuple type and projection info. * Initialize result tuple type and projection info. The node's
* targetlist will contain Vars with varno = INDEX_VAR, referencing the
* scan tuple.
*/ */
ExecAssignResultTypeFromTL(&indexstate->ss.ps); ExecAssignResultTypeFromTL(&indexstate->ss.ps);
ExecAssignScanProjectionInfo(&indexstate->ss); ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR);
/* /*
* If we are just doing EXPLAIN (ie, aren't going to run the plan), stop * If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
......
...@@ -304,21 +304,16 @@ GetFdwRoutine(Oid fdwhandler) ...@@ -304,21 +304,16 @@ GetFdwRoutine(Oid fdwhandler)
/* /*
* GetFdwHandlerByRelId - look up the handler of the foreign-data wrapper * GetForeignServerIdByRelId - look up the foreign server
* for the given foreign table * for the given foreign table, and return its OID.
*/ */
Oid Oid
GetFdwHandlerByRelId(Oid relid) GetForeignServerIdByRelId(Oid relid)
{ {
HeapTuple tp; HeapTuple tp;
Form_pg_foreign_data_wrapper fdwform;
Form_pg_foreign_server serverform;
Form_pg_foreign_table tableform; Form_pg_foreign_table tableform;
Oid serverid; Oid serverid;
Oid fdwid;
Oid fdwhandler;
/* Get server OID for the foreign table. */
tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid)); tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tp)) if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for foreign table %u", relid); elog(ERROR, "cache lookup failed for foreign table %u", relid);
...@@ -326,6 +321,23 @@ GetFdwHandlerByRelId(Oid relid) ...@@ -326,6 +321,23 @@ GetFdwHandlerByRelId(Oid relid)
serverid = tableform->ftserver; serverid = tableform->ftserver;
ReleaseSysCache(tp); ReleaseSysCache(tp);
return serverid;
}
/*
* GetFdwRoutineByServerId - look up the handler of the foreign-data wrapper
* for the given foreign server, and retrieve its FdwRoutine struct.
*/
FdwRoutine *
GetFdwRoutineByServerId(Oid serverid)
{
HeapTuple tp;
Form_pg_foreign_data_wrapper fdwform;
Form_pg_foreign_server serverform;
Oid fdwid;
Oid fdwhandler;
/* Get foreign-data wrapper OID for the server. */ /* Get foreign-data wrapper OID for the server. */
tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)); tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
if (!HeapTupleIsValid(tp)) if (!HeapTupleIsValid(tp))
...@@ -350,9 +362,11 @@ GetFdwHandlerByRelId(Oid relid) ...@@ -350,9 +362,11 @@ GetFdwHandlerByRelId(Oid relid)
ReleaseSysCache(tp); ReleaseSysCache(tp);
return fdwhandler; /* And finally, call the handler function. */
return GetFdwRoutine(fdwhandler);
} }
/* /*
* GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
* for the given foreign table, and retrieve its FdwRoutine struct. * for the given foreign table, and retrieve its FdwRoutine struct.
...@@ -360,9 +374,13 @@ GetFdwHandlerByRelId(Oid relid) ...@@ -360,9 +374,13 @@ GetFdwHandlerByRelId(Oid relid)
FdwRoutine * FdwRoutine *
GetFdwRoutineByRelId(Oid relid) GetFdwRoutineByRelId(Oid relid)
{ {
Oid fdwhandler = GetFdwHandlerByRelId(relid); Oid serverid;
return GetFdwRoutine(fdwhandler); /* Get server OID for the foreign table. */
serverid = GetForeignServerIdByRelId(relid);
/* Now retrieve server's FdwRoutine struct. */
return GetFdwRoutineByServerId(serverid);
} }
/* /*
...@@ -656,7 +674,7 @@ get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok) ...@@ -656,7 +674,7 @@ get_foreign_data_wrapper_oid(const char *fdwname, bool missing_ok)
/* /*
* get_foreign_server_oid - given a FDW name, look up the OID * get_foreign_server_oid - given a server name, look up the OID
* *
* If missing_ok is false, throw an error if name not found. If true, just * If missing_ok is false, throw an error if name not found. If true, just
* return InvalidOid. * return InvalidOid.
......
...@@ -599,11 +599,11 @@ _copyForeignScan(const ForeignScan *from) ...@@ -599,11 +599,11 @@ _copyForeignScan(const ForeignScan *from)
/* /*
* copy remainder of node * copy remainder of node
*/ */
COPY_SCALAR_FIELD(fdw_handler); COPY_SCALAR_FIELD(fs_server);
COPY_NODE_FIELD(fdw_exprs); COPY_NODE_FIELD(fdw_exprs);
COPY_NODE_FIELD(fdw_ps_tlist);
COPY_NODE_FIELD(fdw_private); COPY_NODE_FIELD(fdw_private);
COPY_BITMAPSET_FIELD(fdw_relids); COPY_NODE_FIELD(fdw_scan_tlist);
COPY_BITMAPSET_FIELD(fs_relids);
COPY_SCALAR_FIELD(fsSystemCol); COPY_SCALAR_FIELD(fsSystemCol);
return newnode; return newnode;
...@@ -627,8 +627,8 @@ _copyCustomScan(const CustomScan *from) ...@@ -627,8 +627,8 @@ _copyCustomScan(const CustomScan *from)
*/ */
COPY_SCALAR_FIELD(flags); COPY_SCALAR_FIELD(flags);
COPY_NODE_FIELD(custom_exprs); COPY_NODE_FIELD(custom_exprs);
COPY_NODE_FIELD(custom_ps_tlist);
COPY_NODE_FIELD(custom_private); COPY_NODE_FIELD(custom_private);
COPY_NODE_FIELD(custom_scan_tlist);
COPY_BITMAPSET_FIELD(custom_relids); COPY_BITMAPSET_FIELD(custom_relids);
/* /*
......
...@@ -565,11 +565,11 @@ _outForeignScan(StringInfo str, const ForeignScan *node) ...@@ -565,11 +565,11 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
_outScanInfo(str, (const Scan *) node); _outScanInfo(str, (const Scan *) node);
WRITE_OID_FIELD(fdw_handler); WRITE_OID_FIELD(fs_server);
WRITE_NODE_FIELD(fdw_exprs); WRITE_NODE_FIELD(fdw_exprs);
WRITE_NODE_FIELD(fdw_ps_tlist);
WRITE_NODE_FIELD(fdw_private); WRITE_NODE_FIELD(fdw_private);
WRITE_BITMAPSET_FIELD(fdw_relids); WRITE_NODE_FIELD(fdw_scan_tlist);
WRITE_BITMAPSET_FIELD(fs_relids);
WRITE_BOOL_FIELD(fsSystemCol); WRITE_BOOL_FIELD(fsSystemCol);
} }
...@@ -582,8 +582,8 @@ _outCustomScan(StringInfo str, const CustomScan *node) ...@@ -582,8 +582,8 @@ _outCustomScan(StringInfo str, const CustomScan *node)
WRITE_UINT_FIELD(flags); WRITE_UINT_FIELD(flags);
WRITE_NODE_FIELD(custom_exprs); WRITE_NODE_FIELD(custom_exprs);
WRITE_NODE_FIELD(custom_ps_tlist);
WRITE_NODE_FIELD(custom_private); WRITE_NODE_FIELD(custom_private);
WRITE_NODE_FIELD(custom_scan_tlist);
WRITE_BITMAPSET_FIELD(custom_relids); WRITE_BITMAPSET_FIELD(custom_relids);
appendStringInfoString(str, " :methods "); appendStringInfoString(str, " :methods ");
_outToken(str, node->methods->CustomName); _outToken(str, node->methods->CustomName);
...@@ -1844,6 +1844,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node) ...@@ -1844,6 +1844,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
WRITE_NODE_FIELD(subplan); WRITE_NODE_FIELD(subplan);
WRITE_NODE_FIELD(subroot); WRITE_NODE_FIELD(subroot);
WRITE_NODE_FIELD(subplan_params); WRITE_NODE_FIELD(subplan_params);
WRITE_OID_FIELD(serverid);
/* we don't try to print fdwroutine or fdw_private */ /* we don't try to print fdwroutine or fdw_private */
WRITE_NODE_FIELD(baserestrictinfo); WRITE_NODE_FIELD(baserestrictinfo);
WRITE_NODE_FIELD(joininfo); WRITE_NODE_FIELD(joininfo);
......
This diff is collapsed.
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
static Plan *create_scan_plan(PlannerInfo *root, Path *best_path); static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
static List *build_path_tlist(PlannerInfo *root, Path *path); static List *build_path_tlist(PlannerInfo *root, Path *path);
static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel); static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
...@@ -219,7 +220,7 @@ create_plan(PlannerInfo *root, Path *best_path) ...@@ -219,7 +220,7 @@ create_plan(PlannerInfo *root, Path *best_path)
* create_plan_recurse * create_plan_recurse
* Recursive guts of create_plan(). * Recursive guts of create_plan().
*/ */
Plan * static Plan *
create_plan_recurse(PlannerInfo *root, Path *best_path) create_plan_recurse(PlannerInfo *root, Path *best_path)
{ {
Plan *plan; Plan *plan;
...@@ -1950,7 +1951,7 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path, ...@@ -1950,7 +1951,7 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path,
/* /*
* create_foreignscan_plan * create_foreignscan_plan
* Returns a foreignscan plan for the base relation scanned by 'best_path' * Returns a foreignscan plan for the relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/ */
static ForeignScan * static ForeignScan *
...@@ -1965,9 +1966,11 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, ...@@ -1965,9 +1966,11 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
ListCell *lc; ListCell *lc;
int i; int i;
Assert(rel->fdwroutine != NULL);
/* /*
* If we're scanning a base relation, look up the OID. * If we're scanning a base relation, fetch its OID. (Irrelevant if
* (We can skip this if scanning a join relation.) * scanning a join relation.)
*/ */
if (scan_relid > 0) if (scan_relid > 0)
{ {
...@@ -1978,7 +1981,6 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, ...@@ -1978,7 +1981,6 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
Assert(rte->rtekind == RTE_RELATION); Assert(rte->rtekind == RTE_RELATION);
rel_oid = rte->relid; rel_oid = rte->relid;
} }
Assert(rel->fdwroutine != NULL);
/* /*
* Sort clauses into best execution order. We do this first since the FDW * Sort clauses into best execution order. We do this first since the FDW
...@@ -1996,42 +1998,22 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, ...@@ -1996,42 +1998,22 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid, scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid,
best_path, best_path,
tlist, scan_clauses); tlist, scan_clauses);
/*
* Sanity check. There may be resjunk entries in fdw_ps_tlist that
* are included only to help EXPLAIN deparse plans properly. We require
* that these are at the end, so that when the executor builds the scan
* descriptor based on the non-junk entries, it gets the attribute
* numbers correct.
*/
if (scan_plan->scan.scanrelid == 0)
{
bool found_resjunk = false;
foreach (lc, scan_plan->fdw_ps_tlist)
{
TargetEntry *tle = lfirst(lc);
if (tle->resjunk)
found_resjunk = true;
else if (found_resjunk)
elog(ERROR, "junk TLE should not apper prior to valid one");
}
}
/* Set the relids that are represented by this foreign scan for Explain */
scan_plan->fdw_relids = best_path->path.parent->relids;
/* Copy cost data from Path to Plan; no need to make FDW do this */ /* Copy cost data from Path to Plan; no need to make FDW do this */
copy_path_costsize(&scan_plan->scan.plan, &best_path->path); copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
/* Track FDW server-id; no need to make FDW do this */ /* Copy foreign server OID; likewise, no need to make FDW do this */
scan_plan->fdw_handler = rel->fdw_handler; scan_plan->fs_server = rel->serverid;
/* Likewise, copy the relids that are represented by this foreign scan */
scan_plan->fs_relids = best_path->path.parent->relids;
/* /*
* Replace any outer-relation variables with nestloop params in the qual * Replace any outer-relation variables with nestloop params in the qual
* and fdw_exprs expressions. We do this last so that the FDW doesn't * and fdw_exprs expressions. We do this last so that the FDW doesn't
* have to be involved. (Note that parts of fdw_exprs could have come * have to be involved. (Note that parts of fdw_exprs could have come
* from join clauses, so doing this beforehand on the scan_clauses * from join clauses, so doing this beforehand on the scan_clauses
* wouldn't work.) * wouldn't work.) We assume fdw_scan_tlist contains no such variables.
*/ */
if (best_path->path.param_info) if (best_path->path.param_info)
{ {
...@@ -2087,7 +2069,6 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path, ...@@ -2087,7 +2069,6 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
{ {
CustomScan *cplan; CustomScan *cplan;
RelOptInfo *rel = best_path->path.parent; RelOptInfo *rel = best_path->path.parent;
ListCell *lc;
/* /*
* Sort clauses into the best execution order, although custom-scan * Sort clauses into the best execution order, although custom-scan
...@@ -2106,42 +2087,22 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path, ...@@ -2106,42 +2087,22 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
scan_clauses); scan_clauses);
Assert(IsA(cplan, CustomScan)); Assert(IsA(cplan, CustomScan));
/*
* Sanity check. There may be resjunk entries in custom_ps_tlist that
* are included only to help EXPLAIN deparse plans properly. We require
* that these are at the end, so that when the executor builds the scan
* descriptor based on the non-junk entries, it gets the attribute
* numbers correct.
*/
if (cplan->scan.scanrelid == 0)
{
bool found_resjunk = false;
foreach (lc, cplan->custom_ps_tlist)
{
TargetEntry *tle = lfirst(lc);
if (tle->resjunk)
found_resjunk = true;
else if (found_resjunk)
elog(ERROR, "junk TLE should not apper prior to valid one");
}
}
/* Set the relids that are represented by this custom scan for Explain */
cplan->custom_relids = best_path->path.parent->relids;
/* /*
* Copy cost data from Path to Plan; no need to make custom-plan providers * Copy cost data from Path to Plan; no need to make custom-plan providers
* do this * do this
*/ */
copy_path_costsize(&cplan->scan.plan, &best_path->path); copy_path_costsize(&cplan->scan.plan, &best_path->path);
/* Likewise, copy the relids that are represented by this custom scan */
cplan->custom_relids = best_path->path.parent->relids;
/* /*
* Replace any outer-relation variables with nestloop params in the qual * Replace any outer-relation variables with nestloop params in the qual
* and custom_exprs expressions. We do this last so that the custom-plan * and custom_exprs expressions. We do this last so that the custom-plan
* provider doesn't have to be involved. (Note that parts of custom_exprs * provider doesn't have to be involved. (Note that parts of custom_exprs
* could have come from join clauses, so doing this beforehand on the * could have come from join clauses, so doing this beforehand on the
* scan_clauses wouldn't work.) * scan_clauses wouldn't work.) We assume custom_scan_tlist contains no
* such variables.
*/ */
if (best_path->path.param_info) if (best_path->path.param_info)
{ {
...@@ -3611,7 +3572,8 @@ make_foreignscan(List *qptlist, ...@@ -3611,7 +3572,8 @@ make_foreignscan(List *qptlist,
List *qpqual, List *qpqual,
Index scanrelid, Index scanrelid,
List *fdw_exprs, List *fdw_exprs,
List *fdw_private) List *fdw_private,
List *fdw_scan_tlist)
{ {
ForeignScan *node = makeNode(ForeignScan); ForeignScan *node = makeNode(ForeignScan);
Plan *plan = &node->scan.plan; Plan *plan = &node->scan.plan;
...@@ -3622,8 +3584,13 @@ make_foreignscan(List *qptlist, ...@@ -3622,8 +3584,13 @@ make_foreignscan(List *qptlist,
plan->lefttree = NULL; plan->lefttree = NULL;
plan->righttree = NULL; plan->righttree = NULL;
node->scan.scanrelid = scanrelid; node->scan.scanrelid = scanrelid;
/* fs_server will be filled in by create_foreignscan_plan */
node->fs_server = InvalidOid;
node->fdw_exprs = fdw_exprs; node->fdw_exprs = fdw_exprs;
node->fdw_private = fdw_private; node->fdw_private = fdw_private;
node->fdw_scan_tlist = fdw_scan_tlist;
/* fs_relids will be filled in by create_foreignscan_plan */
node->fs_relids = NULL;
/* fsSystemCol will be filled in by create_foreignscan_plan */ /* fsSystemCol will be filled in by create_foreignscan_plan */
node->fsSystemCol = false; node->fsSystemCol = false;
......
...@@ -86,12 +86,6 @@ static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte); ...@@ -86,12 +86,6 @@ static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob); static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob);
static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte); static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte);
static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset); static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset);
static void set_foreignscan_references(PlannerInfo *root,
ForeignScan *fscan,
int rtoffset);
static void set_customscan_references(PlannerInfo *root,
CustomScan *cscan,
int rtoffset);
static Plan *set_indexonlyscan_references(PlannerInfo *root, static Plan *set_indexonlyscan_references(PlannerInfo *root,
IndexOnlyScan *plan, IndexOnlyScan *plan,
int rtoffset); int rtoffset);
...@@ -99,6 +93,12 @@ static Plan *set_subqueryscan_references(PlannerInfo *root, ...@@ -99,6 +93,12 @@ static Plan *set_subqueryscan_references(PlannerInfo *root,
SubqueryScan *plan, SubqueryScan *plan,
int rtoffset); int rtoffset);
static bool trivial_subqueryscan(SubqueryScan *plan); static bool trivial_subqueryscan(SubqueryScan *plan);
static void set_foreignscan_references(PlannerInfo *root,
ForeignScan *fscan,
int rtoffset);
static void set_customscan_references(PlannerInfo *root,
CustomScan *cscan,
int rtoffset);
static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset); static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context); static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context); static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
...@@ -573,7 +573,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) ...@@ -573,7 +573,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
case T_ForeignScan: case T_ForeignScan:
set_foreignscan_references(root, (ForeignScan *) plan, rtoffset); set_foreignscan_references(root, (ForeignScan *) plan, rtoffset);
break; break;
case T_CustomScan: case T_CustomScan:
set_customscan_references(root, (CustomScan *) plan, rtoffset); set_customscan_references(root, (CustomScan *) plan, rtoffset);
break; break;
...@@ -890,121 +889,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) ...@@ -890,121 +889,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
return plan; return plan;
} }
/*
* set_foreignscan_references
* Do set_plan_references processing on an ForeignScan
*/
static void
set_foreignscan_references(PlannerInfo *root,
ForeignScan *fscan,
int rtoffset)
{
if (rtoffset > 0)
{
Bitmapset *tempset = NULL;
int x = -1;
while ((x = bms_next_member(fscan->fdw_relids, x)) >= 0)
tempset = bms_add_member(tempset, x + rtoffset);
fscan->fdw_relids = tempset;
}
if (fscan->scan.scanrelid == 0)
{
indexed_tlist *pscan_itlist = build_tlist_index(fscan->fdw_ps_tlist);
fscan->scan.plan.targetlist = (List *)
fix_upper_expr(root,
(Node *) fscan->scan.plan.targetlist,
pscan_itlist,
INDEX_VAR,
rtoffset);
fscan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) fscan->scan.plan.qual,
pscan_itlist,
INDEX_VAR,
rtoffset);
fscan->fdw_exprs = (List *)
fix_upper_expr(root,
(Node *) fscan->fdw_exprs,
pscan_itlist,
INDEX_VAR,
rtoffset);
fscan->fdw_ps_tlist =
fix_scan_list(root, fscan->fdw_ps_tlist, rtoffset);
pfree(pscan_itlist);
}
else
{
fscan->scan.scanrelid += rtoffset;
fscan->scan.plan.targetlist =
fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
fscan->scan.plan.qual =
fix_scan_list(root, fscan->scan.plan.qual, rtoffset);
fscan->fdw_exprs =
fix_scan_list(root, fscan->fdw_exprs, rtoffset);
}
}
/*
* set_customscan_references
* Do set_plan_references processing on an CustomScan
*/
static void
set_customscan_references(PlannerInfo *root,
CustomScan *cscan,
int rtoffset)
{
if (rtoffset > 0)
{
Bitmapset *tempset = NULL;
int x = -1;
while ((x = bms_next_member(cscan->custom_relids, x)) >= 0)
tempset = bms_add_member(tempset, x + rtoffset);
cscan->custom_relids = tempset;
}
if (cscan->scan.scanrelid == 0)
{
indexed_tlist *pscan_itlist =
build_tlist_index(cscan->custom_ps_tlist);
cscan->scan.plan.targetlist = (List *)
fix_upper_expr(root,
(Node *) cscan->scan.plan.targetlist,
pscan_itlist,
INDEX_VAR,
rtoffset);
cscan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) cscan->scan.plan.qual,
pscan_itlist,
INDEX_VAR,
rtoffset);
cscan->custom_exprs = (List *)
fix_upper_expr(root,
(Node *) cscan->custom_exprs,
pscan_itlist,
INDEX_VAR,
rtoffset);
cscan->custom_ps_tlist =
fix_scan_list(root, cscan->custom_ps_tlist, rtoffset);
pfree(pscan_itlist);
}
else
{
cscan->scan.scanrelid += rtoffset;
cscan->scan.plan.targetlist =
fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset);
cscan->scan.plan.qual =
fix_scan_list(root, cscan->scan.plan.qual, rtoffset);
cscan->custom_exprs =
fix_scan_list(root, cscan->custom_exprs, rtoffset);
}
}
/* /*
* set_indexonlyscan_references * set_indexonlyscan_references
* Do set_plan_references processing on an IndexOnlyScan * Do set_plan_references processing on an IndexOnlyScan
...@@ -1179,6 +1063,134 @@ trivial_subqueryscan(SubqueryScan *plan) ...@@ -1179,6 +1063,134 @@ trivial_subqueryscan(SubqueryScan *plan)
return true; return true;
} }
/*
* set_foreignscan_references
* Do set_plan_references processing on a ForeignScan
*/
static void
set_foreignscan_references(PlannerInfo *root,
ForeignScan *fscan,
int rtoffset)
{
/* Adjust scanrelid if it's valid */
if (fscan->scan.scanrelid > 0)
fscan->scan.scanrelid += rtoffset;
if (fscan->fdw_scan_tlist != NIL || fscan->scan.scanrelid == 0)
{
/* Adjust tlist, qual, fdw_exprs to reference custom scan tuple */
indexed_tlist *itlist = build_tlist_index(fscan->fdw_scan_tlist);
fscan->scan.plan.targetlist = (List *)
fix_upper_expr(root,
(Node *) fscan->scan.plan.targetlist,
itlist,
INDEX_VAR,
rtoffset);
fscan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) fscan->scan.plan.qual,
itlist,
INDEX_VAR,
rtoffset);
fscan->fdw_exprs = (List *)
fix_upper_expr(root,
(Node *) fscan->fdw_exprs,
itlist,
INDEX_VAR,
rtoffset);
pfree(itlist);
/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
fscan->fdw_scan_tlist =
fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset);
}
else
{
/* Adjust tlist, qual, fdw_exprs in the standard way */
fscan->scan.plan.targetlist =
fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
fscan->scan.plan.qual =
fix_scan_list(root, fscan->scan.plan.qual, rtoffset);
fscan->fdw_exprs =
fix_scan_list(root, fscan->fdw_exprs, rtoffset);
}
/* Adjust fs_relids if needed */
if (rtoffset > 0)
{
Bitmapset *tempset = NULL;
int x = -1;
while ((x = bms_next_member(fscan->fs_relids, x)) >= 0)
tempset = bms_add_member(tempset, x + rtoffset);
fscan->fs_relids = tempset;
}
}
/*
* set_customscan_references
* Do set_plan_references processing on a CustomScan
*/
static void
set_customscan_references(PlannerInfo *root,
CustomScan *cscan,
int rtoffset)
{
/* Adjust scanrelid if it's valid */
if (cscan->scan.scanrelid > 0)
cscan->scan.scanrelid += rtoffset;
if (cscan->custom_scan_tlist != NIL || cscan->scan.scanrelid == 0)
{
/* Adjust tlist, qual, custom_exprs to reference custom scan tuple */
indexed_tlist *itlist = build_tlist_index(cscan->custom_scan_tlist);
cscan->scan.plan.targetlist = (List *)
fix_upper_expr(root,
(Node *) cscan->scan.plan.targetlist,
itlist,
INDEX_VAR,
rtoffset);
cscan->scan.plan.qual = (List *)
fix_upper_expr(root,
(Node *) cscan->scan.plan.qual,
itlist,
INDEX_VAR,
rtoffset);
cscan->custom_exprs = (List *)
fix_upper_expr(root,
(Node *) cscan->custom_exprs,
itlist,
INDEX_VAR,
rtoffset);
pfree(itlist);
/* custom_scan_tlist itself just needs fix_scan_list() adjustments */
cscan->custom_scan_tlist =
fix_scan_list(root, cscan->custom_scan_tlist, rtoffset);
}
else
{
/* Adjust tlist, qual, custom_exprs in the standard way */
cscan->scan.plan.targetlist =
fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset);
cscan->scan.plan.qual =
fix_scan_list(root, cscan->scan.plan.qual, rtoffset);
cscan->custom_exprs =
fix_scan_list(root, cscan->custom_exprs, rtoffset);
}
/* Adjust custom_relids if needed */
if (rtoffset > 0)
{
Bitmapset *tempset = NULL;
int x = -1;
while ((x = bms_next_member(cscan->custom_relids, x)) >= 0)
tempset = bms_add_member(tempset, x + rtoffset);
cscan->custom_relids = tempset;
}
}
/* /*
* copyVar * copyVar
* Copy a Var node. * Copy a Var node.
......
...@@ -2318,12 +2318,14 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, ...@@ -2318,12 +2318,14 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
case T_ForeignScan: case T_ForeignScan:
finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs, finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs,
&context); &context);
/* We assume fdw_scan_tlist cannot contain Params */
context.paramids = bms_add_members(context.paramids, scan_params); context.paramids = bms_add_members(context.paramids, scan_params);
break; break;
case T_CustomScan: case T_CustomScan:
finalize_primnode((Node *) ((CustomScan *) plan)->custom_exprs, finalize_primnode((Node *) ((CustomScan *) plan)->custom_exprs,
&context); &context);
/* We assume custom_scan_tlist cannot contain Params */
context.paramids = bms_add_members(context.paramids, scan_params); context.paramids = bms_add_members(context.paramids, scan_params);
break; break;
......
...@@ -380,17 +380,18 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, ...@@ -380,17 +380,18 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
rel->indexlist = indexinfos; rel->indexlist = indexinfos;
/* Grab the fdwroutine info using the relcache, while we have it */ /* Grab foreign-table info using the relcache, while we have it */
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
{ {
rel->fdw_handler = GetFdwHandlerByRelId(RelationGetRelid(relation)); rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation));
rel->fdwroutine = GetFdwRoutineForRelation(relation, true); rel->fdwroutine = GetFdwRoutineForRelation(relation, true);
} }
else else
{ {
rel->fdw_handler = InvalidOid; rel->serverid = InvalidOid;
rel->fdwroutine = NULL; rel->fdwroutine = NULL;
} }
heap_close(relation, NoLock); heap_close(relation, NoLock);
/* /*
......
...@@ -14,7 +14,6 @@ ...@@ -14,7 +14,6 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "foreign/fdwapi.h"
#include "optimizer/cost.h" #include "optimizer/cost.h"
#include "optimizer/pathnode.h" #include "optimizer/pathnode.h"
#include "optimizer/paths.h" #include "optimizer/paths.h"
...@@ -122,8 +121,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind) ...@@ -122,8 +121,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
rel->subplan = NULL; rel->subplan = NULL;
rel->subroot = NULL; rel->subroot = NULL;
rel->subplan_params = NIL; rel->subplan_params = NIL;
rel->serverid = InvalidOid;
rel->fdwroutine = NULL; rel->fdwroutine = NULL;
rel->fdw_handler = InvalidOid;
rel->fdw_private = NULL; rel->fdw_private = NULL;
rel->baserestrictinfo = NIL; rel->baserestrictinfo = NIL;
rel->baserestrictcost.startup = 0; rel->baserestrictcost.startup = 0;
...@@ -385,6 +384,7 @@ build_join_rel(PlannerInfo *root, ...@@ -385,6 +384,7 @@ build_join_rel(PlannerInfo *root,
joinrel->subplan = NULL; joinrel->subplan = NULL;
joinrel->subroot = NULL; joinrel->subroot = NULL;
joinrel->subplan_params = NIL; joinrel->subplan_params = NIL;
joinrel->serverid = InvalidOid;
joinrel->fdwroutine = NULL; joinrel->fdwroutine = NULL;
joinrel->fdw_private = NULL; joinrel->fdw_private = NULL;
joinrel->baserestrictinfo = NIL; joinrel->baserestrictinfo = NIL;
...@@ -393,6 +393,17 @@ build_join_rel(PlannerInfo *root, ...@@ -393,6 +393,17 @@ build_join_rel(PlannerInfo *root,
joinrel->joininfo = NIL; joinrel->joininfo = NIL;
joinrel->has_eclass_joins = false; joinrel->has_eclass_joins = false;
/*
* Set up foreign-join fields if outer and inner relation are foreign
* tables (or joins) belonging to the same server.
*/
if (OidIsValid(outer_rel->serverid) &&
inner_rel->serverid == outer_rel->serverid)
{
joinrel->serverid = outer_rel->serverid;
joinrel->fdwroutine = outer_rel->fdwroutine;
}
/* /*
* Create a new tlist containing just the vars that need to be output from * Create a new tlist containing just the vars that need to be output from
* this join (ie, are needed for higher joinclauses or final output). * this join (ie, are needed for higher joinclauses or final output).
...@@ -428,18 +439,6 @@ build_join_rel(PlannerInfo *root, ...@@ -428,18 +439,6 @@ build_join_rel(PlannerInfo *root,
set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel, set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,
sjinfo, restrictlist); sjinfo, restrictlist);
/*
* Set FDW handler and routine if both outer and inner relation
* are managed by same FDW driver.
*/
if (OidIsValid(outer_rel->fdw_handler) &&
OidIsValid(inner_rel->fdw_handler) &&
outer_rel->fdw_handler == inner_rel->fdw_handler)
{
joinrel->fdw_handler = outer_rel->fdw_handler;
joinrel->fdwroutine = GetFdwRoutine(joinrel->fdw_handler);
}
/* /*
* Add the joinrel to the query's joinrel list, and store it into the * Add the joinrel to the query's joinrel list, and store it into the
* auxiliary hashtable if there is one. NB: GEQO requires us to append * auxiliary hashtable if there is one. NB: GEQO requires us to append
......
...@@ -128,8 +128,8 @@ typedef struct ...@@ -128,8 +128,8 @@ typedef struct
* varlevelsup > 0). We store the PlanState node that is the immediate * varlevelsup > 0). We store the PlanState node that is the immediate
* parent of the expression to be deparsed, as well as a list of that * parent of the expression to be deparsed, as well as a list of that
* PlanState's ancestors. In addition, we store its outer and inner subplan * PlanState's ancestors. In addition, we store its outer and inner subplan
* state nodes, as well as their plan nodes' targetlists, and the indextlist * state nodes, as well as their plan nodes' targetlists, and the index tlist
* if the current PlanState is an IndexOnlyScanState. (These fields could * if the current plan node might contain INDEX_VAR Vars. (These fields could
* be derived on-the-fly from the current PlanState, but it seems notationally * be derived on-the-fly from the current PlanState, but it seems notationally
* clearer to set them up as separate fields.) * clearer to set them up as separate fields.)
*/ */
...@@ -2586,10 +2586,11 @@ deparse_context_for_plan_rtable(List *rtable, List *rtable_names) ...@@ -2586,10 +2586,11 @@ deparse_context_for_plan_rtable(List *rtable, List *rtable_names)
* provide the parent PlanState node. Then OUTER_VAR and INNER_VAR references * provide the parent PlanState node. Then OUTER_VAR and INNER_VAR references
* can be resolved by drilling down into the left and right child plans. * can be resolved by drilling down into the left and right child plans.
* Similarly, INDEX_VAR references can be resolved by reference to the * Similarly, INDEX_VAR references can be resolved by reference to the
* indextlist given in the parent IndexOnlyScan node. (Note that we don't * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
* currently support deparsing of indexquals in regular IndexScan or * ForeignScan and CustomScan nodes. (Note that we don't currently support
* BitmapIndexScan nodes; for those, we can only deparse the indexqualorig * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
* fields, which won't contain INDEX_VAR Vars.) * for those, we can only deparse the indexqualorig fields, which won't
* contain INDEX_VAR Vars.)
* *
* Note: planstate really ought to be declared as "PlanState *", but we use * Note: planstate really ought to be declared as "PlanState *", but we use
* "Node *" to avoid having to include execnodes.h in ruleutils.h. * "Node *" to avoid having to include execnodes.h in ruleutils.h.
...@@ -3870,13 +3871,13 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps) ...@@ -3870,13 +3871,13 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
else else
dpns->inner_tlist = NIL; dpns->inner_tlist = NIL;
/* index_tlist is set only if it's an IndexOnlyScan */ /* Set up referent for INDEX_VAR Vars, if needed */
if (IsA(ps->plan, IndexOnlyScan)) if (IsA(ps->plan, IndexOnlyScan))
dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist; dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist;
else if (IsA(ps->plan, ForeignScan)) else if (IsA(ps->plan, ForeignScan))
dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_ps_tlist; dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_scan_tlist;
else if (IsA(ps->plan, CustomScan)) else if (IsA(ps->plan, CustomScan))
dpns->index_tlist = ((CustomScan *) ps->plan)->custom_ps_tlist; dpns->index_tlist = ((CustomScan *) ps->plan)->custom_scan_tlist;
else else
dpns->index_tlist = NIL; dpns->index_tlist = NIL;
} }
......
...@@ -257,6 +257,7 @@ typedef bool (*ExecScanRecheckMtd) (ScanState *node, TupleTableSlot *slot); ...@@ -257,6 +257,7 @@ typedef bool (*ExecScanRecheckMtd) (ScanState *node, TupleTableSlot *slot);
extern TupleTableSlot *ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, extern TupleTableSlot *ExecScan(ScanState *node, ExecScanAccessMtd accessMtd,
ExecScanRecheckMtd recheckMtd); ExecScanRecheckMtd recheckMtd);
extern void ExecAssignScanProjectionInfo(ScanState *node); extern void ExecAssignScanProjectionInfo(ScanState *node);
extern void ExecAssignScanProjectionInfoWithVarno(ScanState *node, Index varno);
extern void ExecScanReScan(ScanState *node); extern void ExecScanReScan(ScanState *node);
/* /*
......
...@@ -47,6 +47,13 @@ typedef void (*ReScanForeignScan_function) (ForeignScanState *node); ...@@ -47,6 +47,13 @@ typedef void (*ReScanForeignScan_function) (ForeignScanState *node);
typedef void (*EndForeignScan_function) (ForeignScanState *node); typedef void (*EndForeignScan_function) (ForeignScanState *node);
typedef void (*GetForeignJoinPaths_function) (PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
JoinType jointype,
JoinPathExtraData *extra);
typedef void (*AddForeignUpdateTargets_function) (Query *parsetree, typedef void (*AddForeignUpdateTargets_function) (Query *parsetree,
RangeTblEntry *target_rte, RangeTblEntry *target_rte,
Relation target_relation); Relation target_relation);
...@@ -82,17 +89,6 @@ typedef void (*EndForeignModify_function) (EState *estate, ...@@ -82,17 +89,6 @@ typedef void (*EndForeignModify_function) (EState *estate,
typedef int (*IsForeignRelUpdatable_function) (Relation rel); typedef int (*IsForeignRelUpdatable_function) (Relation rel);
typedef void (*GetForeignJoinPaths_function) (PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
JoinType jointype,
SpecialJoinInfo *sjinfo,
SemiAntiJoinFactors *semifactors,
Relids param_source_rels,
Relids extra_lateral_rels);
typedef void (*ExplainForeignScan_function) (ForeignScanState *node, typedef void (*ExplainForeignScan_function) (ForeignScanState *node,
struct ExplainState *es); struct ExplainState *es);
...@@ -142,6 +138,9 @@ typedef struct FdwRoutine ...@@ -142,6 +138,9 @@ typedef struct FdwRoutine
* are not provided. * are not provided.
*/ */
/* Functions for remote-join planning */
GetForeignJoinPaths_function GetForeignJoinPaths;
/* Functions for updating foreign tables */ /* Functions for updating foreign tables */
AddForeignUpdateTargets_function AddForeignUpdateTargets; AddForeignUpdateTargets_function AddForeignUpdateTargets;
PlanForeignModify_function PlanForeignModify; PlanForeignModify_function PlanForeignModify;
...@@ -161,15 +160,13 @@ typedef struct FdwRoutine ...@@ -161,15 +160,13 @@ typedef struct FdwRoutine
/* Support functions for IMPORT FOREIGN SCHEMA */ /* Support functions for IMPORT FOREIGN SCHEMA */
ImportForeignSchema_function ImportForeignSchema; ImportForeignSchema_function ImportForeignSchema;
/* Support functions for join push-down */
GetForeignJoinPaths_function GetForeignJoinPaths;
} FdwRoutine; } FdwRoutine;
/* Functions in foreign/foreign.c */ /* Functions in foreign/foreign.c */
extern Oid GetFdwHandlerByRelId(Oid relid);
extern FdwRoutine *GetFdwRoutine(Oid fdwhandler); extern FdwRoutine *GetFdwRoutine(Oid fdwhandler);
extern Oid GetForeignServerIdByRelId(Oid relid);
extern FdwRoutine *GetFdwRoutineByServerId(Oid serverid);
extern FdwRoutine *GetFdwRoutineByRelId(Oid relid); extern FdwRoutine *GetFdwRoutineByRelId(Oid relid);
extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy); extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy);
extern bool IsImportableForeignTable(const char *tablename, extern bool IsImportableForeignTable(const char *tablename,
......
...@@ -479,32 +479,46 @@ typedef struct WorkTableScan ...@@ -479,32 +479,46 @@ typedef struct WorkTableScan
* fdw_exprs and fdw_private are both under the control of the foreign-data * fdw_exprs and fdw_private are both under the control of the foreign-data
* wrapper, but fdw_exprs is presumed to contain expression trees and will * wrapper, but fdw_exprs is presumed to contain expression trees and will
* be post-processed accordingly by the planner; fdw_private won't be. * be post-processed accordingly by the planner; fdw_private won't be.
* An optional fdw_ps_tlist is used to map a reference to an attribute of * Note that everything in both lists must be copiable by copyObject().
* underlying relation(s) onto a pair of INDEX_VAR and alternative varattno.
* When fdw_ps_tlist is used, this represents a remote join, and the FDW
* is responsible for setting this field to an appropriate value.
* Note that everything in above lists must be copiable by copyObject().
* One way to store an arbitrary blob of bytes is to represent it as a bytea * One way to store an arbitrary blob of bytes is to represent it as a bytea
* Const. Usually, though, you'll be better off choosing a representation * Const. Usually, though, you'll be better off choosing a representation
* that can be dumped usefully by nodeToString(). * that can be dumped usefully by nodeToString().
*
* fdw_scan_tlist is a targetlist describing the contents of the scan tuple
* returned by the FDW; it can be NIL if the scan tuple matches the declared
* rowtype of the foreign table, which is the normal case for a simple foreign
* table scan. (If the plan node represents a foreign join, fdw_scan_tlist
* is required since there is no rowtype available from the system catalogs.)
* When fdw_scan_tlist is provided, Vars in the node's tlist and quals must
* have varno INDEX_VAR, and their varattnos correspond to resnos in the
* fdw_scan_tlist (which are also column numbers in the actual scan tuple).
* fdw_scan_tlist is never actually executed; it just holds expression trees
* describing what is in the scan tuple's columns.
*
* When the plan node represents a foreign join, scan.scanrelid is zero and
* fs_relids must be consulted to identify the join relation. (fs_relids
* is valid for simple scans as well, but will always match scan.scanrelid.)
* ---------------- * ----------------
*/ */
typedef struct ForeignScan typedef struct ForeignScan
{ {
Scan scan; Scan scan;
Oid fdw_handler; /* OID of FDW handler */ Oid fs_server; /* OID of foreign server */
List *fdw_exprs; /* expressions that FDW may evaluate */ List *fdw_exprs; /* expressions that FDW may evaluate */
List *fdw_ps_tlist; /* tlist, if replacing a join */
List *fdw_private; /* private data for FDW */ List *fdw_private; /* private data for FDW */
Bitmapset *fdw_relids; /* RTIs generated by this scan */ List *fdw_scan_tlist; /* optional tlist describing scan tuple */
Bitmapset *fs_relids; /* RTIs generated by this scan */
bool fsSystemCol; /* true if any "system column" is needed */ bool fsSystemCol; /* true if any "system column" is needed */
} ForeignScan; } ForeignScan;
/* ---------------- /* ----------------
* CustomScan node * CustomScan node
* *
* The comments for ForeignScan's fdw_exprs, fdw_varmap and fdw_private fields * The comments for ForeignScan's fdw_exprs, fdw_private, fdw_scan_tlist,
* apply equally to custom_exprs, custom_ps_tlist and custom_private. * and fs_relids fields apply equally to CustomScan's custom_exprs,
* custom_private, custom_scan_tlist, and custom_relids fields. The
* convention of setting scan.scanrelid to zero for joins applies as well.
*
* Note that since Plan trees can be copied, custom scan providers *must* * Note that since Plan trees can be copied, custom scan providers *must*
* fit all plan data they need into those fields; embedding CustomScan in * fit all plan data they need into those fields; embedding CustomScan in
* a larger struct will not work. * a larger struct will not work.
...@@ -528,8 +542,9 @@ typedef struct CustomScan ...@@ -528,8 +542,9 @@ typedef struct CustomScan
Scan scan; Scan scan;
uint32 flags; /* mask of CUSTOMPATH_* flags, see relation.h */ uint32 flags; /* mask of CUSTOMPATH_* flags, see relation.h */
List *custom_exprs; /* expressions that custom code may evaluate */ List *custom_exprs; /* expressions that custom code may evaluate */
List *custom_ps_tlist;/* tlist, if replacing a join */
List *custom_private; /* private data for custom code */ List *custom_private; /* private data for custom code */
List *custom_scan_tlist; /* optional tlist describing scan
* tuple */
Bitmapset *custom_relids; /* RTIs generated by this scan */ Bitmapset *custom_relids; /* RTIs generated by this scan */
const CustomScanMethods *methods; const CustomScanMethods *methods;
} CustomScan; } CustomScan;
......
...@@ -127,9 +127,13 @@ typedef struct Expr ...@@ -127,9 +127,13 @@ typedef struct Expr
* upper-level plan nodes are reassigned to point to the outputs of their * upper-level plan nodes are reassigned to point to the outputs of their
* subplans; for example, in a join node varno becomes INNER_VAR or OUTER_VAR * subplans; for example, in a join node varno becomes INNER_VAR or OUTER_VAR
* and varattno becomes the index of the proper element of that subplan's * and varattno becomes the index of the proper element of that subplan's
* target list. But varnoold/varoattno continue to hold the original values. * target list. Similarly, INDEX_VAR is used to identify Vars that reference
* The code doesn't really need varnoold/varoattno, but they are very useful * an index column rather than a heap column. (In ForeignScan and CustomScan
* for debugging and interpreting completed plans, so we keep them around. * plan nodes, INDEX_VAR is abused to signify references to columns of a
* custom scan tuple type.) In all these cases, varnoold/varoattno hold the
* original values. The code doesn't really need varnoold/varoattno, but they
* are very useful for debugging and interpreting completed plans, so we keep
* them around.
*/ */
#define INNER_VAR 65000 /* reference to inner subplan */ #define INNER_VAR 65000 /* reference to inner subplan */
#define OUTER_VAR 65001 /* reference to outer subplan */ #define OUTER_VAR 65001 /* reference to outer subplan */
......
...@@ -365,18 +365,21 @@ typedef struct PlannerInfo ...@@ -365,18 +365,21 @@ typedef struct PlannerInfo
* subplan - plan for subquery (NULL if it's not a subquery) * subplan - plan for subquery (NULL if it's not a subquery)
* subroot - PlannerInfo for subquery (NULL if it's not a subquery) * subroot - PlannerInfo for subquery (NULL if it's not a subquery)
* subplan_params - list of PlannerParamItems to be passed to subquery * subplan_params - list of PlannerParamItems to be passed to subquery
* fdwroutine - function hooks for FDW, if foreign table (else NULL)
* fdw_handler - OID of FDW handler, if foreign table (else InvalidOid)
* fdw_private - private state for FDW, if foreign table (else NULL)
* *
* Note: for a subquery, tuples, subplan, subroot are not set immediately * Note: for a subquery, tuples, subplan, subroot are not set immediately
* upon creation of the RelOptInfo object; they are filled in when * upon creation of the RelOptInfo object; they are filled in when
* set_subquery_pathlist processes the object. Likewise, fdwroutine * set_subquery_pathlist processes the object.
* and fdw_private are filled during initial path creation.
* *
* For otherrels that are appendrel members, these fields are filled * For otherrels that are appendrel members, these fields are filled
* in just as for a baserel. * in just as for a baserel.
* *
* If the relation is either a foreign table or a join of foreign tables that
* all belong to the same foreign server, these fields will be set:
*
* serverid - OID of foreign server, if foreign table (else InvalidOid)
* fdwroutine - function hooks for FDW, if foreign table (else NULL)
* fdw_private - private state for FDW, if foreign table (else NULL)
*
* The presence of the remaining fields depends on the restrictions * The presence of the remaining fields depends on the restrictions
* and joins that the relation participates in: * and joins that the relation participates in:
* *
...@@ -460,10 +463,12 @@ typedef struct RelOptInfo ...@@ -460,10 +463,12 @@ typedef struct RelOptInfo
struct Plan *subplan; /* if subquery */ struct Plan *subplan; /* if subquery */
PlannerInfo *subroot; /* if subquery */ PlannerInfo *subroot; /* if subquery */
List *subplan_params; /* if subquery */ List *subplan_params; /* if subquery */
/* Information about foreign tables and foreign joins */
Oid serverid; /* identifies server for the table or join */
/* use "struct FdwRoutine" to avoid including fdwapi.h here */ /* use "struct FdwRoutine" to avoid including fdwapi.h here */
struct FdwRoutine *fdwroutine; /* if foreign table */ struct FdwRoutine *fdwroutine;
Oid fdw_handler; /* if foreign table */ void *fdw_private;
void *fdw_private; /* if foreign table */
/* used by various scans and joins: */ /* used by various scans and joins: */
List *baserestrictinfo; /* RestrictInfo structures (if base List *baserestrictinfo; /* RestrictInfo structures (if base
...@@ -523,7 +528,7 @@ typedef struct IndexOptInfo ...@@ -523,7 +528,7 @@ typedef struct IndexOptInfo
bool *reverse_sort; /* is sort order descending? */ bool *reverse_sort; /* is sort order descending? */
bool *nulls_first; /* do NULLs come first in the sort order? */ bool *nulls_first; /* do NULLs come first in the sort order? */
bool *canreturn; /* which index cols can be returned in an bool *canreturn; /* which index cols can be returned in an
index-only scan? */ * index-only scan? */
Oid relam; /* OID of the access method (in pg_am) */ Oid relam; /* OID of the access method (in pg_am) */
RegProcedure amcostestimate; /* OID of the access method's cost fcn */ RegProcedure amcostestimate; /* OID of the access method's cost fcn */
...@@ -1667,6 +1672,28 @@ typedef struct SemiAntiJoinFactors ...@@ -1667,6 +1672,28 @@ typedef struct SemiAntiJoinFactors
Selectivity match_count; Selectivity match_count;
} SemiAntiJoinFactors; } SemiAntiJoinFactors;
/*
* Struct for extra information passed to subroutines of add_paths_to_joinrel
*
* restrictlist contains all of the RestrictInfo nodes for restriction
* clauses that apply to this join
* mergeclause_list is a list of RestrictInfo nodes for available
* mergejoin clauses in this join
* sjinfo is extra info about special joins for selectivity estimation
* semifactors is as shown above (only valid for SEMI or ANTI joins)
* param_source_rels are OK targets for parameterization of result paths
* extra_lateral_rels are additional parameterization for result paths
*/
typedef struct JoinPathExtraData
{
List *restrictlist;
List *mergeclause_list;
SpecialJoinInfo *sjinfo;
SemiAntiJoinFactors semifactors;
Relids param_source_rels;
Relids extra_lateral_rels;
} JoinPathExtraData;
/* /*
* For speed reasons, cost estimation for join paths is performed in two * For speed reasons, cost estimation for join paths is performed in two
* phases: the first phase tries to quickly derive a lower bound for the * phases: the first phase tries to quickly derive a lower bound for the
......
...@@ -32,15 +32,11 @@ extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook; ...@@ -32,15 +32,11 @@ extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook;
/* Hook for plugins to get control in add_paths_to_joinrel() */ /* Hook for plugins to get control in add_paths_to_joinrel() */
typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root, typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root,
RelOptInfo *joinrel, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *outerrel,
RelOptInfo *innerrel, RelOptInfo *innerrel,
List *restrictlist, JoinType jointype,
JoinType jointype, JoinPathExtraData *extra);
SpecialJoinInfo *sjinfo,
SemiAntiJoinFactors *semifactors,
Relids param_source_rels,
Relids extra_lateral_rels);
extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook; extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook;
/* Hook for plugins to replace standard_join_search() */ /* Hook for plugins to replace standard_join_search() */
......
...@@ -41,11 +41,11 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist, ...@@ -41,11 +41,11 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
* prototypes for plan/createplan.c * prototypes for plan/createplan.c
*/ */
extern Plan *create_plan(PlannerInfo *root, Path *best_path); extern Plan *create_plan(PlannerInfo *root, Path *best_path);
extern Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual, extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
Index scanrelid, Plan *subplan); Index scanrelid, Plan *subplan);
extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual, extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
Index scanrelid, List *fdw_exprs, List *fdw_private); Index scanrelid, List *fdw_exprs, List *fdw_private,
List *fdw_scan_tlist);
extern Append *make_append(List *appendplans, List *tlist); extern Append *make_append(List *appendplans, List *tlist);
extern RecursiveUnion *make_recursive_union(List *tlist, extern RecursiveUnion *make_recursive_union(List *tlist,
Plan *lefttree, Plan *righttree, int wtParam, Plan *lefttree, Plan *righttree, int wtParam,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment