diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index f95f9cc..70d25ae 100644
*** a/doc/src/sgml/config.sgml
--- b/doc/src/sgml/config.sgml
*************** SET ENABLE_SEQSCAN TO OFF;
*** 2360,2365 ****
--- 2360,2378 ----
+
+ enable_foreignjoin (boolean)
+
+ enable_foreignjoin> configuration parameter
+
+
+
+ Enables or disables the query planner's use of foreign-join plan
+ types. The default is on>.
+
+
+
+
enable_hashagg (boolean)
diff --git a/doc/src/sgml/ref/postgres-ref.sgml b/doc/src/sgml/ref/postgres-ref.sgml
index b16bbdf..bebaebe 100644
*** a/doc/src/sgml/ref/postgres-ref.sgml
--- b/doc/src/sgml/ref/postgres-ref.sgml
*************** PostgreSQL documentation
*** 362,375 ****
! { s | i | m | n | h }
Forbids the use of particular scan and join methods:
s and i
disable sequential and index scans respectively, while
! n, m, and h
! disable nested-loop, merge and hash joins respectively.
--- 362,376 ----
! { s | i | m | n | h | f }
Forbids the use of particular scan and join methods:
s and i
disable sequential and index scans respectively, while
! n, m, h, and
! f
! disable nested-loop, merge, hash and foreign joins respectively.
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 6408d16..1bd035d 100644
*** a/src/backend/commands/explain.c
--- b/src/backend/commands/explain.c
*************** static void show_hash_info(HashState *ha
*** 79,84 ****
--- 79,85 ----
static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
static const char *explain_get_index_name(Oid indexId);
static void ExplainScanTarget(Scan *plan, ExplainState *es);
+ static void ExplainForeignScanTarget(Scan *plan, ExplainState *es);
static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
static void ExplainMemberNodes(List *plans, PlanState **planstates,
*************** ExplainNode(PlanState *planstate, List *
*** 833,841 ****
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
- case T_ForeignScan:
ExplainScanTarget((Scan *) plan, es);
break;
case T_BitmapIndexScan:
{
BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
--- 834,844 ----
case T_ValuesScan:
case T_CteScan:
case T_WorkTableScan:
ExplainScanTarget((Scan *) plan, es);
break;
+ case T_ForeignScan:
+ ExplainForeignScanTarget((ForeignScan *) plan, es);
+ break;
case T_BitmapIndexScan:
{
BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
*************** ExplainScanTarget(Scan *plan, ExplainSta
*** 1556,1561 ****
--- 1559,1584 ----
}
/*
+ * Show the target of a ForeignScan node
+ */
+ static void
+ ExplainForeignScanTarget(Scan *plan, ExplainState *es)
+ {
+ Assert(IsA(plan, ForeignScan));
+
+ /*
+ * If scan target is an foreign table, show in normal scan format,
+ * otherwise, show in specific format.
+ */
+ if (plan->scanrelid > 0)
+ ExplainTargetRel((Plan *) plan, plan->scanrelid, es);
+ else
+ {
+ appendStringInfo(es->str, " on multiple foreign tables");
+ }
+ }
+
+ /*
* Show the target of a ModifyTable node
*/
static void
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 4dbf10b..351b746 100644
*** a/src/backend/executor/execUtils.c
--- b/src/backend/executor/execUtils.c
*************** ExecAssignScanTypeFromOuterPlan(ScanStat
*** 756,761 ****
--- 756,786 ----
ExecAssignScanType(scanstate, tupDesc);
}
+ /* ----------------
+ * ExecAssignScanTypeFromTL
+ * ----------------
+ */
+ void
+ ExecAssignScanTypeFromTL(ScanState *scanstate)
+ {
+ bool hasoid;
+ TupleTableSlot *slot = scanstate->ss_ScanTupleSlot;
+ TupleDesc tupDesc;
+
+ if (ExecContextForcesOids(&scanstate->ps, &hasoid))
+ {
+ /* context forces OID choice; hasoid is now set correctly */
+ }
+ else
+ {
+ /* given free choice, don't leave space for OIDs in result tuples */
+ hasoid = false;
+ }
+
+ tupDesc = ExecTypeFromTL(scanstate->ps.plan->targetlist, hasoid);
+ ExecSetSlotDescriptor(slot, tupDesc);
+ }
+
/* ----------------------------------------------------------------
* Scan node support
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 841ae69..b13f0dd 100644
*** a/src/backend/executor/nodeForeignscan.c
--- b/src/backend/executor/nodeForeignscan.c
***************
*** 25,30 ****
--- 25,31 ----
#include "executor/executor.h"
#include "executor/nodeForeignscan.h"
#include "foreign/fdwapi.h"
+ #include "foreign/foreign.h"
#include "utils/rel.h"
static TupleTableSlot *ForeignNext(ForeignScanState *node);
*************** ExecForeignScan(ForeignScanState *node)
*** 101,109 ****
ForeignScanState *
ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
{
! ForeignScanState *scanstate;
! Relation currentRelation;
! FdwRoutine *fdwroutine;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
--- 102,112 ----
ForeignScanState *
ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
{
! ForeignScanState *scanstate;
! Relation currentRelation;
! ForeignServer *server;
! ForeignDataWrapper *wrapper;
! FdwRoutine *fdwroutine;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
*************** ExecInitForeignScan(ForeignScan *node, E
*** 140,166 ****
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
ExecInitScanTupleSlot(estate, &scanstate->ss);
! /*
! * open the base relation and acquire appropriate lock on it.
! */
! currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
! scanstate->ss.ss_currentRelation = currentRelation;
! /*
! * get the scan type from the relation descriptor.
! */
! ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
! /*
! * Initialize result tuple type and projection info.
! */
! ExecAssignResultTypeFromTL(&scanstate->ss.ps);
! ExecAssignScanProjectionInfo(&scanstate->ss);
/*
* Acquire function pointers from the FDW's handler, and init fdw_state.
*/
! fdwroutine = GetFdwRoutineByRelId(RelationGetRelid(currentRelation));
scanstate->fdwroutine = fdwroutine;
scanstate->fdw_state = NULL;
--- 143,193 ----
ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
ExecInitScanTupleSlot(estate, &scanstate->ss);
! if (node->scan.scanrelid != InvalidOid)
! {
! /*
! * open the base relation and acquire appropriate lock on it.
! */
! currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
! scanstate->ss.ss_currentRelation = currentRelation;
! /*
! * get the scan type from the relation descriptor.
! */
! ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
! /*
! * Initialize result tuple type and projection info.
! */
! ExecAssignResultTypeFromTL(&scanstate->ss.ps);
! ExecAssignScanProjectionInfo(&scanstate->ss);
! }
! else
! {
! TupleDesc tupleDesc;
!
! /* TODO: open related relations and acquire appropriate lock on them. */
! scanstate->ss.ss_currentRelation = NULL;
!
! /*
! * get the scan type from the target list.
! */
! ExecAssignScanTypeFromTL(&scanstate->ss);
! tupleDesc = scanstate->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
!
! /*
! * Initialize result tuple type and projection info.
! */
! ExecAssignResultTypeFromTL(&scanstate->ss.ps);
! ExecAssignProjectionInfo(&scanstate->ss.ps, NULL);
! }
/*
* Acquire function pointers from the FDW's handler, and init fdw_state.
*/
! server = GetForeignServer(node->serverid);
! wrapper = GetForeignDataWrapper(server->fdwid);
! fdwroutine = GetFdwRoutine(wrapper->fdwhandler);
scanstate->fdwroutine = fdwroutine;
scanstate->fdw_state = NULL;
*************** ExecEndForeignScan(ForeignScanState *nod
*** 192,198 ****
ExecClearTuple(node->ss.ss_ScanTupleSlot);
/* close the relation. */
! ExecCloseScanRelation(node->ss.ss_currentRelation);
}
/* ----------------------------------------------------------------
--- 219,226 ----
ExecClearTuple(node->ss.ss_ScanTupleSlot);
/* close the relation. */
! if (node->ss.ss_currentRelation != NULL)
! ExecCloseScanRelation(node->ss.ss_currentRelation);
}
/* ----------------------------------------------------------------
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 661a516..30f3d36 100644
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyForeignScan(ForeignScan *from)
*** 565,570 ****
--- 565,571 ----
/*
* copy remainder of node
*/
+ COPY_SCALAR_FIELD(serverid);
COPY_SCALAR_FIELD(fsSystemCol);
COPY_NODE_FIELD(fdwplan);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 0d0ce3c..f0532be 100644
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outForeignScan(StringInfo str, ForeignS
*** 543,548 ****
--- 543,549 ----
_outScanInfo(str, (Scan *) node);
+ WRITE_OID_FIELD(serverid);
WRITE_BOOL_FIELD(fsSystemCol);
WRITE_NODE_FIELD(fdwplan);
}
*************** _outHashPath(StringInfo str, HashPath *n
*** 1647,1652 ****
--- 1648,1663 ----
}
static void
+ _outForeignJoinPath(StringInfo str, ForeignJoinPath *node)
+ {
+ WRITE_NODE_TYPE("FOREIGNJOINPATH");
+
+ _outJoinPathInfo(str, (JoinPath *) node);
+
+ WRITE_NODE_FIELD(fdwplan);
+ }
+
+ static void
_outPlannerGlobal(StringInfo str, PlannerGlobal *node)
{
WRITE_NODE_TYPE("PLANNERGLOBAL");
*************** _outRelOptInfo(StringInfo str, RelOptInf
*** 1734,1739 ****
--- 1745,1751 ----
WRITE_NODE_FIELD(baserestrictinfo);
WRITE_NODE_FIELD(joininfo);
WRITE_BOOL_FIELD(has_eclass_joins);
+ WRITE_OID_FIELD(serverid);
WRITE_BITMAPSET_FIELD(index_outer_relids);
WRITE_NODE_FIELD(index_inner_paths);
}
*************** _outNode(StringInfo str, void *obj)
*** 2967,2972 ****
--- 2979,2987 ----
case T_HashPath:
_outHashPath(str, obj);
break;
+ case T_ForeignJoinPath:
+ _outForeignJoinPath(str, obj);
+ break;
case T_PlannerGlobal:
_outPlannerGlobal(str, obj);
break;
diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README
index aaa754c..f06cb27 100644
*** a/src/backend/optimizer/README
--- b/src/backend/optimizer/README
*************** RelOptInfo - a relation or joined r
*** 356,361 ****
--- 356,362 ----
NestPath - nested-loop joins
MergePath - merge joins
HashPath - hash joins
+ ForeignJoinPath - foreign joins
EquivalenceClass - a data structure representing a set of values known equal
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index b421481..13f7165 100644
*** a/src/backend/optimizer/path/allpaths.c
--- b/src/backend/optimizer/path/allpaths.c
*************** print_path(PlannerInfo *root, Path *path
*** 1590,1595 ****
--- 1590,1599 ----
ptype = "HashJoin";
join = true;
break;
+ case T_ForeignJoinPath:
+ ptype = "ForeignJoin";
+ join = true;
+ break;
default:
ptype = "???Path";
break;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 7812a86..e843ffd 100644
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** bool enable_nestloop = true;
*** 118,123 ****
--- 118,124 ----
bool enable_material = true;
bool enable_mergejoin = true;
bool enable_hashjoin = true;
+ bool enable_foreignjoin = true;
typedef struct
{
*************** cost_mergejoin(MergePath *path, PlannerI
*** 2071,2076 ****
--- 2072,2082 ----
}
/*
+ * cost_foreignjoin() is not defined here because the costs of a foreign join
+ * is estimated by each FDW via PlanForeignJoin.
+ */
+
+ /*
* run mergejoinscansel() with caching
*/
static MergeScanSelCache *
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 7d3cf42..3024565 100644
*** a/src/backend/optimizer/path/joinpath.c
--- b/src/backend/optimizer/path/joinpath.c
*************** match_unsorted_outer(PlannerInfo *root,
*** 541,546 ****
--- 541,566 ----
merge_pathkeys));
}
+ if (enable_foreignjoin &&
+ joinrel->serverid != InvalidOid &&
+ (IsA(outerpath, ForeignPath) || IsA(outerpath, ForeignJoinPath)) &&
+ (IsA(inner_cheapest_total, ForeignPath) ||
+ IsA(inner_cheapest_total, ForeignJoinPath)))
+
+ {
+ ForeignJoinPath *path;
+ path = create_foreignjoin_path(root,
+ joinrel,
+ jointype,
+ sjinfo,
+ outerpath,
+ inner_cheapest_total,
+ restrictlist,
+ merge_pathkeys);
+ if (path != NULL)
+ add_path(joinrel, (Path *) path);
+ }
+
/* Can't do anything else if outer path needs to be unique'd */
if (save_jointype == JOIN_UNIQUE_OUTER)
continue;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index b674afe..ccca59c 100644
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
*************** static WorkTableScan *create_worktablesc
*** 74,79 ****
--- 74,81 ----
List *tlist, List *scan_clauses);
static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
List *tlist, List *scan_clauses);
+ static ForeignScan *create_foreignjoin_plan(PlannerInfo *root, ForeignJoinPath *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,
*************** static CteScan *make_ctescan(List *qptli
*** 117,123 ****
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,
--- 119,126 ----
static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual,
Index scanrelid, int wtParam);
static ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
! Index scanrelid, Oid serverid, bool fsSystemCol,
! FdwPlan *fdwplan);
static BitmapAnd *make_bitmap_and(List *bitmapplans);
static BitmapOr *make_bitmap_or(List *bitmapplans);
static NestLoop *make_nestloop(List *tlist,
*************** create_plan_recurse(PlannerInfo *root, P
*** 214,219 ****
--- 217,223 ----
case T_CteScan:
case T_WorkTableScan:
case T_ForeignScan:
+ case T_ForeignJoin: /* ForeignJoinPath become a ForeignScan */
plan = create_scan_plan(root, best_path);
break;
case T_HashJoin:
*************** create_scan_plan(PlannerInfo *root, Path
*** 361,366 ****
--- 365,377 ----
scan_clauses);
break;
+ case T_ForeignJoin:
+ plan = (Plan *) create_foreignjoin_plan(root,
+ (ForeignJoinPath *) best_path,
+ tlist,
+ scan_clauses);
+ break;
+
default:
elog(ERROR, "unrecognized node type: %d",
(int) best_path->pathtype);
*************** create_foreignscan_plan(PlannerInfo *roo
*** 1813,1818 ****
--- 1824,1830 ----
scan_plan = make_foreignscan(tlist,
scan_clauses,
scan_relid,
+ rel->serverid,
fsSystemCol,
best_path->fdwplan);
*************** create_foreignscan_plan(PlannerInfo *roo
*** 1821,1826 ****
--- 1833,1884 ----
return scan_plan;
}
+ /*
+ * create_foreignjoin_plan
+ * Returns a foreignscan plan for the join relation joined by 'best_path'
+ * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+ static ForeignScan *
+ create_foreignjoin_plan(PlannerInfo *root, ForeignJoinPath *best_path,
+ List *tlist, List *scan_clauses)
+ {
+ ForeignScan *scan_plan;
+ RelOptInfo *rel = best_path->jpath.path.parent;
+ Index scan_relid = rel->relid;
+ bool fsSystemCol;
+ int i;
+
+ /* 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);
+
+ fsSystemCol = false;
+ #ifdef NOT_USED
+ /* Detect whether any system columns are requested from rel */
+ for (i = rel->min_attr; i < 0; i++)
+ {
+ if (!bms_is_empty(rel->attr_needed[i - rel->min_attr]))
+ {
+ fsSystemCol = true;
+ break;
+ }
+ }
+ #endif
+
+ scan_plan = make_foreignscan(tlist,
+ scan_clauses,
+ scan_relid,
+ rel->serverid,
+ fsSystemCol,
+ best_path->fdwplan);
+
+ copy_path_costsize(&scan_plan->scan.plan, &best_path->jpath.path);
+
+ return scan_plan;
+ }
+
/*****************************************************************************
*
*************** static ForeignScan *
*** 3046,3051 ****
--- 3104,3110 ----
make_foreignscan(List *qptlist,
List *qpqual,
Index scanrelid,
+ Oid serverid,
bool fsSystemCol,
FdwPlan *fdwplan)
{
*************** make_foreignscan(List *qptlist,
*** 3058,3063 ****
--- 3117,3123 ----
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
+ node->serverid = serverid;
node->fsSystemCol = fsSystemCol;
node->fdwplan = fdwplan;
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 4a1c94a..b2e2a7a 100644
*** a/src/backend/optimizer/util/pathnode.c
--- b/src/backend/optimizer/util/pathnode.c
***************
*** 17,22 ****
--- 17,23 ----
#include
#include "foreign/fdwapi.h"
+ #include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
*************** create_hashjoin_path(PlannerInfo *root,
*** 1603,1605 ****
--- 1604,1686 ----
return pathnode;
}
+
+ /*
+ * create_foreignjoin_path
+ * Creates a pathnode corresponding to a foreign join between two
+ * relations.
+ *
+ * 'joinrel' is the join relation.
+ * 'jointype' is the type of join required
+ * 'sjinfo' is extra info about the join for selectivity estimation
+ * 'outer_path' is the outer path
+ * 'inner_path' is the inner path
+ * 'restrict_clauses' are the RestrictInfo nodes to apply at the join
+ * 'pathkeys' are the path keys of the new join path
+ *
+ * Returns the resulting path node, or NULL to indicate that this path is
+ * unavailable.
+ */
+ ForeignJoinPath *
+ create_foreignjoin_path(PlannerInfo *root,
+ RelOptInfo *joinrel,
+ JoinType jointype,
+ SpecialJoinInfo *sjinfo,
+ Path *outer_path,
+ Path *inner_path,
+ List *restrict_clauses,
+ List *pathkeys)
+ {
+ ForeignJoinPath *pathnode;
+ ForeignServer *server;
+ ForeignDataWrapper *wrapper;
+ FdwRoutine *fdwroutine;
+ FdwPlan *fdwplan;
+
+ /* Both outer and inner of this join must come from same foreign server. */
+ Assert(IsA(outer_path, ForeignPath) || IsA(outer_path, ForeignJoinPath));
+ Assert(IsA(inner_path, ForeignPath) || IsA(inner_path, ForeignJoinPath));
+
+ /*
+ * First we try to get FDW's callback info. If the FDW has planner for
+ * foreign join, let the FDW plan this join.
+ */
+ server = GetForeignServer(joinrel->serverid);
+ wrapper = GetForeignDataWrapper(server->fdwid);
+ fdwroutine = GetFdwRoutine(wrapper->fdwhandler);
+ if (fdwroutine->PlanForeignJoin == NULL)
+ return NULL;
+
+ fdwplan = fdwroutine->PlanForeignJoin(joinrel->serverid,
+ root,
+ joinrel,
+ jointype,
+ sjinfo,
+ outer_path,
+ inner_path,
+ restrict_clauses,
+ pathkeys);
+ /* Returning NULL indicates that the FDW can't handle this join. */
+ if (fdwplan == NULL)
+ return NULL;
+ Assert(IsA(fdwplan, FdwPlan));
+
+ /* OK, this FDW can handle this join. */
+ pathnode = makeNode(ForeignJoinPath);
+ pathnode->jpath.path.pathtype = T_ForeignJoin;
+ pathnode->jpath.path.parent = joinrel;
+ pathnode->jpath.jointype = jointype;
+ pathnode->jpath.outerjoinpath = outer_path;
+ pathnode->jpath.innerjoinpath = inner_path;
+ pathnode->jpath.joinrestrictinfo = restrict_clauses;
+ pathnode->jpath.path.pathkeys = pathkeys;
+
+ /* Use costs estimated by FDW */
+ pathnode->jpath.path.startup_cost = fdwplan->startup_cost;
+ pathnode->jpath.path.total_cost = fdwplan->total_cost;
+
+ /* Store FDW-private information too. */
+ pathnode->fdwplan = fdwplan;
+
+ return pathnode;
+ }
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 8a3a5d8..32d1bb5 100644
*** a/src/backend/optimizer/util/plancat.c
--- b/src/backend/optimizer/util/plancat.c
***************
*** 22,27 ****
--- 22,28 ----
#include "access/sysattr.h"
#include "access/transam.h"
#include "catalog/catalog.h"
+ #include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
*************** get_relation_info(PlannerInfo *root, Oid
*** 347,352 ****
--- 348,362 ----
rel->indexlist = indexinfos;
+ /* Get server oid for further planning, if this is a foreign table. */
+ if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+ {
+ ForeignTable *table;
+
+ table = GetForeignTable(relationObjectId);
+ rel->serverid = table->serverid;
+ }
+
heap_close(relation, NoLock);
/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 1df727d..2228ec4 100644
*** a/src/backend/optimizer/util/relnode.c
--- b/src/backend/optimizer/util/relnode.c
*************** build_simple_rel(PlannerInfo *root, int
*** 116,121 ****
--- 116,122 ----
rel->baserestrictcost.per_tuple = 0;
rel->joininfo = NIL;
rel->has_eclass_joins = false;
+ rel->serverid = InvalidOid;
rel->index_outer_relids = NULL;
rel->index_inner_paths = NIL;
*************** build_join_rel(PlannerInfo *root,
*** 369,374 ****
--- 370,376 ----
joinrel->baserestrictcost.per_tuple = 0;
joinrel->joininfo = NIL;
joinrel->has_eclass_joins = false;
+ joinrel->serverid = InvalidOid;
joinrel->index_outer_relids = NULL;
joinrel->index_inner_paths = NIL;
*************** build_join_rel(PlannerInfo *root,
*** 441,446 ****
--- 443,456 ----
lappend(root->join_rel_level[root->join_cur_level], joinrel);
}
+ /*
+ * If both outer and inner are from one oreign server, maybe this join can
+ * be pushed down, so remember the oid of the foreign server in this
+ * relation.
+ */
+ if (outer_rel->serverid == inner_rel->serverid)
+ joinrel->serverid = outer_rel->serverid;
+
return joinrel;
}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 072d50c..0fb6b60 100644
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
*************** set_plan_disabling_options(const char *a
*** 3168,3173 ****
--- 3168,3176 ----
case 'h': /* hashjoin */
tmp = "enable_hashjoin";
break;
+ case 'f': /* foreignjoin */
+ tmp = "enable_foreignjoin";
+ break;
}
if (tmp)
{
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index a71729c..013870a 100644
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
*************** static struct config_bool ConfigureNames
*** 756,761 ****
--- 756,770 ----
NULL, NULL, NULL
},
{
+ {"enable_foreignjoin", PGC_USERSET, QUERY_TUNING_METHOD,
+ gettext_noop("Enables the planner's use of foreign join plans."),
+ NULL
+ },
+ &enable_foreignjoin,
+ true,
+ NULL, NULL, NULL
+ },
+ {
{"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
gettext_noop("Enables genetic query optimization."),
gettext_noop("This algorithm attempts to do planning without "
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index a18f14a..34362cd 100644
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 240,245 ****
--- 240,246 ----
# - Planner Method Configuration -
#enable_bitmapscan = on
+ #enable_foreignjoin = on
#enable_hashagg = on
#enable_hashjoin = on
#enable_indexscan = on
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index bdd499b..e4d3426 100644
*** a/src/include/executor/executor.h
--- b/src/include/executor/executor.h
*************** extern void ExecFreeExprContext(PlanStat
*** 332,337 ****
--- 332,338 ----
extern TupleDesc ExecGetScanType(ScanState *scanstate);
extern void ExecAssignScanType(ScanState *scanstate, TupleDesc tupDesc);
extern void ExecAssignScanTypeFromOuterPlan(ScanState *scanstate);
+ extern void ExecAssignScanTypeFromTL(ScanState *scanstate);
extern bool ExecRelationIsTargetRelation(EState *estate, Index scanrelid);
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 3378ba9..e535e12 100644
*** a/src/include/foreign/fdwapi.h
--- b/src/include/foreign/fdwapi.h
*************** typedef void (*ReScanForeignScan_functio
*** 68,73 ****
--- 68,83 ----
typedef void (*EndForeignScan_function) (ForeignScanState *node);
+ typedef FdwPlan *(*PlanForeignJoin_function) (Oid serverid,
+ PlannerInfo *root,
+ RelOptInfo *joinrel,
+ JoinType jointype,
+ SpecialJoinInfo *sjinfo,
+ Path *outer_path,
+ Path *inner_path,
+ List *restrict_clauses,
+ List *pathkeys);
+
/*
* FdwRoutine is the struct returned by a foreign-data wrapper's handler
*************** typedef struct FdwRoutine
*** 88,93 ****
--- 98,106 ----
IterateForeignScan_function IterateForeignScan;
ReScanForeignScan_function ReScanForeignScan;
EndForeignScan_function EndForeignScan;
+
+ /* functions below are optional */
+ PlanForeignJoin_function PlanForeignJoin;
} FdwRoutine;
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index ecf62b3..2110ee4 100644
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 66,71 ****
--- 66,72 ----
T_NestLoop,
T_MergeJoin,
T_HashJoin,
+ T_ForeignJoin,
T_Material,
T_Sort,
T_Group,
*************** typedef enum NodeTag
*** 219,224 ****
--- 220,226 ----
T_NestPath,
T_MergePath,
T_HashPath,
+ T_ForeignJoinPath,
T_TidPath,
T_ForeignPath,
T_AppendPath,
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 535eca7..853f827 100644
*** a/src/include/nodes/plannodes.h
--- b/src/include/nodes/plannodes.h
*************** typedef struct WorkTableScan
*** 440,445 ****
--- 440,446 ----
typedef struct ForeignScan
{
Scan scan;
+ Oid serverid; /* OID of foreign server */
bool fsSystemCol; /* true if any "system column" is needed */
/* use struct pointer to avoid including fdwapi.h here */
struct FdwPlan *fdwplan;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index ecbbc1c..d3309ac 100644
*** a/src/include/nodes/relation.h
--- b/src/include/nodes/relation.h
*************** typedef struct RelOptInfo
*** 414,419 ****
--- 414,420 ----
List *joininfo; /* RestrictInfo structures for join clauses
* involving this rel */
bool has_eclass_joins; /* T means joininfo is incomplete */
+ Oid serverid; /* foriegn server, if foreign scan/join */
/* cached info about inner indexscan paths for relation: */
Relids index_outer_relids; /* other relids in indexable join
*************** typedef struct HashPath
*** 939,944 ****
--- 940,955 ----
} HashPath;
/*
+ * A foreignjoin path has no additional field.
+ */
+ typedef struct ForeignJoinPath
+ {
+ JoinPath jpath;
+ /* use struct pointer to avoid including fdwapi.h here */
+ struct FdwPlan *fdwplan; /* FDW-specific information */
+ } ForeignJoinPath;
+
+ /*
* Restriction clause info.
*
* We create one of these for each AND sub-clause of a restriction condition
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 604df33..58cc64a 100644
*** a/src/include/optimizer/cost.h
--- b/src/include/optimizer/cost.h
*************** extern bool enable_nestloop;
*** 60,65 ****
--- 60,66 ----
extern bool enable_material;
extern bool enable_mergejoin;
extern bool enable_hashjoin;
+ extern bool enable_foreignjoin;
extern int constraint_exclusion;
extern double clamp_row_est(double nrows);
*************** extern void cost_mergejoin(MergePath *pa
*** 112,117 ****
--- 113,122 ----
SpecialJoinInfo *sjinfo);
extern void cost_hashjoin(HashPath *path, PlannerInfo *root,
SpecialJoinInfo *sjinfo);
+ /*
+ * cost_foreignjoin() is not defined here because the costs of a foreign join
+ * is estimated by each FDW via PlanForeignJoin.
+ */
extern void cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan);
extern void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root);
extern void cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root);
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index ee02732..f824f91 100644
*** a/src/include/optimizer/pathnode.h
--- b/src/include/optimizer/pathnode.h
*************** extern HashPath *create_hashjoin_path(Pl
*** 93,98 ****
--- 93,107 ----
List *restrict_clauses,
List *hashclauses);
+ extern ForeignJoinPath *create_foreignjoin_path(PlannerInfo *root,
+ RelOptInfo *joinrel,
+ JoinType jointype,
+ SpecialJoinInfo *sjinfo,
+ Path *outer_path,
+ Path *inner_path,
+ List *restrict_clauses,
+ List *pathkeys);
+
/*
* prototypes for relnode.c
*/
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 51d561b..9b8eccb 100644
*** a/src/test/regress/expected/rangefuncs.out
--- b/src/test/regress/expected/rangefuncs.out
***************
*** 1,17 ****
SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%';
! name | setting
! -------------------+---------
! enable_bitmapscan | on
! enable_hashagg | on
! enable_hashjoin | on
! enable_indexscan | on
! enable_material | on
! enable_mergejoin | on
! enable_nestloop | on
! enable_seqscan | on
! enable_sort | on
! enable_tidscan | on
! (10 rows)
CREATE TABLE foo2(fooid int, f2 int);
INSERT INTO foo2 VALUES(1, 11);
--- 1,18 ----
SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%';
! name | setting
! --------------------+---------
! enable_bitmapscan | on
! enable_foreignjoin | on
! enable_hashagg | on
! enable_hashjoin | on
! enable_indexscan | on
! enable_material | on
! enable_mergejoin | on
! enable_nestloop | on
! enable_seqscan | on
! enable_sort | on
! enable_tidscan | on
! (11 rows)
CREATE TABLE foo2(fooid int, f2 int);
INSERT INTO foo2 VALUES(1, 11);