*** a/contrib/file_fdw/file_fdw.c --- b/contrib/file_fdw/file_fdw.c *************** *** 525,530 **** fileGetForeignPaths(PlannerInfo *root, --- 525,531 ---- total_cost, NIL, /* no pathkeys */ NULL, /* no outer rel either */ + NULL, /* no alternative path */ coptions)); /* *************** *** 563,569 **** fileGetForeignPlan(PlannerInfo *root, scan_relid, NIL, /* no expressions to evaluate */ best_path->fdw_private, ! NIL /* no custom tlist */ ); } /* --- 564,571 ---- scan_relid, NIL, /* no expressions to evaluate */ best_path->fdw_private, ! NIL, /* no custom tlist */ ! NIL /* no remote quals */ ); } /* *** a/contrib/postgres_fdw/postgres_fdw.c --- b/contrib/postgres_fdw/postgres_fdw.c *************** *** 560,565 **** postgresGetForeignPaths(PlannerInfo *root, --- 560,566 ---- fpinfo->total_cost, NIL, /* no pathkeys */ NULL, /* no outer rel either */ + NULL, /* no alternative path */ NIL); /* no fdw_private list */ add_path(baserel, (Path *) path); *************** *** 727,732 **** postgresGetForeignPaths(PlannerInfo *root, --- 728,734 ---- total_cost, NIL, /* no pathkeys */ param_info->ppi_req_outer, + NULL, /* no alternative path */ NIL); /* no fdw_private list */ add_path(baserel, (Path *) path); } *************** *** 748,753 **** postgresGetForeignPlan(PlannerInfo *root, --- 750,756 ---- Index scan_relid = baserel->relid; List *fdw_private; List *remote_conds = NIL; + List *remote_exprs = NIL; List *local_exprs = NIL; List *params_list = NIL; List *retrieved_attrs; *************** *** 769,776 **** postgresGetForeignPlan(PlannerInfo *root, * * This code must match "extract_actual_clauses(scan_clauses, false)" * except for the additional decision about remote versus local execution. ! * Note however that we only strip the RestrictInfo nodes from the ! * local_exprs list, since appendWhereClause expects a list of * RestrictInfos. */ foreach(lc, scan_clauses) --- 772,779 ---- * * This code must match "extract_actual_clauses(scan_clauses, false)" * except for the additional decision about remote versus local execution. ! * Note however that we don't strip the RestrictInfo nodes from the ! * remote_conds list, since appendWhereClause expects a list of * RestrictInfos. */ foreach(lc, scan_clauses) *************** *** 784,794 **** postgresGetForeignPlan(PlannerInfo *root, --- 787,803 ---- continue; if (list_member_ptr(fpinfo->remote_conds, rinfo)) + { remote_conds = lappend(remote_conds, rinfo); + remote_exprs = lappend(remote_exprs, rinfo->clause); + } else if (list_member_ptr(fpinfo->local_conds, rinfo)) local_exprs = lappend(local_exprs, rinfo->clause); else if (is_foreign_expr(root, baserel, rinfo->clause)) + { remote_conds = lappend(remote_conds, rinfo); + remote_exprs = lappend(remote_exprs, rinfo->clause); + } else local_exprs = lappend(local_exprs, rinfo->clause); } *************** *** 874,880 **** postgresGetForeignPlan(PlannerInfo *root, scan_relid, params_list, fdw_private, ! NIL /* no custom tlist */ ); } /* --- 883,890 ---- scan_relid, params_list, fdw_private, ! NIL, /* no custom tlist */ ! remote_exprs); } /* *** a/src/backend/executor/nodeForeignscan.c --- b/src/backend/executor/nodeForeignscan.c *************** *** 72,79 **** ForeignNext(ForeignScanState *node) static bool ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot) { ! /* There are no access-method-specific conditions to recheck. */ ! return true; } /* ---------------------------------------------------------------- --- 72,90 ---- static bool ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot) { ! ExprContext *econtext; ! ! /* ! * extract necessary information from foreign scan node ! */ ! econtext = node->ss.ps.ps_ExprContext; ! ! /* Does the tuple meet the remotequals condition? */ ! econtext->ecxt_scantuple = slot; ! ! ResetExprContext(econtext); ! ! return ExecQual(node->fss_remotequals, econtext, false); } /* ---------------------------------------------------------------- *************** *** 88,93 **** ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot) --- 99,122 ---- TupleTableSlot * ExecForeignScan(ForeignScanState *node) { + EState *estate = node->ss.ps.state; + + if (estate->es_epqTuple != NULL) + { + /* + * We are inside an EvalPlanQual recheck. If foreign join, get next + * tuple from subplan. + */ + Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid; + + if (scanrelid == 0) + { + PlanState *outerPlan = outerPlanState(node); + + return ExecProcNode(outerPlan); + } + } + return ExecScan((ScanState *) node, (ExecScanAccessMtd) ForeignNext, (ExecScanRecheckMtd) ForeignRecheck); *************** *** 117,122 **** ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) --- 146,166 ---- scanstate->ss.ps.plan = (Plan *) node; scanstate->ss.ps.state = estate; + if (estate->es_epqTuple != NULL) + { + /* + * We are inside an EvalPlanQual recheck. If foreign join, initialize + * subplan. + */ + if (scanrelid == 0) + { + Plan *subplan = node->fs_subplan; + + outerPlanState(scanstate) = ExecInitNode(subplan, estate, eflags); + return scanstate; + } + } + /* * Miscellaneous initialization * *************** *** 135,140 **** ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) --- 179,187 ---- scanstate->ss.ps.qual = (List *) ExecInitExpr((Expr *) node->scan.plan.qual, (PlanState *) scanstate); + scanstate->fss_remotequals = (List *) + ExecInitExpr((Expr *) node->fs_remotequals, + (PlanState *) scanstate); /* * tuple table initialization *************** *** 207,212 **** ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) --- 254,276 ---- void ExecEndForeignScan(ForeignScanState *node) { + EState *estate = node->ss.ps.state; + + if (estate->es_epqTuple != NULL) + { + /* + * We are inside an EvalPlanQual recheck. If foreign join, close down + * subplan. + */ + Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid; + + if (scanrelid == 0) + { + ExecEndNode(outerPlanState(node)); + return; + } + } + /* Let the FDW shut down */ node->fdwroutine->EndForeignScan(node); *************** *** 231,236 **** ExecEndForeignScan(ForeignScanState *node) --- 295,324 ---- void ExecReScanForeignScan(ForeignScanState *node) { + EState *estate = node->ss.ps.state; + + if (estate->es_epqTuple != NULL) + { + /* + * We are inside an EvalPlanQual recheck. If foreign join, re-scan + * subplan. + */ + Index scanrelid = ((Scan *) node->ss.ps.plan)->scanrelid; + + if (scanrelid == 0) + { + PlanState *outerPlan = outerPlanState(node); + + /* + * If outerPlan->chgParam is not null then plan will be + * automatically re-scanned by first ExecProcNode. + */ + if (outerPlan->chgParam == NULL) + ExecReScan(outerPlan); + return; + } + } + node->fdwroutine->ReScanForeignScan(node); ExecScanReScan(&node->ss); *** a/src/backend/nodes/copyfuncs.c --- b/src/backend/nodes/copyfuncs.c *************** *** 625,630 **** _copyForeignScan(const ForeignScan *from) --- 625,632 ---- COPY_NODE_FIELD(fdw_private); COPY_NODE_FIELD(fdw_scan_tlist); COPY_BITMAPSET_FIELD(fs_relids); + COPY_NODE_FIELD(fs_subplan); + COPY_NODE_FIELD(fs_remotequals); COPY_SCALAR_FIELD(fsSystemCol); return newnode; *** a/src/backend/nodes/outfuncs.c --- b/src/backend/nodes/outfuncs.c *************** *** 580,585 **** _outForeignScan(StringInfo str, const ForeignScan *node) --- 580,587 ---- WRITE_NODE_FIELD(fdw_private); WRITE_NODE_FIELD(fdw_scan_tlist); WRITE_BITMAPSET_FIELD(fs_relids); + WRITE_NODE_FIELD(fs_subplan); + WRITE_NODE_FIELD(fs_remotequals); WRITE_BOOL_FIELD(fsSystemCol); } *** a/src/backend/optimizer/plan/createplan.c --- b/src/backend/optimizer/plan/createplan.c *************** *** 2117,2125 **** create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path, --- 2117,2134 ---- replace_nestloop_params(root, (Node *) scan_plan->scan.plan.qual); scan_plan->fdw_exprs = (List *) replace_nestloop_params(root, (Node *) scan_plan->fdw_exprs); + scan_plan->fs_remotequals = (List *) + replace_nestloop_params(root, (Node *) scan_plan->fs_remotequals); } /* + * If we're scanning a join relation, generate the local join plan for + * EvalPlanQual support. (Irrelevant if scanning a base relation.) + */ + if (scan_relid == 0) + scan_plan->fs_subplan = create_plan_recurse(root, best_path->subpath); + + /* * Detect whether any system columns are requested from rel. This is a * bit of a kluge and might go away someday, so we intentionally leave it * out of the API presented to FDWs. *************** *** 3702,3708 **** make_foreignscan(List *qptlist, Index scanrelid, List *fdw_exprs, List *fdw_private, ! List *fdw_scan_tlist) { ForeignScan *node = makeNode(ForeignScan); Plan *plan = &node->scan.plan; --- 3711,3718 ---- Index scanrelid, List *fdw_exprs, List *fdw_private, ! List *fdw_scan_tlist, ! List *fs_remotequals) { ForeignScan *node = makeNode(ForeignScan); Plan *plan = &node->scan.plan; *************** *** 3720,3725 **** make_foreignscan(List *qptlist, --- 3730,3738 ---- node->fdw_scan_tlist = fdw_scan_tlist; /* fs_relids will be filled in by create_foreignscan_plan */ node->fs_relids = NULL; + /* fs_subplan will be filled in by create_foreignscan_plan */ + node->fs_subplan = NULL; + node->fs_remotequals = fs_remotequals; /* fsSystemCol will be filled in by create_foreignscan_plan */ node->fsSystemCol = false; *** a/src/backend/optimizer/plan/setrefs.c --- b/src/backend/optimizer/plan/setrefs.c *************** *** 1124,1129 **** set_foreignscan_references(PlannerInfo *root, --- 1124,1131 ---- /* fdw_scan_tlist itself just needs fix_scan_list() adjustments */ fscan->fdw_scan_tlist = fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset); + /* fs_subplan needs set_plan_refs() adjustments */ + set_plan_refs(root, fscan->fs_subplan, rtoffset); } else { *************** *** 1134,1139 **** set_foreignscan_references(PlannerInfo *root, --- 1136,1144 ---- fix_scan_list(root, fscan->scan.plan.qual, rtoffset); fscan->fdw_exprs = fix_scan_list(root, fscan->fdw_exprs, rtoffset); + /* fs_remotequals needs the same adjustments, too */ + fscan->fs_remotequals = + fix_scan_list(root, fscan->fs_remotequals, rtoffset); } /* Adjust fs_relids if needed */ *** a/src/backend/optimizer/plan/subselect.c --- b/src/backend/optimizer/plan/subselect.c *************** *** 2375,2380 **** finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, --- 2375,2391 ---- &context); /* We assume fdw_scan_tlist cannot contain Params */ context.paramids = bms_add_members(context.paramids, scan_params); + + /* + * We need not look at fs_remotequals, since it will have the same + * param references as fdw_exprs. Also we need not include params + * in fs_subplan. However, fs_subplan itself needs finalize_plan() + * processing. + */ + finalize_plan(root, + ((ForeignScan *) plan)->fs_subplan, + valid_params, + scan_params); break; case T_CustomScan: *** a/src/backend/optimizer/util/pathnode.c --- b/src/backend/optimizer/util/pathnode.c *************** *** 1462,1467 **** create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, --- 1462,1468 ---- double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, + Path *subpath, List *fdw_private) { ForeignPath *pathnode = makeNode(ForeignPath); *************** *** 1475,1480 **** create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, --- 1476,1482 ---- pathnode->path.total_cost = total_cost; pathnode->path.pathkeys = pathkeys; + pathnode->subpath = subpath; pathnode->fdw_private = fdw_private; return pathnode; *** a/src/include/nodes/execnodes.h --- b/src/include/nodes/execnodes.h *************** *** 1580,1585 **** typedef struct WorkTableScanState --- 1580,1586 ---- typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ + List *fss_remotequals; /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; void *fdw_state; /* foreign-data wrapper can keep state here */ *** a/src/include/nodes/plannodes.h --- b/src/include/nodes/plannodes.h *************** *** 522,527 **** typedef struct ForeignScan --- 522,529 ---- List *fdw_private; /* private data for FDW */ List *fdw_scan_tlist; /* optional tlist describing scan tuple */ Bitmapset *fs_relids; /* RTIs generated by this scan */ + Plan *fs_subplan; /* alternative Plan node if foreign join */ + List *fs_remotequals; /* list of remote quals if foreign table */ bool fsSystemCol; /* true if any "system column" is needed */ } ForeignScan; *** a/src/include/nodes/relation.h --- b/src/include/nodes/relation.h *************** *** 890,899 **** typedef struct TidPath --- 890,905 ---- * generally a good idea to use a representation that can be dumped by * nodeToString(), so that you can examine the structure during debugging * with tools like pprint(). + * + * If a ForeignPath node represents a remote join of foreign tables, subpath + * is a local join of those tables with equivalent results that will be used + * for EvalPlanQual testing. Note that subpath must have the same pathkeys + * and parameterization as that of the path's output. */ typedef struct ForeignPath { Path path; + Path *subpath; List *fdw_private; } ForeignPath; *** a/src/include/optimizer/pathnode.h --- b/src/include/optimizer/pathnode.h *************** *** 83,88 **** extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel, --- 83,89 ---- double rows, Cost startup_cost, Cost total_cost, List *pathkeys, Relids required_outer, + Path *subpath, List *fdw_private); extern Relids calc_nestloop_required_outer(Path *outer_path, Path *inner_path); *** a/src/include/optimizer/planmain.h --- b/src/include/optimizer/planmain.h *************** *** 45,51 **** extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual, Index scanrelid, Plan *subplan); extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual, Index scanrelid, List *fdw_exprs, List *fdw_private, ! List *fdw_scan_tlist); extern Append *make_append(List *appendplans, List *tlist); extern RecursiveUnion *make_recursive_union(List *tlist, Plan *lefttree, Plan *righttree, int wtParam, --- 45,51 ---- Index scanrelid, Plan *subplan); extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual, Index scanrelid, List *fdw_exprs, List *fdw_private, ! List *fdw_scan_tlist, List *fs_remotequals); extern Append *make_append(List *appendplans, List *tlist); extern RecursiveUnion *make_recursive_union(List *tlist, Plan *lefttree, Plan *righttree, int wtParam,