1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-03 15:22:11 +03:00

Implement an API to let foreign-data wrappers actually be functional.

This commit provides the core code and documentation needed.  A contrib
module test case will follow shortly.

Shigeru Hanada, Jan Urbanski, Heikki Linnakangas
This commit is contained in:
Tom Lane
2011-02-20 00:17:18 -05:00
parent d5813488a4
commit bb74240794
39 changed files with 1202 additions and 62 deletions

View File

@@ -347,6 +347,7 @@ RelOptInfo - a relation or joined relations
IndexPath - index scan
BitmapHeapPath - top of a bitmapped index scan
TidPath - scan by CTID
ForeignPath - scan a foreign table
AppendPath - append multiple subpaths together
MergeAppendPath - merge multiple subpaths, preserving their common sort order
ResultPath - a Result plan node (used for FROM-less SELECT)

View File

@@ -17,6 +17,7 @@
#include <math.h>
#include "catalog/pg_class.h"
#include "nodes/nodeFuncs.h"
#ifdef OPTIMIZER_DEBUG
#include "nodes/print.h"
@@ -34,6 +35,7 @@
#include "parser/parse_clause.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "utils/lsyscache.h"
/* These parameters are set by GUC */
@@ -63,6 +65,8 @@ static void set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static void set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel,
RangeTblEntry *rte);
static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
static bool subquery_is_pushdown_safe(Query *subquery, Query *topquery,
bool *differentTypes);
@@ -197,9 +201,17 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
}
else
{
/* Plain relation */
Assert(rel->rtekind == RTE_RELATION);
set_plain_rel_pathlist(root, rel, rte);
if (get_rel_relkind(rte->relid) == RELKIND_FOREIGN_TABLE)
{
/* Foreign table */
set_foreign_pathlist(root, rel, rte);
}
else
{
/* Plain relation */
set_plain_rel_pathlist(root, rel, rte);
}
}
#ifdef OPTIMIZER_DEBUG
@@ -904,6 +916,23 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
set_cheapest(rel);
}
/*
* set_foreign_pathlist
* Build the (single) access path for a foreign table RTE
*/
static void
set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{
/* Mark rel with estimated output rows, width, etc */
set_foreign_size_estimates(root, rel);
/* Generate appropriate path */
add_path(rel, (Path *) create_foreignscan_path(root, rel));
/* Select cheapest path (pretty easy in this case...) */
set_cheapest(rel);
}
/*
* make_rel_from_joinlist
* Build access paths using a "joinlist" to guide the join path search.
@@ -1503,6 +1532,9 @@ print_path(PlannerInfo *root, Path *path, int indent)
case T_TidPath:
ptype = "TidScan";
break;
case T_ForeignPath:
ptype = "ForeignScan";
break;
case T_AppendPath:
ptype = "Append";
break;

View File

@@ -3324,6 +3324,34 @@ set_cte_size_estimates(PlannerInfo *root, RelOptInfo *rel, Plan *cteplan)
set_baserel_size_estimates(root, rel);
}
/*
* set_foreign_size_estimates
* Set the size estimates for a base relation that is a foreign table.
*
* There is not a whole lot that we can do here; the foreign-data wrapper
* is responsible for producing useful estimates. We can do a decent job
* of estimating baserestrictcost, so we set that, and we also set up width
* using what will be purely datatype-driven estimates from the targetlist.
* There is no way to do anything sane with the rows value, so we just put
* a default estimate and hope that the wrapper can improve on it. The
* wrapper's PlanForeignScan function will be called momentarily.
*
* The rel's targetlist and restrictinfo list must have been constructed
* already.
*/
void
set_foreign_size_estimates(PlannerInfo *root, RelOptInfo *rel)
{
/* Should only be applied to base relations */
Assert(rel->relid > 0);
rel->rows = 1000; /* entirely bogus default estimate */
cost_qual_eval(&rel->baserestrictcost, rel->baserestrictinfo, root);
set_rel_width(root, rel);
}
/*
* set_rel_width

View File

@@ -20,6 +20,7 @@
#include <math.h>
#include "access/skey.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
@@ -71,6 +72,8 @@ static CteScan *create_ctescan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses);
static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
List *tlist, List *scan_clauses);
static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
Plan *outer_plan, Plan *inner_plan);
static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
@@ -112,6 +115,8 @@ static CteScan *make_ctescan(List *qptlist, List *qpqual,
Index scanrelid, int ctePlanId, int cteParam);
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,
@@ -206,6 +211,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
case T_ForeignScan:
plan = create_scan_plan(root, best_path);
break;
case T_HashJoin:
@@ -346,6 +352,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
scan_clauses);
break;
case T_ForeignScan:
plan = (Plan *) create_foreignscan_plan(root,
(ForeignPath *) best_path,
tlist,
scan_clauses);
break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) best_path->pathtype);
@@ -468,6 +481,7 @@ disuse_physical_tlist(Plan *plan, Path *path)
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
case T_ForeignScan:
plan->targetlist = build_relation_tlist(path->parent);
break;
default:
@@ -1755,6 +1769,56 @@ create_worktablescan_plan(PlannerInfo *root, Path *best_path,
return scan_plan;
}
/*
* create_foreignscan_plan
* Returns a foreignscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static ForeignScan *
create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
List *tlist, List *scan_clauses)
{
ForeignScan *scan_plan;
RelOptInfo *rel = best_path->path.parent;
Index scan_relid = rel->relid;
RangeTblEntry *rte;
bool fsSystemCol;
int i;
/* it should be a base rel... */
Assert(scan_relid > 0);
Assert(rel->rtekind == RTE_RELATION);
rte = planner_rt_fetch(scan_relid, root);
Assert(rte->rtekind == RTE_RELATION);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
scan_clauses = extract_actual_clauses(scan_clauses, false);
/* Detect whether any system columns are requested from rel */
fsSystemCol = false;
for (i = rel->min_attr; i < 0; i++)
{
if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
{
fsSystemCol = true;
break;
}
}
scan_plan = make_foreignscan(tlist,
scan_clauses,
scan_relid,
fsSystemCol,
best_path->fdwplan);
copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
return scan_plan;
}
/*****************************************************************************
*
@@ -2978,6 +3042,28 @@ make_worktablescan(List *qptlist,
return node;
}
static ForeignScan *
make_foreignscan(List *qptlist,
List *qpqual,
Index scanrelid,
bool fsSystemCol,
FdwPlan *fdwplan)
{
ForeignScan *node = makeNode(ForeignScan);
Plan *plan = &node->scan.plan;
/* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->fsSystemCol = fsSystemCol;
node->fdwplan = fdwplan;
return node;
}
Append *
make_append(List *appendplans, List *tlist)
{

View File

@@ -1914,7 +1914,8 @@ preprocess_rowmarks(PlannerInfo *root)
newrc->rti = newrc->prti = i;
newrc->rowmarkId = ++(root->glob->lastRowMarkId);
/* real tables support REFERENCE, anything else needs COPY */
if (rte->rtekind == RTE_RELATION)
if (rte->rtekind == RTE_RELATION &&
get_rel_relkind(rte->relid) != RELKIND_FOREIGN_TABLE)
newrc->markType = ROW_MARK_REFERENCE;
else
newrc->markType = ROW_MARK_COPY;

View File

@@ -402,6 +402,18 @@ set_plan_refs(PlannerGlobal *glob, Plan *plan, int rtoffset)
fix_scan_list(glob, splan->scan.plan.qual, rtoffset);
}
break;
case T_ForeignScan:
{
ForeignScan *splan = (ForeignScan *) plan;
splan->scan.scanrelid += rtoffset;
splan->scan.plan.targetlist =
fix_scan_list(glob, splan->scan.plan.targetlist, rtoffset);
splan->scan.plan.qual =
fix_scan_list(glob, splan->scan.plan.qual, rtoffset);
}
break;
case T_NestLoop:
case T_MergeJoin:
case T_HashJoin:

View File

@@ -2054,6 +2054,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_ForeignScan:
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_ModifyTable:
{
ModifyTable *mtplan = (ModifyTable *) plan;

View File

@@ -17,6 +17,7 @@
#include <math.h>
#include "catalog/pg_operator.h"
#include "foreign/fdwapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
@@ -1419,6 +1420,41 @@ create_worktablescan_path(PlannerInfo *root, RelOptInfo *rel)
return pathnode;
}
/*
* 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 */
/* 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.startup_cost = fdwplan->startup_cost;
pathnode->path.total_cost = fdwplan->total_cost;
return pathnode;
}
/*
* create_nestloop_path
* Creates a pathnode corresponding to a nestloop join between two

View File

@@ -90,12 +90,6 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
*/
relation = heap_open(relationObjectId, NoLock);
/* Foreign table scans are not implemented yet. */
if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("foreign table scans are not yet supported")));
rel->min_attr = FirstLowInvalidHeapAttributeNumber + 1;
rel->max_attr = RelationGetNumberOfAttributes(relation);
rel->reltablespace = RelationGetForm(relation)->reltablespace;
@@ -463,6 +457,11 @@ estimate_rel_size(Relation rel, int32 *attr_widths,
*pages = 1;
*tuples = 1;
break;
case RELKIND_FOREIGN_TABLE:
/* Just use whatever's in pg_class */
*pages = rel->rd_rel->relpages;
*tuples = rel->rd_rel->reltuples;
break;
default:
/* else it has no disk storage; probably shouldn't get here? */
*pages = 0;