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);