(2012/02/15 20:50), Etsuro Fujita wrote:
> (2012/02/14 23:50), Tom Lane wrote:
>>> (2012/02/14 17:40), Etsuro Fujita wrote:
>>>> As discussed at
>>>> that thread, it would have to change the PlanForeignScan API to let the
>>>> FDW generate multiple paths and dump them all to add_path instead of
>>>> returning a FdwPlan struct.

>> I would really like to see that happen in 9.2, because the longer we let
>> that mistake live, the harder it will be to change.  More and more FDWs
>> are getting written.  I don't think it's that hard to do: we just have
>> to agree that PlanForeignScan should return void and call add_path for
>> itself, possibly more than once.
> 
> Agreed.  I fixed the PlanForeignScan API.  Please find attached a patch.
> 
>> If we do that, I'm inclined to think
>> we cou;d get rid of the separate Node type FdwPlan, and just incorporate
>> "List *fdw_private" into ForeignPath and ForeignScan.
> 
> +1  While the patch retains the struct FdwPlan, I would like to get rid
> of it at next version of the patch.

Please find attached an updated version of the patch.

Best regards,
Etsuro Fujita
*** a/contrib/file_fdw/file_fdw.c
--- b/contrib/file_fdw/file_fdw.c
***************
*** 25,30 ****
--- 25,31 ----
  #include "miscadmin.h"
  #include "nodes/makefuncs.h"
  #include "optimizer/cost.h"
+ #include "optimizer/pathnode.h"
  #include "utils/rel.h"
  #include "utils/syscache.h"
  
***************
*** 93,99 **** PG_FUNCTION_INFO_V1(file_fdw_validator);
  /*
   * FDW callback routines
   */
! static FdwPlan *filePlanForeignScan(Oid foreigntableid,
                                        PlannerInfo *root,
                                        RelOptInfo *baserel);
  static void fileExplainForeignScan(ForeignScanState *node, ExplainState *es);
--- 94,100 ----
  /*
   * FDW callback routines
   */
! static void filePlanForeignScan(Oid foreigntableid,
                                        PlannerInfo *root,
                                        RelOptInfo *baserel);
  static void fileExplainForeignScan(ForeignScanState *node, ExplainState *es);
***************
*** 406,432 **** get_file_fdw_attribute_options(Oid relid)
  
  /*
   * filePlanForeignScan
!  *            Create a FdwPlan for a scan on the foreign table
   */
! static FdwPlan *
  filePlanForeignScan(Oid foreigntableid,
                                        PlannerInfo *root,
                                        RelOptInfo *baserel)
  {
!       FdwPlan    *fdwplan;
        char       *filename;
        List       *options;
  
        /* Fetch options --- we only need filename at this point */
        fileGetOptions(foreigntableid, &filename, &options);
  
!       /* Construct FdwPlan with cost estimates */
!       fdwplan = makeNode(FdwPlan);
        estimate_costs(root, baserel, filename,
!                                  &fdwplan->startup_cost, 
&fdwplan->total_cost);
!       fdwplan->fdw_private = NIL; /* not used */
  
!       return fdwplan;
  }
  
  /*
--- 407,439 ----
  
  /*
   * filePlanForeignScan
!  *            Create the (single) path for a scan on the foreign table
   */
! static void
  filePlanForeignScan(Oid foreigntableid,
                                        PlannerInfo *root,
                                        RelOptInfo *baserel)
  {
!       ForeignPath *pathnode = makeNode(ForeignPath);
        char       *filename;
        List       *options;
  
+       pathnode->path.pathtype = T_ForeignScan;
+       pathnode->path.parent = baserel;
+       pathnode->path.pathkeys = NIL;          /* result is always unordered */
+       pathnode->path.required_outer = NULL;
+       pathnode->path.param_clauses = NIL;
+       pathnode->fdw_private = NIL;            /* not used */
+ 
        /* Fetch options --- we only need filename at this point */
        fileGetOptions(foreigntableid, &filename, &options);
  
!       /* Estimate costs of scanning the foreign table */
        estimate_costs(root, baserel, filename,
!                                  &pathnode->path.startup_cost, 
&pathnode->path.total_cost);
!       pathnode->path.rows = baserel->rows;
  
!       add_path(baserel, (Path *) pathnode);
  }
  
  /*
*** a/doc/src/sgml/fdwhandler.sgml
--- b/doc/src/sgml/fdwhandler.sgml
***************
*** 88,108 ****
  
      <para>
  <programlisting>
! FdwPlan *
  PlanForeignScan (Oid foreigntableid,
                   PlannerInfo *root,
                   RelOptInfo *baserel);
  </programlisting>
  
!      Plan a scan on a foreign table. This is called when a query is planned.
       <literal>foreigntableid</> is the <structname>pg_class</> OID of the
       foreign table.  <literal>root</> is the planner's global information
       about the query, and <literal>baserel</> is the planner's information
       about this table.
!      The function must return a palloc'd struct that contains cost estimates
!      plus any FDW-private information that is needed to execute the foreign
!      scan at a later time.  (Note that the private information must be
!      represented in a form that <function>copyObject</> knows how to copy.)
      </para>
  
      <para>
--- 88,114 ----
  
      <para>
  <programlisting>
! void
  PlanForeignScan (Oid foreigntableid,
                   PlannerInfo *root,
                   RelOptInfo *baserel);
  </programlisting>
  
!      Create the access paths for a scan on a foreign table. This is called
!      when a query is planned.
       <literal>foreigntableid</> is the <structname>pg_class</> OID of the
       foreign table.  <literal>root</> is the planner's global information
       about the query, and <literal>baserel</> is the planner's information
       about this table.
!      The function must generate at least one access path for a scan on the
!      foreign table and call <function>add_path</> to add it to
!      <literal>baserel-&gt;pathlist</>.  The function may generate multiple
!      access paths for a scan on the foreign table and add them to
!      <literal>baserel-&gt;pathlist</> using <function>add_path</>.  Each
!      access path generated should contain cost estimates plus any FDW-private
!      information that is needed to execute the foreign scan at a later time.
!      (Note that the private information must be represented in a form that
!      <function>copyObject</> knows how to copy.)
      </para>
  
      <para>
***************
*** 159,167 **** BeginForeignScan (ForeignScanState *node,
       its <structfield>fdw_state</> field is still NULL.  Information about
       the table to scan is accessible through the
       <structname>ForeignScanState</> node (in particular, from the underlying
!      <structname>ForeignScan</> plan node, which contains a pointer to the
!      <structname>FdwPlan</> structure returned by
!      <function>PlanForeignScan</>).
      </para>
  
      <para>
--- 165,172 ----
       its <structfield>fdw_state</> field is still NULL.  Information about
       the table to scan is accessible through the
       <structname>ForeignScanState</> node (in particular, from the underlying
!      <structname>ForeignScan</> plan node, which contains FDW-private
!      information about it provided by <function>PlanForeignScan</>).
      </para>
  
      <para>
***************
*** 228,236 **** EndForeignScan (ForeignScanState *node);
      </para>
  
      <para>
!      The <structname>FdwRoutine</> and <structname>FdwPlan</> struct types
!      are declared in <filename>src/include/foreign/fdwapi.h</>, which see
!      for additional details.
      </para>
  
     </sect1>
--- 233,241 ----
      </para>
  
      <para>
!      The <structname>FdwRoutine</> struct type is declared
!      in <filename>src/include/foreign/fdwapi.h</>, which see for additional
!      details.
      </para>
  
     </sect1>
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 591,611 **** _copyForeignScan(const ForeignScan *from)
         * copy remainder of node
         */
        COPY_SCALAR_FIELD(fsSystemCol);
-       COPY_NODE_FIELD(fdwplan);
- 
-       return newnode;
- }
- 
- /*
-  * _copyFdwPlan
-  */
- static FdwPlan *
- _copyFdwPlan(const FdwPlan *from)
- {
-       FdwPlan    *newnode = makeNode(FdwPlan);
- 
-       COPY_SCALAR_FIELD(startup_cost);
-       COPY_SCALAR_FIELD(total_cost);
        COPY_NODE_FIELD(fdw_private);
  
        return newnode;
--- 591,596 ----
***************
*** 3841,3849 **** copyObject(const void *from)
                case T_ForeignScan:
                        retval = _copyForeignScan(from);
                        break;
-               case T_FdwPlan:
-                       retval = _copyFdwPlan(from);
-                       break;
                case T_Join:
                        retval = _copyJoin(from);
                        break;
--- 3826,3831 ----
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 558,573 **** _outForeignScan(StringInfo str, const ForeignScan *node)
        _outScanInfo(str, (const Scan *) node);
  
        WRITE_BOOL_FIELD(fsSystemCol);
-       WRITE_NODE_FIELD(fdwplan);
- }
- 
- static void
- _outFdwPlan(StringInfo str, const FdwPlan *node)
- {
-       WRITE_NODE_TYPE("FDWPLAN");
- 
-       WRITE_FLOAT_FIELD(startup_cost, "%.2f");
-       WRITE_FLOAT_FIELD(total_cost, "%.2f");
        WRITE_NODE_FIELD(fdw_private);
  }
  
--- 558,563 ----
***************
*** 1572,1578 **** _outForeignPath(StringInfo str, const ForeignPath *node)
  
        _outPathInfo(str, (const Path *) node);
  
!       WRITE_NODE_FIELD(fdwplan);
  }
  
  static void
--- 1562,1568 ----
  
        _outPathInfo(str, (const Path *) node);
  
!       WRITE_NODE_FIELD(fdw_private);
  }
  
  static void
***************
*** 2744,2752 **** _outNode(StringInfo str, const void *obj)
                        case T_ForeignScan:
                                _outForeignScan(str, obj);
                                break;
-                       case T_FdwPlan:
-                               _outFdwPlan(str, obj);
-                               break;
                        case T_Join:
                                _outJoin(str, obj);
                                break;
--- 2734,2739 ----
*** a/src/backend/optimizer/path/allpaths.c
--- b/src/backend/optimizer/path/allpaths.c
***************
*** 399,411 **** set_foreign_size(PlannerInfo *root, RelOptInfo *rel, 
RangeTblEntry *rte)
  
  /*
   * set_foreign_pathlist
!  *            Build the (single) access path for a foreign table RTE
   */
  static void
  set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  {
!       /* Generate appropriate path */
!       add_path(rel, (Path *) create_foreignscan_path(root, rel));
  
        /* Select cheapest path (pretty easy in this case...) */
        set_cheapest(rel);
--- 399,411 ----
  
  /*
   * set_foreign_pathlist
!  *            Build one or more access paths for a foreign table RTE
   */
  static void
  set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
  {
!       /* Generate appropriate paths */
!       create_foreignscan_path(root, rel);
  
        /* Select cheapest path (pretty easy in this case...) */
        set_cheapest(rel);
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 121,127 **** static CteScan *make_ctescan(List *qptlist, List *qpqual,
  static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
                                   Index scanrelid, int wtParam);
  static ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
!                                Index scanrelid, bool fsSystemCol, FdwPlan 
*fdwplan);
  static BitmapAnd *make_bitmap_and(List *bitmapplans);
  static BitmapOr *make_bitmap_or(List *bitmapplans);
  static NestLoop *make_nestloop(List *tlist,
--- 121,127 ----
  static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
                                   Index scanrelid, int wtParam);
  static ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
!                                Index scanrelid, bool fsSystemCol, List 
*fdw_private);
  static BitmapAnd *make_bitmap_and(List *bitmapplans);
  static BitmapOr *make_bitmap_or(List *bitmapplans);
  static NestLoop *make_nestloop(List *tlist,
***************
*** 1847,1853 **** create_foreignscan_plan(PlannerInfo *root, ForeignPath 
*best_path,
                                                                 scan_clauses,
                                                                 scan_relid,
                                                                 fsSystemCol,
!                                                                
best_path->fdwplan);
  
        copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
  
--- 1847,1853 ----
                                                                 scan_clauses,
                                                                 scan_relid,
                                                                 fsSystemCol,
!                                                                
best_path->fdw_private);
  
        copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
  
***************
*** 3189,3195 **** make_foreignscan(List *qptlist,
                                 List *qpqual,
                                 Index scanrelid,
                                 bool fsSystemCol,
!                                FdwPlan *fdwplan)
  {
        ForeignScan *node = makeNode(ForeignScan);
        Plan       *plan = &node->scan.plan;
--- 3189,3195 ----
                                 List *qpqual,
                                 Index scanrelid,
                                 bool fsSystemCol,
!                                List *fdw_private)
  {
        ForeignScan *node = makeNode(ForeignScan);
        Plan       *plan = &node->scan.plan;
***************
*** 3201,3207 **** make_foreignscan(List *qptlist,
        plan->righttree = NULL;
        node->scan.scanrelid = scanrelid;
        node->fsSystemCol = fsSystemCol;
!       node->fdwplan = fdwplan;
  
        return node;
  }
--- 3201,3207 ----
        plan->righttree = NULL;
        node->scan.scanrelid = scanrelid;
        node->fsSystemCol = fsSystemCol;
!       node->fdw_private = fdw_private;
  
        return node;
  }
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
***************
*** 1764,1803 **** create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel)
  
  /*
   * create_foreignscan_path
!  *      Creates a path corresponding to a scan of a foreign table,
!  *      returning the pathnode.
   */
! ForeignPath *
  create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel)
  {
-       ForeignPath *pathnode = makeNode(ForeignPath);
        RangeTblEntry *rte;
        FdwRoutine *fdwroutine;
-       FdwPlan    *fdwplan;
- 
-       pathnode->path.pathtype = T_ForeignScan;
-       pathnode->path.parent = rel;
-       pathnode->path.pathkeys = NIL;          /* result is always unordered */
-       pathnode->path.required_outer = NULL;
-       pathnode->path.param_clauses = NIL;
  
        /* Get FDW's callback info */
        rte = planner_rt_fetch(rel->relid, root);
        fdwroutine = GetFdwRoutineByRelId(rte->relid);
  
        /* Let the FDW do its planning */
!       fdwplan = fdwroutine->PlanForeignScan(rte->relid, root, rel);
!       if (fdwplan == NULL || !IsA(fdwplan, FdwPlan))
!               elog(ERROR, "foreign-data wrapper PlanForeignScan function for 
relation %u did not return an FdwPlan struct",
!                        rte->relid);
!       pathnode->fdwplan = fdwplan;
! 
!       /* use costs estimated by FDW */
!       pathnode->path.rows = rel->rows;
!       pathnode->path.startup_cost = fdwplan->startup_cost;
!       pathnode->path.total_cost = fdwplan->total_cost;
! 
!       return pathnode;
  }
  
  /*
--- 1764,1786 ----
  
  /*
   * create_foreignscan_path
!  *      Creates one or more paths corresponding to a scan of a foreign table,
!  *      returning void.
   */
! void
  create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel)
  {
        RangeTblEntry *rte;
        FdwRoutine *fdwroutine;
  
        /* Get FDW's callback info */
        rte = planner_rt_fetch(rel->relid, root);
        fdwroutine = GetFdwRoutineByRelId(rte->relid);
  
        /* Let the FDW do its planning */
!       fdwroutine->PlanForeignScan(rte->relid, root, rel);
!       if (rel->pathlist == NIL)
!               elog(ERROR, "could not devise a query plan for the given 
query");
  }
  
  /*
*** a/src/include/foreign/fdwapi.h
--- b/src/include/foreign/fdwapi.h
***************
*** 20,58 **** struct ExplainState;
  
  
  /*
-  * FdwPlan is the information returned to the planner by PlanForeignScan.
-  */
- typedef struct FdwPlan
- {
-       NodeTag         type;
- 
-       /*
-        * Cost estimation info. The startup_cost is time before retrieving the
-        * first row, so it should include costs of connecting to the remote 
host,
-        * sending over the query, etc.  Note that PlanForeignScan also ought to
-        * set baserel->rows and baserel->width if it can produce any usable
-        * estimates of those values.
-        */
-       Cost            startup_cost;   /* cost expended before fetching any 
tuples */
-       Cost            total_cost;             /* total cost (assuming all 
tuples fetched) */
- 
-       /*
-        * FDW private data, which will be available at execution time.
-        *
-        * Note that everything in this list 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().
-        */
-       List       *fdw_private;
- } FdwPlan;
- 
- 
- /*
   * Callback function signatures --- see fdwhandler.sgml for more info.
   */
  
! typedef FdwPlan *(*PlanForeignScan_function) (Oid foreigntableid,
                                                                                
                                  PlannerInfo *root,
                                                                                
                                RelOptInfo *baserel);
  
--- 20,29 ----
  
  
  /*
   * Callback function signatures --- see fdwhandler.sgml for more info.
   */
  
! typedef void (*PlanForeignScan_function) (Oid foreigntableid,
                                                                                
                                  PlannerInfo *root,
                                                                                
                                RelOptInfo *baserel);
  
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 62,68 **** typedef enum NodeTag
        T_CteScan,
        T_WorkTableScan,
        T_ForeignScan,
-       T_FdwPlan,
        T_Join,
        T_NestLoop,
        T_MergeJoin,
--- 62,67 ----
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
***************
*** 468,475 **** typedef struct ForeignScan
  {
        Scan            scan;
        bool            fsSystemCol;    /* true if any "system column" is 
needed */
!       /* use struct pointer to avoid including fdwapi.h here */
!       struct FdwPlan *fdwplan;
  } ForeignScan;
  
  
--- 468,474 ----
  {
        Scan            scan;
        bool            fsSystemCol;    /* true if any "system column" is 
needed */
!       List       *fdw_private;
  } ForeignScan;
  
  
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
***************
*** 794,805 **** typedef struct TidPath
  
  /*
   * ForeignPath represents a scan of a foreign table
   */
  typedef struct ForeignPath
  {
        Path            path;
!       /* use struct pointer to avoid including fdwapi.h here */
!       struct FdwPlan *fdwplan;
  } ForeignPath;
  
  /*
--- 794,810 ----
  
  /*
   * ForeignPath represents a scan of a foreign table
+  *
+  * fdw_private is a list of FDW private data, which will be available at
+  * execution time.  Note that everything in this list 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().
   */
  typedef struct ForeignPath
  {
        Path            path;
!       List       *fdw_private;
  } ForeignPath;
  
  /*
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
***************
*** 68,74 **** extern Path *create_functionscan_path(PlannerInfo *root, 
RelOptInfo *rel);
  extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel);
  extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel);
  extern Path *create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel);
! extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo 
*rel);
  
  extern Relids calc_nestloop_required_outer(Path *outer_path, Path 
*inner_path);
  extern Relids calc_non_nestloop_required_outer(Path *outer_path, Path 
*inner_path);
--- 68,74 ----
  extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel);
  extern Path *create_ctescan_path(PlannerInfo *root, RelOptInfo *rel);
  extern Path *create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel);
! extern void create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel);
  
  extern Relids calc_nestloop_required_outer(Path *outer_path, Path 
*inner_path);
  extern Relids calc_non_nestloop_required_outer(Path *outer_path, Path 
*inner_path);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to