 doc/src/sgml/custom-scan.sgml           |  6 ++---
 doc/src/sgml/fdwhandler.sgml            |  2 +-
 src/backend/executor/nodeCustom.c       |  2 +-
 src/backend/executor/nodeForeignscan.c  |  2 +-
 src/backend/nodes/copyfuncs.c           |  4 +--
 src/backend/nodes/outfuncs.c            |  4 +--
 src/backend/optimizer/plan/createplan.c | 27 +++++++++++++++++---
 src/backend/optimizer/plan/setrefs.c    | 45 ++++++++++++++++++++++++++++-----
 src/backend/utils/adt/ruleutils.c       |  9 ++++---
 src/include/nodes/plannodes.h           | 10 ++++----
 src/include/optimizer/planmain.h        |  3 ++-
 11 files changed, 85 insertions(+), 29 deletions(-)

diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
index 9fd1db6..920010c 100644
--- a/doc/src/sgml/custom-scan.sgml
+++ b/doc/src/sgml/custom-scan.sgml
@@ -146,7 +146,7 @@ typedef struct CustomScan
     Scan      scan;
     uint32    flags;
     List     *custom_exprs;
-    List     *custom_ps_tlist;
+    List     *custom_scan_tlist;
     List     *custom_private;
     List     *custom_relids;
     const CustomScanMethods *methods;
@@ -176,9 +176,9 @@ typedef struct CustomScan
   <para>
    When a <structname>CustomScan</> scans a single relation,
    <structfield>scan.scanrelid</> should be the range table index of the table
-   to be scanned, and <structfield>custom_ps_tlist</> should be
+   to be scanned, and <structfield>custom_scan_tlist</> should be
    <literal>NULL</>.  When it replaces a join, <structfield>scan.scanrelid</>
-   should be zero, and <structfield>custom_ps_tlist</> should be a list of
+   should be zero, and <structfield>custom_scan_tlist</> should be a list of
    <structname>TargetEntry</> nodes.  This is necessary because, when a join
    is replaced, the target list cannot be constructed from the table
    definition.  At execution time, this list will be used to initialize the
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index 04f3c22..62c8b1e 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -627,7 +627,7 @@ GetForeignJoinPaths(PlannerInfo *root,
     <para>
      Since we cannot construct the slot descriptor for a remote join from
      the catalogs, the FDW should set the <structfield>scanrelid</> of the
-     <structname>ForeignScan</> to zero and <structfield>fdw_ps_tlist</>
+     <structname>ForeignScan</> to zero and <structfield>fdw_scan_tlist</>
      to an appropriate list of <structfield>TargetEntry</> nodes.
      Junk entries will be ignored, but can be present for the benefit of
      deparsing performed by <command>EXPLAIN</>.
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index db1b4f2..71f9352 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -65,7 +65,7 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	{
 		TupleDesc	ps_tupdesc;
 
-		ps_tupdesc = ExecCleanTypeFromTL(cscan->custom_ps_tlist, false);
+		ps_tupdesc = ExecCleanTypeFromTL(cscan->custom_scan_tlist, false);
 		ExecAssignScanType(&css->ss, ps_tupdesc);
 	}
 	css->ss.ps.ps_TupFromTlist = false;
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index fa553ac..4f1c783 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -156,7 +156,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	{
 		TupleDesc	ps_tupdesc;
 
-		ps_tupdesc = ExecCleanTypeFromTL(node->fdw_ps_tlist, false);
+		ps_tupdesc = ExecCleanTypeFromTL(node->fdw_scan_tlist, false);
 		ExecAssignScanType(&scanstate->ss, ps_tupdesc);
 	}
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 805045d..07baa0e 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -594,7 +594,7 @@ _copyForeignScan(const ForeignScan *from)
 	 */
 	COPY_SCALAR_FIELD(fdw_handler);
 	COPY_NODE_FIELD(fdw_exprs);
-	COPY_NODE_FIELD(fdw_ps_tlist);
+	COPY_NODE_FIELD(fdw_scan_tlist);
 	COPY_NODE_FIELD(fdw_private);
 	COPY_BITMAPSET_FIELD(fdw_relids);
 	COPY_SCALAR_FIELD(fsSystemCol);
@@ -620,7 +620,7 @@ _copyCustomScan(const CustomScan *from)
 	 */
 	COPY_SCALAR_FIELD(flags);
 	COPY_NODE_FIELD(custom_exprs);
-	COPY_NODE_FIELD(custom_ps_tlist);
+	COPY_NODE_FIELD(custom_scan_tlist);
 	COPY_NODE_FIELD(custom_private);
 	COPY_BITMAPSET_FIELD(custom_relids);
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index f9f948e..d6b1a9c 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -560,7 +560,7 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
 
 	WRITE_OID_FIELD(fdw_handler);
 	WRITE_NODE_FIELD(fdw_exprs);
-	WRITE_NODE_FIELD(fdw_ps_tlist);
+	WRITE_NODE_FIELD(fdw_scan_tlist);
 	WRITE_NODE_FIELD(fdw_private);
 	WRITE_BITMAPSET_FIELD(fdw_relids);
 	WRITE_BOOL_FIELD(fsSystemCol);
@@ -575,7 +575,7 @@ _outCustomScan(StringInfo str, const CustomScan *node)
 
 	WRITE_UINT_FIELD(flags);
 	WRITE_NODE_FIELD(custom_exprs);
-	WRITE_NODE_FIELD(custom_ps_tlist);
+	WRITE_NODE_FIELD(custom_scan_tlist);
 	WRITE_NODE_FIELD(custom_private);
 	WRITE_BITMAPSET_FIELD(custom_relids);
 	appendStringInfoString(str, " :methods ");
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index eeb2a41..9e2777f 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -1997,17 +1997,31 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 												best_path,
 												tlist, scan_clauses);
 	/*
-	 * Sanity check.  There may be resjunk entries in fdw_ps_tlist that
+	 * Sanity check.  There may be resjunk entries in fdw_scan_tlist that
 	 * are included only to help EXPLAIN deparse plans properly. We require
 	 * that these are at the end, so that when the executor builds the scan
 	 * descriptor based on the non-junk entries, it gets the attribute
 	 * numbers correct.
+	 *
+	 * NOTE: The fdw_scan_tlist is not only used to construct tuple-
+	 * descriptor of scan-tuples, but also used to resolve var-node with
+	 * varno==INDEX_VAR in EXPLAIN command.
+	 * If FDW driver wants to print an expression node which includes var-
+	 * node that should not appear in the scan-tuple of this ForeignScan,
+	 * FDW driver may consider to add junk entries on the fdw_scan_tlist, 
+	 * because ExecCleanTypeFromTL() will construct a tuple-descriptor
+	 * according to the fdw_scan_tlist but except for junk-entries, but
+	 * these entries are available when EXPLAIN command looks up actual
+	 * column name referenced by the var-node with varno==INDEX_VAR.
+	 * It will potentially reduce unnecessary projection for each tuples,
+	 * thus improve the through-put of the ForeignScan on externally
+	 * materialized relation.
 	 */
 	if (scan_plan->scan.scanrelid == 0)
 	{
 		bool	found_resjunk = false;
 
-		foreach (lc, scan_plan->fdw_ps_tlist)
+		foreach (lc, scan_plan->fdw_scan_tlist)
 		{
 			TargetEntry	   *tle = lfirst(lc);
 
@@ -2107,17 +2121,20 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
 	Assert(IsA(cplan, CustomScan));
 
 	/*
-	 * Sanity check.  There may be resjunk entries in custom_ps_tlist that
+	 * Sanity check.  There may be resjunk entries in custom_scan_tlist that
 	 * are included only to help EXPLAIN deparse plans properly. We require
 	 * that these are at the end, so that when the executor builds the scan
 	 * descriptor based on the non-junk entries, it gets the attribute
 	 * numbers correct.
+	 *
+	 * See the comment in create_foreignscan_plan() to know how extension
+	 * uses junk entries for performance optimization.
 	 */
 	if (cplan->scan.scanrelid == 0)
 	{
 		bool	found_resjunk = false;
 
-		foreach (lc, cplan->custom_ps_tlist)
+		foreach (lc, cplan->custom_scan_tlist)
 		{
 			TargetEntry	   *tle = lfirst(lc);
 
@@ -3611,6 +3628,7 @@ make_foreignscan(List *qptlist,
 				 List *qpqual,
 				 Index scanrelid,
 				 List *fdw_exprs,
+				 List *fdw_scan_tlist,
 				 List *fdw_private)
 {
 	ForeignScan *node = makeNode(ForeignScan);
@@ -3623,6 +3641,7 @@ make_foreignscan(List *qptlist,
 	plan->righttree = NULL;
 	node->scan.scanrelid = scanrelid;
 	node->fdw_exprs = fdw_exprs;
+	node->fdw_scan_tlist = fdw_scan_tlist;
 	node->fdw_private = fdw_private;
 	/* fsSystemCol will be filled in by create_foreignscan_plan */
 	node->fsSystemCol = false;
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 69ed2a5..e10a033 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -881,9 +881,27 @@ set_foreignscan_references(PlannerInfo *root,
 		fscan->fdw_relids = tempset;
 	}
 
+	/*
+	 * NOTE: If scanrelid == 0, it implies the ForeignScan node performs
+	 * like scan on a relation which joins multiple foreign-tables on an
+	 * external computing resource; usually, it is remote server.
+	 * In this case, fdw_scan_tlist informs the planner/executor expected
+	 * scan tuple layout, instead of base relation's definition. Also,
+	 * executor expects fetched tuples are stores in the ecxt_scantuple
+	 * of ExprContext when local expressions are evaluated. So, setrefs.c
+	 * put INDEX_VAR on varno of Var node as a synonym of ecxt_scantuple.
+	 * (see ExecEvalScalarVar() to understand how Var-node is evaluated.)
+	 * It also put index of the fdw_scan_tlist on varattno, because FDW
+	 * driver shall return a tuple according to the layout described by
+	 * the list.
+	 * Contents of the fdw_scan_tlist are adjusted by rtoffset as usuall,
+	 * eventually, all the variables that can be locally executed shall
+	 * have a pair of INDEX_VAR and index of the fdw_scan_tlist, even if
+	 * this ForeignScan replaced more than two relations.
+	 */
 	if (fscan->scan.scanrelid == 0)
 	{
-		indexed_tlist *pscan_itlist = build_tlist_index(fscan->fdw_ps_tlist);
+		indexed_tlist *pscan_itlist = build_tlist_index(fscan->fdw_scan_tlist);
 
 		fscan->scan.plan.targetlist = (List *)
 			fix_upper_expr(root,
@@ -903,12 +921,18 @@ set_foreignscan_references(PlannerInfo *root,
 						   pscan_itlist,
 						   INDEX_VAR,
 						   rtoffset);
-		fscan->fdw_ps_tlist =
-			fix_scan_list(root, fscan->fdw_ps_tlist, rtoffset);
+		/* fdw_scan_tlist must NOT be transformed to reference itself! */
+		fscan->fdw_scan_tlist =
+			fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset);
 		pfree(pscan_itlist);
 	}
 	else
 	{
+		/*
+		 * Elsewhere, we don't need to process something special if
+		 * scanrelid > 0; that implies ForeignScan will produce scan-
+		 * tuples according to the relation's definition.
+		 */
 		fscan->scan.scanrelid += rtoffset;
 		fscan->scan.plan.targetlist =
 			fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
@@ -938,10 +962,13 @@ set_customscan_references(PlannerInfo *root,
 		cscan->custom_relids = tempset;
 	}
 
+	/*
+	 * See the comments in set_foreignscan_references().
+	 */
 	if (cscan->scan.scanrelid == 0)
 	{
 		indexed_tlist *pscan_itlist =
-			build_tlist_index(cscan->custom_ps_tlist);
+			build_tlist_index(cscan->custom_scan_tlist);
 
 		cscan->scan.plan.targetlist = (List *)
 			fix_upper_expr(root,
@@ -961,12 +988,18 @@ set_customscan_references(PlannerInfo *root,
 						   pscan_itlist,
 						   INDEX_VAR,
 						   rtoffset);
-		cscan->custom_ps_tlist =
-			fix_scan_list(root, cscan->custom_ps_tlist, rtoffset);
+		/* custom_scan_tlist must NOT be transformed to reference itself! */
+		cscan->custom_scan_tlist =
+			fix_scan_list(root, cscan->custom_scan_tlist, rtoffset);
 		pfree(pscan_itlist);
 	}
 	else
 	{
+		/*
+		 * Elsewhere, we don't need to process something special if
+		 * scanrelid > 0; that implies CustomScan will produce scan-
+		 * tuples according to the relation's definition.
+		 */
 		cscan->scan.scanrelid += rtoffset;
 		cscan->scan.plan.targetlist =
 			fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index fea8db6..7725a0b 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3859,13 +3859,16 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
 	else
 		dpns->inner_tlist = NIL;
 
-	/* index_tlist is set only if it's an IndexOnlyScan */
+	/*
+	 * index_tlist is set only if it's an IndexOnlyScan, ForeignScan or
+	 * CustomScan with scanrelid == 0.
+	 */
 	if (IsA(ps->plan, IndexOnlyScan))
 		dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist;
 	else if (IsA(ps->plan, ForeignScan))
-		dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_ps_tlist;
+		dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_scan_tlist;
 	else if (IsA(ps->plan, CustomScan))
-		dpns->index_tlist = ((CustomScan *) ps->plan)->custom_ps_tlist;
+		dpns->index_tlist = ((CustomScan *) ps->plan)->custom_scan_tlist;
 	else
 		dpns->index_tlist = NIL;
 }
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index baeba2d..d756e6c 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -471,9 +471,9 @@ typedef struct WorkTableScan
  * fdw_exprs and fdw_private are both under the control of the foreign-data
  * wrapper, but fdw_exprs is presumed to contain expression trees and will
  * be post-processed accordingly by the planner; fdw_private won't be.
- * An optional fdw_ps_tlist is used to map a reference to an attribute of
+ * An optional fdw_scan_tlist is used to map a reference to an attribute of
  * underlying relation(s) onto a pair of INDEX_VAR and alternative varattno.
- * When fdw_ps_tlist is used, this represents a remote join, and the FDW
+ * When fdw_scan_tlist is used, this represents a remote join, and the FDW
  * is responsible for setting this field to an appropriate value.
  * Note that everything in above lists must be copiable by copyObject().
  * One way to store an arbitrary blob of bytes is to represent it as a bytea
@@ -486,7 +486,7 @@ typedef struct ForeignScan
 	Scan		scan;
 	Oid			fdw_handler;	/* OID of FDW handler */
 	List	   *fdw_exprs;		/* expressions that FDW may evaluate */
-	List	   *fdw_ps_tlist;	/* tlist, if replacing a join */
+	List	   *fdw_scan_tlist;	/* tlist, if replacing a join */
 	List	   *fdw_private;	/* private data for FDW */
 	Bitmapset  *fdw_relids;		/* RTIs generated by this scan */
 	bool		fsSystemCol;	/* true if any "system column" is needed */
@@ -496,7 +496,7 @@ typedef struct ForeignScan
  *	   CustomScan node
  *
  * The comments for ForeignScan's fdw_exprs, fdw_varmap and fdw_private fields
- * apply equally to custom_exprs, custom_ps_tlist and custom_private.
+ * apply equally to custom_exprs, custom_scan_tlist and custom_private.
  * Note that since Plan trees can be copied, custom scan providers *must*
  * fit all plan data they need into those fields; embedding CustomScan in
  * a larger struct will not work.
@@ -520,7 +520,7 @@ typedef struct CustomScan
 	Scan		scan;
 	uint32		flags;			/* mask of CUSTOMPATH_* flags, see relation.h */
 	List	   *custom_exprs;	/* expressions that custom code may evaluate */
-	List	   *custom_ps_tlist;/* tlist, if replacing a join */
+	List	   *custom_scan_tlist;/* tlist, if replacing a join */
 	List	   *custom_private; /* private data for custom code */
 	Bitmapset  *custom_relids;	/* RTIs generated by this scan */
 	const CustomScanMethods *methods;
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 0c8cbcd..2b75579 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -45,7 +45,8 @@ extern Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
 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);
+									 Index scanrelid, List *fdw_exprs,
+									 List *fdw_scan_tlist, List *fdw_private);
 extern Append *make_append(List *appendplans, List *tlist);
 extern RecursiveUnion *make_recursive_union(List *tlist,
 					 Plan *lefttree, Plan *righttree, int wtParam,
