pgsql-v9.4-custom-scan-part1.v4.patch

application/octet-stream

Filename: pgsql-v9.4-custom-scan-part1.v4.patch
Type: application/octet-stream
Part: 2
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 298 0
doc/src/sgml/filelist.sgml 1 0
doc/src/sgml/postgres.sgml 1 0
src/backend/commands/explain.c 78 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/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 1 1
src/backend/optimizer/path/joinpath.c 18 0
src/backend/optimizer/plan/createplan.c 103 0
src/backend/optimizer/plan/setrefs.c 25 0
src/backend/optimizer/plan/subselect.c 10 0
src/backend/optimizer/util/pathnode.c 40 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/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/pathnode.h 10 0
src/include/optimizer/paths.h 25 0
 doc/src/sgml/custom-scan.sgml           | 298 ++++++++++++++++++++++++++++++++
 doc/src/sgml/filelist.sgml              |   1 +
 doc/src/sgml/postgres.sgml              |   1 +
 src/backend/commands/explain.c          |  78 +++++++++
 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/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   |   2 +-
 src/backend/optimizer/path/joinpath.c   |  18 ++
 src/backend/optimizer/plan/createplan.c | 103 +++++++++++
 src/backend/optimizer/plan/setrefs.c    |  25 +++
 src/backend/optimizer/plan/subselect.c  |  10 ++
 src/backend/optimizer/util/pathnode.c   |  40 +++++
 src/backend/utils/adt/ruleutils.c       |  44 ++++-
 src/include/executor/executor.h         |   3 +-
 src/include/executor/nodeCustom.h       |  94 ++++++++++
 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/pathnode.h        |  10 ++
 src/include/optimizer/paths.h           |  25 +++
 30 files changed, 1182 insertions(+), 15 deletions(-)

diff --git a/doc/src/sgml/custom-scan.sgml b/doc/src/sgml/custom-scan.sgml
new file mode 100644
index 0000000..bb00078
--- /dev/null
+++ b/doc/src/sgml/custom-scan.sgml
@@ -0,0 +1,298 @@
+<!-- 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>
+  Custom-scan API enables extension to provide alternative ways to scan or
+  join relations, being fully integrated with cost based optimizer,
+  in addition to the built-in implementation.
+  It consists of a set of callbacks, with a unique name, to be invoked during
+  query planning and execution. Custom-scan provider should implement these
+  callback functions according to the expectation of API.
+ </para>
+ <para>
+  Overall, here is four major jobs that custom-scan provider should implement.
+  The first one is registration of custom-scan provider itself. Usually, it
+  shall be done once at <literal>_PG_init()</literal> entrypoint on module
+  loading.
+  The other three jobs shall be done for each query planning and execution.
+  The second one is submission of candidate paths to scan or join relations,
+  with an adequate cost, for the core planner.
+  Then, planner shall chooses a cheapest path from all the candidates.
+  If custom path survived, the planner kicks the third job; construction of
+  <literal>CustomScan</literal> plan node, being located within query plan
+  tree instead of the built-in plan node.
+  The last one is execution of its implementation in answer to invocations
+  by the core executor.
+ </para>
+ <para>
+  Some of contrib module utilize the custom-scan API. It may be able to
+  provide a good example for new development.
+  <variablelist>
+   <varlistentry>
+    <term><xref linkend="ctidscan"></term>
+    <listitem>
+     <para>
+      Its logic enables to skip earlier pages or terminate scan prior to
+      end of the relation, if inequality operator on <literal>ctid</literal>
+      system column can narrow down the scope to be scanned, instead of
+      the sequential scan that reads a relation from the head to the end.
+     </para>
+    </listitem>
+   </varlistentry>
+   <varlistentry>
+    <term><xref linkend="postgres-fdw"></term>
+    <listitem>
+     <para>
+      Its logic replaces a local join of foreign tables managed by
+      <literal>postgres_fdw</literal> with a custom scan that fetches
+      remotely joined relations.
+      It shows the way to implement a custom scan node that performs
+      instead join nodes.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </para>
+ <para>
+  Right now, only scan and join are supported to have fully integrated cost
+  based query optimization performing on custom scan API.
+  You might be able to implement other stuff, like sort or aggregation, with
+  manipulation of the planned tree, however, extension has to be responsible
+  to handle this replacement correctly. Here is no support by 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 job for custom scan provider is registration of a set of
+    callbacks with a unique name. Usually, it shall be done once on
+    <literal>_PG_init()</literal> entrypoint of module loading.
+<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 out the best way to scan or join relations from
+    the various potential paths; combination of a scan algorithm and target
+    relations.
+    Prior to this selection, we list up all the potential paths towards
+    a target relation (if base relation) or a pair of relations (if join).
+    The <literal>add_scan_path_hook</> and <literal>add_join_path_hook</>
+    allows extensions to add alternative scan paths in addition to built-in
+    ones.
+    If custom-scan provider can submit a potential scan path towards the
+    supplied relation, it shall construct <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 common field for all the path nodes to store
+    cost estimation. In addition, <literal>custom_name</> is the name of
+    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, 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 scan.
+        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> got choosen by query planner,
+    it calls back its associated custom scan provider to complete setting
+    up <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>
+    Query planner does basic initialization on the <literal>cscan_plan</>
+    being allocated, then custom scan provider can apply final initialization.
+    <literal>cscan_path</> is the path node that was constructed on the
+    previous stage then got 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 relation scan. Its expression portion shall be also
+    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 conscruction of plan node.
+    For example, Var node in the target list of 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 custom scan node is a vanilla relation
+    scan because here is nothing special to do. Elsewhere, it needs to
+    be handled by 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>
+    Query execuror also launches associated callbacks to begin, execute and
+    end 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 custom scan provider to do any initialization job around this
+    plan, however, it is not a good idea to launch 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 custom scan provider.
+    Also, <literal>eflags</> has flag bits of the executor's operating mode
+    for this plan node.
+    Note that 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 join
+    according to the custom logic. Unlike <literal>IterateForeignScan</>
+    method in foreign table, it is also responsible to check whether next
+    tuple matches the qualifier of this scan, or not.
+    A 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
+    join according to the custom logic. Pay attention the data format (and
+    the way to return also) depends on the type of upper node.
+   </para>
+
+   <para>
+<programlisting>
+void
+EndCustomScan(CustomScanState *csstate);
+</programlisting>
+    It ends the scan and release 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 might 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</> 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 d1b7dc6..1e96829 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 522316c..cce0cd8 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 4e93df2..39d2c12 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"
@@ -84,6 +85,7 @@ static void show_hash_info(HashState *hashstate, ExplainState *es);
 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);
@@ -683,6 +685,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,
@@ -809,6 +816,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	const char *sname;			/* node type name for non-text output */
 	const char *strategy = NULL;
 	const char *operation = NULL;
+	char		namebuf[NAMEDATALEN + 32];
 	int			save_indent = es->indent;
 	bool		haschildren;
 
@@ -897,6 +905,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";
+			operation = ((CustomScan *) plan)->custom_name;
+			break;
 		case T_Material:
 			pname = sname = "Materialize";
 			break;
@@ -1013,6 +1028,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;
@@ -1291,6 +1310,17 @@ ExplainNode(PlanState *planstate, List *ancestors,
 										   planstate, es);
 			show_foreignscan_info((ForeignScanState *) planstate, es);
 			break;
+		case T_CustomScan:
+			if (((CustomScan *)plan)->funcexpr != NULL && es->verbose)
+				show_expression(((CustomScan *)plan)->funcexpr,
+								"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);
@@ -1858,6 +1888,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
@@ -2025,6 +2068,41 @@ 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)
+			{
+				Node	   *funcexpr = ((CustomScan *) plan)->funcexpr;
+
+				if (funcexpr && IsA(funcexpr, FuncExpr))
+				{
+					Oid		funcid = ((FuncExpr *) 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 a078104..f80e6c4 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 76dd62f..b1110b9 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 90c2753..e60ac67 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 39e3b2e..df0d295 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/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1733da6..9aaca17 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -602,6 +602,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(funcexpr);
+
+	return newnode;
+}
+
+/*
  * CopyJoinFields
  *
  *		This function copies the fields of the Join node.  It is used by
@@ -3940,6 +3967,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 b39927e..7f0297f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -568,6 +568,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(funcexpr);
+}
+
+static void
 _outJoin(StringInfo str, const Join *node)
 {
 	WRITE_NODE_TYPE("JOIN");
@@ -2815,6 +2831,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 48ef325..29fcba9 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 bfd3809..9d0cbf5 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -46,6 +46,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);
@@ -399,6 +401,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);
 }
@@ -427,6 +432,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);
 }
@@ -1246,6 +1254,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);
 }
@@ -1269,6 +1280,9 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	/* Generate appropriate path */
 	add_path(rel, create_functionscan_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);
 }
@@ -1292,6 +1306,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);
 }
@@ -1361,6 +1378,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);
 }
@@ -1414,6 +1434,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 e7f8cec..e5b0cd7 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -2312,7 +2312,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;
 
 	/*
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 5b477e5..9483614 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 5947e5b..f830ab8 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,
@@ -235,6 +239,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:
@@ -411,6 +416,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);
@@ -2016,6 +2028,97 @@ 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)
+		{
+			Node   *funcexpr = rte->funcexpr;
+
+			if (best_path->path.param_info)
+				funcexpr = replace_nestloop_params(root, funcexpr);
+			scan_plan->funcexpr = funcexpr;
+		}
+	}
+	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 b78d727..aae5a1c 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"
@@ -578,6 +579,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:
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 0df70c4..644a532 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2194,6 +2194,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/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 64b17051..46e814d 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1738,6 +1738,46 @@ create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
 }
 
 /*
+ * create_customscan_path
+ *    Creates a path corresponding to a scan of a relation based on logic
+ *    logic being provided by extensions.
+ *
+ * This function is never called from core PostgreSQL. An usual usage is
+ * invocation from callbacks on add_scan_path_hook. We don't have any
+ * assumption on the custom scan logic, thus, caller is responsible to
+ * set adequate cost estimation here.
+ */
+CustomPath *
+create_customscan_path(PlannerInfo *root,
+					   RelOptInfo *baserel,
+					   double rows,
+					   Cost startup_cost,
+					   Cost total_cost,
+					   List *pathkeys,
+					   Relids required_outer,
+					   const char *custom_name,
+					   uint32 custom_flags,
+					   List *custom_private)
+{
+	CustomPath *pathnode = makeNode(CustomPath);
+
+	pathnode->path.pathtype = T_CustomScan;
+	pathnode->path.parent = baserel;
+	pathnode->path.param_info = get_baserel_parampathinfo(root, baserel,
+														  required_outer);
+	pathnode->path.rows = rows;
+	pathnode->path.startup_cost = startup_cost;
+	pathnode->path.total_cost = total_cost;
+	pathnode->path.pathkeys = pathkeys;
+
+	pathnode->custom_name = pstrdup(custom_name);
+	pathnode->custom_flags = custom_flags;
+	pathnode->custom_private = custom_private;
+
+	return pathnode;
+}
+
+/*
  * calc_nestloop_required_outer
  *	  Compute the required_outer set for a nestloop join path
  *
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 5ffce68..bfceab8 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -143,6 +143,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;
 
 /*
@@ -2363,14 +2364,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.
@@ -3628,6 +3634,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;
 }
 
 /*
@@ -5295,6 +5309,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);
@@ -5565,6 +5591,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 75841c8..51537d2 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/execnodes.h b/src/include/nodes/execnodes.h
index bedcf04..529930f 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1494,6 +1494,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 fc6b1d7..7753a09 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 44ea0b7..936591b 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -483,6 +483,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 */
+	Node	   *funcexpr;			/* valid, if RTE_FUNCTION */
+} CustomScan;
 
 /*
  * ==========
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 7918537..b71c7ca 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 6d7b594..50194f2 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -876,6 +876,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/pathnode.h b/src/include/optimizer/pathnode.h
index 9686229..1225970 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -82,6 +82,16 @@ extern ForeignPath *create_foreignscan_path(PlannerInfo *root, RelOptInfo *rel,
 						List *pathkeys,
 						Relids required_outer,
 						List *fdw_private);
+extern CustomPath *create_customscan_path(PlannerInfo *root,
+										  RelOptInfo *baserel,
+										  double rows,
+										  Cost startup_cost,
+										  Cost total_cost,
+										  List *pathkeys,
+										  Relids required_outer,
+										  const char *custom_name,
+										  uint32 custom_flags,
+										  List *custom_private);
 
 extern Relids calc_nestloop_required_outer(Path *outer_path, Path *inner_path);
 extern Relids calc_non_nestloop_required_outer(Path *outer_path, Path *inner_path);
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 96ffdb1..d7c7ef7 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,