(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->pathlist</>. The function may generate multiple ! access paths for a scan on the foreign table and add them to ! <literal>baserel->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