 doc/src/sgml/fdwhandler.sgml            | 24 +++++++++++++++++++++++-
 src/backend/commands/explain.c          | 23 +++++++++++++++++++++++
 src/backend/executor/execScan.c         | 12 ++++++++++--
 src/backend/executor/nodeForeignscan.c  | 15 +++++++++++++++
 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    |  8 ++++++++
 src/backend/optimizer/plan/subselect.c  | 24 ++++++++++++++++++++----
 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, 131 insertions(+), 9 deletions(-)

diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 1dac7ad..317c21c 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,27 @@ IterateForeignScan (ForeignScanState *node);
 
     <para>
 <programlisting>
+bool
+RecheckForeignScan (ForeignScanState *node, TupleTableSlot *slot);
+</programlisting>
+     Rechecks visibility of the EPQ tuples according to the latest status.
+     Once row-level update or lock contention get detected, EPQ mechanism
+     reloads the target rows using <function>RefetchForeignRow</>, then
+     tries to recheck whether the latest row is still visible.
+    </para>
+    <para>
+     When <structname>ForeignScanState</> represents base relation,
+     the supplied <literal>slot</> is expected to have the latest row
+     of the target relation.
+     Elsewhere, if <literal>scanrelid</> equals zero thus it represents
+     multiple joined relations, the callback is expected to fill up the
+     supplied <literal>slot</> accoding to the <structfield>fdw_scan_tlist</>
+     definition. It should know which EPQ tuples are the source of its
+     result tuple.
+    </para>
+
+    <para>
+<programlisting>
 void
 ReScanForeignScan (ForeignScanState *node);
 </programlisting>
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index f0d9e94..1504069 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,
@@ -1528,6 +1530,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;
@@ -1584,6 +1588,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);
@@ -2624,6 +2632,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..c88da92 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;
 
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index bb28a73..6c3d920 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -72,6 +72,21 @@ ForeignNext(ForeignScanState *node)
 static bool
 ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 {
+	FdwRoutine	   *fdwroutine = node->fdwroutine;
+
+	/*
+	 * This FDW callback have two tasks. (1) If this ForeignScanState
+	 * represents an external join (thus scanrelid==0), it need to
+	 * construct a tuple according to TupleDesc of the slot; that is
+	 * initialized according to the fdw_scan_tlist. (2) If this node
+	 * has any qualifiers not to be executed locally, it has to apply
+	 * visibility checks by the qualifier (because ExecQual on ExecScan
+	 * runs towards node->scan.plan.qual, not on the qualifier pushed-
+	 * down).
+	 */
+	if (fdwroutine->RecheckForeignScan)
+		return fdwroutine->RecheckForeignScan(node, slot);
+
 	/* There are no access-method-specific conditions to recheck. */
 	return true;
 }
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 62355aa..989833e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -622,6 +622,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 c91273c..438d1d4 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -579,6 +579,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);
@@ -1667,6 +1668,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 08519ed..5b274da 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 404c6f5..a915cb6 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -2059,11 +2059,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.)
@@ -2093,7 +2102,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 daeb584..b4972cd 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -1093,6 +1093,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;
@@ -1136,6 +1138,12 @@ set_foreignscan_references(PlannerInfo *root,
 			fix_scan_list(root, fscan->fdw_exprs, 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 d0bc412..cdc8cde 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2394,10 +2394,26 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_ForeignScan:
-			finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs,
-							  &context);
-			/* We assume fdw_scan_tlist cannot contain Params */
-			context.paramids = bms_add_members(context.paramids, scan_params);
+			{
+				ForeignScan	   *fscan = (ForeignScan *) plan;
+				ListCell	   *lc;
+
+				finalize_primnode((Node *) fscan->fdw_exprs, &context);
+				/* 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;
 
 		case T_CustomScan:
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 4ae2f3e..fdab372 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1581,6 +1581,7 @@ typedef struct ForeignScanState
 	ScanState	ss;				/* its first field is NodeTag */
 	/* 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 cc259f1..e253678 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -520,6 +520,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 79bed33..809bf60 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -905,6 +905,7 @@ typedef struct TidPath
 typedef struct ForeignPath
 {
 	Path		path;
+	List	   *fdw_paths;
 	List	   *fdw_private;
 } ForeignPath;
 
