Code review for foreign/custom join pushdown patch.
authorTom Lane
Sun, 10 May 2015 18:36:30 +0000 (14:36 -0400)
committerTom Lane
Sun, 10 May 2015 18:36:36 +0000 (14:36 -0400)
Commit e7cb7ee14555cc9c5773e2c102efd6371f6f2005 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.

26 files changed:
contrib/file_fdw/file_fdw.c
contrib/postgres_fdw/postgres_fdw.c
doc/src/sgml/custom-scan.sgml
doc/src/sgml/fdwhandler.sgml
src/backend/commands/explain.c
src/backend/executor/execScan.c
src/backend/executor/nodeCustom.c
src/backend/executor/nodeForeignscan.c
src/backend/executor/nodeIndexonlyscan.c
src/backend/foreign/foreign.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/outfuncs.c
src/backend/optimizer/path/joinpath.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/setrefs.c
src/backend/optimizer/plan/subselect.c
src/backend/optimizer/util/plancat.c
src/backend/optimizer/util/relnode.c
src/backend/utils/adt/ruleutils.c
src/include/executor/executor.h
src/include/foreign/fdwapi.h
src/include/nodes/plannodes.h
src/include/nodes/primnodes.h
src/include/nodes/relation.h
src/include/optimizer/paths.h
src/include/optimizer/planmain.h

index 4368897581a705f1e27ac6224060eef1f039b916..4c56444751b4f810de70bc10befccfa9e453252d 100644 (file)
@@ -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 */ );
 }
 
 /*
index 173b4f06e65d1f0079238c4767689c8aaf3df90b..ec89b25f61a7ab8b4d9fe793821f5999e622d644 100644 (file)
@@ -872,7 +872,8 @@ postgresGetForeignPlan(PlannerInfo *root,
                            local_exprs,
                            scan_relid,
                            params_list,
-                           fdw_private);
+                           fdw_private,
+                           NIL /* no custom tlist */ );
 }
 
 /*
index 9fd1db6fde48643feeaa92847f4c8c9150790965..62a8a3305bb29a5e7e405a8a1c5b391e1c8299eb 100644 (file)
  
 
  
-  <span class="marked">Implementing Custom</span> Paths
+  <span class="marked">Creating Custom Scan</span> 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 <span class="marked">Scan </span>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,
  
 
  
-  <span class="marked">Implementing Custom</span> Plans
+  <span class="marked">Creating Custom Scan</span> 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 <span class="marked">Plan </span>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.
    
   
  
 
scan">
-  <span class="marked">Implemen</span>ting Custom Scans
execution">
+  <span class="marked">Execu</span>ting 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.
   
 
-  scan-callbacks">
-   Custom <span class="marked">Execution-Time</span> Callbacks
+  execution-callbacks">
+   Custom <span class="marked">Scan Execution</span> 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.
index bc06d2cbb26f8c9d28faa5696bcd1659c011ea7b..33863f04f82899bf1f306e8fc7777527f23e479c 100644 (file)
@@ -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 <command>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 TargetEntrys) 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 ForeignPaths 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
index c5452e3cb6ab7440d75b429d20a3651c275bfe59..eeb8f19017efcae22937b44a6a07b5a276fa6bf2 100644 (file)
@@ -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,
index fa475014f134d527498c5481573c788678a4f0b5..a96e826ba4257a843f478a4d22582196162f40a6 100644 (file)
@@ -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,
index db1b4f2ffa4dbaffcff031277815d5b77c31014b..0a022dff940b7627191831e5648075747a53e832 100644 (file)
 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));
    }
-   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);
-       ExecAssignScanType(&css->ss, ps_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
+   {
+       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
index fa553ace5d687960f4aefc5124834397041018ca..bb28a7372d1be4a8dfc4cc9bd378d531af3a745d 100644 (file)
@@ -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);
 
index 06b7c3c457abc5f4f12eefac4f5d1290bec06586..61bd644ab7135c446e3b467c455f415a917f5cde 100644 (file)
@@ -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
index cdbd550fd4363a17d394df04018b1724682a9175..763ee7c9bcea0b9aa9e654cfec2a6d89202d828d 100644 (file)
@@ -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.
index a3139d3eb5db5c6ad6250a9db3fb58c9a5f8946a..ed8fa72621ece43657d7d647600946f7d0e3ec2d 100644 (file)
@@ -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);
 
    /*
index bc891d391f56943030cfa27c20b77eed6da005f4..fe868b889d96a93c025ae6871a6dec3f7726fbc3 100644 (file)
@@ -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);
index dabef3c3c7fe4ff5df5b3c3c67c15cdd13186d1e..ba78252b8f998e03ca23fb87212d228f71fbb877 100644 (file)
@@ -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);
                }
            }
        }
index 3246332d6e3bda4cb5aab2fdce2a6b823722f84d..c809237283198ad4d29e6ddce1acd057371cf6bb 100644 (file)
@@ -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;
 
index 612d32571af390a61b6eb0561d5c557ea2770a38..fac51c9147470556bf23a063fc375e5f52f715ce 100644 (file)
@@ -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.
index 0220672fc4382cad381ff564e474d3fb6bcf3798..afccee53acf562bf5006f7c63ed89ff6dd192ac5 100644 (file)
@@ -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;
 
index 894e0db802de81e0eaa506c92aeeb6a6121d4e51..b425680f47647f19eaa89da02cfac8d3c8d9f765 100644 (file)
@@ -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);
 
    /*
index 56235663d7f99c6193f8ea128f15abbe4374eca2..1d635cd6d214bcd25b2fef6cab1c09e91e9b3dcb 100644 (file)
@@ -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
index 4b3cd85ad9043d6b6e5c7d49ef95ab8492102c8a..156b5331f36fb1965c3185770b9a23ddbc0028e7 100644 (file)
@@ -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;
 }
index 1b68b54c7ddff721a89ed0a321b360cef581f80b..6c646091976ce84629fd478d67162b64c3ba8c2f 100644 (file)
@@ -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);
 
 /*
index c683d9259e445aadd7e90145db693ca51bfad566..511c96b093275a697c7055c53cfeae3069905c13 100644 (file)
@@ -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,
index c63492fa0be486a06354bbc641e857baa71e5974..9313292222afb97dd5e8b49f322073c5361d8476 100644 (file)
@@ -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;
index 8f2c64847e2f699f43825351a8fdee4a06e21a99..f10ae4efa881dc3a00acc659972ffa5f76d0f78f 100644 (file)
@@ -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 */
index 1713d298de256deb47c0bda3a8659de1283e0b66..d3ee61c4d046101b1a6dcbceb6821958ee72f939 100644 (file)
@@ -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
index c42c69d7460751bb73edf96f015cc2f54fabfff2..3e2378aeb7df731fd600890190f1f9fc34ceb63d 100644 (file)
@@ -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() */
index 1d4ab0488e1e6e47de94fe1539c1f5d0c67b06e0..da15fca1f6c4374062ce8ffc8aaf28c46e416915 100644 (file)
@@ -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,