 doc/src/sgml/fdwhandler.sgml            | 26 ++++++++++++++++++++++++-
 src/backend/commands/explain.c          | 23 ++++++++++++++++++++++
 src/backend/executor/execScan.c         | 34 +++++++++++++++++++++++++++++----
 src/backend/executor/nodeForeignscan.c  | 13 +++++++++++++
 src/backend/nodes/copyfuncs.c           |  1 +
 src/backend/nodes/nodeFuncs.c           |  7 +++++++
 src/backend/nodes/outfuncs.c            |  2 ++
 src/backend/nodes/readfuncs.c           |  1 +
 src/backend/optimizer/plan/createplan.c | 13 ++++++++++++-
 src/backend/optimizer/plan/setrefs.c    | 14 ++++++++++++++
 src/backend/optimizer/plan/subselect.c  | 11 +++++++++++
 src/include/foreign/fdwapi.h            |  7 ++++++-
 src/include/nodes/execnodes.h           |  1 +
 src/include/nodes/plannodes.h           |  1 +
 src/include/nodes/relation.h            |  1 +
 15 files changed, 148 insertions(+), 7 deletions(-)

diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 1533a6b..13bfad9 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -168,7 +168,8 @@ GetForeignPlan (PlannerInfo *root,
                 Oid foreigntableid,
                 ForeignPath *best_path,
                 List *tlist,
-                List *scan_clauses);
+                List *scan_clauses,
+                List *fdw_plans)
 </programlisting>
 
      Create a <structname>ForeignScan</> plan node from the selected foreign
@@ -259,6 +260,29 @@ IterateForeignScan (ForeignScanState *node);
 
     <para>
 <programlisting>
+bool
+RecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot);
+</programlisting>
+     Rechecks visibility of the EPQ tuples according to the qualifiers
+     pushed-down.
+     This callback is optional, if this <structname>ForeignScanState</>
+     runs on a base foreign table. <structfield>fdw_recheck_quals</>
+     can be used instead to recheck on the target EPQ tuple by the backend.
+    </para>
+    <para>
+     On the other hands, if <literal>scanrelid</> equals zero thus it
+     represents a join sub-tree of foreign tables, this callback is
+     expected to reconstruct a joined tuple using the primitive EPQ
+     tuples and fill up the supplied <literal>slot</> according to
+     the <structfield>fdw_scan_tlist</> definition.
+     Also, this callback can or must recheck scan qualifiers and join
+     conditions which are pushed down. Especially, it needs special
+     handling if not simple inner-join, instead of the backend support
+     by <structfield>fdw_recheck_quals</>.
+    </para>
+
+    <para>
+<programlisting>
 void
 ReScanForeignScan (ForeignScanState *node);
 </programlisting>
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 7fb8a14..60522ef 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -114,6 +114,8 @@ static void ExplainMemberNodes(List *plans, PlanState **planstates,
 				   List *ancestors, ExplainState *es);
 static void ExplainSubPlans(List *plans, List *ancestors,
 				const char *relationship, ExplainState *es);
+static void ExplainForeignChildren(ForeignScanState *fss,
+								   List *ancestors, ExplainState *es);
 static void ExplainCustomChildren(CustomScanState *css,
 					  List *ancestors, ExplainState *es);
 static void ExplainProperty(const char *qlabel, const char *value,
@@ -1547,6 +1549,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		IsA(plan, BitmapAnd) ||
 		IsA(plan, BitmapOr) ||
 		IsA(plan, SubqueryScan) ||
+		(IsA(planstate, ForeignScanState) &&
+		 ((ForeignScanState *) planstate)->fdw_ps != NIL) ||
 		(IsA(planstate, CustomScanState) &&
 		 ((CustomScanState *) planstate)->custom_ps != NIL) ||
 		planstate->subPlan;
@@ -1603,6 +1607,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
 						"Subquery", NULL, es);
 			break;
+		case T_ForeignScan:
+			ExplainForeignChildren((ForeignScanState *) planstate,
+								   ancestors, es);
+			break;
 		case T_CustomScan:
 			ExplainCustomChildren((CustomScanState *) planstate,
 								  ancestors, es);
@@ -2643,6 +2651,21 @@ ExplainSubPlans(List *plans, List *ancestors,
 }
 
 /*
+ * Explain a list of children of a ForeignScan.
+ */
+static void
+ExplainForeignChildren(ForeignScanState *fss,
+					   List *ancestors, ExplainState *es)
+{
+	ListCell   *cell;
+	const char *label =
+		(list_length(fss->fdw_ps) != 1 ? "children" : "child");
+
+	foreach(cell, fss->fdw_ps)
+		ExplainNode((PlanState *) lfirst(cell), ancestors, label, NULL, es);
+}
+
+/*
  * Explain a list of children of a CustomScan.
  */
 static void
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index a96e826..b472bf7 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -49,8 +49,16 @@ ExecScanFetch(ScanState *node,
 		 */
 		Index		scanrelid = ((Scan *) node->ps.plan)->scanrelid;
 
-		Assert(scanrelid > 0);
-		if (estate->es_epqTupleSet[scanrelid - 1])
+		if (scanrelid == 0)
+		{
+			TupleTableSlot *slot = node->ss_ScanTupleSlot;
+
+			/* Check if it meets the access-method conditions */
+			if (!(*recheckMtd) (node, slot))
+				ExecClearTuple(slot);	/* would not be returned by scan */
+			return slot;
+		}
+		else if (estate->es_epqTupleSet[scanrelid - 1])
 		{
 			TupleTableSlot *slot = node->ss_ScanTupleSlot;
 
@@ -347,8 +355,26 @@ ExecScanReScan(ScanState *node)
 	{
 		Index		scanrelid = ((Scan *) node->ps.plan)->scanrelid;
 
-		Assert(scanrelid > 0);
+		if (scanrelid > 0)
+			estate->es_epqScanDone[scanrelid - 1] = false;
+		else
+		{
+			Bitmapset  *relids;
+			int			rtindex = -1;
+
+			if (IsA(node->ps.plan, ForeignScan))
+				relids = ((ForeignScan *) node->ps.plan)->fs_relids;
+			else if (IsA(node->ps.plan, CustomScan))
+				relids = ((CustomScan *) node->ps.plan)->custom_relids;
+			else
+				elog(ERROR, "unexpected scan node: %d",
+					 (int)nodeTag(node->ps.plan));
 
-		estate->es_epqScanDone[scanrelid - 1] = false;
+			while ((rtindex = bms_next_member(relids, rtindex)) >= 0)
+			{
+				Assert(rtindex > 0);
+				estate->es_epqScanDone[rtindex - 1] = false;
+			}
+		}
 	}
 }
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 6165e4a..1344c32 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -73,6 +73,7 @@ ForeignNext(ForeignScanState *node)
 static bool
 ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 {
+	FdwRoutine	*fdwroutine = node->fdwroutine;
 	ExprContext *econtext;
 
 	/*
@@ -85,6 +86,18 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
+	/*
+	 * FDW driver has to recheck visibility of EPQ tuple towards
+	 * the scan qualifiers once it gets pushed down.
+	 * In addition, if this node represents a join sub-tree, not
+	 * a scan, FDW driver is also responsible to reconstruct
+	 * a joined tuple according to the primitive EPQ tuples.
+	 */
+	if (fdwroutine->RecheckForeignScan)
+	{
+		if (!fdwroutine->RecheckForeignScan(node, slot))
+			return false;
+	}
 	return ExecQual(node->fdw_recheck_quals, econtext, false);
 }
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c176ff9..21df5ce 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -645,6 +645,7 @@ _copyForeignScan(const ForeignScan *from)
 	 * copy remainder of node
 	 */
 	COPY_SCALAR_FIELD(fs_server);
+	COPY_NODE_FIELD(fdw_plans);
 	COPY_NODE_FIELD(fdw_exprs);
 	COPY_NODE_FIELD(fdw_private);
 	COPY_NODE_FIELD(fdw_scan_tlist);
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index a11cb9f..99e03a9 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3485,6 +3485,13 @@ planstate_tree_walker(PlanState *planstate, bool (*walker) (), void *context)
 			if (walker(((SubqueryScanState *) planstate)->subplan, context))
 				return true;
 			break;
+		case T_ForeignScan:
+			foreach (lc, ((ForeignScanState *) planstate)->fdw_ps)
+			{
+				if (walker((PlanState *) lfirst(lc), context))
+					return true;
+			}
+			break;
 		case T_CustomScan:
 			foreach (lc, ((CustomScanState *) planstate)->custom_ps)
 			{
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3e75cd1..fafd6b3 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -591,6 +591,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
 	_outScanInfo(str, (const Scan *) node);
 
 	WRITE_OID_FIELD(fs_server);
+	WRITE_NODE_FIELD(fdw_plans);
 	WRITE_NODE_FIELD(fdw_exprs);
 	WRITE_NODE_FIELD(fdw_private);
 	WRITE_NODE_FIELD(fdw_scan_tlist);
@@ -1680,6 +1681,7 @@ _outForeignPath(StringInfo str, const ForeignPath *node)
 
 	_outPathInfo(str, (const Path *) node);
 
+	WRITE_NODE_FIELD(fdw_paths);
 	WRITE_NODE_FIELD(fdw_private);
 }
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 94ba6dc..4b54016 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1795,6 +1795,7 @@ _readForeignScan(void)
 	ReadCommonScan(&local_node->scan);
 
 	READ_OID_FIELD(fs_server);
+	READ_NODE_FIELD(fdw_plans);
 	READ_NODE_FIELD(fdw_exprs);
 	READ_NODE_FIELD(fdw_private);
 	READ_NODE_FIELD(fdw_scan_tlist);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 791b64e..9dc445e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2095,11 +2095,20 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 	Index		scan_relid = rel->relid;
 	Oid			rel_oid = InvalidOid;
 	Bitmapset  *attrs_used = NULL;
+	List	   *fdw_plans = NIL;
 	ListCell   *lc;
 	int			i;
 
 	Assert(rel->fdwroutine != NULL);
 
+	/* Recursively transform child paths. */
+	foreach (lc, best_path->fdw_paths)
+	{
+		Plan   *plan = create_plan_recurse(root, (Path *) lfirst(lc));
+
+		fdw_plans = lappend(fdw_plans, plan);
+	}
+
 	/*
 	 * If we're scanning a base relation, fetch its OID.  (Irrelevant if
 	 * scanning a join relation.)
@@ -2129,7 +2138,9 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 	 */
 	scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid,
 												best_path,
-												tlist, scan_clauses);
+												tlist,
+												scan_clauses,
+												fdw_plans);
 
 	/* Copy cost data from Path to Plan; no need to make FDW do this */
 	copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 48d6e6f..7e4d092 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1102,6 +1102,8 @@ set_foreignscan_references(PlannerInfo *root,
 						   ForeignScan *fscan,
 						   int rtoffset)
 {
+	ListCell   *lc;
+
 	/* Adjust scanrelid if it's valid */
 	if (fscan->scan.scanrelid > 0)
 		fscan->scan.scanrelid += rtoffset;
@@ -1129,6 +1131,12 @@ set_foreignscan_references(PlannerInfo *root,
 						   itlist,
 						   INDEX_VAR,
 						   rtoffset);
+		fscan->fdw_recheck_quals = (List *)
+			fix_upper_expr(root,
+						   (Node *) fscan->fdw_recheck_quals,
+						   itlist,
+						   INDEX_VAR,
+						   rtoffset);
 		pfree(itlist);
 		/* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
 		fscan->fdw_scan_tlist =
@@ -1147,6 +1155,12 @@ set_foreignscan_references(PlannerInfo *root,
 			fix_scan_list(root, fscan->fdw_recheck_quals, rtoffset);
 	}
 
+	/* Adjust child plan-nodes recursively, if needed */
+	foreach (lc, fscan->fdw_plans)
+	{
+		lfirst(lc) = set_plan_refs(root, (Plan *) lfirst(lc), rtoffset);
+	}
+
 	/* Adjust fs_relids if needed */
 	if (rtoffset > 0)
 	{
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 82414d4..7b50455 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2396,6 +2396,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 		case T_ForeignScan:
 			{
 				ForeignScan *fscan = (ForeignScan *) plan;
+				ListCell	*lc;
 
 				finalize_primnode((Node *) fscan->fdw_exprs,
 								  &context);
@@ -2405,6 +2406,16 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 				/* We assume fdw_scan_tlist cannot contain Params */
 				context.paramids = bms_add_members(context.paramids,
 												   scan_params);
+				/* child nodes if any */
+				foreach (lc, fscan->fdw_plans)
+				{
+					context.paramids =
+						bms_add_members(context.paramids,
+										finalize_plan(root,
+													  (Plan *) lfirst(lc),
+													  valid_params,
+													  scan_params));
+				}
 			}
 			break;
 
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 69b48b4..4a41351 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -36,13 +36,17 @@ typedef ForeignScan *(*GetForeignPlan_function) (PlannerInfo *root,
 														  Oid foreigntableid,
 													  ForeignPath *best_path,
 															 List *tlist,
-														 List *scan_clauses);
+												 List *scan_clauses,
+												 List *fdw_plans);
 
 typedef void (*BeginForeignScan_function) (ForeignScanState *node,
 													   int eflags);
 
 typedef TupleTableSlot *(*IterateForeignScan_function) (ForeignScanState *node);
 
+typedef bool (*RecheckForeignScan_function) (ForeignScanState *node,
+											 TupleTableSlot *slot);
+
 typedef void (*ReScanForeignScan_function) (ForeignScanState *node);
 
 typedef void (*EndForeignScan_function) (ForeignScanState *node);
@@ -138,6 +142,7 @@ typedef struct FdwRoutine
 	GetForeignPlan_function GetForeignPlan;
 	BeginForeignScan_function BeginForeignScan;
 	IterateForeignScan_function IterateForeignScan;
+	RecheckForeignScan_function RecheckForeignScan;
 	ReScanForeignScan_function ReScanForeignScan;
 	EndForeignScan_function EndForeignScan;
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 58ec889..c5c89de 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1582,6 +1582,7 @@ typedef struct ForeignScanState
 	List	   *fdw_recheck_quals;	/* original quals not in ss.ps.qual */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
+	List	   *fdw_ps;			/* list of child PlanState nodes, if any */
 	void	   *fdw_state;		/* foreign-data wrapper can keep state here */
 } ForeignScanState;
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 6b28c8e..bd73371 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -526,6 +526,7 @@ typedef struct ForeignScan
 {
 	Scan		scan;
 	Oid			fs_server;		/* OID of foreign server */
+	List	   *fdw_plans;		/* list of Plan nodes, if any */
 	List	   *fdw_exprs;		/* expressions that FDW may evaluate */
 	List	   *fdw_private;	/* private data for FDW */
 	List	   *fdw_scan_tlist; /* optional tlist describing scan tuple */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6cf2e24..707927c 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -907,6 +907,7 @@ typedef struct TidPath
 typedef struct ForeignPath
 {
 	Path		path;
+	List	   *fdw_paths;
 	List	   *fdw_private;
 } ForeignPath;
 
