 doc/src/sgml/custom-scan.sgml           | 278 ++++++++++++++++++++++++++++++++
 doc/src/sgml/fdwhandler.sgml            |  54 +++++++
 doc/src/sgml/filelist.sgml              |   1 +
 doc/src/sgml/postgres.sgml              |   1 +
 src/backend/commands/explain.c          |   5 +-
 src/backend/executor/execScan.c         |   4 +
 src/backend/executor/nodeCustom.c       |  38 +++--
 src/backend/executor/nodeForeignscan.c  |  34 ++--
 src/backend/foreign/foreign.c           |  32 +++-
 src/backend/nodes/copyfuncs.c           |   3 +
 src/backend/nodes/outfuncs.c            |   3 +
 src/backend/optimizer/path/joinpath.c   |  34 ++++
 src/backend/optimizer/plan/createplan.c |  76 +++++++--
 src/backend/optimizer/plan/setrefs.c    |  56 +++++++
 src/backend/optimizer/util/plancat.c    |   7 +-
 src/backend/optimizer/util/relnode.c    |  13 ++
 src/backend/utils/adt/ruleutils.c       |   4 +
 src/include/foreign/fdwapi.h            |  15 ++
 src/include/nodes/plannodes.h           |  20 ++-
 src/include/nodes/relation.h            |   2 +
 src/include/optimizer/paths.h           |  13 ++
 src/include/optimizer/planmain.h        |   1 +
 22 files changed, 647 insertions(+), 47 deletions(-)

diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
new file mode 100644
index 0000000..1d103f5
--- /dev/null
+++ b/doc/src/sgml/custom-scan.sgml
@@ -0,0 +1,278 @@
+<!-- doc/src/sgml/custom-scan.sgml -->
+
+<chapter id="custom-scan">
+ <title>Writing A Custom Scan Provider</title>
+
+ <indexterm zone="custom-scan">
+  <primary>custom scan provider</primary>
+  <secondary>handler for</secondary>
+ </indexterm>
+
+ <para>
+  Prior to query execution, the PostgreSQL planner constructs a plan tree
+  that usually consists of built-in plan nodes (eg: SeqScan, HashJoin, etc).
+  The custom-scan interface allows extensions to provide a custom-scan plan
+  that implements its own logic, in addition to the built-in nodes, to scan
+  a relation or join relations. Once a custom-scan node is chosen by planner,
+  callback functions associated with this custom-scan node shall be invoked
+  during query execution. Custom-scan provider is responsible for returning
+  equivalent result set as built-in logic would, but it is free to scan or
+  join the target relations according to its own logic.
+  This chapter explains how to write a custom-scan provider.
+ </para>
+
+ <para>
+  The first thing custom-scan provider to do is adding alternative paths
+  to scan a relation (on the <literal>set_rel_pathlist_hook</>) or
+  to join relations (on the <literal>set_join_pathlist_hook</>).
+  It expects <literal>CustomPath</> node is added with estimated execution
+  cost and a set of callbacks defined at <literal>CustomPathMethods</>.
+  Both of hooks also give extensions enough information to construct
+  <literal>CustomPath</> node, like <literal>RelOptInfo</> of relations
+  to be scanned, joined or read as source of join. Custom-scan provider
+  is responsible to compute a reasonable cost estimation which is
+  comparable to built-in logics.
+ </para>
+
+ <para>
+  Once a custom-path got chosen by planner, custom-scan provider has to
+  populate a plan node according to the <literal>CustomPath</> node.
+  At this moment, <literal>CustomScan</> is the only node type that allows
+  to implement custom-logic towards any <literal>CustomPath</> node.
+  The <literal>CustomScan</> structure has two special fields to keep
+  private information; <literal>custom_exprs</> and <literal>custom_private</>.
+  The <literal>custom_exprs</> intends to save a couple of expression trees
+  that shall be updated on <filename>setrefs.c</> and <filename>subselect.c</>.
+  On the other hands, <literal>custom_private</> is expected to save really
+  private information nobody will touch except for the custom-scan provider
+  itself. A plan-tree, which contains custom-scan node, can be duplicated
+  using <literal>copyObject()</>, so all the data structure stored within
+  these two fields must be safe to <literal>copyObject()</>.
+ </para>
+
+ <para>
+  In case when extension implements its own logic to join relations, it looks
+  like a simple relation scan but on a pseudo materialized relation from
+  multiple source relations, from the standpoint of the core executor.
+  Custom-scan provider is expected to process relation join with its own
+  logic internally, then return a set of records according to the tuple
+  descriptor of the scan node.
+  <literal>CustomScan</> node that replaced a relations join is not
+  associated with a particular tangible relation, unlike simple scan case,
+  so extension needs to inform the core planner expected records type to be
+  fetched from this node.
+  What we should do here is, setting zero on the <literal>scanrelid</> and
+  a valid list of <literal>TargetEntry</> on the <literal>custom_ps_tlist</>
+  instead. These configuration informs the core planner this custom-scan
+  node is not associated with a particular physical table and expected
+  record type to be returned.
+ </para>
+
+ <para>
+  Once a plan-tree is moved to the executor, it has to construct plan-state
+  objects according to the supplied plan-node.
+  Custom-scan is not an exception. Executor invokes a callback to populate
+  <literal>CustomScanState</> node, if <literal>CustomScan</> node gets
+  found in the supplied plan-tree.
+  It does not have fields to save private information unlike
+  <literal>CustomScan</> node, because custom-scan provider can allocate
+  larger object than the bare <literal>CustomScanState</> to store various
+  private execution state.
+  It looks like a relationship of <literal>ScanState</> structure towards
+  <literal>PlanState</>; that expands scan specific fields towards generic
+  plan-state. In addition, custom-scan provider can expand fields on demand.
+  Once a CustomScanState gets constructed, BeginCustomScan is invoked during
+  executor initialization; ExecCustomScan is repeatedly called during
+  execution (returning a TupleTableSlot with each fetched record), then
+  EndCustomScan is invoked on cleanup of the executor.
+ </para>
+
+ <sect1 id="custom-scan-reference">
+  <title>Custom Scan Hooks and Callbacks</title>
+
+  <sect2 id="custom-scan-hooks">
+   <title>Custom Scan Hooks</title>
+   <para>
+    This hooks is invoked when the planner investigates the optimal way to
+    scan a particular relation. Extension can add alternative paths if it
+    can provide its own logic to scan towards the given scan and qualifiers.
+<programlisting>
+typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root,
+                                            RelOptInfo *rel,
+                                            Index rti,
+                                            RangeTblEntry *rte);
+extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook;
+</programlisting>
+   </para>
+
+   <para>
+    This hook is invoked when the planner investigates the optimal combination
+    of relations join. Extension can add alternative paths that replaces the
+    relation join with its own logic. 
+<programlisting>
+typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root,
+                                             RelOptInfo *joinrel,
+                                             RelOptInfo *outerrel,
+                                             RelOptInfo *innerrel,
+                                             List *restrictlist,
+                                             JoinType jointype,
+                                             SpecialJoinInfo *sjinfo,
+                                             SemiAntiJoinFactors *semifactors,
+                                             Relids param_source_rels,
+                                             Relids extra_lateral_rels);
+extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook;
+</programlisting>
+   </para>
+  </sect2>
+
+  <sect2 id="custom-path-callbacks">
+   <title>Custom Path Callbacks</title>
+   <para>
+    A <literal>CustomPathMethods</> table contains a set of callbacks related
+    to <literal>CustomPath</> node. The core backend invokes these callbacks
+    during query planning.
+   </para>
+   <para>
+    This callback is invoked when the core backend tries to populate
+    <literal>CustomScan</> node according to the supplied
+    <literal>CustomPath</> node.
+    Custom-scan provider is responsible to allocate a <literal>CustomScan</>
+    node and initialize each fields of them.
+<programlisting>
+Plan *(*PlanCustomPath) (PlannerInfo *root,
+                         RelOptInfo *rel,
+                         CustomPath *best_path,
+                         List *tlist,
+                         List *clauses);
+</programlisting>
+   </para>
+   <para>
+    This optional callback will be invoked when <literal>nodeToString()</>
+    tries to create a text representation of <literal>CustomPath</> node.
+    A custom-scan provider can utilize this callback, if it wants to output
+    something additional. Note that expression nodes linked to
+    <literal>custom_private</> shall be transformed to text representation
+    by the core, so nothing to do by extension.
+<programlisting>
+void (*TextOutCustomPath) (StringInfo str,
+                           const CustomPath *node);
+</programlisting>
+   </para>
+  </sect2>
+
+  <sect2 id="custom-scan-callbacks">
+   <title>Custom Scan Callbacks</title>
+   <para>
+    A <literal>CustomScanMethods</> contains a set of callbacks related to
+    <literal>CustomScan</> node, then the core backend invokes these callbacks
+    during query planning and initialization of executor.
+   </para>
+   <para>
+    This callback shall be invoked when the core backend tries to populate
+    <literal>CustomScanState</> node according to the supplied
+    <literal>CustomScan</> node. The custom-scan provider is responsible to
+    allocate a <literal>CustomScanState</> (or its own data-type enhanced
+    from it), but no need to initialize the fields here, because
+    <literal>ExecInitCustomScan</> initializes the fields in
+    <literal>CustomScanState</>, then <literal>BeginCustomScan</> shall be
+    kicked on the end of executor initialization.
+<programlisting>
+Node *(*CreateCustomScanState) (CustomScan *cscan);
+</programlisting>
+   </para>
+   <para>
+    This optional callback shall be invoked when <literal>nodeToString()</>
+    tries to make text representation of <literal>CustomScan</> node.
+    Custom-scan provider can utilize this callback, if it wants to output
+    something additional. Note that it is not allowed to expand the data
+    structure of <literal>CustomScan</> node, so we usually don't need to
+    implement this callback.
+<programlisting>
+void (*TextOutCustomScan) (StringInfo str,
+                           const CustomScan *node);
+</programlisting>
+   </para>
+  </sect2>
+
+  <sect2 id="custom-exec-callbacks">
+   <title>Custom Exec Callbacks</title>
+   <para>
+    A <literal>CustomExecMethods</> contains a set of callbacks related to
+    <literal>CustomScanState</> node, then the core backend invokes these
+    callbacks during query execution.
+   </para>
+   <para>
+    This callback allows a custom-scan provider to have final initialization
+    of the <literal>CustomScanState</> node.
+    The supplied <literal>CustomScanState</> node is partially initialized
+    according to either <literal>scanrelid</> or <literal>custom_ps_tlist</>
+    of <literal>CustomScan</> node. If the custom-scan provider wants to
+    apply additional initialization to the private fields, it can be done
+    by this callback.
+<programlisting>
+void (*BeginCustomScan) (CustomScanState *node,
+                         EState *estate,
+                         int eflags);
+</programlisting>
+   </para>
+   <para>
+    This callback requires custom-scan provider to produce the next tuple
+    of the relation scan. If any tuples, it should set it on the
+    <literal>ps_ResultTupleSlot</> then returns the tuple slot. Elsewhere,
+    <literal>NULL</> or empty slot shall be returned to inform end of the
+    relation scan.
+<programlisting>
+TupleTableSlot *(*ExecCustomScan) (CustomScanState *node);
+</programlisting>
+   </para>
+   <para>
+    This callback allows a custom-scan provider to cleanup the
+    <literal>CustomScanState</> node. If it holds any private (and not
+    released automatically) resources on the supplied node, it can release
+    these resources prior to the cleanup of the common portion.
+<programlisting>
+void (*EndCustomScan) (CustomScanState *node);
+</programlisting>
+   </para>
+   <para>
+    This callback requires custom-scan provider to rewind the current scan
+    position to the head of relation. Custom-scan provider is expected to
+    reset its internal state to restart the relation scan again.
+<programlisting>
+void (*ReScanCustomScan) (CustomScanState *node);
+</programlisting>
+   </para>
+   <para>
+    This optional callback requires custom-scan provider to save the current
+    scan position on its internal state. It shall be able to restore the
+    position using <literal>RestrPosCustomScan</> callback. It shall be never
+    called unless <literal>CUSTOMPATH_SUPPORT_MARK_RESTORE</> flag is set.
+<programlisting>
+void (*MarkPosCustomScan) (CustomScanState *node);
+</programlisting>
+   </para>
+   <para>
+    This optional callback requires custom-scan provider to restore the
+    previous scan position that was saved by <literal>MarkPosCustomScan</>
+   callback. It shall be never called unless
+   <literal>CUSTOMPATH_SUPPORT_MARK_RESTORE</> flag is set.
+<programlisting>
+void (*RestrPosCustomScan) (CustomScanState *node);
+</programlisting>
+   </para>
+   <para>
+    This optional callback allows custom-scan provider to output additional
+    information on <command>EXPLAIN</> that involves custom-scan node.
+    Note that it can output common items; target-list, qualifiers, relation
+    to be scanned. So, it can be used when custom-scan provider wants to show
+    something others in addition to the items.
+<programlisting>
+void (*ExplainCustomScan) (CustomScanState *node,
+                           List *ancestors,
+                           ExplainState *es);
+</programlisting>
+   </para>
+  </sect2>
+ </sect1>
+</chapter>
+
diff --git a/doc/src/sgml/fdwhandler.sgml b/doc/src/sgml/fdwhandler.sgml
index c1daa4b..d25d5c9 100644
--- a/doc/src/sgml/fdwhandler.sgml
+++ b/doc/src/sgml/fdwhandler.sgml
@@ -598,6 +598,60 @@ IsForeignRelUpdatable (Relation rel);
 
    </sect2>
 
+   <sect2>
+    <title>FDW Routines for remote join</title>
+    <para>
+<programlisting>
+void
+GetForeignJoinPath(PlannerInfo *root,
+                   RelOptInfo *joinrel,
+                   RelOptInfo *outerrel,
+                   RelOptInfo *innerrel,
+                   JoinType jointype,
+                   SpecialJoinInfo *sjinfo,
+                   SemiAntiJoinFactors *semifactors,
+                   List *restrictlist,
+                   Relids extra_lateral_rels);
+</programlisting>
+     Create possible access paths for a join of two foreign tables or
+     joined relations, but both of them needs to be managed with same
+     FDW driver.
+     This optional function is called during query planning.
+    </para>
+    <para>
+     This function allows FDW driver to add <literal>ForeignScan</> path
+     towards the supplied <literal>joinrel</>. From the standpoint of
+     query planner, it looks like scan-node is added for join-relation.
+     It means, <literal>ForeignScan</> path added instead of the built-in
+     local join logic has to generate tuples as if it scans on a joined
+     and materialized relations.
+    </para>
+    <para>
+     Usually, we expect FDW drivers issues a remote query that involves
+     tables join on remote side, then FDW driver fetches the joined result
+     on local side.
+     Unlike simple table scan, definition of slot descriptor of the joined
+     relations is determined on the fly, thus we cannot know its definition
+     from the system catalog.
+     So, FDW driver is responsible to introduce the query planner expected
+     form of the joined relations. In case when <literal>ForeignScan</>
+     replaced a relations join, <literal>scanrelid</> of the generated plan
+     node shall be zero, to mark this <literal>ForeignScan</> node is not
+     associated with a particular foreign tables.
+     Also, it need to construct pseudo scan tlist (<literal>fdw_ps_tlist</>)
+     to indicate expected tuple definition.
+    </para>
+    <para>
+     Once <literal>scanrelid</> equals zero, executor initializes the slot
+     for scan according to <literal>fdw_ps_tlist</>, but excludes junk
+     entries. This list is also used to solve the name of the original
+     relation and columns, so FDW can chains expression nodes which are
+     not run on local side actually, like a join clause to be executed on
+     the remote side, however, target-entries of them will have
+     <literal>resjunk=true</>.
+    </para>
+   </sect2>
+
    <sect2 id="fdw-callbacks-explain">
     <title>FDW Routines for <command>EXPLAIN</></title>
 
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index f03b72a..89fff77 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -93,6 +93,7 @@
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
+<!ENTITY custom-scan SYSTEM "custom-scan.sgml">
 <!ENTITY logicaldecoding SYSTEM "logicaldecoding.sgml">
 <!ENTITY protocol   SYSTEM "protocol.sgml">
 <!ENTITY sources    SYSTEM "sources.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index a648a4c..e378d69 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -242,6 +242,7 @@
   &nls;
   &plhandler;
   &fdwhandler;
+  &custom-scan;
   &geqo;
   &indexam;
   &gist;
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a951c55..9281874 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1072,9 +1072,12 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_ValuesScan:
 		case T_CteScan:
 		case T_WorkTableScan:
+			ExplainScanTarget((Scan *) plan, es);
+			break;
 		case T_ForeignScan:
 		case T_CustomScan:
-			ExplainScanTarget((Scan *) plan, es);
+			if (((Scan *) plan)->scanrelid > 0)
+				ExplainScanTarget((Scan *) plan, es);
 			break;
 		case T_IndexScan:
 			{
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 3f0d809..2f18a8a 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -251,6 +251,10 @@ ExecAssignScanProjectionInfo(ScanState *node)
 	/* Vars in an index-only scan's tlist should be INDEX_VAR */
 	if (IsA(scan, IndexOnlyScan))
 		varno = INDEX_VAR;
+	/* Also foreign-/custom-scan on pseudo relation should be INDEX_VAR */
+	else if (scan->scanrelid == 0 &&
+			 (IsA(scan, ForeignScan) || IsA(scan, CustomScan)))
+		varno = INDEX_VAR;
 	else
 		varno = scan->scanrelid;
 
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index b07932b..2344129 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -23,6 +23,7 @@ CustomScanState *
 ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 {
 	CustomScanState    *css;
+	Index				scan_relid = cscan->scan.scanrelid;
 	Relation			scan_rel;
 
 	/* populate a CustomScanState according to the CustomScan */
@@ -48,12 +49,31 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	ExecInitScanTupleSlot(estate, &css->ss);
 	ExecInitResultTupleSlot(estate, &css->ss.ps);
 
-	/* initialize scan relation */
-	scan_rel = ExecOpenScanRelation(estate, cscan->scan.scanrelid, eflags);
-	css->ss.ss_currentRelation = scan_rel;
-	css->ss.ss_currentScanDesc = NULL;	/* set by provider */
-	ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel));
-
+	/*
+	 * open the base relation and acquire appropriate lock on it, then
+	 * get the scan type from the relation descriptor, if this custom
+	 * scan is on actual relations.
+	 *
+	 * on the other hands, custom-scan may scan on a pseudo relation;
+	 * that is usually a result-set of relations join by external
+	 * computing resource, or others. It has to get the scan type from
+	 * the pseudo-scan target-list that should be assigned by custom-scan
+	 * provider.
+	 */
+	if (scan_relid > 0)
+	{
+		scan_rel = ExecOpenScanRelation(estate, scan_relid, eflags);
+		css->ss.ss_currentRelation = scan_rel;
+		css->ss.ss_currentScanDesc = NULL;	/* set by provider */
+		ExecAssignScanType(&css->ss, RelationGetDescr(scan_rel));
+	}
+	else
+	{
+		TupleDesc	ps_tupdesc;
+
+		ps_tupdesc = ExecCleanTypeFromTL(cscan->custom_ps_tlist, false);
+		ExecAssignScanType(&css->ss, ps_tupdesc);
+	}
 	css->ss.ps.ps_TupFromTlist = false;
 
 	/*
@@ -89,11 +109,11 @@ ExecEndCustomScan(CustomScanState *node)
 
 	/* Clean out the tuple table */
 	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
-	if (node->ss.ss_ScanTupleSlot)
-		ExecClearTuple(node->ss.ss_ScanTupleSlot);
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
 	/* Close the heap relation */
-	ExecCloseScanRelation(node->ss.ss_currentRelation);
+	if (node->ss.ss_currentRelation)
+		ExecCloseScanRelation(node->ss.ss_currentRelation);
 }
 
 void
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index 7399053..542d176 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -102,6 +102,7 @@ ForeignScanState *
 ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 {
 	ForeignScanState *scanstate;
+	Index		scanrelid = node->scan.scanrelid;
 	Relation	currentRelation;
 	FdwRoutine *fdwroutine;
 
@@ -141,16 +142,28 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	ExecInitScanTupleSlot(estate, &scanstate->ss);
 
 	/*
-	 * open the base relation and acquire appropriate lock on it.
+	 * open the base relation and acquire appropriate lock on it, then
+	 * get the scan type from the relation descriptor, if this foreign
+	 * scan is on actual foreign-table.
+	 *
+	 * on the other hands, foreign-scan may scan on a pseudo relation;
+	 * that is usually a result-set of remote relations join. It has
+	 * to get the scan type from the pseudo-scan target-list that should
+	 * be assigned by FDW driver.
 	 */
-	currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
-	scanstate->ss.ss_currentRelation = currentRelation;
+	if (scanrelid > 0)
+	{
+		currentRelation = ExecOpenScanRelation(estate, scanrelid, eflags);
+		scanstate->ss.ss_currentRelation = currentRelation;
+		ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
+	}
+	else
+	{
+		TupleDesc	ps_tupdesc;
 
-	/*
-	 * get the scan type from the relation descriptor.  (XXX at some point we
-	 * might want to let the FDW editorialize on the scan tupdesc.)
-	 */
-	ExecAssignScanType(&scanstate->ss, RelationGetDescr(currentRelation));
+		ps_tupdesc = ExecCleanTypeFromTL(node->fdw_ps_tlist, false);
+		ExecAssignScanType(&scanstate->ss, ps_tupdesc);
+	}
 
 	/*
 	 * Initialize result tuple type and projection info.
@@ -161,7 +174,7 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	/*
 	 * Acquire function pointers from the FDW's handler, and init fdw_state.
 	 */
-	fdwroutine = GetFdwRoutineForRelation(currentRelation, true);
+	fdwroutine = GetFdwRoutine(node->fdw_handler);
 	scanstate->fdwroutine = fdwroutine;
 	scanstate->fdw_state = NULL;
 
@@ -193,7 +206,8 @@ ExecEndForeignScan(ForeignScanState *node)
 	ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
 	/* close the relation. */
-	ExecCloseScanRelation(node->ss.ss_currentRelation);
+	if (node->ss.ss_currentRelation)
+		ExecCloseScanRelation(node->ss.ss_currentRelation);
 }
 
 /* ----------------------------------------------------------------
diff --git a/src/backend/foreign/foreign.c b/src/backend/foreign/foreign.c
index cbe8b78..df69a95 100644
--- a/src/backend/foreign/foreign.c
+++ b/src/backend/foreign/foreign.c
@@ -302,13 +302,12 @@ GetFdwRoutine(Oid fdwhandler)
 	return routine;
 }
 
-
 /*
- * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
- * for the given foreign table, and retrieve its FdwRoutine struct.
+ * GetFdwHandlerByRelId - look up the handler of the foreign-data wrapper
+ * for the given foreign table
  */
-FdwRoutine *
-GetFdwRoutineByRelId(Oid relid)
+static Oid
+GetFdwHandlerByRelId(Oid relid)
 {
 	HeapTuple	tp;
 	Form_pg_foreign_data_wrapper fdwform;
@@ -350,7 +349,18 @@ GetFdwRoutineByRelId(Oid relid)
 
 	ReleaseSysCache(tp);
 
-	/* And finally, call the handler function. */
+	return fdwhandler;
+}
+
+/*
+ * GetFdwRoutineByRelId - look up the handler of the foreign-data wrapper
+ * for the given foreign table, and retrieve its FdwRoutine struct.
+ */
+FdwRoutine *
+GetFdwRoutineByRelId(Oid relid)
+{
+	Oid			fdwhandler = GetFdwHandlerByRelId(relid);
+
 	return GetFdwRoutine(fdwhandler);
 }
 
@@ -398,6 +408,16 @@ GetFdwRoutineForRelation(Relation relation, bool makecopy)
 	return relation->rd_fdwroutine;
 }
 
+/*
+ * GetFdwHandlerForRelation
+ *
+ * returns OID of FDW handler which is associated with the given relation.
+ */
+Oid
+GetFdwHandlerForRelation(Relation relation)
+{
+	return GetFdwHandlerByRelId(RelationGetRelid(relation));
+}
 
 /*
  * IsImportableForeignTable - filter table names for IMPORT FOREIGN SCHEMA
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 9fe8008..9300b70 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -592,7 +592,9 @@ _copyForeignScan(const ForeignScan *from)
 	/*
 	 * copy remainder of node
 	 */
+	COPY_SCALAR_FIELD(fdw_handler);
 	COPY_NODE_FIELD(fdw_exprs);
+	COPY_NODE_FIELD(fdw_ps_tlist);
 	COPY_NODE_FIELD(fdw_private);
 	COPY_SCALAR_FIELD(fsSystemCol);
 
@@ -617,6 +619,7 @@ _copyCustomScan(const CustomScan *from)
 	 */
 	COPY_SCALAR_FIELD(flags);
 	COPY_NODE_FIELD(custom_exprs);
+	COPY_NODE_FIELD(custom_ps_tlist);
 	COPY_NODE_FIELD(custom_private);
 
 	/*
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 775f482..f3676ec 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -558,7 +558,9 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
 
 	_outScanInfo(str, (const Scan *) node);
 
+	WRITE_OID_FIELD(fdw_handler);
 	WRITE_NODE_FIELD(fdw_exprs);
+	WRITE_NODE_FIELD(fdw_ps_tlist);
 	WRITE_NODE_FIELD(fdw_private);
 	WRITE_BOOL_FIELD(fsSystemCol);
 }
@@ -572,6 +574,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_private);
 	appendStringInfoString(str, " :methods ");
 	_outToken(str, node->methods->CustomName);
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 1da953f..d68164c 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -17,10 +17,13 @@
 #include <math.h>
 
 #include "executor/executor.h"
+#include "foreign/fdwapi.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 
+/* Hook for plugins to get control in add_paths_to_joinrel() */
+set_join_pathlist_hook_type set_join_pathlist_hook = NULL;
 
 #define PATH_PARAM_BY_REL(path, rel)  \
 	((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids))
@@ -260,6 +263,37 @@ add_paths_to_joinrel(PlannerInfo *root,
 							 restrictlist, jointype,
 							 sjinfo, &semifactors,
 							 param_source_rels, extra_lateral_rels);
+
+	/*
+	 * 5. Consider paths added by FDW drivers or custom-scan providers, in
+	 * addition to built-in paths.
+	 *
+	 * XXX - In case of FDW, we may be able to omit invocation if joinrel's
+	 * fdwhandler (set only if both relations are managed by same FDW server).
+	 */
+	if (set_join_pathlist_hook)
+		set_join_pathlist_hook(root, joinrel, outerrel, innerrel,
+							   restrictlist, jointype,
+							   sjinfo, &semifactors,
+							   param_source_rels, extra_lateral_rels);
+
+	/*
+	 * 6. Consider paths added by FDWs when both outer and inner relations are
+	 * managed by same foreign-data wrapper.  Matching of foreign server and/or
+	 * checkAsUser should be checked in GetForeignJoinPath by the FDW.
+	 */
+	if (joinrel->fdwroutine && joinrel->fdwroutine->GetForeignJoinPath)
+	{
+		joinrel->fdwroutine->GetForeignJoinPath(root,
+												joinrel,
+												outerrel,
+												innerrel,
+												jointype,
+												sjinfo,
+												&semifactors,
+												restrictlist,
+												extra_lateral_rels);
+	}
 }
 
 /*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 76ba1bf..7a37824 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -44,7 +44,6 @@
 #include "utils/lsyscache.h"
 
 
-static Plan *create_plan_recurse(PlannerInfo *root, Path *best_path);
 static Plan *create_scan_plan(PlannerInfo *root, Path *best_path);
 static List *build_path_tlist(PlannerInfo *root, Path *path);
 static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
@@ -220,7 +219,7 @@ create_plan(PlannerInfo *root, Path *best_path)
  * create_plan_recurse
  *	  Recursive guts of create_plan().
  */
-static Plan *
+Plan *
 create_plan_recurse(PlannerInfo *root, Path *best_path)
 {
 	Plan	   *plan;
@@ -1958,16 +1957,26 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 	ForeignScan *scan_plan;
 	RelOptInfo *rel = best_path->path.parent;
 	Index		scan_relid = rel->relid;
-	RangeTblEntry *rte;
+	Oid			rel_oid = InvalidOid;
 	Bitmapset  *attrs_used = NULL;
 	ListCell   *lc;
 	int			i;
 
-	/* it should be a base rel... */
-	Assert(scan_relid > 0);
-	Assert(rel->rtekind == RTE_RELATION);
-	rte = planner_rt_fetch(scan_relid, root);
-	Assert(rte->rtekind == RTE_RELATION);
+	/*
+	 * Fetch relation-id, if this foreign-scan node actuall scans on
+	 * a particular real relation. Elsewhere, InvalidOid shall be
+	 * informed to the FDW driver.
+	 */
+	if (scan_relid > 0)
+	{
+		RangeTblEntry *rte;
+
+		Assert(rel->rtekind == RTE_RELATION);
+		rte = planner_rt_fetch(scan_relid, root);
+		Assert(rte->rtekind == RTE_RELATION);
+		rel_oid = rte->relid;
+	}
+	Assert(rel->fdwroutine != NULL);
 
 	/*
 	 * Sort clauses into best execution order.  We do this first since the FDW
@@ -1982,13 +1991,35 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 	 * has selected some join clauses for remote use but also wants them
 	 * rechecked locally).
 	 */
-	scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rte->relid,
+	scan_plan = rel->fdwroutine->GetForeignPlan(root, rel, rel_oid,
 												best_path,
 												tlist, scan_clauses);
+	/*
+	 * Sanity check. Pseudo scan tuple-descriptor shall be constructed
+	 * based on the fdw_ps_tlist, excluding resjunk=true, so we need to
+	 * ensure all valid TLEs have to locate prior to junk ones.
+	 */
+	if (scan_plan->scan.scanrelid == 0)
+	{
+		bool	found_resjunk = false;
+
+		foreach (lc, scan_plan->fdw_ps_tlist)
+		{
+			TargetEntry	   *tle = lfirst(lc);
+
+			if (tle->resjunk)
+				found_resjunk = true;
+			else if (found_resjunk)
+				elog(ERROR, "junk TLE should not apper prior to valid one");
+		}
+	}
 
 	/* Copy cost data from Path to Plan; no need to make FDW do this */
 	copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
 
+	/* Track FDW server-id; no need to make FDW do this */
+	scan_plan->fdw_handler = rel->fdw_handler;
+
 	/*
 	 * Replace any outer-relation variables with nestloop params in the qual
 	 * and fdw_exprs expressions.  We do this last so that the FDW doesn't
@@ -2050,12 +2081,7 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
 {
 	CustomScan *cplan;
 	RelOptInfo *rel = best_path->path.parent;
-
-	/*
-	 * Right now, all we can support is CustomScan node which is associated
-	 * with a particular base relation to be scanned.
-	 */
-	Assert(rel && rel->reloptkind == RELOPT_BASEREL);
+	ListCell   *lc;
 
 	/*
 	 * Sort clauses into the best execution order, although custom-scan
@@ -2075,6 +2101,26 @@ create_customscan_plan(PlannerInfo *root, CustomPath *best_path,
 	Assert(IsA(cplan, CustomScan));
 
 	/*
+	 * Sanity check. Pseudo scan tuple-descriptor shall be constructed
+	 * based on the custom_ps_tlist, excluding resjunk=true, so we need
+	 * to ensure all valid TLEs have to locate prior to junk ones.
+	 */
+	if (cplan->scan.scanrelid == 0)
+	{
+		bool	found_resjunk = false;
+
+		foreach (lc, cplan->custom_ps_tlist)
+		{
+			TargetEntry	   *tle = lfirst(lc);
+
+			if (tle->resjunk)
+				found_resjunk = true;
+			else if (found_resjunk)
+				elog(ERROR, "junk TLE should not apper prior to valid one");
+		}
+	}
+
+	/*
 	 * Copy cost data from Path to Plan; no need to make custom-plan providers
 	 * do this
 	 */
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index ec828cd..a41c4f0 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -568,6 +568,34 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 			{
 				ForeignScan *splan = (ForeignScan *) plan;
 
+				if (splan->scan.scanrelid == 0)
+				{
+					indexed_tlist *pscan_itlist =
+						build_tlist_index(splan->fdw_ps_tlist);
+
+					splan->scan.plan.targetlist = (List *)
+						fix_upper_expr(root,
+									   (Node *) splan->scan.plan.targetlist,
+									   pscan_itlist,
+									   INDEX_VAR,
+									   rtoffset);
+					splan->scan.plan.qual = (List *)
+						fix_upper_expr(root,
+									   (Node *) splan->scan.plan.qual,
+									   pscan_itlist,
+									   INDEX_VAR,
+									   rtoffset);
+					splan->fdw_exprs = (List *)
+						fix_upper_expr(root,
+									   (Node *) splan->fdw_exprs,
+									   pscan_itlist,
+									   INDEX_VAR,
+									   rtoffset);
+					splan->fdw_ps_tlist =
+						fix_scan_list(root, splan->fdw_ps_tlist, rtoffset);
+					pfree(pscan_itlist);
+					break;
+				}
 				splan->scan.scanrelid += rtoffset;
 				splan->scan.plan.targetlist =
 					fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
@@ -582,6 +610,34 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 			{
 				CustomScan *splan = (CustomScan *) plan;
 
+				if (splan->scan.scanrelid == 0)
+				{
+					indexed_tlist *pscan_itlist =
+						build_tlist_index(splan->custom_ps_tlist);
+
+					splan->scan.plan.targetlist = (List *)
+						fix_upper_expr(root,
+									   (Node *) splan->scan.plan.targetlist,
+									   pscan_itlist,
+									   INDEX_VAR,
+									   rtoffset);
+					splan->scan.plan.qual = (List *)
+						fix_upper_expr(root,
+									   (Node *) splan->scan.plan.qual,
+									   pscan_itlist,
+									   INDEX_VAR,
+									   rtoffset);
+					splan->custom_exprs = (List *)
+						fix_upper_expr(root,
+									   (Node *) splan->custom_exprs,
+									   pscan_itlist,
+									   INDEX_VAR,
+									   rtoffset);
+					splan->custom_ps_tlist =
+						fix_scan_list(root, splan->custom_ps_tlist, rtoffset);
+					pfree(pscan_itlist);
+					break;
+				}
 				splan->scan.scanrelid += rtoffset;
 				splan->scan.plan.targetlist =
 					fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 313a5c1..1c570c8 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -378,10 +378,15 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
 
 	/* Grab the fdwroutine info using the relcache, while we have it */
 	if (relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
+	{
+		rel->fdw_handler = GetFdwHandlerForRelation(relation);
 		rel->fdwroutine = GetFdwRoutineForRelation(relation, true);
+	}
 	else
+	{
+		rel->fdw_handler = InvalidOid;
 		rel->fdwroutine = NULL;
-
+	}
 	heap_close(relation, NoLock);
 
 	/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 8cfbea0..ca71093 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -14,6 +14,7 @@
  */
 #include "postgres.h"
 
+#include "foreign/fdwapi.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
@@ -427,6 +428,18 @@ build_join_rel(PlannerInfo *root,
 							   sjinfo, restrictlist);
 
 	/*
+	 * Set FDW handler and routine if both outer and inner relation
+	 * are managed by same FDW driver.
+	 */
+	if (OidIsValid(outer_rel->fdw_handler) &&
+		OidIsValid(inner_rel->fdw_handler) &&
+		outer_rel->fdw_handler == inner_rel->fdw_handler)
+	{
+		joinrel->fdw_handler = outer_rel->fdw_handler;
+		joinrel->fdwroutine = GetFdwRoutine(joinrel->fdw_handler);
+	}
+
+	/*
 	 * Add the joinrel to the query's joinrel list, and store it into the
 	 * auxiliary hashtable if there is one.  NB: GEQO requires us to append
 	 * the new joinrel to the end of the list!
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 2fa30be..87f84a7 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3842,6 +3842,10 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
 	/* index_tlist is set only if it's an IndexOnlyScan */
 	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;
+	else if (IsA(ps->plan, CustomScan))
+		dpns->index_tlist = ((CustomScan *) ps->plan)->custom_ps_tlist;
 	else
 		dpns->index_tlist = NIL;
 }
diff --git a/src/include/foreign/fdwapi.h b/src/include/foreign/fdwapi.h
index 1d76841..d4ab71a 100644
--- a/src/include/foreign/fdwapi.h
+++ b/src/include/foreign/fdwapi.h
@@ -82,6 +82,16 @@ typedef void (*EndForeignModify_function) (EState *estate,
 
 typedef int (*IsForeignRelUpdatable_function) (Relation rel);
 
+typedef void (*GetForeignJoinPath_function ) (PlannerInfo *root,
+											  RelOptInfo *joinrel,
+											  RelOptInfo *outerrel,
+											  RelOptInfo *innerrel,
+											  JoinType jointype,
+											  SpecialJoinInfo *sjinfo,
+											  SemiAntiJoinFactors *semifactors,
+											  List *restrictlist,
+											  Relids extra_lateral_rels);
+
 typedef void (*ExplainForeignScan_function) (ForeignScanState *node,
 													struct ExplainState *es);
 
@@ -150,6 +160,10 @@ typedef struct FdwRoutine
 
 	/* Support functions for IMPORT FOREIGN SCHEMA */
 	ImportForeignSchema_function ImportForeignSchema;
+
+	/* Support functions for join push-down */
+	GetForeignJoinPath_function GetForeignJoinPath;
+
 } FdwRoutine;
 
 
@@ -157,6 +171,7 @@ typedef struct FdwRoutine
 extern FdwRoutine *GetFdwRoutine(Oid fdwhandler);
 extern FdwRoutine *GetFdwRoutineByRelId(Oid relid);
 extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy);
+extern Oid	GetFdwHandlerForRelation(Relation relation);
 extern bool IsImportableForeignTable(const char *tablename,
 						 ImportForeignSchemaStmt *stmt);
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index f6683f0..213034b 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -471,7 +471,13 @@ 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.
- * Note that everything in both lists must be copiable by copyObject().
+ * An optional fdw_ps_tlist is used to map a reference to an attribute of
+ * underlying relation(s) on a pair of INDEX_VAR and alternative varattno.
+ * It looks like a scan on pseudo relation that is usually result of
+ * relations join on remote data source, and FDW driver is responsible to
+ * set expected target list for this. If FDW returns records as foreign-
+ * table definition, just put NIL here.
+ * 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
  * Const.  Usually, though, you'll be better off choosing a representation
  * that can be dumped usefully by nodeToString().
@@ -480,7 +486,9 @@ typedef struct WorkTableScan
 typedef struct ForeignScan
 {
 	Scan		scan;
+	Oid			fdw_handler;	/* OID of FDW handler */
 	List	   *fdw_exprs;		/* expressions that FDW may evaluate */
+	List	   *fdw_ps_tlist;	/* optional pseudo-scan tlist for FDW */
 	List	   *fdw_private;	/* private data for FDW */
 	bool		fsSystemCol;	/* true if any "system column" is needed */
 } ForeignScan;
@@ -488,10 +496,11 @@ typedef struct ForeignScan
 /* ----------------
  *	   CustomScan node
  *
- * The comments for ForeignScan's fdw_exprs and fdw_private fields apply
- * equally to custom_exprs 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.
+ * The comments for ForeignScan's fdw_exprs, fdw_varmap and fdw_private fields
+ * apply equally to custom_exprs, custom_ps_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.
  * ----------------
  */
 struct CustomScan;
@@ -512,6 +521,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;/* optional pseudo-scan target list */
 	List	   *custom_private; /* private data for custom code */
 	const CustomScanMethods *methods;
 } CustomScan;
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6845a40..9ef0b56 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -366,6 +366,7 @@ typedef struct PlannerInfo
  *		subroot - PlannerInfo for subquery (NULL if it's not a subquery)
  *		subplan_params - list of PlannerParamItems to be passed to subquery
  *		fdwroutine - function hooks for FDW, if foreign table (else NULL)
+ *		fdw_handler - OID of FDW handler, if foreign table (else InvalidOid)
  *		fdw_private - private state for FDW, if foreign table (else NULL)
  *
  *		Note: for a subquery, tuples, subplan, subroot are not set immediately
@@ -461,6 +462,7 @@ typedef struct RelOptInfo
 	List	   *subplan_params; /* if subquery */
 	/* use "struct FdwRoutine" to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;		/* if foreign table */
+	Oid			fdw_handler;	/* if foreign table */
 	void	   *fdw_private;	/* if foreign table */
 
 	/* used by various scans and joins: */
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 6cad92e..c42c69d 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -30,6 +30,19 @@ typedef void (*set_rel_pathlist_hook_type) (PlannerInfo *root,
 														RangeTblEntry *rte);
 extern PGDLLIMPORT set_rel_pathlist_hook_type set_rel_pathlist_hook;
 
+/* Hook for plugins to get control in add_paths_to_joinrel() */
+typedef void (*set_join_pathlist_hook_type) (PlannerInfo *root,
+											 RelOptInfo *joinrel,
+											 RelOptInfo *outerrel,
+											 RelOptInfo *innerrel,
+											 List *restrictlist,
+											 JoinType jointype,
+											 SpecialJoinInfo *sjinfo,
+											 SemiAntiJoinFactors *semifactors,
+											 Relids param_source_rels,
+											 Relids extra_lateral_rels);
+extern PGDLLIMPORT set_join_pathlist_hook_type set_join_pathlist_hook;
+
 /* Hook for plugins to replace standard_join_search() */
 typedef RelOptInfo *(*join_search_hook_type) (PlannerInfo *root,
 														  int levels_needed,
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index fa72918..0c8cbcd 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -41,6 +41,7 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
  * prototypes for plan/createplan.c
  */
 extern Plan *create_plan(PlannerInfo *root, Path *best_path);
+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,
