mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Redesign PlanForeignScan API to allow multiple paths for a foreign table.
The original API specification only allowed an FDW to create a single access path, which doesn't seem like a terribly good idea in hindsight. Instead, move the responsibility for building the Path node and calling add_path() into the FDW's PlanForeignScan function. Now, it can do that more than once if appropriate. There is no longer any need for the transient FdwPlan struct, so get rid of that. Etsuro Fujita, Shigeru Hanada, Tom Lane
This commit is contained in:
@ -23,7 +23,8 @@
|
||||
#include "postgres.h"
|
||||
|
||||
#include "miscadmin.h"
|
||||
#include "foreign/fdwapi.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "utils/datum.h"
|
||||
|
||||
|
||||
@ -591,21 +592,6 @@ _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;
|
||||
@ -3842,9 +3828,6 @@ 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;
|
||||
|
@ -23,7 +23,9 @@
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "foreign/fdwapi.h"
|
||||
#include "lib/stringinfo.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "nodes/relation.h"
|
||||
#include "utils/datum.h"
|
||||
|
||||
|
||||
@ -558,16 +560,6 @@ _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);
|
||||
}
|
||||
|
||||
@ -1572,7 +1564,7 @@ _outForeignPath(StringInfo str, const ForeignPath *node)
|
||||
|
||||
_outPathInfo(str, (const Path *) node);
|
||||
|
||||
WRITE_NODE_FIELD(fdwplan);
|
||||
WRITE_NODE_FIELD(fdw_private);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -2745,9 +2737,6 @@ _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;
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "catalog/pg_class.h"
|
||||
#include "foreign/fdwapi.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#ifdef OPTIMIZER_DEBUG
|
||||
#include "nodes/print.h"
|
||||
@ -399,15 +400,18 @@ set_foreign_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
|
||||
|
||||
/*
|
||||
* set_foreign_pathlist
|
||||
* Build the (single) access path for a foreign table RTE
|
||||
* Build access paths 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));
|
||||
FdwRoutine *fdwroutine;
|
||||
|
||||
/* Select cheapest path (pretty easy in this case...) */
|
||||
/* Call the FDW's PlanForeignScan function to generate path(s) */
|
||||
fdwroutine = GetFdwRoutineByRelId(rte->relid);
|
||||
fdwroutine->PlanForeignScan(rte->relid, root, rel);
|
||||
|
||||
/* Select cheapest path */
|
||||
set_cheapest(rel);
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <math.h>
|
||||
|
||||
#include "access/skey.h"
|
||||
#include "foreign/fdwapi.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
@ -121,7 +120,7 @@ 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);
|
||||
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,7 +1846,7 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
|
||||
scan_clauses,
|
||||
scan_relid,
|
||||
fsSystemCol,
|
||||
best_path->fdwplan);
|
||||
best_path->fdw_private);
|
||||
|
||||
copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
|
||||
|
||||
@ -3189,7 +3188,7 @@ make_foreignscan(List *qptlist,
|
||||
List *qpqual,
|
||||
Index scanrelid,
|
||||
bool fsSystemCol,
|
||||
FdwPlan *fdwplan)
|
||||
List *fdw_private)
|
||||
{
|
||||
ForeignScan *node = makeNode(ForeignScan);
|
||||
Plan *plan = &node->scan.plan;
|
||||
@ -3201,7 +3200,7 @@ make_foreignscan(List *qptlist,
|
||||
plan->righttree = NULL;
|
||||
node->scan.scanrelid = scanrelid;
|
||||
node->fsSystemCol = fsSystemCol;
|
||||
node->fdwplan = fdwplan;
|
||||
node->fdw_private = fdw_private;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
@ -16,7 +16,6 @@
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "foreign/fdwapi.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "optimizer/clauses.h"
|
||||
@ -1766,36 +1765,31 @@ create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel)
|
||||
* create_foreignscan_path
|
||||
* Creates a path corresponding to a scan of a foreign table,
|
||||
* returning the pathnode.
|
||||
*
|
||||
* This function is never called from core Postgres; rather, it's expected
|
||||
* to be called by the PlanForeignScan function of a foreign data wrapper.
|
||||
* We make the FDW supply all fields of the path, since we do not have any
|
||||
* way to calculate them in core.
|
||||
*/
|
||||
ForeignPath *
|
||||
create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel)
|
||||
create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
double rows, Cost startup_cost, Cost total_cost,
|
||||
List *pathkeys,
|
||||
Relids required_outer, List *param_clauses,
|
||||
List *fdw_private)
|
||||
{
|
||||
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;
|
||||
pathnode->path.rows = rows;
|
||||
pathnode->path.startup_cost = startup_cost;
|
||||
pathnode->path.total_cost = total_cost;
|
||||
pathnode->path.pathkeys = pathkeys;
|
||||
pathnode->path.required_outer = required_outer;
|
||||
pathnode->path.param_clauses = param_clauses;
|
||||
|
||||
/* 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;
|
||||
pathnode->fdw_private = fdw_private;
|
||||
|
||||
return pathnode;
|
||||
}
|
||||
|
@ -19,42 +19,13 @@
|
||||
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);
|
||||
typedef void (*PlanForeignScan_function) (Oid foreigntableid,
|
||||
PlannerInfo *root,
|
||||
RelOptInfo *baserel);
|
||||
|
||||
typedef void (*ExplainForeignScan_function) (ForeignScanState *node,
|
||||
struct ExplainState *es);
|
||||
|
@ -62,7 +62,6 @@ typedef enum NodeTag
|
||||
T_CteScan,
|
||||
T_WorkTableScan,
|
||||
T_ForeignScan,
|
||||
T_FdwPlan,
|
||||
T_Join,
|
||||
T_NestLoop,
|
||||
T_MergeJoin,
|
||||
|
@ -468,8 +468,7 @@ 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;
|
||||
List *fdw_private; /* private data for FDW */
|
||||
} ForeignScan;
|
||||
|
||||
|
||||
|
@ -794,12 +794,18 @@ typedef struct TidPath
|
||||
|
||||
/*
|
||||
* ForeignPath represents a scan of a foreign table
|
||||
*
|
||||
* fdw_private contains FDW private data about the scan, which will be copied
|
||||
* to the final ForeignScan plan node so that it is 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;
|
||||
/* use struct pointer to avoid including fdwapi.h here */
|
||||
struct FdwPlan *fdwplan;
|
||||
List *fdw_private;
|
||||
} ForeignPath;
|
||||
|
||||
/*
|
||||
|
@ -68,7 +68,11 @@ 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 ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
|
||||
double rows, Cost startup_cost, Cost total_cost,
|
||||
List *pathkeys,
|
||||
Relids required_outer, List *param_clauses,
|
||||
List *fdw_private);
|
||||
|
||||
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);
|
||||
|
Reference in New Issue
Block a user