diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c
index 4368897581..4c56444751 100644
--- a/contrib/file_fdw/file_fdw.c
+++ b/contrib/file_fdw/file_fdw.c
@@ -561,7 +561,8 @@ fileGetForeignPlan(PlannerInfo *root,
scan_clauses,
scan_relid,
NIL, /* no expressions to evaluate */
- best_path->fdw_private);
+ best_path->fdw_private,
+ NIL /* no custom tlist */ );
}
/*
diff --git a/contrib/postgres_fdw/postgres_fdw.c b/contrib/postgres_fdw/postgres_fdw.c
index 173b4f06e6..ec89b25f61 100644
--- a/contrib/postgres_fdw/postgres_fdw.c
+++ b/contrib/postgres_fdw/postgres_fdw.c
@@ -872,7 +872,8 @@ postgresGetForeignPlan(PlannerInfo *root,
local_exprs,
scan_relid,
params_list,
- fdw_private);
+ fdw_private,
+ NIL /* no custom tlist */ );
}
/*
diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index 9fd1db6fde..62a8a3305b 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -32,12 +32,13 @@
- Implementing Custom Paths
+ Creating Custom Scan Paths
- A custom scan provider will typically add paths by setting the following
- hook, which is called after the core code has generated what it believes
- to be the complete and correct set of access paths for the relation.
+ A custom scan provider will typically add paths for a base relation by
+ setting the following hook, which is called after the core code has
+ generated what it believes to be the complete and correct set of access
+ paths for the relation.
typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root,
RelOptInfo *rel,
@@ -74,7 +75,7 @@ typedef struct CustomPath
can support mark and restore. Both capabilities are optional.
custom_private> can be used to store the custom path's
private data. Private data should be stored in a form that can be handled
- by nodeToString>, so that debugging routines which attempt to
+ by nodeToString>, so that debugging routines that attempt to
print the custom path will work as designed. methods> must
point to a (usually statically allocated) object implementing the required
custom path methods, of which there are currently only two, as further
@@ -82,29 +83,28 @@ typedef struct CustomPath
- A custom scan provider can also add join paths; in this case, the scan
- must produce the same output as would normally be produced by the join
- it replaces. To do this, the join provider should set the following hook.
- This hook may be invoked repeatedly for the same pair of relations, with
- different combinations of inner and outer relations; it is the
- responsibility of the hook to minimize duplicated work.
+ A custom scan provider can also provide join paths. Just as for base
+ relations, such a path must produce the same output as would normally be
+ produced by the join it replaces. To do this, the join provider should
+ set the following hook, and then within the hook function,
+ create CustomPath> path(s) for the join relation.
typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
- List *restrictlist,
JoinType jointype,
- SpecialJoinInfo *sjinfo,
- SemiAntiJoinFactors *semifactors,
- Relids param_source_rels,
- Relids extra_lateral_rels);
+ JoinPathExtraData *extra);
extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook;
+
+ This hook will be invoked repeatedly for the same join relation, with
+ different combinations of inner and outer relations; it is the
+ responsibility of the hook to minimize duplicated work.
- Custom Path Callbacks
+ Custom Scan Path Callbacks
@@ -125,7 +125,7 @@ void (*TextOutCustomPath) (StringInfo str,
const CustomPath *node);
Generate additional output when nodeToString> is invoked on
- this custom path. This callback is optional. Since
+ this custom path. This callback is optional. Since
nodeToString> will automatically dump all fields in the
structure that it can see, including custom_private>, this
is only useful if the CustomPath> is actually embedded in a
@@ -135,7 +135,7 @@ void (*TextOutCustomPath) (StringInfo str,
- Implementing Custom Plans
+ Creating Custom Scan Plans
A custom scan is represented in a finished plan tree using the following
@@ -146,9 +146,9 @@ typedef struct CustomScan
Scan scan;
uint32 flags;
List *custom_exprs;
- List *custom_ps_tlist;
List *custom_private;
- List *custom_relids;
+ List *custom_scan_tlist;
+ Bitmapset *custom_relids;
const CustomScanMethods *methods;
} CustomScan;
@@ -158,16 +158,21 @@ typedef struct CustomScan
scan> must be initialized as for any other scan, including
estimated costs, target lists, qualifications, and so on.
flags> is a bitmask with the same meaning as in
- CustomPath>. custom_exprs> should be used to
+ CustomPath>.
+ custom_exprs> should be used to
store expression trees that will need to be fixed up by
setrefs.c> and subselect.c>, while
- custom_private> should be used to store other private data that
- is only used by the custom scan provider itself. Plan trees must be able
- to be duplicated using copyObject>, so all the data stored
- within these two fields must consist of nodes that function can handle.
- custom_relids> is set by the core code to the set of relations
- which this scan node must handle; except when this scan is replacing a
- join, it will have only one member.
+ custom_private> should be used to store other private data
+ that is only used by the custom scan provider itself.
+ custom_scan_tlist> can be NIL when scanning a base
+ relation, indicating that the custom scan returns scan tuples that match
+ the base relation's rowtype. Otherwise it is a targetlist describing
+ the actual scan tuples. custom_scan_tlist> must be
+ provided for joins, and could be provided for scans if the custom scan
+ provider can compute some non-Var expressions.
+ custom_relids> is set by the core code to the set of
+ relations (rangetable indexes) that this scan node handles; except when
+ this scan is replacing a join, it will have only one member.
methods> must point to a (usually statically allocated)
object implementing the required custom scan methods, which are further
detailed below.
@@ -175,19 +180,22 @@ typedef struct CustomScan
When a CustomScan> scans a single relation,
- scan.scanrelid> should be the range table index of the table
- to be scanned, and custom_ps_tlist> should be
- NULL>. When it replaces a join, scan.scanrelid>
- should be zero, and custom_ps_tlist> should be a list of
- TargetEntry> nodes. This is necessary because, when a join
- is replaced, the target list cannot be constructed from the table
- definition. At execution time, this list will be used to initialize the
- tuple descriptor of the TupleTableSlot>. It will also be
- used by EXPLAIN>, when deparsing.
+ scan.scanrelid> must be the range table index of the table
+ to be scanned. When it replaces a join, scan.scanrelid>
+ should be zero.
+
+
+
+ Plan trees must be able to be duplicated using copyObject>,
+ so all the data stored within the custom> fields must consist of
+ nodes that that function can handle. Furthermore, custom scan providers
+ cannot substitute a larger structure that embeds
+ a CustomScan> for the structure itself, as would be possible
+ for a CustomPath> or CustomScanState>.
- Custom Scan Callbacks
+ Custom Scan Plan Callbacks
Node *(*CreateCustomScanState) (CustomScan *cscan);
@@ -195,12 +203,12 @@ Node *(*CreateCustomScanState) (CustomScan *cscan);
Allocate a CustomScanState> for this
CustomScan>. The actual allocation will often be larger than
required for an ordinary CustomScanState>, because many
- scan types will wish to embed that as the first field of a large structure.
+ providers will wish to embed that as the first field of a larger structure.
The value returned must have the node tag and methods>
- set appropriately, but the other fields need not be initialized at this
+ set appropriately, but other fields should be left as zeroes at this
stage; after ExecInitCustomScan> performs basic initialization,
the BeginCustomScan> callback will be invoked to give the
- custom scan state a chance to do whatever else is needed.
+ custom scan provider a chance to do whatever else is needed.
@@ -209,23 +217,21 @@ void (*TextOutCustomScan) (StringInfo str,
const CustomScan *node);
Generate additional output when nodeToString> is invoked on
- this custom plan. This callback is optional. Since a
- CustomScan> must be copyable by copyObject>,
- custom scan providers cannot substitute a larger structure that embeds a
- CustomScan> for the structure itself, as would be possible
- for a CustomPath> or CustomScanState>.
- Therefore, providing this callback is unlikely to be useful.
+ this custom plan node. This callback is optional. Since
+ nodeToString> will automatically dump all fields in the
+ structure, including the substructure of the custom> fields,
+ there is usually not much need for this callback.
-
- Implementing Custom Scans
+
+ Executing Custom Scans
When a CustomScan> is executed, its execution state is
represented by a CustomScanState>, which is declared as
- follows.
+ follows:
typedef struct CustomScanState
{
@@ -237,7 +243,9 @@ typedef struct CustomScanState
- ss> must be initialized as for any other scanstate;
+ ss> is initialized as for any other scanstate,
+ except that if the scan is for a join rather than a base relation,
+ ss.ss_currentRelation> is left NULL.
flags> is a bitmask with the same meaning as in
CustomPath> and CustomScan>.
methods> must point to a (usually statically allocated)
@@ -247,8 +255,8 @@ typedef struct CustomScanState
structure embedding the above as its first member.
-
- Custom Execution-Time Callbacks
+
+ Custom Scan Execution Callbacks
@@ -257,8 +265,8 @@ void (*BeginCustomScan) (CustomScanState *node,
int eflags);
Complete initialization of the supplied CustomScanState>.
- Some initialization is performed by ExecInitCustomScan>, but
- any private fields should be initialized here.
+ Standard fields have been initialized by ExecInitCustomScan>,
+ but any private fields should be initialized here.
@@ -276,8 +284,8 @@ TupleTableSlot *(*ExecCustomScan) (CustomScanState *node);
void (*EndCustomScan) (CustomScanState *node);
Clean up any private data associated with the CustomScanState>.
- This method is required, but may not need to do anything if the associated
- data does not exist or will be cleaned up automatically.
+ This method is required, but it does not need to do anything if there is
+ no associated data or it will be cleaned up automatically.
@@ -293,8 +301,8 @@ void (*ReScanCustomScan) (CustomScanState *node);
void (*MarkPosCustomScan) (CustomScanState *node);
Save the current scan position so that it can subsequently be restored
- by the RestrPosCustomScan> callback. This calback is optional,
- and need only be supplied if
+ by the RestrPosCustomScan> callback. This callback is
+ optional, and need only be supplied if the
CUSTOMPATH_SUPPORT_MARK_RESTORE> flag is set.
@@ -304,7 +312,7 @@ void (*RestrPosCustomScan) (CustomScanState *node);
Restore the previous scan position as saved by the
MarkPosCustomScan> callback. This callback is optional,
- and need only be supplied if
+ and need only be supplied if the
CUSTOMPATH_SUPPORT_MARK_RESTORE> flag is set.
@@ -314,8 +322,8 @@ void (*ExplainCustomScan) (CustomScanState *node,
List *ancestors,
ExplainState *es);
- Output additional information on EXPLAIN> that involves
- custom-scan node. This callback is optional. Common data stored in the
+ Output additional information for EXPLAIN> of a custom-scan
+ plan node. This callback is optional. Common data stored in the
ScanState>, such as the target list and scan relation, will
be shown even without this callback, but the callback allows the display
of additional, private state.
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index bc06d2cbb2..33863f04f8 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -175,8 +175,11 @@ GetForeignPlan (PlannerInfo *root,
access path. This is called at the end of query planning.
The parameters are as for GetForeignRelSize>, plus
the selected ForeignPath> (previously produced by
- GetForeignPaths>), the target list to be emitted by the
- plan node, and the restriction clauses to be enforced by the plan node.
+ GetForeignPaths> or GetForeignJoinPaths>),
+ 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, foreigntableid> is InvalidOid>.)
@@ -235,9 +238,12 @@ IterateForeignScan (ForeignScanState *node);
- The rows returned must match the column signature of the foreign table
- being scanned. If you choose to optimize away fetching columns that
- are not needed, you should insert nulls in those column positions.
+ The rows returned must match the fdw_scan_tlist> target
+ list if one was supplied, otherwise they must match the rowtype of the
+ 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 fdw_scan_tlist> list with
+ those columns omitted.
@@ -275,6 +281,67 @@ EndForeignScan (ForeignScanState *node);
+
+ FDW Routines For Scanning Foreign Joins
+
+
+ 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:
+
+
+
+
+void
+GetForeignJoinPaths (PlannerInfo *root,
+ RelOptInfo *joinrel,
+ RelOptInfo *outerrel,
+ RelOptInfo *innerrel,
+ JoinType jointype,
+ JoinPathExtraData *extra);
+
+ 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 GetForeignPaths>, this function should
+ generate ForeignPath> path(s) for the
+ supplied joinrel>, and call add_path> to add these
+ paths to the set of paths considered for the join. But unlike
+ GetForeignPaths>, it is not necessary that this function
+ succeed in creating at least one path, since paths involving local
+ joining are always possible.
+
+
+
+ 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.
+
+
+
+ If a 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 scanrelid> of
+ the resulting ForeignScan> plan node should be set to zero,
+ since there is no single relation that it represents; instead,
+ the fs_relids> field of the 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 fdw_scan_tlist> with an appropriate list
+ of TargetEntry> nodes, representing the set of columns
+ it will supply at runtime in the tuples it returns.
+
+
+
+ See for additional information.
+
+
+
+
FDW Routines For Updating Foreign Tables
@@ -598,42 +665,6 @@ IsForeignRelUpdatable (Relation rel);
-
- FDW Routines For Remote Joins
-
-
-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);
-
- 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.
-
-
- This function the FDW to add ForeignScan> paths for the
- supplied 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.
-
-
- Since we cannot construct the slot descriptor for a remote join from
- the catalogs, the FDW should set the scanrelid> of the
- ForeignScan> to zero and fdw_ps_tlist>
- to an appropriate list of TargetEntry> nodes.
- Junk entries will be ignored, but can be present for the benefit of
- deparsing performed by EXPLAIN>.
-
-
-
FDW Routines for EXPLAIN>
@@ -904,10 +935,10 @@ GetForeignServerByName(const char *name, bool missing_ok);
The FDW callback functions GetForeignRelSize>,
- GetForeignPaths>, GetForeignPlan>, and
- PlanForeignModify> must fit into the workings of the
- PostgreSQL> planner. Here are some notes about what
- they must do.
+ GetForeignPaths>, GetForeignPlan>,
+ PlanForeignModify>, and GetForeignJoinPaths>
+ must fit into the workings of the PostgreSQL> planner.
+ Here are some notes about what they must do.
@@ -934,7 +965,7 @@ GetForeignServerByName(const char *name, bool missing_ok);
baserel->fdw_private> is a void> pointer that is
available for FDW planning functions to store information relevant to
the particular foreign table. The core planner does not touch it except
- to initialize it to NULL when the baserel> node is created.
+ to initialize it to NULL when the RelOptInfo> node is created.
It is useful for passing information forward from
GetForeignRelSize> to GetForeignPaths> and/or
GetForeignPaths> to GetForeignPlan>, thereby
@@ -1002,6 +1033,23 @@ GetForeignServerByName(const char *name, bool missing_ok);
evaluation of the fdw_exprs> expression tree.
+
+ Another ForeignScan> field that can be filled by FDWs
+ is 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 NIL>, implying that the returned tuples have the
+ rowtype declared for the foreign table. A non-NIL value must be a
+ targetlist (list of 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 fdw_scan_tlist>. Note that join
+ plans (created from paths made by GetForeignJoinPaths>) must
+ always supply fdw_scan_tlist> to describe the set of
+ columns they will return.
+
+
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
@@ -1019,6 +1067,18 @@ GetForeignServerByName(const char *name, bool missing_ok);
same as for an ordinary restriction clause.
+
+ If an FDW supports remote joins, GetForeignJoinPaths> should
+ produce ForeignPath>s for potential remote joins in much
+ the same way as GetForeignPaths> works for base tables.
+ Information about the intended join can be passed forward
+ to GetForeignPlan> in the same ways described above.
+ However, baserestrictinfo> is not relevant for join
+ relations; instead, the relevant join clauses for a particular join are
+ passed to GetForeignJoinPaths> as a separate parameter
+ (extra->restrictlist>).
+
+
When planning an UPDATE> or DELETE>,
PlanForeignModify> can look up the RelOptInfo>
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c5452e3cb6..eeb8f19017 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -736,11 +736,11 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
break;
case T_ForeignScan:
*rels_used = bms_add_members(*rels_used,
- ((ForeignScan *) plan)->fdw_relids);
+ ((ForeignScan *) plan)->fs_relids);
break;
case T_CustomScan:
*rels_used = bms_add_members(*rels_used,
- ((CustomScan *) plan)->custom_relids);
+ ((CustomScan *) plan)->custom_relids);
break;
case T_ModifyTable:
*rels_used = bms_add_member(*rels_used,
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fa475014f1..a96e826ba4 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -246,19 +246,18 @@ void
ExecAssignScanProjectionInfo(ScanState *node)
{
Scan *scan = (Scan *) node->ps.plan;
- Index varno;
- /* Vars in an index-only scan's tlist should be INDEX_VAR */
- if (IsA(scan, IndexOnlyScan))
- varno = INDEX_VAR;
- /* Also foreign or custom scan on pseudo relation should be INDEX_VAR */
- else if (scan->scanrelid == 0)
- {
- Assert(IsA(scan, ForeignScan) || IsA(scan, CustomScan));
- varno = INDEX_VAR;
- }
- else
- varno = scan->scanrelid;
+ ExecAssignScanProjectionInfoWithVarno(node, scan->scanrelid);
+}
+
+/*
+ * ExecAssignScanProjectionInfoWithVarno
+ * As above, but caller can specify varno expected in Vars in the tlist.
+ */
+void
+ExecAssignScanProjectionInfoWithVarno(ScanState *node, Index varno)
+{
+ Scan *scan = (Scan *) node->ps.plan;
if (tlist_matches_tupdesc(&node->ps,
scan->plan.targetlist,
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index db1b4f2ffa..0a022dff94 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -22,13 +22,24 @@
CustomScanState *
ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
{
- CustomScanState *css;
- Index scan_relid = cscan->scan.scanrelid;
+ CustomScanState *css;
+ 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);
Assert(IsA(css, CustomScanState));
+ /* ensure flags is filled correctly */
+ css->flags = cscan->flags;
+
/* fill up fields of ScanState */
css->ss.ps.plan = &cscan->scan.plan;
css->ss.ps.state = estate;
@@ -36,6 +47,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
/* create expression context for node */
ExecAssignExprContext(estate, &css->ss.ps);
+ css->ss.ps.ps_TupFromTlist = false;
+
/* initialize child expressions */
css->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
@@ -49,32 +62,40 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
ExecInitResultTupleSlot(estate, &css->ss.ps);
/*
- * open the base relation and acquire an appropriate lock on it;
- * also, get and assign the scan type
+ * open the base relation, if any, and acquire an appropriate lock on it
*/
- if (scan_relid > 0)
+ if (scanrelid > 0)
{
- Relation scan_rel;
-
- scan_rel = ExecOpenScanRelation(estate, scan_relid, eflags);
+ scan_rel = ExecOpenScanRelation(estate, scanrelid, eflags);
css->ss.ss_currentRelation = scan_rel;
- css->ss.ss_currentScanDesc = NULL; /* set by provider */
- ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel));
+ }
+
+ /*
+ * 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 scan_tupdesc;
+
+ scan_tupdesc = ExecTypeFromTL(cscan->custom_scan_tlist, false);
+ ExecAssignScanType(&css->ss, scan_tupdesc);
+ /* Node's targetlist will contain Vars with varno = INDEX_VAR */
+ tlistvarno = INDEX_VAR;
}
else
{
- TupleDesc ps_tupdesc;
-
- ps_tupdesc = ExecCleanTypeFromTL(cscan->custom_ps_tlist, false);
- ExecAssignScanType(&css->ss, ps_tupdesc);
+ 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.
*/
ExecAssignResultTypeFromTL(&css->ss.ps);
- ExecAssignScanProjectionInfo(&css->ss);
+ ExecAssignScanProjectionInfoWithVarno(&css->ss, tlistvarno);
/*
* The callback of custom-scan provider applies the final initialization
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index fa553ace5d..bb28a7372d 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -102,7 +102,9 @@ ForeignScanState *
ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
{
ForeignScanState *scanstate;
+ Relation currentRelation = NULL;
Index scanrelid = node->scan.scanrelid;
+ Index tlistvarno;
FdwRoutine *fdwroutine;
/* check for unsupported flags */
@@ -141,40 +143,55 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
ExecInitScanTupleSlot(estate, &scanstate->ss);
/*
- * open the base relation and acquire an appropriate lock on it;
- * also, get and assign the scan type
+ * open the base relation, if any, and acquire an appropriate lock on it;
+ * also acquire function pointers from the FDW's handler
*/
if (scanrelid > 0)
{
- Relation currentRelation;
-
currentRelation = ExecOpenScanRelation(estate, scanrelid, eflags);
scanstate->ss.ss_currentRelation = currentRelation;
- ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
+ fdwroutine = GetFdwRoutineForRelation(currentRelation, true);
}
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.
*/
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->fdw_state = NULL;
/*
- * Tell the FDW to initiate the scan.
+ * Tell the FDW to initialize the scan.
*/
fdwroutine->BeginForeignScan(scanstate, eflags);
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 06b7c3c457..61bd644ab7 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -442,10 +442,12 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
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);
- ExecAssignScanProjectionInfo(&indexstate->ss);
+ ExecAssignScanProjectionInfoWithVarno(&indexstate->ss, INDEX_VAR);
/*
* If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index cdbd550fd4..763ee7c9bc 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -304,21 +304,16 @@ GetFdwRoutine(Oid fdwhandler)
/*
- * GetFdwHandlerByRelId - look up the handler of the foreign-data wrapper
- * for the given foreign table
+ * GetForeignServerIdByRelId - look up the foreign server
+ * for the given foreign table, and return its OID.
*/
Oid
-GetFdwHandlerByRelId(Oid relid)
+GetForeignServerIdByRelId(Oid relid)
{
HeapTuple tp;
- Form_pg_foreign_data_wrapper fdwform;
- Form_pg_foreign_server serverform;
Form_pg_foreign_table tableform;
Oid serverid;
- Oid fdwid;
- Oid fdwhandler;
- /* Get server OID for the foreign table. */
tp = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for foreign table %u", relid);
@@ -326,6 +321,23 @@ GetFdwHandlerByRelId(Oid relid)
serverid = tableform->ftserver;
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. */
tp = SearchSysCache1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid));
if (!HeapTupleIsValid(tp))
@@ -350,9 +362,11 @@ GetFdwHandlerByRelId(Oid relid)
ReleaseSysCache(tp);
- return fdwhandler;
+ /* And finally, call the handler function. */
+ return GetFdwRoutine(fdwhandler);
}
+
/*
* GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
* for the given foreign table, and retrieve its FdwRoutine struct.
@@ -360,9 +374,13 @@ GetFdwHandlerByRelId(Oid relid)
FdwRoutine *
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)
/*
- * 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
* return InvalidOid.
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a3139d3eb5..ed8fa72621 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -599,11 +599,11 @@ _copyForeignScan(const ForeignScan *from)
/*
* copy remainder of node
*/
- COPY_SCALAR_FIELD(fdw_handler);
+ COPY_SCALAR_FIELD(fs_server);
COPY_NODE_FIELD(fdw_exprs);
- COPY_NODE_FIELD(fdw_ps_tlist);
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);
return newnode;
@@ -627,8 +627,8 @@ _copyCustomScan(const CustomScan *from)
*/
COPY_SCALAR_FIELD(flags);
COPY_NODE_FIELD(custom_exprs);
- COPY_NODE_FIELD(custom_ps_tlist);
COPY_NODE_FIELD(custom_private);
+ COPY_NODE_FIELD(custom_scan_tlist);
COPY_BITMAPSET_FIELD(custom_relids);
/*
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bc891d391f..fe868b889d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -565,11 +565,11 @@ _outForeignScan(StringInfo str, const ForeignScan *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_ps_tlist);
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);
}
@@ -582,8 +582,8 @@ _outCustomScan(StringInfo str, const CustomScan *node)
WRITE_UINT_FIELD(flags);
WRITE_NODE_FIELD(custom_exprs);
- WRITE_NODE_FIELD(custom_ps_tlist);
WRITE_NODE_FIELD(custom_private);
+ WRITE_NODE_FIELD(custom_scan_tlist);
WRITE_BITMAPSET_FIELD(custom_relids);
appendStringInfoString(str, " :methods ");
_outToken(str, node->methods->CustomName);
@@ -1844,6 +1844,7 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
WRITE_NODE_FIELD(subplan);
WRITE_NODE_FIELD(subroot);
WRITE_NODE_FIELD(subplan_params);
+ WRITE_OID_FIELD(serverid);
/* we don't try to print fdwroutine or fdw_private */
WRITE_NODE_FIELD(baserestrictinfo);
WRITE_NODE_FIELD(joininfo);
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index dabef3c3c7..ba78252b8f 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -30,21 +30,13 @@ set_join_pathlist_hook_type set_join_pathlist_hook = NULL;
static void sort_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
- List *restrictlist, List *mergeclause_list,
- JoinType jointype, SpecialJoinInfo *sjinfo,
- Relids param_source_rels, Relids extra_lateral_rels);
+ JoinType jointype, JoinPathExtraData *extra);
static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
- List *restrictlist, List *mergeclause_list,
- JoinType jointype, SpecialJoinInfo *sjinfo,
- SemiAntiJoinFactors *semifactors,
- Relids param_source_rels, Relids extra_lateral_rels);
+ JoinType jointype, JoinPathExtraData *extra);
static void hash_inner_and_outer(PlannerInfo *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
- List *restrictlist,
- JoinType jointype, SpecialJoinInfo *sjinfo,
- SemiAntiJoinFactors *semifactors,
- Relids param_source_rels, Relids extra_lateral_rels);
+ JoinType jointype, JoinPathExtraData *extra);
static List *select_mergejoin_clauses(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
@@ -86,13 +78,16 @@ add_paths_to_joinrel(PlannerInfo *root,
SpecialJoinInfo *sjinfo,
List *restrictlist)
{
- List *mergeclause_list = NIL;
+ JoinPathExtraData extra;
bool mergejoin_allowed = true;
- SemiAntiJoinFactors semifactors;
- Relids param_source_rels = NULL;
- Relids extra_lateral_rels = NULL;
ListCell *lc;
+ extra.restrictlist = restrictlist;
+ extra.mergeclause_list = NIL;
+ extra.sjinfo = sjinfo;
+ extra.param_source_rels = NULL;
+ extra.extra_lateral_rels = NULL;
+
/*
* Find potential mergejoin clauses. We can skip this if we are not
* interested in doing a mergejoin. However, mergejoin may be our only
@@ -100,13 +95,13 @@ add_paths_to_joinrel(PlannerInfo *root,
* it's a full join.
*/
if (enable_mergejoin || jointype == JOIN_FULL)
- mergeclause_list = select_mergejoin_clauses(root,
- joinrel,
- outerrel,
- innerrel,
- restrictlist,
- jointype,
- &mergejoin_allowed);
+ extra.mergeclause_list = select_mergejoin_clauses(root,
+ joinrel,
+ outerrel,
+ innerrel,
+ restrictlist,
+ jointype,
+ &mergejoin_allowed);
/*
* If it's SEMI or ANTI join, compute correction factors for cost
@@ -115,7 +110,7 @@ add_paths_to_joinrel(PlannerInfo *root,
if (jointype == JOIN_SEMI || jointype == JOIN_ANTI)
compute_semi_anti_join_factors(root, outerrel, innerrel,
jointype, sjinfo, restrictlist,
- &semifactors);
+ &extra.semifactors);
/*
* Decide whether it's sensible to generate parameterized paths for this
@@ -142,16 +137,16 @@ add_paths_to_joinrel(PlannerInfo *root,
*/
if (bms_overlap(joinrel->relids, sjinfo->min_righthand) &&
!bms_overlap(joinrel->relids, sjinfo->min_lefthand))
- param_source_rels = bms_join(param_source_rels,
- bms_difference(root->all_baserels,
+ extra.param_source_rels = bms_join(extra.param_source_rels,
+ bms_difference(root->all_baserels,
sjinfo->min_righthand));
/* full joins constrain both sides symmetrically */
if (sjinfo->jointype == JOIN_FULL &&
bms_overlap(joinrel->relids, sjinfo->min_lefthand) &&
!bms_overlap(joinrel->relids, sjinfo->min_righthand))
- param_source_rels = bms_join(param_source_rels,
- bms_difference(root->all_baserels,
+ extra.param_source_rels = bms_join(extra.param_source_rels,
+ bms_difference(root->all_baserels,
sjinfo->min_lefthand));
}
@@ -168,9 +163,9 @@ add_paths_to_joinrel(PlannerInfo *root,
LateralJoinInfo *ljinfo = (LateralJoinInfo *) lfirst(lc);
if (bms_is_subset(ljinfo->lateral_rhs, joinrel->relids))
- param_source_rels = bms_join(param_source_rels,
- bms_difference(ljinfo->lateral_lhs,
- joinrel->relids));
+ extra.param_source_rels = bms_join(extra.param_source_rels,
+ bms_difference(ljinfo->lateral_lhs,
+ joinrel->relids));
}
/*
@@ -195,8 +190,8 @@ add_paths_to_joinrel(PlannerInfo *root,
!bms_is_subset(phinfo->ph_eval_at, innerrel->relids))
{
/* Yes, remember its lateral rels */
- extra_lateral_rels = bms_add_members(extra_lateral_rels,
- phinfo->ph_lateral);
+ extra.extra_lateral_rels = bms_add_members(extra.extra_lateral_rels,
+ phinfo->ph_lateral);
}
}
@@ -206,9 +201,10 @@ add_paths_to_joinrel(PlannerInfo *root,
* it to required_outer below, while preserving the property that
* required_outer is exactly NULL if empty.)
*/
- extra_lateral_rels = bms_del_members(extra_lateral_rels, joinrel->relids);
- if (bms_is_empty(extra_lateral_rels))
- extra_lateral_rels = NULL;
+ extra.extra_lateral_rels = bms_del_members(extra.extra_lateral_rels,
+ joinrel->relids);
+ if (bms_is_empty(extra.extra_lateral_rels))
+ extra.extra_lateral_rels = NULL;
/*
* 1. Consider mergejoin paths where both relations must be explicitly
@@ -216,9 +212,7 @@ add_paths_to_joinrel(PlannerInfo *root,
*/
if (mergejoin_allowed)
sort_inner_and_outer(root, joinrel, outerrel, innerrel,
- restrictlist, mergeclause_list, jointype,
- sjinfo,
- param_source_rels, extra_lateral_rels);
+ jointype, &extra);
/*
* 2. Consider paths where the outer relation need not be explicitly
@@ -229,9 +223,7 @@ add_paths_to_joinrel(PlannerInfo *root,
*/
if (mergejoin_allowed)
match_unsorted_outer(root, joinrel, outerrel, innerrel,
- restrictlist, mergeclause_list, jointype,
- sjinfo, &semifactors,
- param_source_rels, extra_lateral_rels);
+ jointype, &extra);
#ifdef NOT_USED
@@ -248,9 +240,7 @@ add_paths_to_joinrel(PlannerInfo *root,
*/
if (mergejoin_allowed)
match_unsorted_inner(root, joinrel, outerrel, innerrel,
- restrictlist, mergeclause_list, jointype,
- sjinfo, &semifactors,
- param_source_rels, extra_lateral_rels);
+ jointype, &extra);
#endif
/*
@@ -260,30 +250,24 @@ add_paths_to_joinrel(PlannerInfo *root,
*/
if (enable_hashjoin || jointype == JOIN_FULL)
hash_inner_and_outer(root, joinrel, outerrel, innerrel,
- restrictlist, jointype,
- sjinfo, &semifactors,
- param_source_rels, extra_lateral_rels);
+ jointype, &extra);
/*
- * 5. If both inner and outer relations are managed by the same FDW,
- * give it a chance to push down joins.
+ * 5. If inner and outer relations are foreign tables (or joins) belonging
+ * to the same server, give the FDW a chance to push down joins.
*/
if (joinrel->fdwroutine &&
joinrel->fdwroutine->GetForeignJoinPaths)
joinrel->fdwroutine->GetForeignJoinPaths(root, joinrel,
outerrel, innerrel,
- restrictlist, jointype, sjinfo,
- &semifactors,
- param_source_rels,
- extra_lateral_rels);
+ jointype, &extra);
+
/*
* 6. Finally, give extensions a chance to manipulate the path list.
*/
if (set_join_pathlist_hook)
set_join_pathlist_hook(root, joinrel, outerrel, innerrel,
- restrictlist, jointype,
- sjinfo, &semifactors,
- param_source_rels, extra_lateral_rels);
+ jointype, &extra);
}
/*
@@ -294,15 +278,11 @@ add_paths_to_joinrel(PlannerInfo *root,
static void
try_nestloop_path(PlannerInfo *root,
RelOptInfo *joinrel,
- JoinType jointype,
- SpecialJoinInfo *sjinfo,
- SemiAntiJoinFactors *semifactors,
- Relids param_source_rels,
- Relids extra_lateral_rels,
Path *outer_path,
Path *inner_path,
- List *restrict_clauses,
- List *pathkeys)
+ List *pathkeys,
+ JoinType jointype,
+ JoinPathExtraData *extra)
{
Relids required_outer;
JoinCostWorkspace workspace;
@@ -314,7 +294,7 @@ try_nestloop_path(PlannerInfo *root,
required_outer = calc_nestloop_required_outer(outer_path,
inner_path);
if (required_outer &&
- !bms_overlap(required_outer, param_source_rels))
+ !bms_overlap(required_outer, extra->param_source_rels))
{
/*
* We override the param_source_rels heuristic to accept nestloop
@@ -345,7 +325,7 @@ try_nestloop_path(PlannerInfo *root,
* Independently of that, add parameterization needed for any
* PlaceHolderVars that need to be computed at the join.
*/
- required_outer = bms_add_members(required_outer, extra_lateral_rels);
+ required_outer = bms_add_members(required_outer, extra->extra_lateral_rels);
/*
* Do a precheck to quickly eliminate obviously-inferior paths. We
@@ -358,7 +338,7 @@ try_nestloop_path(PlannerInfo *root,
*/
initial_cost_nestloop(root, &workspace, jointype,
outer_path, inner_path,
- sjinfo, semifactors);
+ extra->sjinfo, &extra->semifactors);
if (add_path_precheck(joinrel,
workspace.startup_cost, workspace.total_cost,
@@ -369,11 +349,11 @@ try_nestloop_path(PlannerInfo *root,
joinrel,
jointype,
&workspace,
- sjinfo,
- semifactors,
+ extra->sjinfo,
+ &extra->semifactors,
outer_path,
inner_path,
- restrict_clauses,
+ extra->restrictlist,
pathkeys,
required_outer));
}
@@ -392,17 +372,14 @@ try_nestloop_path(PlannerInfo *root,
static void
try_mergejoin_path(PlannerInfo *root,
RelOptInfo *joinrel,
- JoinType jointype,
- SpecialJoinInfo *sjinfo,
- Relids param_source_rels,
- Relids extra_lateral_rels,
Path *outer_path,
Path *inner_path,
- List *restrict_clauses,
List *pathkeys,
List *mergeclauses,
List *outersortkeys,
- List *innersortkeys)
+ List *innersortkeys,
+ JoinType jointype,
+ JoinPathExtraData *extra)
{
Relids required_outer;
JoinCostWorkspace workspace;
@@ -414,7 +391,7 @@ try_mergejoin_path(PlannerInfo *root,
required_outer = calc_non_nestloop_required_outer(outer_path,
inner_path);
if (required_outer &&
- !bms_overlap(required_outer, param_source_rels))
+ !bms_overlap(required_outer, extra->param_source_rels))
{
/* Waste no memory when we reject a path here */
bms_free(required_outer);
@@ -425,7 +402,7 @@ try_mergejoin_path(PlannerInfo *root,
* Independently of that, add parameterization needed for any
* PlaceHolderVars that need to be computed at the join.
*/
- required_outer = bms_add_members(required_outer, extra_lateral_rels);
+ required_outer = bms_add_members(required_outer, extra->extra_lateral_rels);
/*
* If the given paths are already well enough ordered, we can skip doing
@@ -444,7 +421,7 @@ try_mergejoin_path(PlannerInfo *root,
initial_cost_mergejoin(root, &workspace, jointype, mergeclauses,
outer_path, inner_path,
outersortkeys, innersortkeys,
- sjinfo);
+ extra->sjinfo);
if (add_path_precheck(joinrel,
workspace.startup_cost, workspace.total_cost,
@@ -455,10 +432,10 @@ try_mergejoin_path(PlannerInfo *root,
joinrel,
jointype,
&workspace,
- sjinfo,
+ extra->sjinfo,
outer_path,
inner_path,
- restrict_clauses,
+ extra->restrictlist,
pathkeys,
required_outer,
mergeclauses,
@@ -480,15 +457,11 @@ try_mergejoin_path(PlannerInfo *root,
static void
try_hashjoin_path(PlannerInfo *root,
RelOptInfo *joinrel,
- JoinType jointype,
- SpecialJoinInfo *sjinfo,
- SemiAntiJoinFactors *semifactors,
- Relids param_source_rels,
- Relids extra_lateral_rels,
Path *outer_path,
Path *inner_path,
- List *restrict_clauses,
- List *hashclauses)
+ List *hashclauses,
+ JoinType jointype,
+ JoinPathExtraData *extra)
{
Relids required_outer;
JoinCostWorkspace workspace;
@@ -500,7 +473,7 @@ try_hashjoin_path(PlannerInfo *root,
required_outer = calc_non_nestloop_required_outer(outer_path,
inner_path);
if (required_outer &&
- !bms_overlap(required_outer, param_source_rels))
+ !bms_overlap(required_outer, extra->param_source_rels))
{
/* Waste no memory when we reject a path here */
bms_free(required_outer);
@@ -511,7 +484,7 @@ try_hashjoin_path(PlannerInfo *root,
* Independently of that, add parameterization needed for any
* PlaceHolderVars that need to be computed at the join.
*/
- required_outer = bms_add_members(required_outer, extra_lateral_rels);
+ required_outer = bms_add_members(required_outer, extra->extra_lateral_rels);
/*
* See comments in try_nestloop_path(). Also note that hashjoin paths
@@ -519,7 +492,7 @@ try_hashjoin_path(PlannerInfo *root,
*/
initial_cost_hashjoin(root, &workspace, jointype, hashclauses,
outer_path, inner_path,
- sjinfo, semifactors);
+ extra->sjinfo, &extra->semifactors);
if (add_path_precheck(joinrel,
workspace.startup_cost, workspace.total_cost,
@@ -530,11 +503,11 @@ try_hashjoin_path(PlannerInfo *root,
joinrel,
jointype,
&workspace,
- sjinfo,
- semifactors,
+ extra->sjinfo,
+ &extra->semifactors,
outer_path,
inner_path,
- restrict_clauses,
+ extra->restrictlist,
required_outer,
hashclauses));
}
@@ -584,26 +557,16 @@ clause_sides_match_join(RestrictInfo *rinfo, RelOptInfo *outerrel,
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
- * '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
* 'jointype' is the type of join to do
- * 'sjinfo' is extra info about the join for selectivity estimation
- * 'param_source_rels' are OK targets for parameterization of result paths
- * 'extra_lateral_rels' are additional parameterization for result paths
+ * 'extra' contains additional input values
*/
static void
sort_inner_and_outer(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
- List *restrictlist,
- List *mergeclause_list,
JoinType jointype,
- SpecialJoinInfo *sjinfo,
- Relids param_source_rels,
- Relids extra_lateral_rels)
+ JoinPathExtraData *extra)
{
Path *outer_path;
Path *inner_path;
@@ -643,14 +606,14 @@ sort_inner_and_outer(PlannerInfo *root,
if (jointype == JOIN_UNIQUE_OUTER)
{
outer_path = (Path *) create_unique_path(root, outerrel,
- outer_path, sjinfo);
+ outer_path, extra->sjinfo);
Assert(outer_path);
jointype = JOIN_INNER;
}
else if (jointype == JOIN_UNIQUE_INNER)
{
inner_path = (Path *) create_unique_path(root, innerrel,
- inner_path, sjinfo);
+ inner_path, extra->sjinfo);
Assert(inner_path);
jointype = JOIN_INNER;
}
@@ -684,7 +647,7 @@ sort_inner_and_outer(PlannerInfo *root,
* exactly as-is as well as making variants.
*/
all_pathkeys = select_outer_pathkeys_for_merge(root,
- mergeclause_list,
+ extra->mergeclause_list,
joinrel);
foreach(l, all_pathkeys)
@@ -707,10 +670,10 @@ sort_inner_and_outer(PlannerInfo *root,
cur_mergeclauses = find_mergeclauses_for_pathkeys(root,
outerkeys,
true,
- mergeclause_list);
+ extra->mergeclause_list);
/* Should have used them all... */
- Assert(list_length(cur_mergeclauses) == list_length(mergeclause_list));
+ Assert(list_length(cur_mergeclauses) == list_length(extra->mergeclause_list));
/* Build sort pathkeys for the inner side */
innerkeys = make_inner_pathkeys_for_merge(root,
@@ -730,17 +693,14 @@ sort_inner_and_outer(PlannerInfo *root,
*/
try_mergejoin_path(root,
joinrel,
- jointype,
- sjinfo,
- param_source_rels,
- extra_lateral_rels,
outer_path,
inner_path,
- restrictlist,
merge_pathkeys,
cur_mergeclauses,
outerkeys,
- innerkeys);
+ innerkeys,
+ jointype,
+ extra);
}
}
@@ -771,28 +731,16 @@ sort_inner_and_outer(PlannerInfo *root,
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
- * '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
* 'jointype' is the type of join to do
- * 'sjinfo' is extra info about the join for selectivity estimation
- * 'semifactors' contains valid data if jointype is SEMI or ANTI
- * 'param_source_rels' are OK targets for parameterization of result paths
- * 'extra_lateral_rels' are additional parameterization for result paths
+ * 'extra' contains additional input values
*/
static void
match_unsorted_outer(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
- List *restrictlist,
- List *mergeclause_list,
JoinType jointype,
- SpecialJoinInfo *sjinfo,
- SemiAntiJoinFactors *semifactors,
- Relids param_source_rels,
- Relids extra_lateral_rels)
+ JoinPathExtraData *extra)
{
JoinType save_jointype = jointype;
bool nestjoinOK;
@@ -854,7 +802,7 @@ match_unsorted_outer(PlannerInfo *root,
if (inner_cheapest_total == NULL)
return;
inner_cheapest_total = (Path *)
- create_unique_path(root, innerrel, inner_cheapest_total, sjinfo);
+ create_unique_path(root, innerrel, inner_cheapest_total, extra->sjinfo);
Assert(inner_cheapest_total);
}
else if (nestjoinOK)
@@ -898,7 +846,7 @@ match_unsorted_outer(PlannerInfo *root,
if (outerpath != outerrel->cheapest_total_path)
continue;
outerpath = (Path *) create_unique_path(root, outerrel,
- outerpath, sjinfo);
+ outerpath, extra->sjinfo);
Assert(outerpath);
}
@@ -918,15 +866,11 @@ match_unsorted_outer(PlannerInfo *root,
*/
try_nestloop_path(root,
joinrel,
- jointype,
- sjinfo,
- semifactors,
- param_source_rels,
- extra_lateral_rels,
outerpath,
inner_cheapest_total,
- restrictlist,
- merge_pathkeys);
+ merge_pathkeys,
+ jointype,
+ extra);
}
else if (nestjoinOK)
{
@@ -944,30 +888,22 @@ match_unsorted_outer(PlannerInfo *root,
try_nestloop_path(root,
joinrel,
- jointype,
- sjinfo,
- semifactors,
- param_source_rels,
- extra_lateral_rels,
outerpath,
innerpath,
- restrictlist,
- merge_pathkeys);
+ merge_pathkeys,
+ jointype,
+ extra);
}
/* Also consider materialized form of the cheapest inner path */
if (matpath != NULL)
try_nestloop_path(root,
joinrel,
- jointype,
- sjinfo,
- semifactors,
- param_source_rels,
- extra_lateral_rels,
outerpath,
matpath,
- restrictlist,
- merge_pathkeys);
+ merge_pathkeys,
+ jointype,
+ extra);
}
/* Can't do anything else if outer path needs to be unique'd */
@@ -982,7 +918,7 @@ match_unsorted_outer(PlannerInfo *root,
mergeclauses = find_mergeclauses_for_pathkeys(root,
outerpath->pathkeys,
true,
- mergeclause_list);
+ extra->mergeclause_list);
/*
* Done with this outer path if no chance for a mergejoin.
@@ -1000,7 +936,7 @@ match_unsorted_outer(PlannerInfo *root,
else
continue;
}
- if (useallclauses && list_length(mergeclauses) != list_length(mergeclause_list))
+ if (useallclauses && list_length(mergeclauses) != list_length(extra->mergeclause_list))
continue;
/* Compute the required ordering of the inner path */
@@ -1016,17 +952,14 @@ match_unsorted_outer(PlannerInfo *root,
*/
try_mergejoin_path(root,
joinrel,
- jointype,
- sjinfo,
- param_source_rels,
- extra_lateral_rels,
outerpath,
inner_cheapest_total,
- restrictlist,
merge_pathkeys,
mergeclauses,
NIL,
- innersortkeys);
+ innersortkeys,
+ jointype,
+ extra);
/* Can't do anything else if inner path needs to be unique'd */
if (save_jointype == JOIN_UNIQUE_INNER)
@@ -1115,17 +1048,14 @@ match_unsorted_outer(PlannerInfo *root,
newclauses = mergeclauses;
try_mergejoin_path(root,
joinrel,
- jointype,
- sjinfo,
- param_source_rels,
- extra_lateral_rels,
outerpath,
innerpath,
- restrictlist,
merge_pathkeys,
newclauses,
NIL,
- NIL);
+ NIL,
+ jointype,
+ extra);
cheapest_total_inner = innerpath;
}
/* Same on the basis of cheapest startup cost ... */
@@ -1161,17 +1091,14 @@ match_unsorted_outer(PlannerInfo *root,
}
try_mergejoin_path(root,
joinrel,
- jointype,
- sjinfo,
- param_source_rels,
- extra_lateral_rels,
outerpath,
innerpath,
- restrictlist,
merge_pathkeys,
newclauses,
NIL,
- NIL);
+ NIL,
+ jointype,
+ extra);
}
cheapest_startup_inner = innerpath;
}
@@ -1193,25 +1120,16 @@ match_unsorted_outer(PlannerInfo *root,
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
- * 'restrictlist' contains all of the RestrictInfo nodes for restriction
- * clauses that apply to this join
* 'jointype' is the type of join to do
- * 'sjinfo' is extra info about the join for selectivity estimation
- * 'semifactors' contains valid data if jointype is SEMI or ANTI
- * 'param_source_rels' are OK targets for parameterization of result paths
- * 'extra_lateral_rels' are additional parameterization for result paths
+ * 'extra' contains additional input values
*/
static void
hash_inner_and_outer(PlannerInfo *root,
RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
- List *restrictlist,
JoinType jointype,
- SpecialJoinInfo *sjinfo,
- SemiAntiJoinFactors *semifactors,
- Relids param_source_rels,
- Relids extra_lateral_rels)
+ JoinPathExtraData *extra)
{
bool isouterjoin = IS_OUTER_JOIN(jointype);
List *hashclauses;
@@ -1225,7 +1143,7 @@ hash_inner_and_outer(PlannerInfo *root,
* usable with this pair of sub-relations.
*/
hashclauses = NIL;
- foreach(l, restrictlist)
+ foreach(l, extra->restrictlist)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
@@ -1276,53 +1194,41 @@ hash_inner_and_outer(PlannerInfo *root,
{
cheapest_total_outer = (Path *)
create_unique_path(root, outerrel,
- cheapest_total_outer, sjinfo);
+ cheapest_total_outer, extra->sjinfo);
Assert(cheapest_total_outer);
jointype = JOIN_INNER;
try_hashjoin_path(root,
joinrel,
- jointype,
- sjinfo,
- semifactors,
- param_source_rels,
- extra_lateral_rels,
cheapest_total_outer,
cheapest_total_inner,
- restrictlist,
- hashclauses);
+ hashclauses,
+ jointype,
+ extra);
/* no possibility of cheap startup here */
}
else if (jointype == JOIN_UNIQUE_INNER)
{
cheapest_total_inner = (Path *)
create_unique_path(root, innerrel,
- cheapest_total_inner, sjinfo);
+ cheapest_total_inner, extra->sjinfo);
Assert(cheapest_total_inner);
jointype = JOIN_INNER;
try_hashjoin_path(root,
joinrel,
- jointype,
- sjinfo,
- semifactors,
- param_source_rels,
- extra_lateral_rels,
cheapest_total_outer,
cheapest_total_inner,
- restrictlist,
- hashclauses);
+ hashclauses,
+ jointype,
+ extra);
if (cheapest_startup_outer != NULL &&
cheapest_startup_outer != cheapest_total_outer)
try_hashjoin_path(root,
joinrel,
- jointype,
- sjinfo,
- semifactors,
- param_source_rels,
- extra_lateral_rels,
cheapest_startup_outer,
cheapest_total_inner,
- restrictlist,
- hashclauses);
+ hashclauses,
+ jointype,
+ extra);
}
else
{
@@ -1339,15 +1245,11 @@ hash_inner_and_outer(PlannerInfo *root,
if (cheapest_startup_outer != NULL)
try_hashjoin_path(root,
joinrel,
- jointype,
- sjinfo,
- semifactors,
- param_source_rels,
- extra_lateral_rels,
cheapest_startup_outer,
cheapest_total_inner,
- restrictlist,
- hashclauses);
+ hashclauses,
+ jointype,
+ extra);
foreach(lc1, outerrel->cheapest_parameterized_paths)
{
@@ -1377,15 +1279,11 @@ hash_inner_and_outer(PlannerInfo *root,
try_hashjoin_path(root,
joinrel,
- jointype,
- sjinfo,
- semifactors,
- param_source_rels,
- extra_lateral_rels,
outerpath,
innerpath,
- restrictlist,
- hashclauses);
+ hashclauses,
+ jointype,
+ extra);
}
}
}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 3246332d6e..c809237283 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -44,6 +44,7 @@
#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 List *build_path_tlist(PlannerInfo *root, Path *path);
static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
@@ -219,7 +220,7 @@ create_plan(PlannerInfo *root, Path *best_path)
* create_plan_recurse
* Recursive guts of create_plan().
*/
-Plan *
+static Plan *
create_plan_recurse(PlannerInfo *root, Path *best_path)
{
Plan *plan;
@@ -1950,7 +1951,7 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path,
/*
* 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'.
*/
static ForeignScan *
@@ -1965,9 +1966,11 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
ListCell *lc;
int i;
+ Assert(rel->fdwroutine != NULL);
+
/*
- * If we're scanning a base relation, look up the OID.
- * (We can skip this if scanning a join relation.)
+ * If we're scanning a base relation, fetch its OID. (Irrelevant if
+ * scanning a join relation.)
*/
if (scan_relid > 0)
{
@@ -1978,7 +1981,6 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
Assert(rte->rtekind == RTE_RELATION);
rel_oid = rte->relid;
}
- Assert(rel->fdwroutine != NULL);
/*
* 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,
scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid,
best_path,
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_path_costsize(&scan_plan->scan.plan, &best_path->path);
- /* Track FDW server-id; no need to make FDW do this */
- scan_plan->fdw_handler = rel->fdw_handler;
+ /* Copy foreign server OID; likewise, no need to make FDW do this */
+ 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
* 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
* 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)
{
@@ -2087,7 +2069,6 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
{
CustomScan *cplan;
RelOptInfo *rel = best_path->path.parent;
- ListCell *lc;
/*
* Sort clauses into the best execution order, although custom-scan
@@ -2106,42 +2087,22 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
scan_clauses);
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
* do this
*/
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
* 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
* 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)
{
@@ -3611,7 +3572,8 @@ make_foreignscan(List *qptlist,
List *qpqual,
Index scanrelid,
List *fdw_exprs,
- List *fdw_private)
+ List *fdw_private,
+ List *fdw_scan_tlist)
{
ForeignScan *node = makeNode(ForeignScan);
Plan *plan = &node->scan.plan;
@@ -3622,8 +3584,13 @@ make_foreignscan(List *qptlist,
plan->lefttree = NULL;
plan->righttree = NULL;
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_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 */
node->fsSystemCol = false;
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 612d32571a..fac51c9147 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -86,12 +86,6 @@ static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob);
static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte);
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,
IndexOnlyScan *plan,
int rtoffset);
@@ -99,6 +93,12 @@ static Plan *set_subqueryscan_references(PlannerInfo *root,
SubqueryScan *plan,
int rtoffset);
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_mutator(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)
case T_ForeignScan:
set_foreignscan_references(root, (ForeignScan *) plan, rtoffset);
break;
-
case T_CustomScan:
set_customscan_references(root, (CustomScan *) plan, rtoffset);
break;
@@ -890,121 +889,6 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
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
* Do set_plan_references processing on an IndexOnlyScan
@@ -1179,6 +1063,134 @@ trivial_subqueryscan(SubqueryScan *plan)
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
* Copy a Var node.
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 0220672fc4..afccee53ac 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2318,12 +2318,14 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
case T_ForeignScan:
finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs,
&context);
+ /* We assume fdw_scan_tlist cannot contain Params */
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_CustomScan:
finalize_primnode((Node *) ((CustomScan *) plan)->custom_exprs,
&context);
+ /* We assume custom_scan_tlist cannot contain Params */
context.paramids = bms_add_members(context.paramids, scan_params);
break;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 894e0db802..b425680f47 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -380,17 +380,18 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
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)
{
- rel->fdw_handler = GetFdwHandlerByRelId(RelationGetRelid(relation));
+ rel->serverid = GetForeignServerIdByRelId(RelationGetRelid(relation));
rel->fdwroutine = GetFdwRoutineForRelation(relation, true);
}
else
{
- rel->fdw_handler = InvalidOid;
+ rel->serverid = InvalidOid;
rel->fdwroutine = NULL;
}
+
heap_close(relation, NoLock);
/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 56235663d7..1d635cd6d2 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -14,7 +14,6 @@
*/
#include "postgres.h"
-#include "foreign/fdwapi.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
@@ -122,8 +121,8 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
rel->subplan = NULL;
rel->subroot = NULL;
rel->subplan_params = NIL;
+ rel->serverid = InvalidOid;
rel->fdwroutine = NULL;
- rel->fdw_handler = InvalidOid;
rel->fdw_private = NULL;
rel->baserestrictinfo = NIL;
rel->baserestrictcost.startup = 0;
@@ -385,6 +384,7 @@ build_join_rel(PlannerInfo *root,
joinrel->subplan = NULL;
joinrel->subroot = NULL;
joinrel->subplan_params = NIL;
+ joinrel->serverid = InvalidOid;
joinrel->fdwroutine = NULL;
joinrel->fdw_private = NULL;
joinrel->baserestrictinfo = NIL;
@@ -393,6 +393,17 @@ build_join_rel(PlannerInfo *root,
joinrel->joininfo = NIL;
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
* this join (ie, are needed for higher joinclauses or final output).
@@ -428,18 +439,6 @@ build_join_rel(PlannerInfo *root,
set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,
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
* auxiliary hashtable if there is one. NB: GEQO requires us to append
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 4b3cd85ad9..156b5331f3 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -128,8 +128,8 @@ typedef struct
* 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
* PlanState's ancestors. In addition, we store its outer and inner subplan
- * state nodes, as well as their plan nodes' targetlists, and the indextlist
- * if the current PlanState is an IndexOnlyScanState. (These fields could
+ * state nodes, as well as their plan nodes' targetlists, and the index tlist
+ * 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
* clearer to set them up as separate fields.)
*/
@@ -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
* can be resolved by drilling down into the left and right child plans.
* Similarly, INDEX_VAR references can be resolved by reference to the
- * indextlist given in the parent IndexOnlyScan node. (Note that we don't
- * currently support deparsing of indexquals in regular IndexScan or
- * BitmapIndexScan nodes; for those, we can only deparse the indexqualorig
- * fields, which won't contain INDEX_VAR Vars.)
+ * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
+ * ForeignScan and CustomScan nodes. (Note that we don't currently support
+ * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
+ * 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
* "Node *" to avoid having to include execnodes.h in ruleutils.h.
@@ -3870,13 +3871,13 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
else
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))
dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist;
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))
- dpns->index_tlist = ((CustomScan *) ps->plan)->custom_ps_tlist;
+ dpns->index_tlist = ((CustomScan *) ps->plan)->custom_scan_tlist;
else
dpns->index_tlist = NIL;
}
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 1b68b54c7d..6c64609197 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -257,6 +257,7 @@ typedef bool (*ExecScanRecheckMtd) (ScanState *node, TupleTableSlot *slot);
extern TupleTableSlot *ExecScan(ScanState *node, ExecScanAccessMtd accessMtd,
ExecScanRecheckMtd recheckMtd);
extern void ExecAssignScanProjectionInfo(ScanState *node);
+extern void ExecAssignScanProjectionInfoWithVarno(ScanState *node, Index varno);
extern void ExecScanReScan(ScanState *node);
/*
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index c683d9259e..511c96b093 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -47,6 +47,13 @@ typedef void (*ReScanForeignScan_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,
RangeTblEntry *target_rte,
Relation target_relation);
@@ -82,17 +89,6 @@ typedef void (*EndForeignModify_function) (EState *estate,
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,
struct ExplainState *es);
@@ -142,6 +138,9 @@ typedef struct FdwRoutine
* are not provided.
*/
+ /* Functions for remote-join planning */
+ GetForeignJoinPaths_function GetForeignJoinPaths;
+
/* Functions for updating foreign tables */
AddForeignUpdateTargets_function AddForeignUpdateTargets;
PlanForeignModify_function PlanForeignModify;
@@ -161,15 +160,13 @@ typedef struct FdwRoutine
/* Support functions for IMPORT FOREIGN SCHEMA */
ImportForeignSchema_function ImportForeignSchema;
-
- /* Support functions for join push-down */
- GetForeignJoinPaths_function GetForeignJoinPaths;
} FdwRoutine;
/* Functions in foreign/foreign.c */
-extern Oid GetFdwHandlerByRelId(Oid relid);
extern FdwRoutine *GetFdwRoutine(Oid fdwhandler);
+extern Oid GetForeignServerIdByRelId(Oid relid);
+extern FdwRoutine *GetFdwRoutineByServerId(Oid serverid);
extern FdwRoutine *GetFdwRoutineByRelId(Oid relid);
extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy);
extern bool IsImportableForeignTable(const char *tablename,
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index c63492fa0b..9313292222 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -479,32 +479,46 @@ typedef struct WorkTableScan
* 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
* 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
- * 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().
+ * Note that everything in both lists must be copiable by copyObject().
* 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
* 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
{
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_ps_tlist; /* tlist, if replacing a join */
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 */
} ForeignScan;
/* ----------------
* CustomScan node
*
- * The comments for ForeignScan's fdw_exprs, fdw_varmap and fdw_private fields
- * apply equally to custom_exprs, custom_ps_tlist and custom_private.
+ * The comments for ForeignScan's fdw_exprs, fdw_private, fdw_scan_tlist,
+ * 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*
* fit all plan data they need into those fields; embedding CustomScan in
* a larger struct will not work.
@@ -528,8 +542,9 @@ typedef struct CustomScan
Scan scan;
uint32 flags; /* mask of CUSTOMPATH_* flags, see relation.h */
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_scan_tlist; /* optional tlist describing scan
+ * tuple */
Bitmapset *custom_relids; /* RTIs generated by this scan */
const CustomScanMethods *methods;
} CustomScan;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 8f2c64847e..f10ae4efa8 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -127,9 +127,13 @@ typedef struct Expr
* 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
* and varattno becomes the index of the proper element of that subplan's
- * target list. But varnoold/varoattno continue to 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.
+ * target list. Similarly, INDEX_VAR is used to identify Vars that reference
+ * an index column rather than a heap column. (In ForeignScan and CustomScan
+ * 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 OUTER_VAR 65001 /* reference to outer subplan */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 1713d298de..d3ee61c4d0 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -365,18 +365,21 @@ typedef struct PlannerInfo
* subplan - plan 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
- * 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
* upon creation of the RelOptInfo object; they are filled in when
- * set_subquery_pathlist processes the object. Likewise, fdwroutine
- * and fdw_private are filled during initial path creation.
+ * set_subquery_pathlist processes the object.
*
* For otherrels that are appendrel members, these fields are filled
* 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
* and joins that the relation participates in:
*
@@ -460,10 +463,12 @@ typedef struct RelOptInfo
struct Plan *subplan; /* if subquery */
PlannerInfo *subroot; /* 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 */
- struct FdwRoutine *fdwroutine; /* if foreign table */
- Oid fdw_handler; /* if foreign table */
- void *fdw_private; /* if foreign table */
+ struct FdwRoutine *fdwroutine;
+ void *fdw_private;
/* used by various scans and joins: */
List *baserestrictinfo; /* RestrictInfo structures (if base
@@ -523,7 +528,7 @@ typedef struct IndexOptInfo
bool *reverse_sort; /* is sort order descending? */
bool *nulls_first; /* do NULLs come first in the sort order? */
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) */
RegProcedure amcostestimate; /* OID of the access method's cost fcn */
@@ -1667,6 +1672,28 @@ typedef struct SemiAntiJoinFactors
Selectivity match_count;
} 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
* phases: the first phase tries to quickly derive a lower bound for the
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index c42c69d746..3e2378aeb7 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -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() */
typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root,
- RelOptInfo *joinrel,
- RelOptInfo *outerrel,
- RelOptInfo *innerrel,
- List *restrictlist,
- JoinType jointype,
- SpecialJoinInfo *sjinfo,
- SemiAntiJoinFactors *semifactors,
- Relids param_source_rels,
- Relids extra_lateral_rels);
+ RelOptInfo *joinrel,
+ RelOptInfo *outerrel,
+ RelOptInfo *innerrel,
+ JoinType jointype,
+ JoinPathExtraData *extra);
extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook;
/* Hook for plugins to replace standard_join_search() */
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 1d4ab0488e..da15fca1f6 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -41,11 +41,11 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
* prototypes for plan/createplan.c
*/
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,
Index scanrelid, Plan *subplan);
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 RecursiveUnion *make_recursive_union(List *tlist,
Plan *lefttree, Plan *righttree, int wtParam,