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

application/octet-stream

Filename: pgsql-v9.4-custom-scan.part-1.v9.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-scan.sgml 295 0
doc/src/sgml/filelist.sgml 1 0
doc/src/sgml/postgres.sgml 1 0
src/backend/commands/explain.c 99 0
src/backend/executor/execAmi.c 32 2
src/backend/executor/execProcnode.c 14 0
src/backend/executor/execQual.c 5 5
src/backend/executor/execUtils.c 2 2
src/backend/executor/Makefile 1 1
src/backend/executor/nodeCustom.c 252 0
src/backend/nodes/bitmapset.c 61 0
src/backend/nodes/copyfuncs.c 30 0
src/backend/nodes/outfuncs.c 19 0
src/backend/nodes/print.c 4 0
src/backend/optimizer/path/allpaths.c 23 0
src/backend/optimizer/path/costsize.c 2 5
src/backend/optimizer/path/joinpath.c 18 0
src/backend/optimizer/plan/createplan.c 104 0
src/backend/optimizer/plan/setrefs.c 26 1
src/backend/optimizer/plan/subselect.c 10 0
src/backend/utils/adt/ruleutils.c 41 3
src/include/executor/executor.h 2 1
src/include/executor/nodeCustom.h 94 0
src/include/nodes/bitmapset.h 4 0
src/include/nodes/execnodes.h 17 0
src/include/nodes/nodes.h 3 0
src/include/nodes/plannodes.h 16 0
src/include/nodes/primnodes.h 1 0
src/include/nodes/relation.h 16 0
src/include/optimizer/cost.h 4 0
src/include/optimizer/paths.h 25 0
src/include/optimizer/planmain.h 1 0
src/test/regress/expected/.gitignore 1 0
src/test/regress/sql/.gitignore 1 0
 doc/src/sgml/custom-scan.sgml           | 295 ++++++++++++++++++++++++++++++++
 doc/src/sgml/filelist.sgml              |   1 +
 doc/src/sgml/postgres.sgml              |   1 +
 src/backend/commands/explain.c          |  99 +++++++++++
 src/backend/executor/Makefile           |   2 +-
 src/backend/executor/execAmi.c          |  34 +++-
 src/backend/executor/execProcnode.c     |  14 ++
 src/backend/executor/execQual.c         |  10 +-
 src/backend/executor/execUtils.c        |   4 +-
 src/backend/executor/nodeCustom.c       | 252 +++++++++++++++++++++++++++
 src/backend/nodes/bitmapset.c           |  61 +++++++
 src/backend/nodes/copyfuncs.c           |  30 ++++
 src/backend/nodes/outfuncs.c            |  19 ++
 src/backend/nodes/print.c               |   4 +
 src/backend/optimizer/path/allpaths.c   |  23 +++
 src/backend/optimizer/path/costsize.c   |   7 +-
 src/backend/optimizer/path/joinpath.c   |  18 ++
 src/backend/optimizer/plan/createplan.c | 104 +++++++++++
 src/backend/optimizer/plan/setrefs.c    |  27 ++-
 src/backend/optimizer/plan/subselect.c  |  10 ++
 src/backend/utils/adt/ruleutils.c       |  44 ++++-
 src/include/executor/executor.h         |   3 +-
 src/include/executor/nodeCustom.h       |  94 ++++++++++
 src/include/nodes/bitmapset.h           |   4 +
 src/include/nodes/execnodes.h           |  17 ++
 src/include/nodes/nodes.h               |   3 +
 src/include/nodes/plannodes.h           |  16 ++
 src/include/nodes/primnodes.h           |   1 +
 src/include/nodes/relation.h            |  16 ++
 src/include/optimizer/cost.h            |   4 +
 src/include/optimizer/paths.h           |  25 +++
 src/include/optimizer/planmain.h        |   1 +
 src/test/regress/expected/.gitignore    |   1 +
 src/test/regress/sql/.gitignore         |   1 +
 34 files changed, 1225 insertions(+), 20 deletions(-)

diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
new file mode 100644
index 0000000..b57d82f
--- /dev/null
+++ b/doc/src/sgml/custom-scan.sgml
@@ -0,0 +1,295 @@
+<!-- doc/src/sgml/custom-scan.sgml -->
+
+<chapter id="custom-scan">
+ <title>Writing A Custom Scan Provider</title>
+
+ <indexterm zone="custom-scan">
+  <primary>custom scan</primary>
+  <secondary>handler for</secondary>
+ </indexterm>
+ <para>
+  The custom-scan API enables an extension to provide alternative ways to scan
+  or join relations leveraging the cost based optimizer. The API consists of a
+  set of callbacks, with a unique names, to be invoked during query planning 
+  and execution. A custom-scan provider should implement these callback 
+  functions according to the expectation of the API.
+ </para>
+ <para>
+  Overall, there are four major tasks that a custom-scan provider should 
+  implement. The first task is the registration of custom-scan provider itself.
+  Usually, this needs to be done once at the <literal>_PG_init()</literal> 
+  entrypoint when the module is loading. The remaing three tasks are all done
+  when a query is planning and executing. The second task is the submission of
+  candidate paths to either scan or join relations with an adequate cost for
+  the core planner. Then, the planner will choose the cheapest path from all of
+  the candidates. If the custom path survived, the planner starts the third 
+  task; construction of a <literal>CustomScan</literal> plan node, located
+  within the query plan tree instead of the built-in plan node. The last task
+  is the execution of its implementation in answer to invocations by the core
+  executor.
+ </para>
+ <para>
+  Some of contrib modules utilize the custom-scan API. They may provide a good
+  example for new development.
+  <variablelist>
+   <varlistentry>
+    <term><xref linkend="ctidscan"></term>
+    <listitem>
+     <para>
+      This custom scan in this module enables a scan to skip earlier pages or
+      terminate prior to end of the relation, if the inequality operator on the
+      <literal>ctid</literal> system column can narrow down the scope to be
+      scanned, instead of a sequential scan which reads a relation from the
+      head to the end.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><xref linkend="postgres-fdw"></term>
+    <listitem>
+     <para>
+      This custom scan in this module replaces a local join of foreign tables
+      managed by <literal>postgres_fdw</literal> with a scan that fetches
+      remotely joined relations. It demostrates the way to implement a custom
+      scan node that performs join nodes.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </para>
+ <para>
+  Currently, only scan and join are fully supported with integrated cost
+  based query optimization using the custom scan API. You might be able to
+  implement other stuff, like sort or aggregation, with manipulation of the
+  planned tree, however, the extension has to be responsible to handle this
+  replacement correctly. There is no support in the core.
+ </para>
+
+ <sect1 id="custom-scan-spec">
+  <title>Custom Scan API Functions and Callbacks</title>
+  <sect2 id="custom-scan-register">
+   <title>Registration of custom scan provider</title>
+   <para>
+    The first task for a custom scan provider is the registration of a set of
+    callbacks with a unique names. Usually, this is done once upon module
+    loading in the <literal>_PG_init()</literal> entrypoint.
+<programlisting>
+void
+register_custom_provider(const CustomProvider *provider);
+</programlisting>
+    Its argument, <literal>CustomProvider</literal> structure, contains
+    a name and a set of callback function pointers but some of them are
+    optional.
+    Once registered, it is copied on the internal table, so the caller
+    does not need to keep this structure any more.
+   </para>
+  </sect2>
+
+  <sect2 id="custom-scan-path">
+   <title>Submission of custom paths</title>
+   <para>
+    The query planner finds the best way to scan or join relations from various
+    potential paths using a combination of scan algorithms and target 
+    relations. Prior to this selection, we list all of the potential paths
+    towards a target relation (if it is a base relation) or a pair of relations
+    (if it is a join). The <literal>add_scan_path_hook</> and
+    <literal>add_join_path_hook</> allow extensions to add alternative scan
+    paths in addition to built-in paths.
+    If custom-scan provider can submit a potential scan path towards the
+    supplied relation, it shall construct a <literal>CustomPath</> object
+    with appropriate parameters.
+<programlisting>
+typedef struct CustomPath
+{
+    Path        path;
+    const char *custom_name;        /* name of custom scan provider */
+    int         custom_flags;       /* CUSTOM__* flags in nodeCustom.h */
+    List       *custom_private;     /* can be used for private data */
+} CustomPath;
+</programlisting>
+    Its <literal>path</> is a common field for all the path nodes to store
+    a cost estimation. In addition, <literal>custom_name</> is the name of
+    the registered custom scan provider, <literal>custom_flags</> is a set of
+    flags below, and <literal>custom_private</> can be used to store private
+    data of the custom scan provider.
+   </para>
+   <para>
+    <variablelist>
+     <varlistentry>
+      <term><literal>CUSTOM__SUPPORT_MARK_RESTORE</></term>
+      <listitem>
+       <para>
+        It informs the query planner this custom scan node supports
+        <literal>ExecMarkPosCustomScan</> and
+        <literal>ExecRestorePosCustomScan</> methods.
+        Also, the custom scan provider has to be responsible to mark and
+        restore a particular position.
+       </para>
+      </listitem>
+     </varlistentry>
+     <varlistentry>
+      <term><literal>CUSTOM__SUPPORT_BACKWARD_SCAN</></term>
+      <listitem>
+       <para>
+        It informs the query planner this custom scan node supports
+        backward scans.
+        Also, custom scan provider has to be responsible to scan with
+        backward direction.
+       </para>
+      </listitem>
+     </varlistentry>
+    </variablelist>
+   </para>
+  </sect2>
+
+  <sect2 id="custom-scan-plan">
+   <title>Construction of custom plan node</title>
+   <para>
+    Once <literal>CustomPath</literal> was choosen by the query planner,
+    it calls back to its associated to the custom scan provider to complete 
+    setting up the <literal>CustomScan</literal> plan node according to the
+    path information.
+<programlisting>
+void
+InitCustomScanPlan(PlannerInfo *root,
+                   CustomScan *cscan_plan,
+                   CustomPath *cscan_path,
+                   List *tlist,
+                   List *scan_clauses);
+</programlisting>
+    The query planner does basic initialization on the <literal>cscan_plan</>
+    being allocated, then the custom scan provider can apply final 
+    initialization. <literal>cscan_path</> is the path node that was 
+    constructed on the previous stage then was choosen.
+    <literal>tlist</> is a list of <literal>TargetEntry</> to be assigned
+    on the <literal>Plan</> portion in the <literal>cscan_plan</>.
+    Also, <literal>scan_clauses</> is a list of <literal>RestrictInfo</> to
+    be checked during a relation scan. Its expression portion will also be
+    assigned on the <literal>Plan</> portion, but can be eliminated from
+    this list if custom scan provider can handle these checks by itself.
+   </para>
+   <para>
+    It often needs to adjust <literal>varno</> of <literal>Var</> node that
+    references a particular scan node, after construction of the plan node.
+    For example, Var node in the target list of the join node originally
+    references a particular relation underlying a join, however, it has to
+    be adjusted to either inner or outer reference.
+<programlisting>
+void
+SetPlanRefCustomScan(PlannerInfo *root,
+                     CustomScan *cscan_plan,
+                     int rtoffset);
+</programlisting>
+    This callback is optional if the custom scan node is a vanilla relation
+    scan because there is nothing special to do. Elsewhere, it needs to
+    be handled by the custom scan provider in case when a custom scan replaced
+    a join with two or more relations for example.
+   </para>
+  </sect2>
+
+  <sect2 id="custom-scan-exec">
+   <title>Execution of custom scan node</title>
+   <para>
+    The query executor also launches the associated callbacks to begin, execute
+    and end the custom scan according to the executor's manner.
+   </para>
+   <para>
+<programlisting>
+void
+BeginCustomScan(CustomScanState *csstate, int eflags);
+</programlisting>
+    It begins execution of the custom scan on starting up executor.
+    It allows the custom scan provider to do any initialization job around this
+    plan, however, it is not a good idea to launch the actual scanning jobs.
+    (It shall be done on the first invocation of <literal>ExecCustomScan</>
+    instead.)
+    The <literal>custom_state</> field of <literal>CustomScanState</> is
+    intended to save the private state being managed by the custom scan
+    provider. Also, <literal>eflags</> has flag bits of the executor's
+    operating mode for this plan node. Note that the custom scan provider
+    should not perform anything visible externally if 
+    <literal>EXEC_FLAG_EXPLAIN_ONLY</> would be given,
+   </para>
+
+   <para>
+<programlisting>
+TupleTableSlot *
+ExecCustomScan(CustomScanState *csstate);
+</programlisting>
+    It fetches one tuple from the underlying relation or relations, if joining,
+    according to the custom logic. Unlike <literal>IterateForeignScan</>
+    method in foreign table, it is also responsible to check whether the next
+    tuple matches the qualifier of this scan, or not.
+    The usual way to implement this method is the callback performs just an
+    entrypoint of <literal>ExecQual</> with its own access method.
+   </para>
+
+   <para>
+<programlisting>
+Node *
+MultiExecCustomScan(CustomScanState *csstate);
+</programlisting>
+    It fetches multiple tuples from the underlying relation or relations, if
+    joining, according to the custom logic. Pay attention the data format (and
+    the way to return also) since it depends on the type of upper node.
+   </para>
+
+   <para>
+<programlisting>
+void
+EndCustomScan(CustomScanState *csstate);
+</programlisting>
+    It ends the scan and releases resources privately allocated.
+    It is usually not important to release memory in per-execution memory
+    context. So, all this callback should be responsible is its own
+    resources regardless from the framework.
+   </para>
+  </sect2>
+
+  <sect2 id="custom-scan-misc">
+   <title>Miscellaneous jobs</title>
+   <para>
+<programlisting>
+void
+ReScanCustomScan(CustomScanState *csstate);
+</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
+MarkPosCustomScan(CustomScanState *csstate);
+</programlisting>
+    It saves the current position of the custom scan on somewhere private
+    state.
+    Note that it is optional to implement, only when
+    <literal>CUSTOM__SUPPORT_MARK_RESTORE</> is set.
+   </para>
+   <para>
+<programlisting>
+void
+RestorePosCustom(CustomScanState *csstate);
+</programlisting>
+    It rewinds the current position of the custom scan to the position
+    where <literal>MarkPosCustomScan</> was saved before.
+    Note that it is optional to implement, only when
+    <literal>CUSTOM__SUPPORT_MARK_RESTORE</> is set.
+   </para>
+   <para>
+<programlisting>
+void
+ExplainCustomScan(CustomScanState *csstate,
+                  ExplainState *es);
+</programlisting>
+    It prints additional <command>EXPLAIN</> output for a custom scan plan.
+    This callback is expected to call <literal>ExplainPropertyText</> to
+    make additional field of <command>EXPLAIN</> output.
+    The flag fields in <literal>ExplainState</> indicates what shall be
+    printed, and the state of the <literal>CustomScanState</> will provide
+    run-time statistics in the <command>EXPLAIN ANALYZE</> case.
+   </para>
+  </sect2>
+ </sect1>
+</chapter>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 09de4bd..d63b1a8 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-scan  SYSTEM "custom-scan.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..ed76d33 100644
--- a/doc/src/sgml/postgres.sgml
+++ b/doc/src/sgml/postgres.sgml
@@ -241,6 +241,7 @@
   &nls;
   &plhandler;
   &fdwhandler;
+  &custom-scan;
   &geqo;
   &indexam;
   &gist;
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 08f3167..2a6136d 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"
@@ -90,6 +91,7 @@ static void show_tidbitmap_info(BitmapHeapScanState *planstate,
 static void show_instrumentation_count(const char *qlabel, int which,
 						   PlanState *planstate, ExplainState *es);
 static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
+static void show_customscan_info(CustomScanState *cstate, ExplainState *es);
 static const char *explain_get_index_name(Oid indexId);
 static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
 						ExplainState *es);
@@ -721,6 +723,11 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
 			*rels_used = bms_add_member(*rels_used,
 										((Scan *) plan)->scanrelid);
 			break;
+		case T_CustomScan:
+			if (((Scan *) plan)->scanrelid > 0)
+				*rels_used = bms_add_member(*rels_used,
+											((Scan *) plan)->scanrelid);
+			break;
 		case T_ModifyTable:
 			/* cf ExplainModifyTarget */
 			*rels_used = bms_add_member(*rels_used,
@@ -847,6 +854,8 @@ 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;
+	char		namebuf[NAMEDATALEN + 32];
 	int			save_indent = es->indent;
 	bool		haschildren;
 
@@ -935,6 +944,13 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_ForeignScan:
 			pname = sname = "Foreign Scan";
 			break;
+		case T_CustomScan:
+			snprintf(namebuf, sizeof(namebuf), "Custom Scan (%s)",
+					 ((CustomScan *) plan)->custom_name);
+			pname = pstrdup(namebuf);
+			sname = "Custom Scan";
+		    custom_name = ((CustomScan *) plan)->custom_name;
+			break;
 		case T_Material:
 			pname = sname = "Materialize";
 			break;
@@ -1036,6 +1052,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 Provider", custom_name, es);
 	}
 
 	switch (nodeTag(plan))
@@ -1051,6 +1069,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_ForeignScan:
 			ExplainScanTarget((Scan *) plan, es);
 			break;
+		case T_CustomScan:
+			if (((Scan *) plan)->scanrelid > 0)
+				ExplainScanTarget((Scan *) plan, es);
+			break;
 		case T_IndexScan:
 			{
 				IndexScan  *indexscan = (IndexScan *) plan;
@@ -1347,6 +1369,29 @@ ExplainNode(PlanState *planstate, List *ancestors,
 										   planstate, es);
 			show_foreignscan_info((ForeignScanState *) planstate, es);
 			break;
+		case T_CustomScan:
+			if (((CustomScan *)plan)->functions != NIL && es->verbose)
+			{
+				List	   *fexprs = NIL;
+				ListCell   *lc;
+
+				foreach(lc, ((CustomScan *) plan)->functions)
+				{
+					RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
+
+					fexprs = lappend(fexprs, rtfunc->funcexpr);
+				}
+				/* We rely on show_expression to insert commas as needed */
+				show_expression((Node *) fexprs,
+								"Function Call", planstate, ancestors,
+								es->verbose, es);
+			}
+			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
+			if (plan->qual)
+				show_instrumentation_count("Rows Removed by Filter", 1,
+										   planstate, es);
+			show_customscan_info((CustomScanState *) planstate, es);
+			break;
 		case T_NestLoop:
 			show_upper_qual(((NestLoop *) plan)->join.joinqual,
 							"Join Filter", planstate, ancestors, es);
@@ -1986,6 +2031,19 @@ show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
 }
 
 /*
+ * Show extra information for a CustomScan node.
+ */
+static void
+show_customscan_info(CustomScanState *cstate, ExplainState *es)
+{
+	CustomProvider *provider = cstate->custom_provider;
+
+	/* Let custom scan provider emit whatever fields it wants */
+	if (provider->ExplainCustomScan != NULL)
+		provider->ExplainCustomScan(cstate, es);
+}
+
+/*
  * Fetch the name of an index in an EXPLAIN
  *
  * We allow plugins to get control here so that plans involving hypothetical
@@ -2158,6 +2216,47 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
 			objectname = rte->ctename;
 			objecttag = "CTE Name";
 			break;
+		case T_CustomScan:
+			if (rte->rtekind == RTE_RELATION)
+			{
+				objectname = get_rel_name(rte->relid);
+				if (es->verbose)
+					namespace =
+						get_namespace_name(get_rel_namespace(rte->relid));
+				objecttag = "Relation Name";
+			}
+			else if (rte->rtekind == RTE_JOIN)
+			{
+				objectname = rte->eref->aliasname;
+				objecttag = "Join Alias";
+			}
+			else if (rte->rtekind == RTE_FUNCTION)
+			{
+				List	   *functions = ((CustomScan *) plan)->functions;
+
+				if (functions && list_length(functions) == 1)
+				{
+					RangeTblFunction *rtfunc = linitial(functions);
+
+					if (IsA(rtfunc->funcexpr, FuncExpr))
+					{
+						FuncExpr   *funcexpr = (FuncExpr *) rtfunc->funcexpr;
+						Oid			funcid = funcexpr->funcid;
+
+						objectname = get_func_name(funcid);
+						if (es->verbose)
+							namespace =
+								get_namespace_name(get_func_namespace(funcid));
+					}
+				}
+				objecttag = "Function Name";
+			}
+			else if (rte->rtekind == RTE_CTE)
+			{
+				objectname = rte->ctename;
+				objecttag = "CTE Name";
+			}
+			break;
 		default:
 			break;
 	}
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..2443e24 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_CustomScanState:
+			ExecReScanCustomScan((CustomScanState *) node);
+			break;
+
 		case T_NestLoopState:
 			ExecReScanNestLoop((NestLoopState *) node);
 			break;
@@ -291,6 +296,10 @@ ExecMarkPos(PlanState *node)
 			ExecValuesMarkPos((ValuesScanState *) node);
 			break;
 
+		case T_CustomScanState:
+			ExecCustomMarkPos((CustomScanState *) node);
+			break;
+
 		case T_MaterialState:
 			ExecMaterialMarkPos((MaterialState *) node);
 			break;
@@ -348,6 +357,10 @@ ExecRestrPos(PlanState *node)
 			ExecValuesRestrPos((ValuesScanState *) node);
 			break;
 
+		case T_CustomScanState:
+			ExecCustomRestrPos((CustomScanState *) node);
+			break;
+
 		case T_MaterialState:
 			ExecMaterialRestrPos((MaterialState *) node);
 			break;
@@ -379,9 +392,9 @@ ExecRestrPos(PlanState *node)
  * and valuesscan support is actually useless code at present.)
  */
 bool
-ExecSupportsMarkRestore(NodeTag plantype)
+ExecSupportsMarkRestore(Path *path)
 {
-	switch (plantype)
+	switch (path->pathtype)
 	{
 		case T_SeqScan:
 		case T_IndexScan:
@@ -392,6 +405,14 @@ ExecSupportsMarkRestore(NodeTag plantype)
 		case T_Sort:
 			return true;
 
+		case T_CustomPath:
+			{
+				int	flags = ((CustomPath *) path)->custom_flags;
+				if (flags & CUSTOM__SUPPORT_MARK_RESTORE)
+					return true;
+				return false;
+			}
+
 		case T_Result:
 
 			/*
@@ -465,6 +486,15 @@ ExecSupportsBackwardScan(Plan *node)
 			return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) &&
 				TargetListSupportsBackwardScan(node->targetlist);
 
+		case T_CustomScan:
+			{
+				int		flags = ((CustomScan *) node)->custom_flags;
+
+				if (flags & CUSTOM__SUPPORT_BACKWARD_SCAN)
+					return TargetListSupportsBackwardScan(node->targetlist);
+			}
+			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..b4a7411 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,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 													   estate, eflags);
 			break;
 
+		case T_CustomScan:
+			result = (PlanState *) ExecInitCustomScan((CustomScan *) node,
+													  estate, eflags);
+			break;
+
 			/*
 			 * join nodes
 			 */
@@ -442,6 +448,10 @@ ExecProcNode(PlanState *node)
 			result = ExecForeignScan((ForeignScanState *) node);
 			break;
 
+		case T_CustomScanState:
+			result = ExecCustomScan((CustomScanState *) node);
+			break;
+
 			/*
 			 * join nodes
 			 */
@@ -678,6 +688,10 @@ ExecEndNode(PlanState *node)
 			ExecEndForeignScan((ForeignScanState *) node);
 			break;
 
+		case T_CustomScanState:
+			ExecEndCustomScan((CustomScanState *) node);
+			break;
+
 			/*
 			 * join nodes
 			 */
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 0eba025..e71ce9b 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -592,7 +592,7 @@ ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
 			slot = econtext->ecxt_outertuple;
 			break;
 
-			/* INDEX_VAR is handled by default case */
+			/* INDEX_VAR and CUSTOM_VAR are handled by default case */
 
 		default:				/* get the tuple from the relation being
 								 * scanned */
@@ -680,7 +680,7 @@ ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
 			slot = econtext->ecxt_outertuple;
 			break;
 
-			/* INDEX_VAR is handled by default case */
+			/* INDEX_VAR and CUSTOM_VAR are handled by default case */
 
 		default:				/* get the tuple from the relation being
 								 * scanned */
@@ -732,7 +732,7 @@ ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
 			slot = econtext->ecxt_outertuple;
 			break;
 
-			/* INDEX_VAR is handled by default case */
+			/* INDEX_VAR and CUSTOM_VAR are handled by default case */
 
 		default:				/* get the tuple from the relation being
 								 * scanned */
@@ -915,7 +915,7 @@ ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
 			slot = econtext->ecxt_outertuple;
 			break;
 
-			/* INDEX_VAR is handled by default case */
+			/* INDEX_VAR and CUSTOM_VAR are handled by default case */
 
 		default:				/* get the tuple from the relation being
 								 * scanned */
@@ -991,7 +991,7 @@ ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
 			slot = econtext->ecxt_outertuple;
 			break;
 
-			/* INDEX_VAR is handled by default case */
+			/* INDEX_VAR and CUSTOM_VAR are handled by default case */
 
 		default:				/* get the tuple from the relation being
 								 * scanned */
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 46895b2..58d7190 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -578,7 +578,7 @@ ExecBuildProjectionInfo(List *targetList,
 						projInfo->pi_lastOuterVar = attnum;
 					break;
 
-					/* INDEX_VAR is handled by default case */
+					/* INDEX_VAR and CUSTOM_VAR are handled by default case */
 
 				default:
 					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
@@ -638,7 +638,7 @@ get_last_attnums(Node *node, ProjectionInfo *projInfo)
 					projInfo->pi_lastOuterVar = attnum;
 				break;
 
-				/* INDEX_VAR is handled by default case */
+				/* INDEX_VAR and CUSTOM_VAR are handled by default case */
 
 			default:
 				if (projInfo->pi_lastScanVar < attnum)
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
new file mode 100644
index 0000000..2d89d7a
--- /dev/null
+++ b/src/backend/executor/nodeCustom.c
@@ -0,0 +1,252 @@
+/* ------------------------------------------------------------------------
+ *
+ * nodeCustom.c
+ *    Routines to handle execution of custom plan, scan and join 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/nodeCustom.h"
+#include "parser/parsetree.h"
+#include "utils/hsearch.h"
+#include "utils/memutils.h"
+#include "utils/rel.h"
+
+/* static variables */
+static HTAB *custom_provider_hash = NULL;
+
+/*
+ * register_custom_provider
+ *
+ * It registers a custom execution provider; that consists of a set of
+ * callbacks and is identified with a unique name.
+ */
+void
+register_custom_provider(const CustomProvider *provider)
+{
+	CustomProvider *entry;
+	bool			found;
+
+	if (!custom_provider_hash)
+	{
+		HASHCTL		ctl;
+
+		memset(&ctl, 0, sizeof(ctl));
+		ctl.hcxt = CacheMemoryContext;
+		ctl.keysize = NAMEDATALEN;
+		ctl.entrysize = sizeof(CustomProvider);
+
+		custom_provider_hash = hash_create("custom execution providers",
+										   32,
+										   &ctl,
+										   HASH_ELEM | HASH_CONTEXT);
+	}
+
+	entry = hash_search(custom_provider_hash,
+						provider->name,
+						HASH_ENTER, &found);
+	if (found)
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("duplicate custom execution provider \"%s\"",
+						provider->name)));
+
+	Assert(strcmp(provider->name, entry->name) == 0);
+	memcpy(entry, provider, sizeof(CustomProvider));
+}
+
+/*
+ * get_custom_provider
+ *
+ * It finds a registered custom execution provide by its name
+ */
+CustomProvider *
+get_custom_provider(const char *custom_name)
+{
+	CustomProvider *entry;
+
+	/* lookup custom execution provider */
+	if (!custom_provider_hash)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("no custom execution provider was registered")));
+
+	entry = (CustomProvider *) hash_search(custom_provider_hash,
+										   custom_name, HASH_FIND, NULL);
+	if (!entry)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_OBJECT),
+				 errmsg("custom execution provider \"%s\" was not registered",
+						custom_name)));
+
+	return entry;
+}
+
+/*
+ * ExecInitCustomScan
+ *
+ * Allocation of CustomScanState and various initialization stuff.
+ * Note that some of initialization jobs are skipped if scanrelid is zero
+ * (that means this custom scan plan is not associated with a particular
+ * relation in range-table list.)
+ */
+CustomScanState *
+ExecInitCustomScan(CustomScan *node, EState *estate, int eflags)
+{
+	CustomProvider	   *provider = get_custom_provider(node->custom_name);
+	CustomScanState	   *csstate;
+	Plan			   *plan = &node->scan.plan;
+	Index				scanrelid = node->scan.scanrelid;
+
+	/*
+	 * Create state structure
+	 */
+	csstate = makeNode(CustomScanState);
+	csstate->ss.ps.plan = plan;
+	csstate->ss.ps.state = estate;
+	csstate->custom_provider = provider;
+	csstate->custom_flags = node->custom_flags;
+	csstate->custom_state = NULL;
+
+	/*
+	 * Miscellaneous initialization
+	 */
+	ExecAssignExprContext(estate, &csstate->ss.ps);
+
+	/*
+	 * Initialization of child expressions
+	 */
+	csstate->ss.ps.targetlist =
+		(List *) ExecInitExpr((Expr *) plan->targetlist, &csstate->ss.ps);
+	csstate->ss.ps.qual =
+		(List *) ExecInitExpr((Expr *) plan->qual, &csstate->ss.ps);
+
+	/*
+	 * tuple table initialization
+	 *
+	 * Note that ss_ScanTupleSlot is set only when scanrelid is associated
+	 * with a particular relation. Elsewhere, it needs to be initialized by
+	 * custom-scan provider itself if it internally uses ss_ScanTupleSlot.
+	 * If it replaces varno of Var node by CUSTOM_VAR, it has to be set to
+	 * reference underlying attribute name to generate EXPLAIN output.
+	 */
+	ExecInitResultTupleSlot(estate, &csstate->ss.ps);
+	if (scanrelid > 0)
+		ExecInitScanTupleSlot(estate, &csstate->ss);
+
+	/*
+	 * open the base relation and acquire appropriate lock on it,
+	 * if this custom scan is connected with a particular relaion.
+	 * Also, assign its scan type according to the table definition.
+	 */
+	if (scanrelid > 0)
+	{
+		Relation	rel = ExecOpenScanRelation(estate, scanrelid, eflags);
+
+		csstate->ss.ss_currentRelation = rel;
+		ExecAssignScanType(&csstate->ss, RelationGetDescr(rel));
+
+		csstate->ss.ps.ps_TupFromTlist = false;
+	}
+
+	/*
+	 * Initialize result tuple type and projection info.
+	 */
+	ExecAssignResultTypeFromTL(&csstate->ss.ps);
+
+	if (scanrelid > 0)
+		ExecAssignScanProjectionInfo(&csstate->ss);
+	else
+		ExecAssignProjectionInfo(&csstate->ss.ps, NULL);
+
+	/*
+	 * Final initialization based on callback of BeginCustomScan method.
+	 * Extension may be able to override initialization stuff above, if
+	 * needed.
+	 */
+	csstate->custom_provider->BeginCustomScan(csstate, eflags);
+
+	return csstate;
+}
+
+/*
+ * ExecCustomScan
+ *
+ * Just an entrypoint of ExecCustomScan method. All the stuff to fetch
+ * a tuple is a job of custom-scan provider.
+ */
+TupleTableSlot *
+ExecCustomScan(CustomScanState *csstate)
+{
+	return csstate->custom_provider->ExecCustomScan(csstate);
+}
+
+/*
+ * MultiExecCustomScan
+ *
+ * Aldo, just an entrypoint of MultiExecCustomScan method. All the stuff
+ * to fetch multiple tuples (according to expectation of upper node) is
+ * a job of custom-scan provider.
+ */
+Node *
+MultiExecCustomScan(CustomScanState *csstate)
+{
+	return csstate->custom_provider->MultiExecCustomScan(csstate);
+}
+
+/*
+ * ExecEndCustomScan
+ *
+ * It releases all the resources allocated on this scan.
+ */
+void
+ExecEndCustomScan(CustomScanState *csstate)
+{
+	/* Let the custom-exec shut down */
+	csstate->custom_provider->EndCustomScan(csstate);
+
+	/* Free the exprcontext */
+	ExecFreeExprContext(&csstate->ss.ps);
+
+	/* Clean out the tuple table, if exists */
+	ExecClearTuple(csstate->ss.ps.ps_ResultTupleSlot);
+	if (csstate->ss.ss_ScanTupleSlot)
+		ExecClearTuple(csstate->ss.ss_ScanTupleSlot);
+
+	/* close the relation, if opened */
+	if (csstate->ss.ss_currentRelation)
+		ExecCloseScanRelation(csstate->ss.ss_currentRelation);
+}
+
+/*
+ * ExecReScanCustomScan
+ */
+void
+ExecReScanCustomScan(CustomScanState *csstate)
+{
+	csstate->custom_provider->ReScanCustomScan(csstate);
+}
+
+/*
+ * ExecCustomMarkPos
+ */
+void
+ExecCustomMarkPos(CustomScanState *csstate)
+{
+	Assert((csstate->custom_flags & CUSTOM__SUPPORT_MARK_RESTORE) != 0);
+	csstate->custom_provider->MarkPosCustomScan(csstate);
+}
+
+/*
+ * ExecCustomRestrPos
+ */
+void
+ExecCustomRestrPos(CustomScanState *csstate)
+{
+	Assert((csstate->custom_flags & CUSTOM__SUPPORT_MARK_RESTORE) != 0);
+	csstate->custom_provider->RestorePosCustom(csstate);
+}
diff --git a/src/backend/nodes/bitmapset.c b/src/backend/nodes/bitmapset.c
index 3a6d0fb..3a1465e 100644
--- a/src/backend/nodes/bitmapset.c
+++ b/src/backend/nodes/bitmapset.c
@@ -865,3 +865,64 @@ bms_hash_value(const Bitmapset *a)
 	return DatumGetUInt32(hash_any((const unsigned char *) a->words,
 								   (lastword + 1) * sizeof(bitmapword)));
 }
+
+/*
+ * bms_to_string / bms_from_string - transform bitmapset to/from text form
+ */
+char *
+bms_to_string(Bitmapset *a)
+{
+	char   *result;
+	char   *pos;
+	int		i;
+
+	if (bms_is_empty(a))
+		return NULL;
+
+	result = palloc(a->nwords * (BITS_PER_BITMAPWORD / 4) + 1);
+	for (i = a->nwords, pos = result; i > 0; i--)
+		pos += sprintf(pos, "%08x", a->words[i - 1]);
+
+	return result;
+}
+
+Bitmapset *
+bms_from_string(const char *a)
+{
+	Bitmapset  *result;
+	Size		len;
+	int			nwords;
+	int			i, offset = 0;
+
+	if (a == NULL)
+		return NULL;
+
+	len = strlen(a);
+	if (len % (BITS_PER_BITMAPWORD / 4) != 0)
+		elog(WARNING, "strange bitmapset text form: %s", a);
+
+	nwords = (len + BITS_PER_BITMAPWORD / 4 - 1) / (BITS_PER_BITMAPWORD / 4);
+	result = palloc(BITMAPSET_SIZE(nwords));
+	result->nwords = nwords;
+
+	for (i=result->nwords; i > 0; i--)
+	{
+		bitmapword	word = 0;
+
+		do {
+			int		c = a[offset++];
+
+			if (c >= '0' && c <= '9')
+				word = (word << 4) | (c - '0');
+			else if (c >= 'a' && c <= 'f')
+				word = (word << 4) | (c - 'a' + 10);
+			else if (c >= 'A' && c <= 'F')
+				word = (word << 4) | (c - 'A' + 10);
+			else
+				elog(ERROR, "invalid hexadecimal digit");
+		} while ((len - offset) % (BITS_PER_BITMAPWORD / 4) != 0);
+
+		result->words[i - 1] = word;
+	}
+	return result;
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c89d808..d48b3d7 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -598,6 +598,33 @@ _copyForeignScan(const ForeignScan *from)
 }
 
 /*
+ * _copyCustomScan
+ */
+static CustomScan *
+_copyCustomScan(const CustomScan *from)
+{
+	CustomScan *newnode = makeNode(CustomScan);
+
+	/*
+	 * copy node superclass fields
+	 */
+	CopyScanFields((const Scan *) from, (Scan *) newnode);
+
+	/*
+	 * copy remainder of node
+	 */
+	COPY_STRING_FIELD(custom_name);
+	COPY_SCALAR_FIELD(custom_flags);
+	COPY_NODE_FIELD(custom_private);
+	COPY_NODE_FIELD(custom_exprs);
+
+	COPY_NODE_FIELD(subqry_plan);
+	COPY_NODE_FIELD(functions);
+
+	return newnode;
+}
+
+/*
  * CopyJoinFields
  *
  *		This function copies the fields of the Join node.  It is used by
@@ -3983,6 +4010,9 @@ copyObject(const void *from)
 		case T_ForeignScan:
 			retval = _copyForeignScan(from);
 			break;
+		case T_CustomScan:
+			retval = _copyCustomScan(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..7dc1631 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -564,6 +564,22 @@ _outForeignScan(StringInfo str, const ForeignScan *node)
 }
 
 static void
+_outCustomScan(StringInfo str, const CustomScan *node)
+{
+	WRITE_NODE_TYPE("CUSTOMSCAN");
+
+	_outScanInfo(str, (const Scan *) node);
+
+	WRITE_STRING_FIELD(custom_name);
+	WRITE_INT_FIELD(custom_flags);
+	WRITE_NODE_FIELD(custom_private);
+	WRITE_NODE_FIELD(custom_exprs);
+
+	WRITE_NODE_FIELD(subqry_plan);
+	WRITE_NODE_FIELD(functions);
+}
+
+static void
 _outJoin(StringInfo str, const Join *node)
 {
 	WRITE_NODE_TYPE("JOIN");
@@ -2828,6 +2844,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_ForeignScan:
 				_outForeignScan(str, obj);
 				break;
+			case T_CustomScan:
+				_outCustomScan(str, obj);
+				break;
 			case T_Join:
 				_outJoin(str, obj);
 				break;
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index 9f7f322..9f2b6bb 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -333,6 +333,10 @@ print_expr(const Node *expr, const List *rtable)
 				relname = "INDEX";
 				attname = "?";
 				break;
+			case CUSTOM_VAR:
+				relname = "CUSTOM";
+				attname = "?";
+				break;
 			default:
 				{
 					RangeTblEntry *rte;
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 03be7b1..6201a97 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);
@@ -389,6 +391,9 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	/* Consider TID scans */
 	create_tidscan_paths(root, rel);
 
+	/* Consider Custom scans */
+	add_custom_scan_paths(root, rel, rte);
+
 	/* Now find the cheapest of the paths for this rel */
 	set_cheapest(rel);
 }
@@ -417,6 +422,9 @@ 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);
 
+	/* Consider Custom scans */
+	add_custom_scan_paths(root, rel, rte);
+
 	/* Select cheapest path */
 	set_cheapest(rel);
 }
@@ -1236,6 +1244,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
 	/* Generate appropriate path */
 	add_path(rel, create_subqueryscan_path(root, rel, pathkeys, required_outer));
 
+	/* Consider Custom scans */
+	add_custom_scan_paths(root, rel, rte);
+
 	/* Select cheapest path (pretty easy in this case...) */
 	set_cheapest(rel);
 }
@@ -1307,6 +1318,9 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	add_path(rel, create_functionscan_path(root, rel,
 										   pathkeys, required_outer));
 
+	/* Consider Custom scans */
+	add_custom_scan_paths(root, rel, rte);
+
 	/* Select cheapest path (pretty easy in this case...) */
 	set_cheapest(rel);
 }
@@ -1330,6 +1344,9 @@ set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	/* Generate appropriate path */
 	add_path(rel, create_valuesscan_path(root, rel, required_outer));
 
+	/* Consider Custom scans */
+	add_custom_scan_paths(root, rel, rte);
+
 	/* Select cheapest path (pretty easy in this case...) */
 	set_cheapest(rel);
 }
@@ -1399,6 +1416,9 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	/* Generate appropriate path */
 	add_path(rel, create_ctescan_path(root, rel, required_outer));
 
+	/* Consider Custom scans */
+	add_custom_scan_paths(root, rel, rte);
+
 	/* Select cheapest path (pretty easy in this case...) */
 	set_cheapest(rel);
 }
@@ -1452,6 +1472,9 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	/* Generate appropriate path */
 	add_path(rel, create_worktablescan_path(root, rel, required_outer));
 
+	/* Consider Custom scans */
+	add_custom_scan_paths(root, rel, rte);
+
 	/* Select cheapest path (pretty easy in this case...) */
 	set_cheapest(rel);
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 9bca968..c42dc9e 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -132,9 +132,6 @@ static MergeScanSelCache *cached_scansel(PlannerInfo *root,
 static void cost_rescan(PlannerInfo *root, Path *path,
 			Cost *rescan_startup_cost, Cost *rescan_total_cost);
 static bool cost_qual_eval_walker(Node *node, cost_qual_eval_context *context);
-static void get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
-						  ParamPathInfo *param_info,
-						  QualCost *qpqual_cost);
 static bool has_indexed_join_quals(NestPath *joinpath);
 static double approx_tuple_count(PlannerInfo *root, JoinPath *path,
 				   List *quals);
@@ -2268,7 +2265,7 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path,
 	 * it off does not entitle us to deliver an invalid plan.
 	 */
 	else if (innersortkeys == NIL &&
-			 !ExecSupportsMarkRestore(inner_path->pathtype))
+			 !ExecSupportsMarkRestore(inner_path))
 		path->materialize_inner = true;
 
 	/*
@@ -3157,7 +3154,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
  * some of the quals.  We assume baserestrictcost was previously set by
  * set_baserel_size_estimates().
  */
-static void
+void
 get_restriction_qual_cost(PlannerInfo *root, RelOptInfo *baserel,
 						  ParamPathInfo *param_info,
 						  QualCost *qpqual_cost)
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index a996116..48f5ad4 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,22 @@ 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,
+							  mergeclause_list,
+							  &semifactors,
+							  param_source_rels,
+							  extra_lateral_rels);
 }
 
 /*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 184d37a..c07e000 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -21,6 +21,7 @@
 
 #include "access/skey.h"
 #include "catalog/pg_class.h"
+#include "executor/nodeCustom.h"
 #include "foreign/fdwapi.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
@@ -77,6 +78,9 @@ 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 CustomScan *create_customscan_plan(PlannerInfo *root,
+										  CustomPath *best_path,
+										  List *tlist, List *scan_clauses);
 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,
@@ -233,6 +237,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
 		case T_CteScan:
 		case T_WorkTableScan:
 		case T_ForeignScan:
+		case T_CustomScan:
 			plan = create_scan_plan(root, best_path);
 			break;
 		case T_HashJoin:
@@ -409,6 +414,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
 													scan_clauses);
 			break;
 
+		case T_CustomScan:
+			plan = (Plan *) create_customscan_plan(root,
+												   (CustomPath *) best_path,
+												   tlist,
+												   scan_clauses);
+			break;
+
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) best_path->pathtype);
@@ -2006,6 +2018,98 @@ create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 	return scan_plan;
 }
 
+/*
+ * create_customscan_plan
+ *   Returns a custom-scan plan for the base relation scanned by 'best_path'
+ *   with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static CustomScan *
+create_customscan_plan(PlannerInfo *root,
+					   CustomPath *best_path,
+					   List *tlist,
+					   List *scan_clauses)
+{
+	CustomProvider *provider = get_custom_provider(best_path->custom_name);
+	CustomScan	   *scan_plan = makeNode(CustomScan);
+	RelOptKind		reloptkind = best_path->path.parent->reloptkind;
+	RangeTblEntry  *rte;
+	Index			scan_relid;
+
+	if (reloptkind == RELOPT_BASEREL ||
+		reloptkind == RELOPT_OTHER_MEMBER_REL)
+	{
+		scan_relid = best_path->path.parent->relid;
+
+		rte = planner_rt_fetch(scan_relid, root);
+		/*
+		 * For EXPLAIN output, we save various information in CustomScan plan
+		 * structure. Custom-scan provider can utilize them, but it is not
+		 * recommendablt to adjust.
+		 */
+		if (rte->rtekind == RTE_SUBQUERY)
+		{
+			if (best_path->path.param_info)
+			{
+				List   *subplan_params
+					= best_path->path.parent->subplan_params;
+				process_subquery_nestloop_params(root, subplan_params);
+			}
+			scan_plan->subqry_plan = best_path->path.parent->subplan;
+		}
+		else if (rte->rtekind == RTE_FUNCTION)
+		{
+			List   *functions = rte->functions;
+
+			if (best_path->path.param_info)
+				functions = (List *)
+					replace_nestloop_params(root, (Node *)functions);
+			scan_plan->functions = functions;
+		}
+	}
+	else if (reloptkind == RELOPT_JOINREL)
+		scan_relid = 0;
+	else
+		elog(ERROR, "unexpected reloptkind: %d", (int)reloptkind);
+
+	scan_clauses = order_qual_clauses(root, scan_clauses);
+	scan_plan->scan.plan.targetlist = NULL;	/* to be set by callback */
+	scan_plan->scan.plan.qual = NULL;		/* to be set by callback */
+	scan_plan->scan.plan.lefttree = NULL;
+	scan_plan->scan.plan.righttree = NULL;
+	scan_plan->scan.scanrelid = scan_relid;
+
+	scan_plan->custom_name = pstrdup(best_path->custom_name);
+	scan_plan->custom_flags = best_path->custom_flags;
+	scan_plan->custom_private = NIL;
+	scan_plan->custom_exprs = NULL;
+
+	/*
+	 * Let custom scan provider perform to set up this custom-scan plan
+	 * according to the given path information.
+	 */
+	provider->InitCustomScanPlan(root, scan_plan,
+								 best_path, tlist, scan_clauses);
+
+	/* Copy cost data from Path to Plan; no need to make callback do this */
+	copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+
+	/*
+	 * Replace any outer-relation variables with nestloop params in the qual
+	 * and custom_exprs expressions.  We do this last so that the FDW doesn't
+	 * have to be involved.  (Note that parts of custom_exprs could have come
+	 * from join clauses, so doing this beforehand on the scan_clauses
+	 * wouldn't work.)
+	 */
+	if (best_path->path.param_info)
+	{
+		scan_plan->scan.plan.qual = (List *)
+			replace_nestloop_params(root, (Node *) scan_plan->scan.plan.qual);
+		scan_plan->custom_exprs = (List *)
+			replace_nestloop_params(root, (Node *) scan_plan->custom_exprs);
+	}
+
+	return scan_plan;
+}
 
 /*****************************************************************************
  *
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 46affe7..ee3fbab 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"
@@ -576,6 +577,30 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 			}
 			break;
 
+		case T_CustomScan:
+			{
+				CustomScan	   *splan = (CustomScan *) plan;
+				CustomProvider *provider
+					= get_custom_provider(splan->custom_name);
+
+				if (provider->SetPlanRefCustomScan)
+					provider->SetPlanRefCustomScan(root, splan, rtoffset);
+				else if (splan->scan.scanrelid > 0)
+				{
+					splan->scan.scanrelid += rtoffset;
+					splan->scan.plan.targetlist =
+						fix_scan_list(root, splan->scan.plan.targetlist,
+									  rtoffset);
+					splan->scan.plan.qual =
+						fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+					splan->custom_exprs =
+						fix_scan_list(root, splan->custom_exprs, rtoffset);
+				}
+				else
+					elog(ERROR, "No implementation to set plan reference");
+			}
+			break;
+
 		case T_NestLoop:
 		case T_MergeJoin:
 		case T_HashJoin:
@@ -1057,7 +1082,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 */
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index a3f3583..74ff415 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2236,6 +2236,16 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			context.paramids = bms_add_members(context.paramids, scan_params);
 			break;
 
+		case T_CustomScan:
+			finalize_primnode((Node *) ((CustomScan *) plan)->custom_exprs,
+							  &context);
+			context.paramids = bms_add_members(context.paramids, scan_params);
+			/*
+			 * XXX - Is it sufficient to do? Don't we need something special
+			 * if CustomScan override FunctionScan or SubqueryScan.
+			 */
+			break;
+
 		case T_ModifyTable:
 			{
 				ModifyTable *mtplan = (ModifyTable *) plan;
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index add5cd1..d099d16 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -145,6 +145,7 @@ typedef struct
 	List	   *outer_tlist;	/* referent for OUTER_VAR Vars */
 	List	   *inner_tlist;	/* referent for INNER_VAR Vars */
 	List	   *index_tlist;	/* referent for INDEX_VAR Vars */
+	TupleDesc	custom_tupdesc;	/* referent for CUSTOM_VAR Vars */
 } deparse_namespace;
 
 /*
@@ -2482,14 +2483,19 @@ deparse_context_for(const char *aliasname, Oid relid)
  * deparse_context_for_planstate	- Build deparse context for a plan
  *
  * When deparsing an expression in a Plan tree, we might have to resolve
- * OUTER_VAR, INNER_VAR, or INDEX_VAR references.  To do this, the caller must
- * provide the parent PlanState node.  Then OUTER_VAR and INNER_VAR references
- * can be resolved by drilling down into the left and right child plans.
+ * special varno (OUTER_VAR, INNER_VAR, INDEX_VAR or CUSTOM_VAR) references.
+ * To do this, the caller must provide the parent PlanState node.  Then
+ * OUTER_VAR and INNER_VAR references can be resolved by drilling down into
+ * the left and right child plans.
  * Similarly, INDEX_VAR references can be resolved by reference to the
  * indextlist given in the parent IndexOnlyScan node.  (Note that we don't
  * currently support deparsing of indexquals in regular IndexScan or
  * BitmapIndexScan nodes; for those, we can only deparse the indexqualorig
  * fields, which won't contain INDEX_VAR Vars.)
+ * Also, CUSTOM_VAR references can be resolved by reference to the TupleDesc
+ * of ss_ScanTupleSlot in CustomScanState node. (Note that custom scan
+ * provider must be responsible to initialize the ss_ScanTupleSlot with
+ * appropriate TupleDesc; being likely constructed by ExecTypeFromTL).
  *
  * Note: planstate really ought to be declared as "PlanState *", but we use
  * "Node *" to avoid having to include execnodes.h in builtins.h.
@@ -3747,6 +3753,14 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
 		dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist;
 	else
 		dpns->index_tlist = NIL;
+
+	/* custom_tupdesc is set only if it's an CustomScan */
+	if (IsA(ps, CustomScanState) &&
+		((CustomScanState *)ps)->ss.ss_ScanTupleSlot)
+		dpns->custom_tupdesc =
+			((CustomScanState *)ps)->ss.ss_ScanTupleSlot->tts_tupleDescriptor;
+	else
+		dpns->custom_tupdesc = NULL;
 }
 
 /*
@@ -5414,6 +5428,18 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
 
 		return NULL;
 	}
+	else if (var->varno == CUSTOM_VAR && dpns->custom_tupdesc)
+	{
+		TupleDesc	tupdesc = dpns->custom_tupdesc;
+
+		Assert(netlevelsup == 0);
+		Assert(var->varattno > 0 && var->varattno <= tupdesc->natts);
+
+		attname = NameStr(tupdesc->attrs[var->varattno - 1]->attname);
+		appendStringInfoString(buf, quote_identifier(attname));
+
+		return attname;
+	}
 	else
 	{
 		elog(ERROR, "bogus varno: %d", var->varno);
@@ -5684,6 +5710,18 @@ get_name_for_var_field(Var *var, int fieldno,
 
 		return result;
 	}
+	else if (var->varno == CUSTOM_VAR && dpns->custom_tupdesc)
+	{
+		TupleDesc	tupdesc = dpns->custom_tupdesc;
+		const char *result;
+
+		Assert(netlevelsup == 0);
+		Assert(var->varattno > 0 && var->varattno <= tupdesc->natts);
+
+		result = NameStr(tupdesc->attrs[var->varattno - 1]->attname);
+
+		return result;
+	}
 	else
 	{
 		elog(ERROR, "bogus varno: %d", var->varno);
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index eb78776..7fe0998 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -16,6 +16,7 @@
 
 #include "executor/execdesc.h"
 #include "nodes/parsenodes.h"
+#include "nodes/relation.h"
 
 
 /*
@@ -102,7 +103,7 @@ extern PGDLLIMPORT ExecutorCheckPerms_hook_type ExecutorCheckPerms_hook;
 extern void ExecReScan(PlanState *node);
 extern void ExecMarkPos(PlanState *node);
 extern void ExecRestrPos(PlanState *node);
-extern bool ExecSupportsMarkRestore(NodeTag plantype);
+extern bool ExecSupportsMarkRestore(Path *path);
 extern bool ExecSupportsBackwardScan(Plan *node);
 extern bool ExecMaterializesOutput(NodeTag plantype);
 
diff --git a/src/include/executor/nodeCustom.h b/src/include/executor/nodeCustom.h
new file mode 100644
index 0000000..a484f8b
--- /dev/null
+++ b/src/include/executor/nodeCustom.h
@@ -0,0 +1,94 @@
+/* ------------------------------------------------------------------------
+ *
+ * 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 "commands/explain.h"
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
+
+/*
+ * Definition of the custom execution provider callbacks
+ */
+typedef void (*InitCustomScanPlan_function)(PlannerInfo *root,
+											CustomScan *cscan_plan,
+											CustomPath *cscan_path,
+											List *tlist,
+											List *scan_clauses);
+typedef void (*SetPlanRefCustomScan_function)(PlannerInfo *root,
+											  CustomScan *cscan_plan,
+											  int rtoffset);
+typedef void (*BeginCustomScan_function)(CustomScanState *csstate, int eflags);
+typedef TupleTableSlot *(*ExecCustomScan_function)(CustomScanState *csstate);
+typedef Node *(*MultiExecCustomScan_function)(CustomScanState *csstate);
+typedef void (*EndCustomScan_function)(CustomScanState *csstate);
+
+typedef void (*ReScanCustomScan_function)(CustomScanState *csstate);
+typedef void (*MarkPosCustomScan_function)(CustomScanState *csstate);
+typedef void (*RestorePosCustom_function)(CustomScanState *csstate);
+
+typedef void (*ExplainCustomScan_function)(CustomScanState *csstate,
+										   ExplainState *es);
+
+typedef struct CustomProvider
+{
+	char							name[NAMEDATALEN];
+
+	InitCustomScanPlan_function		InitCustomScanPlan;
+	SetPlanRefCustomScan_function	SetPlanRefCustomScan;
+
+	BeginCustomScan_function		BeginCustomScan;
+	ExecCustomScan_function			ExecCustomScan;
+	MultiExecCustomScan_function	MultiExecCustomScan;
+	EndCustomScan_function			EndCustomScan;
+
+	ReScanCustomScan_function		ReScanCustomScan;
+	MarkPosCustomScan_function		MarkPosCustomScan;
+	RestorePosCustom_function		RestorePosCustom;
+
+	ExplainCustomScan_function		ExplainCustomScan;
+} CustomProvider;
+
+/* Flags of CustomScan */
+
+/*
+ * CUSTOM__SUPPORT_MARK_RESTORE informs optimizer this custom scan provider
+ * support ExecCustomMarkPos and ExecCustomRestrPos callbacks.
+ */
+#define CUSTOM__SUPPORT_MARK_RESTORE			0x0001
+
+/*
+ * CUSTOM__SUPPORT_BACKWARD_SCAN informs optimizer this custom scan provider
+ * is designed to support backward scan.
+ */
+#define CUSTOM__SUPPORT_BACKWARD_SCAN			0x0002
+
+/*
+ * Registration and lookup custom execution provider
+ */
+extern void register_custom_provider(const CustomProvider *provider);
+
+extern CustomProvider *get_custom_provider(const char *custom_name);
+
+/*
+ * General executor code
+ */
+extern CustomScanState *ExecInitCustomScan(CustomScan *csstate,
+										   EState *estate, int eflags);
+extern TupleTableSlot *ExecCustomScan(CustomScanState *csstate);
+extern Node *MultiExecCustomScan(CustomScanState *csstate);
+extern void ExecEndCustomScan(CustomScanState *csstate);
+
+extern void ExecReScanCustomScan(CustomScanState *csstate);
+extern void ExecCustomMarkPos(CustomScanState *csstate);
+extern void ExecCustomRestrPos(CustomScanState *csstate);
+
+#endif	/* NODECUSTOM_H */
diff --git a/src/include/nodes/bitmapset.h b/src/include/nodes/bitmapset.h
index f770608..a860a4e 100644
--- a/src/include/nodes/bitmapset.h
+++ b/src/include/nodes/bitmapset.h
@@ -93,4 +93,8 @@ extern int	bms_first_member(Bitmapset *a);
 /* support for hashtables using Bitmapsets as keys: */
 extern uint32 bms_hash_value(const Bitmapset *a);
 
+/* support for text form */
+extern char *bms_to_string(Bitmapset *a);
+extern Bitmapset *bms_from_string(const char *a);
+
 #endif   /* BITMAPSET_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index a301a08..621830a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1501,6 +1501,23 @@ typedef struct ForeignScanState
 	void	   *fdw_state;		/* foreign-data wrapper can keep state here */
 } ForeignScanState;
 
+/* ----------------
+ *	 CustomScanState information
+ *
+ *		CustomScan nodes are used to scan various relations using custom
+ *		logic.
+ * ----------------
+ */
+typedef struct CustomScanState
+{
+	ScanState	ss;
+
+	/* use struct pointer to avoid including nodeCustom.h here */
+	struct CustomProvider *custom_provider;
+	int			custom_flags;
+	void	   *custom_state;
+} CustomScanState;
+
 /* ----------------------------------------------------------------
  *				 Join State Information
  * ----------------------------------------------------------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5b8df59..681c1b1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -62,6 +62,7 @@ typedef enum NodeTag
 	T_CteScan,
 	T_WorkTableScan,
 	T_ForeignScan,
+	T_CustomScan,
 	T_Join,
 	T_NestLoop,
 	T_MergeJoin,
@@ -107,6 +108,7 @@ typedef enum NodeTag
 	T_CteScanState,
 	T_WorkTableScanState,
 	T_ForeignScanState,
+	T_CustomScanState,
 	T_JoinState,
 	T_NestLoopState,
 	T_MergeJoinState,
@@ -224,6 +226,7 @@ typedef enum NodeTag
 	T_HashPath,
 	T_TidPath,
 	T_ForeignPath,
+	T_CustomPath,
 	T_AppendPath,
 	T_MergeAppendPath,
 	T_ResultPath,
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 38c039c..85d088d 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -479,6 +479,22 @@ typedef struct ForeignScan
 	bool		fsSystemCol;	/* true if any "system column" is needed */
 } ForeignScan;
 
+/* ----------------
+ *		CustomScan node
+ * ----------------
+ */
+typedef struct CustomScan
+{
+	Scan		scan;
+
+	const char *custom_name;		/* name of custom scan provider */
+	int			custom_flags;		/* a set of CUSTOM__* flags */
+	List	   *custom_private;		/* private data for CSP  */
+	List	   *custom_exprs;		/* expressions that CSP may execute */
+
+	Plan	   *subqry_plan;		/* valid, if RTE_SUBQUERY */
+	List	   *functions;			/* valid, if RTE_FUNCTION */
+} CustomScan;
 
 /*
  * ==========
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 4992bc0..7d9b0c0 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -134,6 +134,7 @@ typedef struct Expr
 #define    INNER_VAR		65000		/* reference to inner subplan */
 #define    OUTER_VAR		65001		/* reference to outer subplan */
 #define    INDEX_VAR		65002		/* reference to index column */
+#define    CUSTOM_VAR		65003		/* reference to custom column */
 
 #define IS_SPECIAL_VARNO(varno)		((varno) >= INNER_VAR)
 
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 8aa40d0..527a060 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -877,6 +877,22 @@ typedef struct ForeignPath
 } ForeignPath;
 
 /*
+ * CustomPath represents a scan using custom logic
+ *
+ * custom_name is the identifier of custom scan provider when it was
+ * registered. custom_flags is a set of CUSTOM__* bits to control its
+ * behavior. custom_private allows extension to store its private data
+ * but has to be safe for copyObject().
+ */
+typedef struct CustomPath
+{
+	Path		path;
+	const char *custom_name;		/* name of custom scan provider */
+	int			custom_flags;		/* CUSTOM__* flags in nodeCustom.h */
+	List	   *custom_private;		/* can be used for private data */
+} CustomPath;
+
+/*
  * AppendPath represents an Append plan, ie, successive execution of
  * several member plans.
  *
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index ec1605d..8857206 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -143,6 +143,10 @@ extern void final_cost_hashjoin(PlannerInfo *root, HashPath *path,
 extern void cost_subplan(PlannerInfo *root, SubPlan *subplan, Plan *plan);
 extern void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root);
 extern void cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root);
+extern void get_restriction_qual_cost(PlannerInfo *root,
+									  RelOptInfo *baserel,
+									  ParamPathInfo *param_info,
+									  QualCost *qpqual_cost);
 extern void compute_semi_anti_join_factors(PlannerInfo *root,
 							   RelOptInfo *outerrel,
 							   RelOptInfo *innerrel,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9b22fda..b613946 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -29,6 +29,31 @@ 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;
+
+#define add_custom_scan_paths(root, baserel, rte)				\
+	do {														\
+		if (add_scan_path_hook)									\
+			(*add_scan_path_hook)((root), (baserel), (rte));	\
+	} while(0)
+
+/* 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,
+										List *mergeclause_list,
+										SemiAntiJoinFactors *semifactors,
+										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..064640c 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -127,6 +127,7 @@ extern List *remove_useless_joins(PlannerInfo *root, List *joinlist);
  * prototypes for plan/setrefs.c
  */
 extern Plan *set_plan_references(PlannerInfo *root, Plan *plan);
+extern void fix_expr_common(PlannerInfo *root, Node *node);
 extern void fix_opfuncids(Node *node);
 extern void set_opfuncid(OpExpr *opexpr);
 extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
diff --git a/src/test/regress/expected/.gitignore b/src/test/regress/expected/.gitignore
index 93c56c8..7e35e74 100644
--- a/src/test/regress/expected/.gitignore
+++ b/src/test/regress/expected/.gitignore
@@ -2,6 +2,7 @@
 /copy.out
 /create_function_1.out
 /create_function_2.out
+/custom_scan.out
 /largeobject.out
 /largeobject_1.out
 /misc.out
diff --git a/src/test/regress/sql/.gitignore b/src/test/regress/sql/.gitignore
index 46c8112..8eeb461 100644
--- a/src/test/regress/sql/.gitignore
+++ b/src/test/regress/sql/.gitignore
@@ -2,6 +2,7 @@
 /copy.sql
 /create_function_1.sql
 /create_function_2.sql
+/custom_scan.sql
 /largeobject.sql
 /misc.sql
 /security_label.sql