diff --git a/contrib/file_fdw/file_fdw.c b/contrib/file_fdw/file_fdw.c index 46394a8..bb541e3 100644 *** 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" *************** PG_FUNCTION_INFO_V1(file_fdw_validator); *** 93,99 **** /* * 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); *************** get_file_fdw_attribute_options(Oid relid *** 406,432 **** /* * 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,443 ---- /* * filePlanForeignScan ! * Create possible access paths for a scan on the foreign table ! * ! * Currently we don't support any push-down feature, so there is only one ! * possible access path, which simply returns all records in the order in ! * the data file. */ ! static void filePlanForeignScan(Oid foreigntableid, PlannerInfo *root, RelOptInfo *baserel) { ! ForeignPath *pathnode = makeNode(ForeignPath); char *filename; List *options; /* Fetch options --- we only need filename at this point */ fileGetOptions(foreigntableid, &filename, &options); ! /* Create a ForeignPath node and add it as only one possible path. */ ! pathnode = create_foreignscan_path(root, baserel); ! pathnode->fdw_private = NIL; estimate_costs(root, baserel, filename, ! &pathnode->path.startup_cost, &pathnode->path.total_cost); ! pathnode->path.rows = baserel->rows; ! add_path(baserel, (Path *) pathnode); ! /* ! * If data file was sorted, and we knew it somehow, we can create another ! * ForeignPath node with valid pathkeys to tell planner that sorting this ! * file by such key is not expensive. ! */ } /* diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml index 76ff243..63fe8bd 100644 *** a/doc/src/sgml/fdwhandler.sgml --- b/doc/src/sgml/fdwhandler.sgml *************** *** 88,108 **** ! FdwPlan * PlanForeignScan (Oid foreigntableid, PlannerInfo *root, RelOptInfo *baserel); ! Plan a scan on a foreign table. This is called when a query is planned. foreigntableid is the pg_class OID of the foreign table. root is the planner's global information about the query, and 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 copyObject knows how to copy.) --- 88,117 ---- ! void PlanForeignScan (Oid foreigntableid, PlannerInfo *root, RelOptInfo *baserel); ! Create possible access paths for a scan on a foreign table. This is ! called when a query is planned. foreigntableid is the pg_class OID of the foreign table. root is the planner's global information about the query, and 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 add_path to add it to ! baserel->pathlist. The function may generate extra ! access paths, e.g. a path which has valid pathkeys for sorted ! result. Each access path should contain cost estimates, and can contain ! 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 ! copyObject knows how to copy.) *************** BeginForeignScan (ForeignScanState *node *** 159,167 **** its fdw_state field is still NULL. Information about the table to scan is accessible through the ForeignScanState node (in particular, from the underlying ! ForeignScan plan node, which contains a pointer to the ! FdwPlan structure returned by ! PlanForeignScan). --- 168,175 ---- its fdw_state field is still NULL. Information about the table to scan is accessible through the ForeignScanState node (in particular, from the underlying ! ForeignScan plan node, which contains FDW-private ! information about it provided by PlanForeignScan). *************** EndForeignScan (ForeignScanState *node); *** 228,236 **** ! The FdwRoutine and FdwPlan struct types ! are declared in src/include/foreign/fdwapi.h, which see ! for additional details. --- 236,244 ---- ! The FdwRoutine struct type is declared ! in src/include/foreign/fdwapi.h, which see for additional ! details. diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 7fec4db..9b2f688 100644 *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** _copyForeignScan(const ForeignScan *from *** 591,611 **** * 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 ---- *************** copyObject(const void *from) *** 3842,3850 **** case T_ForeignScan: retval = _copyForeignScan(from); break; - case T_FdwPlan: - retval = _copyFdwPlan(from); - break; case T_Join: retval = _copyJoin(from); break; --- 3827,3832 ---- diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 25a215e..08b74fb 100644 *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** _outForeignScan(StringInfo str, const Fo *** 558,573 **** _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 ---- *************** _outForeignPath(StringInfo str, const Fo *** 1572,1578 **** _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 *************** _outNode(StringInfo str, const void *obj *** 2745,2753 **** case T_ForeignScan: _outForeignScan(str, obj); break; - case T_FdwPlan: - _outFdwPlan(str, obj); - break; case T_Join: _outJoin(str, obj); break; --- 2735,2740 ---- diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 8f03417..060d49d 100644 *** a/src/backend/optimizer/path/allpaths.c --- b/src/backend/optimizer/path/allpaths.c *************** *** 18,23 **** --- 18,24 ---- #include #include "catalog/pg_class.h" + #include "foreign/fdwapi.h" #include "nodes/nodeFuncs.h" #ifdef OPTIMIZER_DEBUG #include "nodes/print.h" *************** set_foreign_size(PlannerInfo *root, RelO *** 399,411 **** /* * 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); --- 400,414 ---- /* * set_foreign_pathlist ! * Build access paths for a foreign table RTE */ static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte) { ! FdwRoutine *fdwroutine; ! ! fdwroutine = GetFdwRoutineByRelId(rte->relid); ! fdwroutine->PlanForeignScan(rte->relid, root, rel); /* Select cheapest path (pretty easy in this case...) */ set_cheapest(rel); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 9ac0c99..c348fd6 100644 *** a/src/backend/optimizer/plan/createplan.c --- b/src/backend/optimizer/plan/createplan.c *************** static CteScan *make_ctescan(List *qptli *** 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, 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, *************** create_foreignscan_plan(PlannerInfo *roo *** 1847,1853 **** 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); *************** make_foreignscan(List *qptlist, *** 3189,3195 **** 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; *************** make_foreignscan(List *qptlist, *** 3201,3207 **** 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; } diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index d29b454..9c285a9 100644 *** a/src/backend/optimizer/util/pathnode.c --- b/src/backend/optimizer/util/pathnode.c *************** create_worktablescan_path(PlannerInfo *r *** 1764,1801 **** /* * 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,1795 ---- /* * create_foreignscan_path ! * Creates a path corresponding to a scan of a foreign table, and returns ! * the pathnode. ! * ! * It's assumed that this function is called from PlanForeignScan ! * imeplementation of FDWs, but not from planner directly; planner never ! * calls this function directly. Generated ForeignPath node has valid ! * values for only requried fields, so each FDW should set optional fields ! * as they want. ! * ! * If FDW needs to pass FDW-specific information from planner to executor, ! * fdw_private field is available. Its type is List *, so you can store ! * Node subclass objects. Planner will copy fdw_private of cheapest ! * ForeignPath to ForeignScan plan node. */ ForeignPath * create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel) { ForeignPath *pathnode = makeNode(ForeignPath); pathnode->path.pathtype = T_ForeignScan; pathnode->path.parent = rel; ! pathnode->path.pathkeys = NIL; pathnode->path.required_outer = NULL; pathnode->path.param_clauses = NIL; ! pathnode->fdw_private = NIL; return pathnode; } diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h index 3696623..4831e7c 100644 *** a/src/include/foreign/fdwapi.h --- b/src/include/foreign/fdwapi.h *************** struct ExplainState; *** 20,58 **** /* - * 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); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 0e7d184..905458f 100644 *** a/src/include/nodes/nodes.h --- b/src/include/nodes/nodes.h *************** typedef enum NodeTag *** 62,68 **** T_CteScan, T_WorkTableScan, T_ForeignScan, - T_FdwPlan, T_Join, T_NestLoop, T_MergeJoin, --- 62,67 ---- diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 7d90b91..23d9256 100644 *** a/src/include/nodes/plannodes.h --- b/src/include/nodes/plannodes.h *************** typedef struct ForeignScan *** 468,475 **** { 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; diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 6ba920a..1acc4f8 100644 *** a/src/include/nodes/relation.h --- b/src/include/nodes/relation.h *************** typedef struct TidPath *** 794,805 **** /* * 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; /*