pgsql-v9.4-custom-scan.part-1.v11.patch

application/octet-stream

Filename: pgsql-v9.4-custom-scan.part-1.v11.patch
Type: application/octet-stream
Part: 1
Message: Re: Custom Scan APIs (Re: Custom Plan node)

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch v9
File+
doc/src/sgml/custom-plan.sgml 315 0
doc/src/sgml/filelist.sgml 1 0
doc/src/sgml/postgres.sgml 1 0
src/backend/commands/explain.c 43 2
src/backend/executor/execAmi.c 23 0
src/backend/executor/execProcnode.c 15 0
src/backend/executor/Makefile 1 1
src/backend/executor/nodeCustom.c 73 0
src/backend/nodes/copyfuncs.c 42 0
src/backend/nodes/outfuncs.c 40 0
src/backend/optimizer/path/allpaths.c 11 23
src/backend/optimizer/path/joinpath.c 16 0
src/backend/optimizer/plan/createplan.c 35 20
src/backend/optimizer/plan/setrefs.c 20 5
src/backend/optimizer/plan/subselect.c 75 53
src/backend/utils/adt/ruleutils.c 56 0
src/include/commands/explain.h 1 0
src/include/executor/nodeCustom.h 30 0
src/include/nodes/execnodes.h 12 0
src/include/nodes/nodes.h 6 0
src/include/nodes/plannodes.h 77 0
src/include/nodes/relation.h 29 0
src/include/optimizer/paths.h 17 0
src/include/optimizer/planmain.h 12 0
src/include/optimizer/subselect.h 7 0
 doc/src/sgml/custom-plan.sgml           | 315 ++++++++++++++++++++++++++++++++
 doc/src/sgml/filelist.sgml              |   1 +
 doc/src/sgml/postgres.sgml              |   1 +
 src/backend/commands/explain.c          |  45 ++++-
 src/backend/executor/Makefile           |   2 +-
 src/backend/executor/execAmi.c          |  23 +++
 src/backend/executor/execProcnode.c     |  15 ++
 src/backend/executor/nodeCustom.c       |  73 ++++++++
 src/backend/nodes/copyfuncs.c           |  42 +++++
 src/backend/nodes/outfuncs.c            |  40 ++++
 src/backend/optimizer/path/allpaths.c   |  34 ++--
 src/backend/optimizer/path/joinpath.c   |  16 ++
 src/backend/optimizer/plan/createplan.c |  55 ++++--
 src/backend/optimizer/plan/setrefs.c    |  25 ++-
 src/backend/optimizer/plan/subselect.c  | 128 +++++++------
 src/backend/utils/adt/ruleutils.c       |  56 ++++++
 src/include/commands/explain.h          |   1 +
 src/include/executor/nodeCustom.h       |  30 +++
 src/include/nodes/execnodes.h           |  12 ++
 src/include/nodes/nodes.h               |   6 +
 src/include/nodes/plannodes.h           |  77 ++++++++
 src/include/nodes/relation.h            |  29 +++
 src/include/optimizer/paths.h           |  17 ++
 src/include/optimizer/planmain.h        |  12 ++
 src/include/optimizer/subselect.h       |   7 +
 25 files changed, 958 insertions(+), 104 deletions(-)

diff --git a/doc/src/sgml/custom-plan.sgml b/doc/src/sgml/custom-plan.sgml
new file mode 100644
index 0000000..8d456f9
--- /dev/null
+++ b/doc/src/sgml/custom-plan.sgml
@@ -0,0 +1,315 @@
+<!-- doc/src/sgml/custom-plan.sgml -->
+
+<chapter id="custom-plan">
+ <title>Writing A Custom Plan Provider</title>
+
+ <indexterm zone="custom-plan">
+  <primary>custom plan</primary>
+  <secondary>handler for</secondary>
+ </indexterm>
+ <para>
+  The custom-plan interface enables extensions to implement its custom
+  behavior, instead of built-in plan node, according to the cost based
+  optimizer manner.
+  Its key component is <literal>CustomPlan</> node that has usual
+  <literal>Plan</> field and a table of function-pointers; that performs
+  like methods of base class in object oriented programming language,
+  thus <literal>CustomPlan</> node works as a polymorphism plan / execution
+  node.
+  The core backend does not assume anything about behavior of this node
+  type, thus, note that it is responsibility of the custom-plan provider
+  to work its custom node as if the built-in plan / execution node being
+  replaced.
+ </para>
+ <para>
+  Overall steps to use this custom-plan interface is below.
+ </para>
+ <para>
+  Custom-plan provider can add <literal>CustomPath</> on a particular
+  relation scan using <literal>add_scan_path_hook</> or a particular
+  relations join using <literal>add_join_path_hook</>.
+  Then, the planner chooses the cheapest path towards a particular
+  scan or join in the built-in and custom paths.
+  So, <literal>CustomPath</> node has to have proper cost estimation
+  for right plan selection, no need to say.
+ </para>
+ <para>
+  Usually, custom-plan provider extends <literal>CustomPath</> type
+  to have its private fields, like:
+<programlisting>
+typedef struct {
+    CustomPath    cpath;
+        :
+    List         *somethin_private;
+        :
+} YourOwnCustomPath;
+</programlisting>
+  You can also extend <literal>CustomPlan</> and <literal>CustomPlanState</>
+  type with similar manner.
+ </para>
+ <para>
+  <literal>CustomPathMethods</> is table of function-pointers
+  for <literal>CustomPath</>, and <literal>CustomPlanMethods</> is
+  table of function-pointers for <literal>CustomPlan</> and
+  <literal>CustomPlanState</>.
+  Extension has to implement the functions according to the specification
+  in the next section.
+ </para>
+
+ <sect1 id="custom-plan-spec">
+  <title>Specification of Custom Plan Interface</title>
+  <sect2 id="custom-scan-register">
+   <title>Registration of custom-plan path</title>
+   <para>
+    The first task of custom-plan provide is to add <literal>CustomPath</>
+    towards a particular relation scan or relations join.
+    Right now, only scan and join are supported by planner thus cost-based
+    optimization shall be applied, however, other kind of nodes (like sort,
+    aggregate and so on...) are not supported.
+   </para>
+   <para>
+<programlisting>
+typedef void (*add_scan_path_hook_type)(PlannerInfo *root,
+                                        RelOptInfo *baserel,
+                                        RangeTblEntry *rte);
+extern PGDLLIMPORT add_scan_path_hook_type add_scan_path_hook;
+</programlisting>
+    Custom-plan provider can add its custom-path using
+    <literal>add_scan_path_hook</> to provide alternative way to scan
+    the relation being specified.
+   </para>
+   <para>
+<programlisting>
+typedef void (*add_join_path_hook_type)(PlannerInfo *root,
+                                        RelOptInfo *joinrel,
+                                        RelOptInfo *outerrel,
+                                        RelOptInfo *innerrel,
+                                        JoinType jointype,
+                                        SpecialJoinInfo *sjinfo,
+                                        List *restrictlist,
+                                        Relids param_source_rels,
+                                        Relids extra_lateral_rels);
+extern PGDLLIMPORT add_join_path_hook_type add_join_path_hook;
+</programlisting>
+    Also, custom-plan provider can add its custom-path using
+    <literal>add_join_path_hook</> to provide alternative way to join
+    two relations (note that both or either of relations are also joined
+    relations, not only base relations) being specified.
+   </para>
+  </sect2>
+
+  <sect2 id="custom-path-methods">
+   <title>Methods of CustomPath</title>
+   <para>
+    This section introduces the method functions of <literal>CustomPath</>.
+   </para>
+   <para>
+<programlisting>
+CustomPlan *
+CreateCustomPlan(PlannerInfo *root,
+                 CustomPath *custom_path);
+</programlisting>
+    This method pupolates a node object that (at least) extends
+    <literal>CustomPlan</> data type, according to the supplied
+    <literal>CustomPath</>.
+    If this custom-plan support mark-and-restore position, its
+    node tag should be <literal>CustomPlanMarkPos</>, instead of
+    <literal>CustomPlan</>.
+   </para>
+   <para>
+<programlisting>
+void
+TextOutCustomPath(StringInfo str, Node *node);
+</programlisting>    
+    This method is needed to support <literal>nodeToString</> for your
+    custom path type to dump its private fields also.
+    The message format has to follow the manner in <filename>outfuncs.c</>.
+   </para>
+  </sect2>
+  <sect2 id="custom-plan-methods">
+   <title>Methods of CustomPlan</title>
+   <para>
+    This section introduces the method functions of <literal>CustomPlan</>.
+   </para>
+   <para>
+<programlisting>
+void
+SetCustomPlanRef(PlannerInfo *root,
+                 CustomPlan *custom_plan,
+                 int rtoffset);
+</programlisting>
+    This method requires custom-plan provides to adjust <literal>Var</> node
+    references in the supplied <literal>CustomPlan</> node.
+    Usually, it shall be shifted by <literal>rtoffset</>, or replaced by
+    <literal>INNER_VAR</> or <literal>OUTER_VAR</> if it references either
+    left or right subplan.
+   </para>
+   <para>
+<programlisting>
+bool
+SupportBackwardScan(CustomPlan *custom_plan);
+</programlisting>
+    This optional method informs the core backend whether this custom-plan
+    supports backward scan capability, or not.
+    If this method is implemented and returns <literal>true</>, it means
+    this custom-plan node supports backward scan. Elsewhere, it is not
+    available.
+   </para>
+   <para>
+<programlisting>
+void
+FinalizeCustomPlan(PlannerInfo *root,
+                   CustomPlan *custom_plan,
+                   Bitmapset **paramids,
+                   Bitmapset **valid_params,
+                   Bitmapset **scan_params);
+</programlisting>
+    This optional method informs the core backend which query parameters
+    are referenced in this custom-plan node, in addition to the ones
+    considered in the <literal>targetlist</> and <literal>qual</> fields
+    of the base <literal>Plan</> node.
+    If parameters are found in the private data field managed by custom-
+    plan provider, it needs to update the supplied bitmapset as expected
+    in the <literal>finalize_plan()</>.
+   </para>
+   <para>
+<programlisting>
+CustomPlanState *
+BeginCustomPlan(CustomPlan *custom_plan,
+                EState *estate,
+                int eflags);
+</programlisting>
+    This method populates a <literal>CustomPlanState</> object according to
+    the supplied <literal>CustomPlan</>, and initializes execution of this
+    custom-plan node, first of all.
+   </para>
+   <para>
+<programlisting>
+TupleTableSlot *
+ExecCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+    It fetches one tuple from this custom-plan node. This custom-plan node
+    has to set a valid tuple on the <literal>ps_ResultTupleSlot</> and
+    return if any, or returns <literal>NULL</> to inform the upper node
+    it already reached end of the scan.
+   </para>
+   <para>
+<programlisting>
+Node *
+MultiExecCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+    Unlike <literal>ExecCustomPlan</>, it allows upper node to fetch
+    multiple tuples, however, you need to pay attention the data format
+    and the way to return it because it fully depends on the type of
+    upper node.
+   </para>
+   <para>
+<programlisting>
+void
+EndCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+    It ends the execution of this custom-plan node, and releases the
+    resources being allocated. Usually, it is not important to release
+    memory in the per execution memory context, so custom-plan provider
+    should be responsible to its own resources regardless of the framework.
+   </para>
+   <para>
+<programlisting>
+void
+ReScanCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+    It restarts the current scan from the beginning.
+    Note that parameters of the scan depends on may change values,
+    so rewinded scan does not need to return exactly identical tuples.
+   </para>
+   <para>
+<programlisting>
+void
+MarkPosCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+    It is optional, but should be implemented if <literal>CustomPlanMarkPos</>
+    was applied, instead of <literal>CustomPlan</>.
+    It saves the current position of the custom-plan on somewhere private
+    state, to restore the position later.    
+   </para>
+   <para>
+<programlisting>
+void
+RestrPosCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+    It is optional, but should be implemented if <literal>CustomPlanMarkPos</>
+    was applied, instead of <literal>CustomPlan</>.
+    It restores the current position of the custom-plan from the private
+    information being saved somewhere at <literal>MarkPosCustomPlan</>.
+   </para>
+   <para>
+<programlisting>
+void
+ExplainCustomPlanTargetRel(CustomPlanState *cpstate,
+                           ExplainState *es);
+</programlisting>
+    It shows the target relation, if this custom-plan node replaced
+    a particular relation scan. Because of implementation reason, this
+    method is separated from the <literal>ExplainCustomPlan</>.
+   </para>
+   <para>
+<programlisting>
+void
+ExplainCustomPlan(CustomPlanState *cpstate,
+                  List *ancestors,
+                  ExplainState *es);
+</programlisting>
+    It put properties of this custom-plan node into the supplied
+    <literal>ExplainState</> according to the usual <command>EXPLAIN</>
+    manner.
+   </para>
+   <para>
+<programlisting>
+Bitmapset *
+GetRelidsCustomPlan(CustomPlanState *cpstate);
+</programlisting>
+    It returns a set of range-table indexes being scanned by this custom-
+    plan node. In case of multiple relations are underlying, it is not
+    always singleton bitmap.
+   </para>
+   <para>
+<programlisting>
+Node *
+GetSpecialCustomVar(CustomPlanState *cpstate,
+                    Var *varnode);
+</programlisting>
+    This optional method returns an expression node to be referenced by
+    the supplied <literal>varnode</> that has special <literal>varno</>
+    (<literal>INNER_VAR</>, <literal>OUTER_VAR</> or <literal>INDEX_VAR</>).
+    <command>EXPLAIN</> command shows column name being referenced in the
+    targetlist or qualifiers of plan nodes. If a var node has special
+    <literal>varno</>, it recursively walks down the underlying subplan to
+    ensure the actual expression referenced by this special varno.
+    In case when a custom-plan node replaced a join node but does not have
+    underlying sub-plan on the left- and right-tree, it is unavailable to
+    use a usual logic, so custom-plan provider has to implement this method
+    to inform the core backend the expression node being referenced by
+    the supplied <literal>varnode</> that has special <literal>varno</>.
+    If this method is not implemented or returns <literal>NULL</>,
+    the core backend solves the special varnode reference as usual.
+   </para>
+   <para>
+<programlisting>
+void
+TextOutCustomPlan(StringInfo str, const CustomPlan *node);
+</programlisting>
+    This method is needed to support <literal>nodeToString</> for your
+    custom plan type to dump its private fields also.
+    The message format has to follow the manner in <filename>outfuncs.c</>.
+   </para>
+   <para>
+<programlisting>
+CustomPlan *
+CopyCustomPlan(const CustomPlan *from);
+</programlisting>
+    This methos is needed to support <literal>copyObject</> for your
+    custom plan type to copy its private fields also.
+   </para>
+  </sect2>
+ </sect1>
+</chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 0e863ee..33f964e 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -91,6 +91,7 @@
 <!ENTITY nls        SYSTEM "nls.sgml">
 <!ENTITY plhandler  SYSTEM "plhandler.sgml">
 <!ENTITY fdwhandler SYSTEM "fdwhandler.sgml">
+<!ENTITY custom-plan  SYSTEM "custom-plan.sgml">
 <!ENTITY protocol   SYSTEM "protocol.sgml">
 <!ENTITY sources    SYSTEM "sources.sgml">
 <!ENTITY storage    SYSTEM "storage.sgml">
diff --git a/doc/src/sgml/postgres.sgml b/doc/src/sgml/postgres.sgml
index b47bf52..45e9d32 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -241,6 +241,7 @@
   &nls;
   &plhandler;
   &fdwhandler;
+  &custom-plan;
   &geqo;
   &indexam;
   &gist;
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 08f3167..ff9fc7b 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -19,6 +19,7 @@
 #include "commands/defrem.h"
 #include "commands/prepare.h"
 #include "executor/hashjoin.h"
+#include "executor/nodeCustom.h"
 #include "foreign/fdwapi.h"
 #include "optimizer/clauses.h"
 #include "parser/parsetree.h"
@@ -51,7 +52,6 @@ static void ExplainOneQuery(Query *query, IntoClause *into, ExplainState *es,
 static void report_triggers(ResultRelInfo *rInfo, bool show_relname,
 				ExplainState *es);
 static double elapsed_time(instr_time *starttime);
-static void ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
 static void ExplainPreScanMemberNodes(List *plans, PlanState **planstates,
 						  Bitmapset **rels_used);
 static void ExplainPreScanSubPlans(List *plans, Bitmapset **rels_used);
@@ -700,7 +700,7 @@ elapsed_time(instr_time *starttime)
  * This ensures that we don't confusingly assign un-suffixed aliases to RTEs
  * that never appear in the EXPLAIN output (such as inheritance parents).
  */
-static void
+void
 ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
 {
 	Plan	   *plan = planstate->plan;
@@ -721,6 +721,16 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
 			*rels_used = bms_add_member(*rels_used,
 										((Scan *) plan)->scanrelid);
 			break;
+		case T_CustomPlan:
+		case T_CustomPlanMarkPos:
+			{
+				CustomPlanState	   *cpstate = (CustomPlanState *)planstate;
+				Bitmapset		   *temp
+					= cpstate->methods->GetRelidsCustomPlan(cpstate);
+
+				*rels_used = bms_union(*rels_used, temp);
+			}
+			break;
 		case T_ModifyTable:
 			/* cf ExplainModifyTarget */
 			*rels_used = bms_add_member(*rels_used,
@@ -847,6 +857,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	const char *sname;			/* node type name for non-text output */
 	const char *strategy = NULL;
 	const char *operation = NULL;
+	const char *custom_name = NULL;
 	int			save_indent = es->indent;
 	bool		haschildren;
 
@@ -935,6 +946,14 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_ForeignScan:
 			pname = sname = "Foreign Scan";
 			break;
+		case T_CustomPlan:
+			sname = "Custom";
+			custom_name = ((CustomPlan *) plan)->methods->CustomName;
+			if (custom_name != NULL)
+				pname = psprintf("Custom (%s)", custom_name);
+			else
+				pname = sname;
+			break;
 		case T_Material:
 			pname = sname = "Materialize";
 			break;
@@ -1036,6 +1055,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			ExplainPropertyText("Parent Relationship", relationship, es);
 		if (plan_name)
 			ExplainPropertyText("Subplan Name", plan_name, es);
+		if (custom_name)
+			ExplainPropertyText("Custom", custom_name, es);
 	}
 
 	switch (nodeTag(plan))
@@ -1051,6 +1072,14 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_ForeignScan:
 			ExplainScanTarget((Scan *) plan, es);
 			break;
+		case T_CustomPlan:
+			{
+				CustomPlanState	*cps = (CustomPlanState *)planstate;
+
+				if (cps->methods->ExplainCustomPlanTargetRel)
+					cps->methods->ExplainCustomPlanTargetRel(cps, es);
+			}
+			break;
 		case T_IndexScan:
 			{
 				IndexScan  *indexscan = (IndexScan *) plan;
@@ -1347,6 +1376,18 @@ ExplainNode(PlanState *planstate, List *ancestors,
 										   planstate, es);
 			show_foreignscan_info((ForeignScanState *) planstate, es);
 			break;
+		case T_CustomPlan:
+			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
+			if (plan->qual)
+				show_instrumentation_count("Rows Removed by Filter", 1,
+										   planstate, es);
+			if (((CustomPlanState *) planstate)->methods->ExplainCustomPlan)
+			{
+				CustomPlanState *cpstate = (CustomPlanState *) planstate;
+
+				cpstate->methods->ExplainCustomPlan(cpstate, ancestors, es);
+			}
+			break;
 		case T_NestLoop:
 			show_upper_qual(((NestLoop *) plan)->join.joinqual,
 							"Join Filter", planstate, ancestors, es);
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 6081b56..4dece5a 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -15,7 +15,7 @@ include $(top_builddir)/src/Makefile.global
 OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
        execProcnode.o execQual.o execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
-       nodeBitmapAnd.o nodeBitmapOr.o \
+       nodeBitmapAnd.o nodeBitmapOr.o nodeCustom.o \
        nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
        nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \
        nodeLimit.o nodeLockRows.o \
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 8c01a63..47e7a3c 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -21,6 +21,7 @@
 #include "executor/nodeBitmapIndexscan.h"
 #include "executor/nodeBitmapOr.h"
 #include "executor/nodeCtescan.h"
+#include "executor/nodeCustom.h"
 #include "executor/nodeForeignscan.h"
 #include "executor/nodeFunctionscan.h"
 #include "executor/nodeGroup.h"
@@ -197,6 +198,10 @@ ExecReScan(PlanState *node)
 			ExecReScanForeignScan((ForeignScanState *) node);
 			break;
 
+		case T_CustomPlanState:
+			ExecReScanCustomPlan((CustomPlanState *) node);
+			break;
+
 		case T_NestLoopState:
 			ExecReScanNestLoop((NestLoopState *) node);
 			break;
@@ -291,6 +296,10 @@ ExecMarkPos(PlanState *node)
 			ExecValuesMarkPos((ValuesScanState *) node);
 			break;
 
+		case T_CustomPlanState:
+			ExecCustomMarkPos((CustomPlanState *) node);
+			break;
+
 		case T_MaterialState:
 			ExecMaterialMarkPos((MaterialState *) node);
 			break;
@@ -348,6 +357,10 @@ ExecRestrPos(PlanState *node)
 			ExecValuesRestrPos((ValuesScanState *) node);
 			break;
 
+		case T_CustomPlanState:
+			ExecCustomRestrPos((CustomPlanState *) node);
+			break;
+
 		case T_MaterialState:
 			ExecMaterialRestrPos((MaterialState *) node);
 			break;
@@ -390,6 +403,7 @@ ExecSupportsMarkRestore(NodeTag plantype)
 		case T_ValuesScan:
 		case T_Material:
 		case T_Sort:
+		case T_CustomPlanMarkPos:
 			return true;
 
 		case T_Result:
@@ -465,6 +479,15 @@ ExecSupportsBackwardScan(Plan *node)
 			return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) &&
 				TargetListSupportsBackwardScan(node->targetlist);
 
+		case T_CustomPlan:
+			{
+				CustomPlan *cplan = (CustomPlan *) node;
+
+				if (cplan->methods->SupportBackwardScan)
+					return cplan->methods->SupportBackwardScan(cplan);
+			}
+			return false;
+
 		case T_Material:
 		case T_Sort:
 			/* these don't evaluate tlist */
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index c5ecd18..5aa117b 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -85,6 +85,7 @@
 #include "executor/nodeBitmapIndexscan.h"
 #include "executor/nodeBitmapOr.h"
 #include "executor/nodeCtescan.h"
+#include "executor/nodeCustom.h"
 #include "executor/nodeForeignscan.h"
 #include "executor/nodeFunctionscan.h"
 #include "executor/nodeGroup.h"
@@ -244,6 +245,12 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 													   estate, eflags);
 			break;
 
+		case T_CustomPlan:
+		case T_CustomPlanMarkPos:
+			result = (PlanState *) ExecInitCustomPlan((CustomPlan *) node,
+													  estate, eflags);
+			break;
+
 			/*
 			 * join nodes
 			 */
@@ -442,6 +449,10 @@ ExecProcNode(PlanState *node)
 			result = ExecForeignScan((ForeignScanState *) node);
 			break;
 
+		case T_CustomPlanState:
+			result = ExecCustomPlan((CustomPlanState *) node);
+			break;
+
 			/*
 			 * join nodes
 			 */
@@ -678,6 +689,10 @@ ExecEndNode(PlanState *node)
 			ExecEndForeignScan((ForeignScanState *) node);
 			break;
 
+		case T_CustomPlanState:
+			ExecEndCustomPlan((CustomPlanState *) node);
+			break;
+
 			/*
 			 * join nodes
 			 */
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
new file mode 100644
index 0000000..e3c8f58
--- /dev/null
+++ b/src/backend/executor/nodeCustom.c
@@ -0,0 +1,73 @@
+/* ------------------------------------------------------------------------
+ *
+ * nodeCustom.c
+ *    Routines to handle execution of custom plan node
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * ------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "executor/executor.h"
+#include "executor/nodeCustom.h"
+#include "nodes/execnodes.h"
+#include "nodes/plannodes.h"
+#include "parser/parsetree.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+CustomPlanState *
+ExecInitCustomPlan(CustomPlan *custom_plan, EState *estate, int eflags)
+{
+	CustomPlanState	   *cpstate
+		= custom_plan->methods->BeginCustomPlan(custom_plan, estate, eflags);
+
+	Assert(IsA(cpstate, CustomPlanState));
+
+	return cpstate;
+}
+
+TupleTableSlot *
+ExecCustomPlan(CustomPlanState *cpstate)
+{
+	Assert(cpstate->methods->ExecCustomPlan != NULL);
+	return cpstate->methods->ExecCustomPlan(cpstate);
+}
+
+Node *
+MultiExecCustomPlan(CustomPlanState *cpstate)
+{
+	Assert(cpstate->methods->MultiExecCustomPlan != NULL);
+	return cpstate->methods->MultiExecCustomPlan(cpstate);
+}
+
+void
+ExecEndCustomPlan(CustomPlanState *cpstate)
+{
+	Assert(cpstate->methods->EndCustomPlan != NULL);
+	cpstate->methods->EndCustomPlan(cpstate);
+}
+
+void
+ExecReScanCustomPlan(CustomPlanState *cpstate)
+{
+	Assert(cpstate->methods->ReScanCustomPlan != NULL);
+	cpstate->methods->ReScanCustomPlan(cpstate);
+}
+
+void
+ExecCustomMarkPos(CustomPlanState *cpstate)
+{
+	Assert(cpstate->methods->MarkPosCustomPlan != NULL);
+	cpstate->methods->MarkPosCustomPlan(cpstate);
+}
+
+void
+ExecCustomRestrPos(CustomPlanState *cpstate)
+{
+	Assert(cpstate->methods->RestrPosCustomPlan != NULL);
+	cpstate->methods->RestrPosCustomPlan(cpstate);
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c89d808..18505cd 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -598,6 +598,42 @@ _copyForeignScan(const ForeignScan *from)
 }
 
 /*
+ * _copyCustomPlan
+ */
+static CustomPlan *
+_copyCustomPlan(const CustomPlan *from)
+{
+	CustomPlan *newnode = from->methods->CopyCustomPlan(from);
+
+	Assert(IsA(newnode, CustomPlan));
+	return newnode;
+}
+
+/*
+ * _copyCustomPlanMarkPos
+ */
+static CustomPlanMarkPos *
+_copyCustomPlanMarkPos(const CustomPlanMarkPos *from)
+{
+	CustomPlanMarkPos *newnode = from->methods->CopyCustomPlan(from);
+
+	Assert(IsA(newnode, CustomPlanMarkPos));
+	return newnode;
+}
+
+/* copy common part of CustomPlan */
+void
+CopyCustomPlanCommon(const Node *__from, Node *__newnode)
+{
+	CustomPlan *from = (CustomPlan *) __from;
+	CustomPlan *newnode = (CustomPlan *) __newnode;
+
+	((Node *) newnode)->type = nodeTag(from);
+	CopyPlanFields((const Plan *) from, (Plan *) newnode);
+	COPY_SCALAR_FIELD(methods);
+}
+
+/*
  * CopyJoinFields
  *
  *		This function copies the fields of the Join node.  It is used by
@@ -3983,6 +4019,12 @@ copyObject(const void *from)
 		case T_ForeignScan:
 			retval = _copyForeignScan(from);
 			break;
+		case T_CustomPlan:
+			retval = _copyCustomPlan(from);
+			break;
+		case T_CustomPlanMarkPos:
+			retval = _copyCustomPlanMarkPos(from);
+			break;
 		case T_Join:
 			retval = _copyJoin(from);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index bfb4b9f..8a93bc5 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -563,6 +563,27 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
 	WRITE_BOOL_FIELD(fsSystemCol);
 }
 
+/* dump common part of CustomPlan structure */
+static void
+_outCustomPlan(StringInfo str, const CustomPlan *node)
+{
+	WRITE_NODE_TYPE("CUSTOMPLAN");
+	_outPlanInfo(str, (const Plan *) node);
+	appendStringInfo(str, " :methods");
+	_outToken(str, node->methods->CustomName);
+	node->methods->TextOutCustomPlan(str, node);
+}
+
+static void
+_outCustomPlanMarkPos(StringInfo str, const CustomPlanMarkPos *node)
+{
+	WRITE_NODE_TYPE("CUSTOMPLANMARKPOS");
+	_outPlanInfo(str, (const Plan *) node);
+	appendStringInfo(str, " :methods");
+	_outToken(str, node->methods->CustomName);
+	node->methods->TextOutCustomPlan(str, node);
+}
+
 static void
 _outJoin(StringInfo str, const Join *node)
 {
@@ -1581,6 +1602,16 @@ _outForeignPath(StringInfo str, const ForeignPath *node)
 }
 
 static void
+_outCustomPath(StringInfo str, const CustomPath *node)
+{
+	WRITE_NODE_TYPE("CUSTOMPATH");
+	_outPathInfo(str, (const Path *) node);
+	appendStringInfo(str, " :methods");
+	_outToken(str, node->methods->CustomName);
+	node->methods->TextOutCustomPath(str, (Node *)node);
+}
+
+static void
 _outAppendPath(StringInfo str, const AppendPath *node)
 {
 	WRITE_NODE_TYPE("APPENDPATH");
@@ -2828,6 +2859,12 @@ _outNode(StringInfo str, const void *obj)
 			case T_ForeignScan:
 				_outForeignScan(str, obj);
 				break;
+			case T_CustomPlan:
+				_outCustomPlan(str, obj);
+				break;
+			case T_CustomPlanMarkPos:
+				_outCustomPlanMarkPos(str, obj);
+				break;
 			case T_Join:
 				_outJoin(str, obj);
 				break;
@@ -3036,6 +3073,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_ForeignPath:
 				_outForeignPath(str, obj);
 				break;
+			case T_CustomPath:
+				_outCustomPath(str, obj);
+				break;
 			case T_AppendPath:
 				_outAppendPath(str, obj);
 				break;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 03be7b1..6c1ea7e 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -47,6 +47,8 @@ int			geqo_threshold;
 /* Hook for plugins to replace standard_join_search() */
 join_search_hook_type join_search_hook = NULL;
 
+/* Hook for plugins to add custom scan paths */
+add_scan_path_hook_type add_scan_path_hook = NULL;
 
 static void set_base_rel_sizes(PlannerInfo *root);
 static void set_base_rel_pathlists(PlannerInfo *root);
@@ -323,7 +325,7 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 				}
 				break;
 			case RTE_SUBQUERY:
-				/* Subquery --- fully handled during set_rel_size */
+				/* Subquery --- path was added during set_rel_size */
 				break;
 			case RTE_FUNCTION:
 				/* RangeFunction */
@@ -334,12 +336,19 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 				set_values_pathlist(root, rel, rte);
 				break;
 			case RTE_CTE:
-				/* CTE reference --- fully handled during set_rel_size */
+				/* CTE reference --- path was added during set_rel_size */
 				break;
 			default:
 				elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
 				break;
 		}
+
+		/* Also, consider custom plans */
+		if (add_scan_path_hook)
+			(*add_scan_path_hook)(root, rel, rte);
+
+		/* Select cheapest path */
+		set_cheapest(rel);
 	}
 
 #ifdef OPTIMIZER_DEBUG
@@ -388,9 +397,6 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 
 	/* Consider TID scans */
 	create_tidscan_paths(root, rel);
-
-	/* Now find the cheapest of the paths for this rel */
-	set_cheapest(rel);
 }
 
 /*
@@ -416,9 +422,6 @@ set_foreign_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 {
 	/* Call the FDW's GetForeignPaths function to generate path(s) */
 	rel->fdwroutine->GetForeignPaths(root, rel, rte->relid);
-
-	/* Select cheapest path */
-	set_cheapest(rel);
 }
 
 /*
@@ -1235,9 +1238,6 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 
 	/* Generate appropriate path */
 	add_path(rel, create_subqueryscan_path(root, rel, pathkeys, required_outer));
-
-	/* Select cheapest path (pretty easy in this case...) */
-	set_cheapest(rel);
 }
 
 /*
@@ -1306,9 +1306,6 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	/* Generate appropriate path */
 	add_path(rel, create_functionscan_path(root, rel,
 										   pathkeys, required_outer));
-
-	/* Select cheapest path (pretty easy in this case...) */
-	set_cheapest(rel);
 }
 
 /*
@@ -1329,9 +1326,6 @@ set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 
 	/* Generate appropriate path */
 	add_path(rel, create_valuesscan_path(root, rel, required_outer));
-
-	/* Select cheapest path (pretty easy in this case...) */
-	set_cheapest(rel);
 }
 
 /*
@@ -1398,9 +1392,6 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 
 	/* Generate appropriate path */
 	add_path(rel, create_ctescan_path(root, rel, required_outer));
-
-	/* Select cheapest path (pretty easy in this case...) */
-	set_cheapest(rel);
 }
 
 /*
@@ -1451,9 +1442,6 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 
 	/* Generate appropriate path */
 	add_path(rel, create_worktablescan_path(root, rel, required_outer));
-
-	/* Select cheapest path (pretty easy in this case...) */
-	set_cheapest(rel);
 }
 
 /*
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index a996116..2fb6678 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -21,6 +21,8 @@
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 
+/* Hook for plugins to add custom join paths */
+add_join_path_hook_type add_join_path_hook = NULL;
 
 #define PATH_PARAM_BY_REL(path, rel)  \
 	((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids))
@@ -259,6 +261,20 @@ add_paths_to_joinrel(PlannerInfo *root,
 							 restrictlist, jointype,
 							 sjinfo, &semifactors,
 							 param_source_rels, extra_lateral_rels);
+
+	/*
+	 * 5. Also consider paths being provided with custom execution provider.
+	 */
+	if (add_join_path_hook)
+		(*add_join_path_hook)(root,
+							  joinrel,
+							  outerrel,
+							  innerrel,
+							  jointype,
+							  sjinfo,
+							  restrictlist,
+							  param_source_rels,
+							  extra_lateral_rels);
 }
 
 /*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 184d37a..055a818 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -42,11 +42,7 @@
 #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);
-static void disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path);
 static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *quals);
 static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path);
 static Plan *create_append_plan(PlannerInfo *root, AppendPath *best_path);
@@ -77,23 +73,20 @@ static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_pa
 						  List *tlist, List *scan_clauses);
 static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 						List *tlist, List *scan_clauses);
+static CustomPlan *create_custom_plan(PlannerInfo *root,
+									  CustomPath *best_path);
 static NestLoop *create_nestloop_plan(PlannerInfo *root, NestPath *best_path,
 					 Plan *outer_plan, Plan *inner_plan);
 static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path,
 					  Plan *outer_plan, Plan *inner_plan);
 static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
 					 Plan *outer_plan, Plan *inner_plan);
-static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
 static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
 static void process_subquery_nestloop_params(PlannerInfo *root,
 								 List *subplan_params);
 static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
 static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
 static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
-static List *get_switched_clauses(List *clauses, Relids outerrelids);
-static List *order_qual_clauses(PlannerInfo *root, List *clauses);
-static void copy_path_costsize(Plan *dest, Path *src);
-static void copy_plan_costsize(Plan *dest, Plan *src);
 static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
 static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
 			   Oid indexid, List *indexqual, List *indexqualorig,
@@ -215,7 +208,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;
@@ -261,6 +254,9 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
 			plan = create_unique_plan(root,
 									  (UniquePath *) best_path);
 			break;
+		case T_CustomPlan:
+			plan = (Plan *) create_custom_plan(root, (CustomPath *) best_path);
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) best_path->pathtype);
@@ -430,7 +426,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
 /*
  * Build a target list (ie, a list of TargetEntry) for the Path's output.
  */
-static List *
+List *
 build_path_tlist(PlannerInfo *root, Path *path)
 {
 	RelOptInfo *rel = path->parent;
@@ -466,7 +462,7 @@ build_path_tlist(PlannerInfo *root, Path *path)
  *		Decide whether to use a tlist matching relation structure,
  *		rather than only those Vars actually referenced.
  */
-static bool
+bool
 use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
 {
 	int			i;
@@ -526,7 +522,7 @@ use_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
  * undo the decision made by use_physical_tlist().	Currently, Hash, Sort,
  * and Material nodes want this, so they don't have to store useless columns.
  */
-static void
+void
 disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path)
 {
 	/* Only need to undo it for path types handled by create_scan_plan() */
@@ -569,7 +565,7 @@ disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path)
  * in most cases we have only a very bad idea of the probability of the gating
  * qual being true.
  */
-static Plan *
+Plan *
 create_gating_plan(PlannerInfo *root, Plan *plan, List *quals)
 {
 	List	   *pseudoconstants;
@@ -1072,6 +1068,26 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
 	return plan;
 }
 
+/*
+ * create_custom_plan
+ *   Returns a custom-scan plan for the base relation scanned by 'best_path'
+ *   with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static CustomPlan *
+create_custom_plan(PlannerInfo *root, CustomPath *best_path)
+{
+	CustomPlan	   *cplan;
+
+	/* Populate CustomPlan according to the CustomPath */
+	Assert(best_path->methods->CreateCustomPlan != NULL);
+	cplan = best_path->methods->CreateCustomPlan(root, best_path);
+	Assert(IsA(cplan, CustomPlan) || IsA(cplan, CustomPlanMarkPos));
+
+	/* Copy cost data from Path to Plan; no need to make callback do this */
+	copy_path_costsize(&cplan->plan, &best_path->path);
+
+	return cplan;
+}
 
 /*****************************************************************************
  *
@@ -2006,7 +2022,6 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 	return scan_plan;
 }
 
-
 /*****************************************************************************
  *
  *	JOIN METHODS
@@ -2540,7 +2555,7 @@ create_hashjoin_plan(PlannerInfo *root,
  * root->curOuterRels are replaced by Params, and entries are added to
  * root->curOuterParams if not already present.
  */
-static Node *
+Node *
 replace_nestloop_params(PlannerInfo *root, Node *expr)
 {
 	/* No setup needed for tree walk, so away we go */
@@ -3023,7 +3038,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol)
  *	  touched; a modified list is returned.  We do, however, set the transient
  *	  outer_is_left field in each RestrictInfo to show which side was which.
  */
-static List *
+List *
 get_switched_clauses(List *clauses, Relids outerrelids)
 {
 	List	   *t_list = NIL;
@@ -3089,7 +3104,7 @@ get_switched_clauses(List *clauses, Relids outerrelids)
  * instead of bare clauses.  It's OK because we only sort by cost, but
  * a cost/selectivity combination would likely do the wrong thing.
  */
-static List *
+List *
 order_qual_clauses(PlannerInfo *root, List *clauses)
 {
 	typedef struct
@@ -3156,7 +3171,7 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
  * Copy cost and size info from a Path node to the Plan node created from it.
  * The executor usually won't use this info, but it's needed by EXPLAIN.
  */
-static void
+void
 copy_path_costsize(Plan *dest, Path *src)
 {
 	if (src)
@@ -3179,7 +3194,7 @@ copy_path_costsize(Plan *dest, Path *src)
  * Copy cost and size info from a lower plan node to an inserted node.
  * (Most callers alter the info after copying it.)
  */
-static void
+void
 copy_plan_costsize(Plan *dest, Plan *src)
 {
 	if (src)
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 46affe7..e0fd9a2 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -17,6 +17,7 @@
 
 #include "access/transam.h"
 #include "catalog/pg_type.h"
+#include "executor/nodeCustom.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/pathnode.h"
@@ -86,7 +87,6 @@ static void add_rtes_to_flat_rtable(PlannerInfo *root, bool recursing);
 static void flatten_unplanned_rtes(PlannerGlobal *glob, RangeTblEntry *rte);
 static bool flatten_rtes_walker(Node *node, PlannerGlobal *glob);
 static void add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte);
-static Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset);
 static Plan *set_indexonlyscan_references(PlannerInfo *root,
 							 IndexOnlyScan *plan,
 							 int rtoffset);
@@ -94,7 +94,6 @@ static Plan *set_subqueryscan_references(PlannerInfo *root,
 							SubqueryScan *plan,
 							int rtoffset);
 static bool trivial_subqueryscan(SubqueryScan *plan);
-static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
 static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
 static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
 static void set_join_references(PlannerInfo *root, Join *join, int rtoffset);
@@ -419,7 +418,7 @@ add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte)
 /*
  * set_plan_refs: recurse through the Plan nodes of a single subquery level
  */
-static Plan *
+Plan *
 set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 {
 	ListCell   *l;
@@ -576,6 +575,22 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 			}
 			break;
 
+		case T_CustomPlan:
+		case T_CustomPlanMarkPos:
+			{
+				CustomPlan	   *cplan = (CustomPlan *) plan;
+
+				/*
+				 * Extension is responsible to handle set-reference
+				 * correctly.
+				 */
+				Assert(cplan->methods->SetCustomPlanRef != NULL);
+				cplan->methods->SetCustomPlanRef(root,
+												 cplan,
+												 rtoffset);
+			}
+			break;
+
 		case T_NestLoop:
 		case T_MergeJoin:
 		case T_HashJoin:
@@ -1057,7 +1072,7 @@ copyVar(Var *var)
  * We assume it's okay to update opcode info in-place.  So this could possibly
  * scribble on the planner's input data structures, but it's OK.
  */
-static void
+void
 fix_expr_common(PlannerInfo *root, Node *node)
 {
 	/* We assume callers won't call us on a NULL pointer */
@@ -1126,7 +1141,7 @@ fix_expr_common(PlannerInfo *root, Node *node)
  * looking up operator opcode info for OpExpr and related nodes,
  * and adding OIDs from regclass Const nodes into root->glob->relationOids.
  */
-static Node *
+Node *
 fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset)
 {
 	fix_scan_expr_context context;
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index a3f3583..6b0c762 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -75,12 +75,8 @@ static Query *convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect,
 static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root);
 static Node *process_sublinks_mutator(Node *node,
 						 process_sublinks_context *context);
-static Bitmapset *finalize_plan(PlannerInfo *root,
-			  Plan *plan,
-			  Bitmapset *valid_params,
-			  Bitmapset *scan_params);
-static bool finalize_primnode(Node *node, finalize_primnode_context *context);
-
+static bool finalize_primnode_walker(Node *node,
+									 finalize_primnode_context *context);
 
 /*
  * Select a PARAM_EXEC number to identify the given Var as a parameter for
@@ -2045,7 +2041,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans)
  * The return value is the computed allParam set for the given Plan node.
  * This is just an internal notational convenience.
  */
-static Bitmapset *
+Bitmapset *
 finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			  Bitmapset *scan_params)
 {
@@ -2070,15 +2066,15 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 	 */
 
 	/* Find params in targetlist and qual */
-	finalize_primnode((Node *) plan->targetlist, &context);
-	finalize_primnode((Node *) plan->qual, &context);
+	finalize_primnode_walker((Node *) plan->targetlist, &context);
+	finalize_primnode_walker((Node *) plan->qual, &context);
 
 	/* Check additional node-type-specific fields */
 	switch (nodeTag(plan))
 	{
 		case T_Result:
-			finalize_primnode(((Result *) plan)->resconstantqual,
-							  &context);
+			finalize_primnode_walker(((Result *) plan)->resconstantqual,
+									 &context);
 			break;
 
 		case T_SeqScan:
@@ -2086,10 +2082,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_IndexScan:
-			finalize_primnode((Node *) ((IndexScan *) plan)->indexqual,
-							  &context);
-			finalize_primnode((Node *) ((IndexScan *) plan)->indexorderby,
-							  &context);
+			finalize_primnode_walker((Node *)((IndexScan *)plan)->indexqual,
+									 &context);
+			finalize_primnode_walker((Node *)((IndexScan *)plan)->indexorderby,
+									 &context);
 
 			/*
 			 * we need not look at indexqualorig, since it will have the same
@@ -2100,10 +2096,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_IndexOnlyScan:
-			finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexqual,
-							  &context);
-			finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexorderby,
-							  &context);
+			finalize_primnode_walker((Node *)((IndexOnlyScan *) plan)->indexqual,
+									 &context);
+			finalize_primnode_walker((Node *) ((IndexOnlyScan *) plan)->indexorderby,
+									 &context);
 
 			/*
 			 * we need not look at indextlist, since it cannot contain Params.
@@ -2112,8 +2108,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_BitmapIndexScan:
-			finalize_primnode((Node *) ((BitmapIndexScan *) plan)->indexqual,
-							  &context);
+			finalize_primnode_walker((Node *) ((BitmapIndexScan *) plan)->indexqual,
+									&context);
 
 			/*
 			 * we need not look at indexqualorig, since it will have the same
@@ -2122,14 +2118,14 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_BitmapHeapScan:
-			finalize_primnode((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
-							  &context);
+			finalize_primnode_walker((Node *) ((BitmapHeapScan *) plan)->bitmapqualorig,
+									 &context);
 			context.paramids = bms_add_members(context.paramids, scan_params);
 			break;
 
 		case T_TidScan:
-			finalize_primnode((Node *) ((TidScan *) plan)->tidquals,
-							  &context);
+			finalize_primnode_walker((Node *) ((TidScan *) plan)->tidquals,
+									 &context);
 			context.paramids = bms_add_members(context.paramids, scan_params);
 			break;
 
@@ -2167,7 +2163,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 					funccontext = context;
 					funccontext.paramids = NULL;
 
-					finalize_primnode(rtfunc->funcexpr, &funccontext);
+					finalize_primnode_walker(rtfunc->funcexpr, &funccontext);
 
 					/* remember results for execution */
 					rtfunc->funcparams = funccontext.paramids;
@@ -2183,8 +2179,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_ValuesScan:
-			finalize_primnode((Node *) ((ValuesScan *) plan)->values_lists,
-							  &context);
+			finalize_primnode_walker((Node *) ((ValuesScan *) plan)->values_lists,
+									 &context);
 			context.paramids = bms_add_members(context.paramids, scan_params);
 			break;
 
@@ -2231,11 +2227,24 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_ForeignScan:
-			finalize_primnode((Node *) ((ForeignScan *) plan)->fdw_exprs,
-							  &context);
+			finalize_primnode_walker((Node *)((ForeignScan *) plan)->fdw_exprs,
+									 &context);
 			context.paramids = bms_add_members(context.paramids, scan_params);
 			break;
 
+		case T_CustomPlan:
+			{
+				CustomPlan *cplan = (CustomPlan *) plan;
+
+				if (cplan->methods->FinalizeCustomPlan)
+					cplan->methods->FinalizeCustomPlan(root,
+													   cplan,
+													   &context.paramids,
+													   &valid_params,
+													   &scan_params);
+			}
+			break;
+
 		case T_ModifyTable:
 			{
 				ModifyTable *mtplan = (ModifyTable *) plan;
@@ -2247,8 +2256,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 											  locally_added_param);
 				scan_params = bms_add_member(bms_copy(scan_params),
 											 locally_added_param);
-				finalize_primnode((Node *) mtplan->returningLists,
-								  &context);
+				finalize_primnode_walker((Node *) mtplan->returningLists,
+										 &context);
 				foreach(l, mtplan->plans)
 				{
 					context.paramids =
@@ -2329,8 +2338,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			{
 				ListCell   *l;
 
-				finalize_primnode((Node *) ((Join *) plan)->joinqual,
-								  &context);
+				finalize_primnode_walker((Node *) ((Join *) plan)->joinqual,
+										 &context);
 				/* collect set of params that will be passed to right child */
 				foreach(l, ((NestLoop *) plan)->nestParams)
 				{
@@ -2343,24 +2352,24 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_MergeJoin:
-			finalize_primnode((Node *) ((Join *) plan)->joinqual,
-							  &context);
-			finalize_primnode((Node *) ((MergeJoin *) plan)->mergeclauses,
-							  &context);
+			finalize_primnode_walker((Node *) ((Join *) plan)->joinqual,
+									 &context);
+			finalize_primnode_walker((Node *) ((MergeJoin *) plan)->mergeclauses,
+									 &context);
 			break;
 
 		case T_HashJoin:
-			finalize_primnode((Node *) ((Join *) plan)->joinqual,
-							  &context);
-			finalize_primnode((Node *) ((HashJoin *) plan)->hashclauses,
+			finalize_primnode_walker((Node *) ((Join *) plan)->joinqual,
+									 &context);
+			finalize_primnode_walker((Node *) ((HashJoin *) plan)->hashclauses,
 							  &context);
 			break;
 
 		case T_Limit:
-			finalize_primnode(((Limit *) plan)->limitOffset,
-							  &context);
-			finalize_primnode(((Limit *) plan)->limitCount,
-							  &context);
+			finalize_primnode_walker(((Limit *) plan)->limitOffset,
+									 &context);
+			finalize_primnode_walker(((Limit *) plan)->limitCount,
+									 &context);
 			break;
 
 		case T_RecursiveUnion:
@@ -2381,10 +2390,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_WindowAgg:
-			finalize_primnode(((WindowAgg *) plan)->startOffset,
-							  &context);
-			finalize_primnode(((WindowAgg *) plan)->endOffset,
-							  &context);
+			finalize_primnode_walker(((WindowAgg *) plan)->startOffset,
+									 &context);
+			finalize_primnode_walker(((WindowAgg *) plan)->endOffset,
+									 &context);
 			break;
 
 		case T_Hash:
@@ -2473,8 +2482,21 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
  * finalize_primnode: add IDs of all PARAM_EXEC params appearing in the given
  * expression tree to the result set.
  */
+Bitmapset *
+finalize_primnode(PlannerInfo *root, Node *node, Bitmapset *paramids)
+{
+	finalize_primnode_context	context;
+
+	context.root = root;
+	context.paramids = paramids;
+
+	finalize_primnode_walker(node, &context);
+
+	return context.paramids;
+}
+
 static bool
-finalize_primnode(Node *node, finalize_primnode_context *context)
+finalize_primnode_walker(Node *node, finalize_primnode_context *context)
 {
 	if (node == NULL)
 		return false;
@@ -2496,7 +2518,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
 		Bitmapset  *subparamids;
 
 		/* Recurse into the testexpr, but not into the Plan */
-		finalize_primnode(subplan->testexpr, context);
+		finalize_primnode_walker(subplan->testexpr, context);
 
 		/*
 		 * Remove any param IDs of output parameters of the subplan that were
@@ -2513,7 +2535,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
 		}
 
 		/* Also examine args list */
-		finalize_primnode((Node *) subplan->args, context);
+		finalize_primnode_walker((Node *) subplan->args, context);
 
 		/*
 		 * Add params needed by the subplan to paramids, but excluding those
@@ -2528,7 +2550,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context)
 
 		return false;			/* no more to do here */
 	}
-	return expression_tree_walker(node, finalize_primnode,
+	return expression_tree_walker(node, finalize_primnode_walker,
 								  (void *) context);
 }
 
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 566b4c9..934d796 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -5292,6 +5292,25 @@ get_utility_query_def(Query *query, deparse_context *context)
 	}
 }
 
+/*
+ * GetSpecialCustomVar
+ *
+ * Utility routine to call optional GetSpecialCustomVar method of
+ * CustomPlanState
+ */
+static Node *
+GetSpecialCustomVar(PlanState *ps, Var *varnode)
+{
+	CustomPlanState *cps = (CustomPlanState *) ps;
+
+	Assert(IsA(ps, CustomPlanState));
+	Assert(IS_SPECIAL_VARNO(varnode->varno));
+
+	if (cps->methods->GetSpecialCustomVar)
+		return (Node *)cps->methods->GetSpecialCustomVar(cps, varnode);
+
+	return NULL;
+}
 
 /*
  * Display a Var appropriately.
@@ -5323,6 +5342,7 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
 	deparse_columns *colinfo;
 	char	   *refname;
 	char	   *attname;
+	Node	   *expr;
 
 	/* Find appropriate nesting depth */
 	netlevelsup = var->varlevelsup + levelsup;
@@ -5345,6 +5365,22 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
 		colinfo = deparse_columns_fetch(var->varno, dpns);
 		attnum = var->varattno;
 	}
+	else if (IS_SPECIAL_VARNO(var->varno) &&
+			 IsA(dpns->planstate, CustomPlanState) &&
+			 (expr = GetSpecialCustomVar(dpns->planstate, var)) != NULL)
+	{
+		/*
+		 * Force parentheses because our caller probably assumed a Var is a
+		 * simple expression.
+		 */
+		if (!IsA(expr, Var))
+			appendStringInfoChar(buf, '(');
+		get_rule_expr((Node *) expr, context, true);
+		if (!IsA(expr, Var))
+			appendStringInfoChar(buf, ')');
+
+		return NULL;
+	}
 	else if (var->varno == OUTER_VAR && dpns->outer_tlist)
 	{
 		TargetEntry *tle;
@@ -5633,6 +5669,26 @@ get_name_for_var_field(Var *var, int fieldno,
 		rte = rt_fetch(var->varno, dpns->rtable);
 		attnum = var->varattno;
 	}
+	else if (IS_SPECIAL_VARNO(var->varno) &&
+			 IsA(dpns->planstate, CustomPlanState) &&
+			 (expr = GetSpecialCustomVar(dpns->planstate, var)) != NULL)
+	{
+		StringInfo		saved = context->buf;
+		StringInfoData	temp;
+
+		initStringInfo(&temp);
+		context->buf = &temp;
+
+		if (!IsA(expr, Var))
+			appendStringInfoChar(context->buf, '(');
+		get_rule_expr((Node *) expr, context, true);
+		if (!IsA(expr, Var))
+			appendStringInfoChar(context->buf, ')');
+
+		context->buf = saved;
+
+		return temp.data;
+	}
 	else if (var->varno == OUTER_VAR && dpns->outer_tlist)
 	{
 		TargetEntry *tle;
diff --git a/src/include/commands/explain.h b/src/include/commands/explain.h
index 3488be3..f914696 100644
--- a/src/include/commands/explain.h
+++ b/src/include/commands/explain.h
@@ -54,6 +54,7 @@ extern PGDLLIMPORT ExplainOneQuery_hook_type ExplainOneQuery_hook;
 typedef const char *(*explain_get_index_name_hook_type) (Oid indexId);
 extern PGDLLIMPORT explain_get_index_name_hook_type explain_get_index_name_hook;
 
+extern void ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used);
 
 extern void ExplainQuery(ExplainStmt *stmt, const char *queryString,
 			 ParamListInfo params, DestReceiver *dest);
diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h
new file mode 100644
index 0000000..e6e049e
--- /dev/null
+++ b/src/include/executor/nodeCustom.h
@@ -0,0 +1,30 @@
+/* ------------------------------------------------------------------------
+ *
+ * nodeCustom.h
+ *
+ * prototypes for CustomScan nodes
+ *
+ * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * ------------------------------------------------------------------------
+ */
+#ifndef NODECUSTOM_H
+#define NODECUSTOM_H
+#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
+
+/*
+ * General executor code
+ */
+extern CustomPlanState *ExecInitCustomPlan(CustomPlan *custom_plan,
+										   EState *estate, int eflags);
+extern TupleTableSlot *ExecCustomPlan(CustomPlanState *cpstate);
+extern Node *MultiExecCustomPlan(CustomPlanState *cpstate);
+extern void ExecEndCustomPlan(CustomPlanState *cpstate);
+
+extern void ExecReScanCustomPlan(CustomPlanState *cpstate);
+extern void ExecCustomMarkPos(CustomPlanState *cpstate);
+extern void ExecCustomRestrPos(CustomPlanState *cpstate);
+
+#endif	/* NODECUSTOM_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a301a08..8af5bf2 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1501,6 +1501,18 @@ typedef struct ForeignScanState
 	void	   *fdw_state;		/* foreign-data wrapper can keep state here */
 } ForeignScanState;
 
+/* ----------------
+ *	 CustomPlanState information
+ *
+ *		CustomPlan nodes are used to execute custom code within executor.
+ * ----------------
+ */
+typedef struct CustomPlanState
+{
+	PlanState	ps;
+	const CustomPlanMethods	   *methods;
+} CustomPlanState;
+
 /* ----------------------------------------------------------------
  *				 Join State Information
  * ----------------------------------------------------------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5b8df59..f4a1246 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -62,6 +62,8 @@ typedef enum NodeTag
 	T_CteScan,
 	T_WorkTableScan,
 	T_ForeignScan,
+	T_CustomPlan,
+	T_CustomPlanMarkPos,
 	T_Join,
 	T_NestLoop,
 	T_MergeJoin,
@@ -107,6 +109,7 @@ typedef enum NodeTag
 	T_CteScanState,
 	T_WorkTableScanState,
 	T_ForeignScanState,
+	T_CustomPlanState,
 	T_JoinState,
 	T_NestLoopState,
 	T_MergeJoinState,
@@ -224,6 +227,7 @@ typedef enum NodeTag
 	T_HashPath,
 	T_TidPath,
 	T_ForeignPath,
+	T_CustomPath,
 	T_AppendPath,
 	T_MergeAppendPath,
 	T_ResultPath,
@@ -513,6 +517,8 @@ extern void *stringToNode(char *str);
  */
 extern void *copyObject(const void *obj);
 
+extern void CopyCustomPlanCommon(const Node *from, Node *newnode);
+
 /*
  * nodes/equalfuncs.c
  */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 38c039c..7468d4c 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -15,8 +15,10 @@
 #define PLANNODES_H
 
 #include "access/sdir.h"
+#include "lib/stringinfo.h"
 #include "nodes/bitmapset.h"
 #include "nodes/primnodes.h"
+#include "nodes/relation.h"
 
 
 /* ----------------------------------------------------------------
@@ -479,6 +481,81 @@ typedef struct ForeignScan
 	bool		fsSystemCol;	/* true if any "system column" is needed */
 } ForeignScan;
 
+/* ----------------
+ *		CustomPlan node
+ * ----------------
+ */
+struct CustomPlanMethods;
+
+typedef struct CustomPlan
+{
+	Plan		plan;
+	const struct CustomPlanMethods *methods;
+} CustomPlan;
+
+/* almost same to CustomPlan, but support MarkPos/RestorePos */
+typedef CustomPlan CustomPlanMarkPos;
+
+/* not to include execnodes.h here */
+typedef struct CustomPlanState CustomPlanState;
+typedef struct EState EState;
+typedef struct ExplainState	ExplainState;
+typedef struct TupleTableSlot TupleTableSlot;
+
+typedef void (*SetCustomPlanRef_function)(PlannerInfo *root,
+										  CustomPlan *custom_plan,
+										  int rtoffset);
+typedef bool (*SupportCustomBackwardScan_function)(CustomPlan *custom_plan);
+typedef void (*FinalizeCustomPlan_function)(PlannerInfo *root,
+											CustomPlan *custom_plan,
+											Bitmapset **paramids,
+											Bitmapset **valid_params,
+											Bitmapset **scan_params);
+typedef CustomPlanState *(*BeginCustomPlan_function)(CustomPlan *custom_plan,
+													 EState *estate,
+													 int eflags);
+typedef TupleTableSlot *(*ExecCustomPlan_function)(CustomPlanState *cpstate);
+typedef Node *(*MultiExecCustomPlan_function)(CustomPlanState *cpstate);
+typedef void (*EndCustomPlan_function)(CustomPlanState *cpstate);
+typedef void (*ReScanCustomPlan_function)(CustomPlanState *cpstate);
+typedef void (*MarkPosCustomPlan_function)(CustomPlanState *cpstate);
+typedef void (*RestrPosCustomPlan_function)(CustomPlanState *cpstate);
+typedef void (*ExplainCustomPlanTargetRel_function)(CustomPlanState *cpstate,
+													ExplainState *es);
+typedef void (*ExplainCustomPlan_function)(CustomPlanState *cpstate,
+										   List *ancestors,
+										   ExplainState *es);
+typedef Bitmapset *(*GetRelidsCustomPlan_function)(CustomPlanState *cpstate);
+typedef Node *(*GetSpecialCustomVar_function)(CustomPlanState *cpstate,
+											  Var *varnode);
+typedef void (*TextOutCustomPlan_function)(StringInfo str,
+										   const CustomPlan *node);
+typedef CustomPlan *(*CopyCustomPlan_function)(const CustomPlan *from);
+
+typedef struct CustomPlanMethods
+{
+	const char						   *CustomName;
+	/* callbacks for the planner stage */
+	SetCustomPlanRef_function			SetCustomPlanRef;
+	SupportCustomBackwardScan_function	SupportBackwardScan;
+	FinalizeCustomPlan_function			FinalizeCustomPlan;
+	/* callbacks for the executor stage */
+	BeginCustomPlan_function			BeginCustomPlan;
+	ExecCustomPlan_function				ExecCustomPlan;
+	MultiExecCustomPlan_function		MultiExecCustomPlan;
+	EndCustomPlan_function				EndCustomPlan;
+	ReScanCustomPlan_function			ReScanCustomPlan;
+	MarkPosCustomPlan_function			MarkPosCustomPlan;
+	RestrPosCustomPlan_function			RestrPosCustomPlan;
+	/* callbacks for EXPLAIN */
+	ExplainCustomPlanTargetRel_function	ExplainCustomPlanTargetRel;
+	ExplainCustomPlan_function			ExplainCustomPlan;
+	GetRelidsCustomPlan_function		GetRelidsCustomPlan;
+	GetSpecialCustomVar_function		GetSpecialCustomVar;
+	/* callbacks for general node management */
+	TextOutCustomPlan_function			TextOutCustomPlan;
+	CopyCustomPlan_function				CopyCustomPlan;
+} CustomPlanMethods;
 
 /*
  * ==========
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index c607b36..cbbf1e0 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -15,6 +15,7 @@
 #define RELATION_H
 
 #include "access/sdir.h"
+#include "lib/stringinfo.h"
 #include "nodes/params.h"
 #include "nodes/parsenodes.h"
 #include "storage/block.h"
@@ -878,6 +879,34 @@ typedef struct ForeignPath
 } ForeignPath;
 
 /*
+ * CustomPath represents a scan using custom logic
+ *
+ * custom_flags is a set of CUSTOM_* bits to control its behavior.
+ * custom_methods is a set of function pointers that are declared in
+ * CustomPathMethods structure; extension has to set up correctly.
+ */
+struct CustomPathMethods;
+
+typedef struct CustomPath
+{
+	Path		path;
+	const struct CustomPathMethods   *methods;
+} CustomPath;
+
+typedef struct CustomPlan CustomPlan;
+
+typedef CustomPlan *(*CreateCustomPlan_function)(PlannerInfo *root,
+												 CustomPath *custom_path);
+typedef void (*TextOutCustomPath_function)(StringInfo str, Node *node);
+
+typedef struct CustomPathMethods
+{
+	const char				   *CustomName;
+	CreateCustomPlan_function	CreateCustomPlan;
+	TextOutCustomPath_function	TextOutCustomPath;
+} CustomPathMethods;
+
+/*
  * AppendPath represents an Append plan, ie, successive execution of
  * several member plans.
  *
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9b22fda..3047d3d 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -29,6 +29,23 @@ typedef RelOptInfo *(*join_search_hook_type) (PlannerInfo *root,
 														  List *initial_rels);
 extern PGDLLIMPORT join_search_hook_type join_search_hook;
 
+/* Hook for plugins to add custom scan path, in addition to default ones */
+typedef void (*add_scan_path_hook_type)(PlannerInfo *root,
+										RelOptInfo *baserel,
+										RangeTblEntry *rte);
+extern PGDLLIMPORT add_scan_path_hook_type add_scan_path_hook;
+
+/* Hook for plugins to add custom join path, in addition to default ones */
+typedef void (*add_join_path_hook_type)(PlannerInfo *root,
+										RelOptInfo *joinrel,
+										RelOptInfo *outerrel,
+										RelOptInfo *innerrel,
+										JoinType jointype,
+										SpecialJoinInfo *sjinfo,
+										List *restrictlist,
+										Relids param_source_rels,
+										Relids extra_lateral_rels);
+extern PGDLLIMPORT add_join_path_hook_type add_join_path_hook;
 
 extern RelOptInfo *make_one_rel(PlannerInfo *root, List *joinlist);
 extern RelOptInfo *standard_join_search(PlannerInfo *root, int levels_needed,
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 8bdb7db..28b89d9 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -41,6 +41,10 @@ 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 List *build_path_tlist(PlannerInfo *root, Path *path);
+extern bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel);
+extern void disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path);
 extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
 				  Index scanrelid, Plan *subplan);
 extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
@@ -86,6 +90,11 @@ extern ModifyTable *make_modifytable(PlannerInfo *root,
 				 List *withCheckOptionLists, List *returningLists,
 				 List *rowMarks, int epqParam);
 extern bool is_projection_capable_plan(Plan *plan);
+extern List *order_qual_clauses(PlannerInfo *root, List *clauses);
+extern List *get_switched_clauses(List *clauses, Relids outerrelids);
+extern void copy_path_costsize(Plan *dest, Path *src);
+extern void copy_plan_costsize(Plan *dest, Plan *src);
+extern Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
 
 /*
  * prototypes for plan/initsplan.c
@@ -127,6 +136,9 @@ extern List *remove_useless_joins(PlannerInfo *root, List *joinlist);
  * prototypes for plan/setrefs.c
  */
 extern Plan *set_plan_references(PlannerInfo *root, Plan *plan);
+extern Plan *set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset);
+extern void fix_expr_common(PlannerInfo *root, Node *node);
+extern Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
 extern void fix_opfuncids(Node *node);
 extern void set_opfuncid(OpExpr *opexpr);
 extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h
index 5607e98..138b60b 100644
--- a/src/include/optimizer/subselect.h
+++ b/src/include/optimizer/subselect.h
@@ -29,6 +29,13 @@ extern void SS_finalize_plan(PlannerInfo *root, Plan *plan,
 				 bool attach_initplans);
 extern Param *SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
 					Oid resulttype, int32 resulttypmod, Oid resultcollation);
+extern Bitmapset *finalize_plan(PlannerInfo *root,
+								Plan *plan,
+								Bitmapset *valid_params,
+								Bitmapset *scan_params);
+extern Bitmapset *finalize_primnode(PlannerInfo *root,
+									Node *node,
+									Bitmapset *paramids);
 extern Param *assign_nestloop_param_var(PlannerInfo *root, Var *var);
 extern Param *assign_nestloop_param_placeholdervar(PlannerInfo *root,
 									 PlaceHolderVar *phv);