 src/backend/commands/explain.c          |  84 ++++++++++++++++++
 src/backend/executor/Makefile           |   2 +-
 src/backend/executor/execAmi.c          |  39 +++++++++
 src/backend/nodes/copyfuncs.c           |  77 ++++++++++++++++
 src/backend/nodes/outfuncs.c            |  49 +++++++++++
 src/backend/optimizer/path/allpaths.c   |  23 +++++
 src/backend/optimizer/path/joinpath.c   |  19 ++++
 src/backend/optimizer/plan/createplan.c | 150 +++++++++++++++++++++++++++++++-
 src/backend/optimizer/util/pathnode.c   | 110 +++++++++++++++++++++++
 src/include/nodes/execnodes.h           |  45 ++++++++++
 src/include/nodes/nodes.h               |   9 ++
 src/include/nodes/plannodes.h           |  38 ++++++++
 src/include/nodes/relation.h            |  24 +++++
 src/include/optimizer/pathnode.h        |  26 ++++++
 src/include/optimizer/paths.h           |  25 ++++++
 15 files changed, 718 insertions(+), 2 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 91bea51..e4d0120 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_custom_info(Node *cstate, ExplainState *es);
 static const char *explain_get_index_name(Oid indexId);
 static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
 						ExplainState *es);
@@ -861,6 +863,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			pname = "Hash";		/* "Join" gets added by jointype switch */
 			sname = "Hash Join";
 			break;
+		case T_CustomJoin:
+			pname = sname = "Custom Join";
+			break;
 		case T_SeqScan:
 			pname = sname = "Seq Scan";
 			break;
@@ -897,6 +902,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_ForeignScan:
 			pname = sname = "Foreign Scan";
 			break;
+		case T_CustomScan:
+			pname = sname = "Custom Scan";
+			break;
 		case T_Material:
 			pname = sname = "Materialize";
 			break;
@@ -958,6 +966,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_Limit:
 			pname = sname = "Limit";
 			break;
+		case T_CustomPlan:
+			pname = sname = "Custom Plan";
+			break;
 		case T_Hash:
 			pname = sname = "Hash";
 			break;
@@ -1011,6 +1022,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_CteScan:
 		case T_WorkTableScan:
 		case T_ForeignScan:
+		case T_CustomScan:
 			ExplainScanTarget((Scan *) plan, es);
 			break;
 		case T_IndexScan:
@@ -1051,6 +1063,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_NestLoop:
 		case T_MergeJoin:
 		case T_HashJoin:
+		case T_CustomJoin:
 			{
 				const char *jointype;
 
@@ -1291,6 +1304,13 @@ ExplainNode(PlanState *planstate, List *ancestors,
 										   planstate, es);
 			show_foreignscan_info((ForeignScanState *) planstate, es);
 			break;
+		case T_CustomScan:
+			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
+			if (plan->qual)
+				show_instrumentation_count("Rows Removed by Filter", 1,
+										   planstate, es);
+			show_custom_info((Node *) planstate, es);
+			break;
 		case T_NestLoop:
 			show_upper_qual(((NestLoop *) plan)->join.joinqual,
 							"Join Filter", planstate, ancestors, es);
@@ -1328,6 +1348,14 @@ ExplainNode(PlanState *planstate, List *ancestors,
 				show_instrumentation_count("Rows Removed by Filter", 2,
 										   planstate, es);
 			break;
+		case T_CustomJoin:
+			show_upper_qual(((HashJoin *) plan)->join.joinqual,
+							"Join Filter", planstate, ancestors, es);
+			if (plan->qual)
+				show_instrumentation_count("Rows Removed by Filter", 2,
+										   planstate, es);
+			show_custom_info((Node *) planstate, es);
+			break;
 		case T_Agg:
 		case T_Group:
 			show_upper_qual(plan->qual, "Filter", planstate, ancestors, es);
@@ -1357,6 +1385,13 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_Hash:
 			show_hash_info((HashState *) planstate, es);
 			break;
+		case T_CustomPlan:
+			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
+			if (plan->qual)
+				show_instrumentation_count("Rows Removed by Filter", 1,
+										   planstate, es);
+			show_custom_info((Node *) planstate, es);
+			break;
 		default:
 			break;
 	}
@@ -1477,6 +1512,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		IsA(plan, BitmapAnd) ||
 		IsA(plan, BitmapOr) ||
 		IsA(plan, SubqueryScan) ||
+		(IsA(plan, CustomPlan) && ((CustomPlan *) plan)->custom_subplans) ||
 		planstate->subPlan;
 	if (haschildren)
 	{
@@ -1531,6 +1567,12 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			ExplainNode(((SubqueryScanState *) planstate)->subplan, ancestors,
 						"Subquery", NULL, es);
 			break;
+		case T_CustomPlan:
+			if (((CustomPlan *) plan)->custom_subplans)
+				ExplainMemberNodes(((CustomPlan *) plan)->custom_subplans,
+						 ((CustomPlanState *) planstate)->custom_subplans,
+								   ancestors, es);
+			break;
 		default:
 			break;
 	}
@@ -1858,6 +1900,35 @@ show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es)
 }
 
 /*
+ * Show extra information for Custom(Plan|Scan|Join) node
+ */
+static void
+show_custom_info(Node *cstate, ExplainState *es)
+{
+	CustomProvider *provider;
+	const char	   *custom_name;
+
+	switch (nodeTag(cstate))
+	{
+		case T_CustomPlan:
+			custom_name = ((CustomPlan *) cstate)->custom_name;
+			break;
+		case T_CustomScan:
+			custom_name = ((CustomScan *) cstate)->custom_name;
+			break;
+		case T_CustomJoin:
+			custom_name = ((CustomJoin *) cstate)->custom_name;
+			break;
+		default:
+			return;
+	}
+
+	provider = get_custom_provider(custom_name);
+	if (provider->ExplainCustom)
+		(*provider->ExplainCustom)(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 +2096,19 @@ 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
+			{
+				/* TODO: add support for other rtekind */
+			}
+			break;
 		default:
 			break;
 	}
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 6081b56..af707b0 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -16,7 +16,7 @@ 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 \
-       nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
+       nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeCustom.o nodeHash.o \
        nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \
        nodeLimit.o nodeLockRows.o \
        nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index a078104..9cc6bdd 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"
@@ -149,6 +150,10 @@ ExecReScan(PlanState *node)
 			ExecReScanBitmapOr((BitmapOrState *) node);
 			break;
 
+		case T_CustomPlanState:
+			ExecReScanCustomPlan((CustomPlanState *) node);
+			break;
+
 		case T_SeqScanState:
 			ExecReScanSeqScan((SeqScanState *) node);
 			break;
@@ -197,6 +202,10 @@ ExecReScan(PlanState *node)
 			ExecReScanForeignScan((ForeignScanState *) node);
 			break;
 
+		case T_CustomScanState:
+			ExecReScanCustomScan((CustomScanState *) node);
+			break;
+
 		case T_NestLoopState:
 			ExecReScanNestLoop((NestLoopState *) node);
 			break;
@@ -209,6 +218,10 @@ ExecReScan(PlanState *node)
 			ExecReScanHashJoin((HashJoinState *) node);
 			break;
 
+		case T_CustomJoinState:
+			ExecReScanCustomJoin((CustomJoinState *) node);
+			break;
+
 		case T_MaterialState:
 			ExecReScanMaterial((MaterialState *) node);
 			break;
@@ -421,6 +434,9 @@ ExecSupportsMarkRestore(NodeTag plantype)
 bool
 ExecSupportsBackwardScan(Plan *node)
 {
+	ListCell   *cell;
+	uint		flags;
+
 	if (node == NULL)
 		return false;
 
@@ -461,6 +477,29 @@ ExecSupportsBackwardScan(Plan *node)
 			return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid) &&
 				TargetListSupportsBackwardScan(node->targetlist);
 
+		case T_CustomPlan:
+			flags = ((CustomPlan *) node)->custom_flags;
+			if ((flags & CUSTOM_FLAGS_SUPPORT_BACKWARD_SCAN) == 0)
+				return false;
+
+			foreach(cell, ((CustomPlan *) node)->custom_subplans)
+			{
+				if (!ExecSupportsBackwardScan((Plan *) lfirst(cell)))
+					return false;
+			}
+			if (outerPlan(node) && !ExecSupportsBackwardScan(outerPlan(node)))
+				return false;
+			if (innerPlan(node) && !ExecSupportsBackwardScan(innerPlan(node)))
+				return false;
+			return TargetListSupportsBackwardScan(node->targetlist);
+
+		case T_CustomScan:
+			flags = ((CustomScan *) node)->custom_flags;
+			if ((flags & CUSTOM_FLAGS_SUPPORT_BACKWARD_SCAN) == 0)
+				return false;
+
+			return TargetListSupportsBackwardScan(node->targetlist);
+
 		case T_SubqueryScan:
 			return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) &&
 				TargetListSupportsBackwardScan(node->targetlist);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 65f3b98..974cd67 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -304,6 +304,27 @@ _copyBitmapOr(const BitmapOr *from)
 	return newnode;
 }
 
+/*
+ * _copyCustomPlan
+ */
+static CustomPlan *
+_copyCustomPlan(const CustomPlan *from)
+{
+	CustomPlan *newnode = makeNode(CustomPlan);
+
+	/*
+	 * copy node superclass fields
+	 */
+	CopyPlanFields((const Plan *) from, (Plan *) newnode);
+
+	COPY_STRING_FIELD(custom_name);
+	COPY_SCALAR_FIELD(custom_flags);
+	COPY_NODE_FIELD(custom_private);
+	COPY_NODE_FIELD(custom_expr);
+	COPY_NODE_FIELD(custom_subplans);
+
+	return newnode;
+}
 
 /*
  * CopyScanFields
@@ -602,6 +623,30 @@ _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_expr);
+
+	return newnode;
+}
+
+/*
  * CopyJoinFields
  *
  *		This function copies the fields of the Join node.  It is used by
@@ -707,6 +752,29 @@ _copyHashJoin(const HashJoin *from)
 	return newnode;
 }
 
+/*
+ * _copyCustomJoin
+ */
+static CustomJoin *
+_copyCustomJoin(const CustomJoin *from)
+{
+	CustomJoin *newnode = makeNode(CustomJoin);
+
+	/*
+	 * copy node superclass fields
+	 */
+	CopyJoinFields((const Join *) from, (Join *) newnode);
+
+	/*
+	 * copy remainder of node
+	 */
+	COPY_STRING_FIELD(custom_name);
+	COPY_SCALAR_FIELD(custom_flags);
+	COPY_NODE_FIELD(custom_private);
+	COPY_NODE_FIELD(custom_expr);
+
+	return newnode;
+}
 
 /*
  * _copyMaterial
@@ -3890,6 +3958,9 @@ copyObject(const void *from)
 		case T_BitmapOr:
 			retval = _copyBitmapOr(from);
 			break;
+		case T_CustomPlan:
+			retval = _copyCustomPlan(from);
+			break;
 		case T_Scan:
 			retval = _copyScan(from);
 			break;
@@ -3929,6 +4000,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;
@@ -3941,6 +4015,9 @@ copyObject(const void *from)
 		case T_HashJoin:
 			retval = _copyHashJoin(from);
 			break;
+		case T_CustomJoin:
+			retval = _copyCustomJoin(from);
+			break;
 		case T_Material:
 			retval = _copyMaterial(from);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index a2903f9..d260763 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -423,6 +423,20 @@ _outBitmapOr(StringInfo str, const BitmapOr *node)
 }
 
 static void
+_outCustomPlan(StringInfo str, const CustomPlan *node)
+{
+	WRITE_NODE_TYPE("CUSTOMPLAN");
+
+	_outPlanInfo(str, (const Plan *) node);
+
+	WRITE_STRING_FIELD(custom_name);
+	WRITE_UINT_FIELD(custom_flags);
+	WRITE_NODE_FIELD(custom_private);
+	WRITE_NODE_FIELD(custom_exprs);
+	WRITE_NODE_FIELD(custom_subplans);
+}
+
+static void
 _outScan(StringInfo str, const Scan *node)
 {
 	WRITE_NODE_TYPE("SCAN");
@@ -568,6 +582,19 @@ _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_UINT_FIELD(custom_flags);
+	WRITE_NODE_FIELD(custom_private);
+	WRITE_NODE_FIELD(custom_exprs);
+}
+
+static void
 _outJoin(StringInfo str, const Join *node)
 {
 	WRITE_NODE_TYPE("JOIN");
@@ -627,6 +654,19 @@ _outHashJoin(StringInfo str, const HashJoin *node)
 }
 
 static void
+_outCustomJoin(StringInfo str, const CustomJoin *node)
+{
+	WRITE_NODE_TYPE("CUSTOMJOIN");
+
+	_outJoinPlanInfo(str, (const Join *) node);
+
+	WRITE_STRING_FIELD(custom_name);
+	WRITE_UINT_FIELD(custom_flags);
+	WRITE_NODE_FIELD(custom_private);
+	WRITE_NODE_FIELD(custom_exprs);
+}
+
+static void
 _outAgg(StringInfo str, const Agg *node)
 {
 	int			i;
@@ -2775,6 +2815,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_BitmapOr:
 				_outBitmapOr(str, obj);
 				break;
+			case T_CustomPlan:
+				_outCustomPlan(str, obj);
+				break;
 			case T_Scan:
 				_outScan(str, obj);
 				break;
@@ -2814,6 +2857,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;
@@ -2826,6 +2872,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_HashJoin:
 				_outHashJoin(str, obj);
 				break;
+			case T_CustomJoin:
+				_outCustomJoin(str, obj);
+				break;
 			case T_Agg:
 				_outAgg(str, obj);
 				break;
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/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 5b477e5..162b21d 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -21,6 +21,9 @@
 #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 +262,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 9b9eb2f..a165a9b 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,12 +78,20 @@ static WorkTableScan *create_worktablescan_plan(PlannerInfo *root, Path *best_pa
 						  List *tlist, List *scan_clauses);
 static ForeignScan *create_foreignscan_plan(PlannerInfo *root, ForeignPath *best_path,
 						List *tlist, List *scan_clauses);
+static CustomScan *create_customscan_plan(PlannerInfo *root,
+                                          CustomScanPath *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,
 					  Plan *outer_plan, Plan *inner_plan);
 static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
 					 Plan *outer_plan, Plan *inner_plan);
+static CustomJoin *create_customjoin_plan(PlannerInfo *root,
+										  CustomJoinPath *best_path,
+										  Plan *outer_plan,
+										  Plan *inner_plan);
 static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
 static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
 static void process_subquery_nestloop_params(PlannerInfo *root,
@@ -235,11 +244,13 @@ 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:
 		case T_MergeJoin:
 		case T_NestLoop:
+		case T_CustomJoin:
 			plan = create_join_plan(root,
 									(JoinPath *) best_path);
 			break;
@@ -411,6 +422,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
 													scan_clauses);
 			break;
 
+		case T_CustomScan:
+			plan = (Plan *)create_customscan_plan(root,
+												  (CustomScanPath *)best_path,
+												  tlist,
+												  scan_clauses);
+			break;
+
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) best_path->pathtype);
@@ -607,9 +625,14 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
 	outer_plan = create_plan_recurse(root, best_path->outerjoinpath);
 
 	/* For a nestloop, include outer relids in curOuterRels for inner side */
-	if (best_path->path.pathtype == T_NestLoop)
+	if (best_path->path.pathtype == T_NestLoop ||
+		(best_path->path.pathtype == T_CustomJoin &&
+		 (((CustomJoinPath *) best_path)->custom_flags &
+		  CUSTOM_FLAGS_APPLY_PARAMS_ON_INNER) != 0))
+	{
 		root->curOuterRels = bms_union(root->curOuterRels,
 								   best_path->outerjoinpath->parent->relids);
+	}
 
 	inner_plan = create_plan_recurse(root, best_path->innerjoinpath);
 
@@ -637,6 +660,17 @@ create_join_plan(PlannerInfo *root, JoinPath *best_path)
 												 outer_plan,
 												 inner_plan);
 			break;
+		case T_CustomJoin:
+			if (saveOuterRels != root->curOuterRels)
+			{
+				bms_free(root->curOuterRels);
+				root->curOuterRels = saveOuterRels;
+			}
+			plan = (Plan *)create_customjoin_plan(root,
+												  (CustomJoinPath *) best_path,
+												  outer_plan,
+												  inner_plan);
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) best_path->path.pathtype);
@@ -2016,6 +2050,51 @@ 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,
+					   CustomScanPath *best_path,
+					   List *tlist,
+					   List *scan_clauses)
+{
+	CustomProvider *provider = get_custom_provider(best_path->custom_name);
+	CustomScan	   *scan_plan;
+	RelOptInfo	   *rel = best_path->path.parent;
+	Index			scan_relid = rel->relid;
+	RangeTblEntry  *rte;
+
+	/* it should be a base rel */
+	Assert(scan_relid > 0);
+	rte = planner_rt_fetch(scan_relid, root);
+
+	/* Sort clauses into best execution order. */
+    scan_clauses = order_qual_clauses(root, scan_clauses);
+
+	scan_plan = provider->GetCustomScan(root, rel, rte,
+										best_path,
+										tlist,
+										scan_clauses);
+	Assert(IsA(scan_plan, CustomScan));
+    Assert(strcmp(scan_plan->custom_name, provider->name) == 0);
+
+	/* 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 */
+	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;
+}
 
 /*****************************************************************************
  *
@@ -2534,6 +2613,69 @@ create_hashjoin_plan(PlannerInfo *root,
 	return join_plan;
 }
 
+CustomJoin *
+create_customjoin_plan(PlannerInfo *root,
+					   CustomJoinPath *best_path,
+					   Plan *outer_plan,
+					   Plan *inner_plan)
+{
+	CustomProvider *provider = get_custom_provider(best_path->custom_name);
+	RelOptInfo *joinrel = best_path->jpath.path.parent;
+	CustomJoin *join_plan;
+	List	   *join_clauses;
+	List	   *other_clauses;
+	List	   *tlist;
+
+	/*
+	 * Construct target-list of this join path because build_path_tlist()
+	 * is static function.
+	 */
+	tlist = build_path_tlist(root, &best_path->jpath.path);
+
+	/* Sort join qual clauses into best execution order */
+    join_clauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo);
+
+
+	/* Get the join qual clauses (in plain expression form) */
+	/* Any pseudoconstant clauses are ignored here */
+	if (IS_OUTER_JOIN(best_path->jpath.jointype))
+	{
+		extract_actual_join_clauses(join_clauses,
+									&join_clauses, &other_clauses);
+	}
+	else
+	{
+		/* We can treat all clauses alike for an inner join */
+		join_clauses = extract_actual_clauses(join_clauses, false);
+		other_clauses = NIL;
+	}
+
+	/* Construct CustomJoin node */
+	join_plan = provider->GetCustomJoin(root,
+										joinrel,
+										best_path,
+										tlist,
+										join_clauses,
+										other_clauses,
+										outer_plan,
+										inner_plan);
+	Assert(IsA(join_plan, CustomJoin));
+	Assert(strcmp(join_plan->custom_name, provider->name) == 0);
+
+	/* Copy cost data from Path to Plan; no need to make callback do this */
+	copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
+
+	/* Replace any outer-relation variables with nestloop params */
+	if (best_path->jpath.path.param_info)
+	{
+		join_clauses = (List *)
+			replace_nestloop_params(root, (Node *) join_clauses);
+		other_clauses = (List *)
+			replace_nestloop_params(root, (Node *) other_clauses);
+	}
+
+	return join_plan;
+}
 
 /*****************************************************************************
  *
@@ -4863,6 +5005,12 @@ is_projection_capable_plan(Plan *plan)
 		case T_MergeAppend:
 		case T_RecursiveUnion:
 			return false;
+		/*
+		 * XXX - Due to nature of CustomScan and CustomJoin nodes, they heve
+		 * to be capable for projection. It depends on implementation if
+		 * CustomPlan, however, core PostgreSQL does not add CustomPlan node
+		 * right now.
+		 */
 		default:
 			break;
 	}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 64b17051..7dc51be 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.
+ */
+CustomScanPath *
+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)
+{
+	CustomScanPath *pathnode = makeNode(CustomScanPath);
+
+	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
  *
@@ -2000,6 +2040,76 @@ create_hashjoin_path(PlannerInfo *root,
 }
 
 /*
+ * create_customjoin_path
+ *    Creates a pathnode corresponding to a join between two relations based
+ *    on custom logic provided by extension.
+ *
+ * 'joinrel' is the join relation
+ * 'jointype' is the type of join required
+ * 'rows' is number of estimated rows
+ * 'startup_cost' is estimated startup cost
+ * 'total_cost' is estimated total cost
+ * 'sjinfo' is extra info about the join for selectivity estimation
+ * 'outer_path' is the cheapest outer path
+ * 'inner_path' is the cheapest inner path
+ * 'restrict_clauses' are the RestrictInfo nodes to apply at the join
+ * 'pathkeys' are the path keys of the new join path
+ * 'required_outer' is the set of required outer rels
+ * 'custom_name' is the name of custom execution provider
+ * 'custom_flag' is set of CUSTOM_* flags
+ * 'custom_private' is a list of private datum
+ */
+CustomJoinPath *
+create_customjoin_path(PlannerInfo *root,
+					   RelOptInfo *joinrel,
+					   JoinType jointype,
+					   double rows,
+					   Cost startup_cost,
+					   Cost total_cost,
+					   SpecialJoinInfo *sjinfo,
+					   Path *outer_path,
+					   Path *inner_path,
+					   List *restrict_clauses,
+					   List *pathkeys,
+					   Relids required_outer,
+					   const char *custom_name,
+					   uint32 custom_flags,
+					   List *custom_private)
+{
+	CustomJoinPath *pathnode = makeNode(CustomJoinPath);
+
+	pathnode->jpath.path.pathtype = T_CustomJoin;
+	pathnode->jpath.path.parent = joinrel;
+	pathnode->jpath.path.param_info
+		= get_joinrel_parampathinfo(root,
+									joinrel,
+									outer_path,
+									inner_path,
+									sjinfo,
+									required_outer,
+									&restrict_clauses);
+	/*
+	 * Unlike other create_XXXjoin_path routines, it is caller's job to
+	 * calculate adequate cost estimation for this custom join logic.
+	 */
+	pathnode->jpath.path.rows = rows;
+	pathnode->jpath.path.startup_cost = startup_cost;
+	pathnode->jpath.path.total_cost = total_cost;
+
+	pathnode->jpath.path.pathkeys = pathkeys;
+	pathnode->jpath.jointype = jointype;
+	pathnode->jpath.outerjoinpath = outer_path;
+	pathnode->jpath.innerjoinpath = inner_path;
+	pathnode->jpath.joinrestrictinfo = restrict_clauses;
+
+	pathnode->custom_name = pstrdup(custom_name);
+	pathnode->custom_flags = custom_flags;
+	pathnode->custom_private = custom_private;
+
+	return pathnode;
+}
+
+/*
  * reparameterize_path
  *		Attempt to modify a Path to have greater parameterization
  *
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 3b430e0..ecf816c 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1175,6 +1175,24 @@ typedef struct BitmapOrState
 	int			nplans;			/* number of input plans */
 } BitmapOrState;
 
+/* ----------------
+ *   CustomPlanState information
+ *
+ *   CustomPlan is used to implement custom behavior but expect for scan
+ *   and join.
+ */
+typedef struct CustomPlanState
+{
+	PlanState	ps;
+	/* use struct pointer to avoid including custom.h here */
+	struct CustomProvider *custom_provider;
+	uint		custom_flags;
+	void	   *custom_state;
+	/* special handling if multiple subplans are here, as Append */
+	PlanState **custom_subplans;
+	int			custom_numplans;
+} CustomPlanState;
+
 /* ----------------------------------------------------------------
  *				 Scan State Information
  * ----------------------------------------------------------------
@@ -1494,6 +1512,20 @@ typedef struct ForeignScanState
 	void	   *fdw_state;		/* foreign-data wrapper can keep state here */
 } ForeignScanState;
 
+/* ----------------
+ *   CustomScanState information
+ *
+ *   CustomScan nodes are used to scan relation based on custom logic.
+ */
+typedef struct CustomScanState
+{
+	ScanState	ss;
+	/* use struct pointer to avoid including nodeCustom.h here */
+	struct CustomProvider *custom_provider;
+	uint		custom_flags;
+	void	   *custom_state;
+} CustomScanState;
+
 /* ----------------------------------------------------------------
  *				 Join State Information
  * ----------------------------------------------------------------
@@ -1626,6 +1658,19 @@ typedef struct HashJoinState
 	bool		hj_OuterNotEmpty;
 } HashJoinState;
 
+/* ----------------
+ *   CustomJoinState information
+ *
+ *   CustomJoin nodes are used to join relations based on custom logic
+ */
+typedef struct CustomJoinState
+{
+	JoinState	js;
+	/* use struct pointer to avoid including nodeCustom.h here */
+	struct CustomProvider *custom_provider;
+	uint		custom_flags;
+	void	   *custom_state;
+} CustomJoinState;
 
 /* ----------------------------------------------------------------
  *				 Materialization State Information
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 78368c6..d04eccd 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -76,6 +76,9 @@ typedef enum NodeTag
 	T_SetOp,
 	T_LockRows,
 	T_Limit,
+	T_CustomPlan,
+	T_CustomScan,
+	T_CustomJoin,
 	/* these aren't subclasses of Plan: */
 	T_NestLoopParam,
 	T_PlanRowMark,
@@ -121,6 +124,9 @@ typedef enum NodeTag
 	T_SetOpState,
 	T_LockRowsState,
 	T_LimitState,
+	T_CustomPlanState,
+	T_CustomScanState,
+	T_CustomJoinState,
 
 	/*
 	 * TAGS FOR PRIMITIVE NODES (primnodes.h)
@@ -229,6 +235,9 @@ typedef enum NodeTag
 	T_ResultPath,
 	T_MaterialPath,
 	T_UniquePath,
+	T_CustomPlanPath,
+	T_CustomScanPath,
+	T_CustomJoinPath,
 	T_EquivalenceClass,
 	T_EquivalenceMember,
 	T_PathKey,
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 44ea0b7..aa12205 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -483,6 +483,18 @@ typedef struct ForeignScan
 	bool		fsSystemCol;	/* true if any "system column" is needed */
 } ForeignScan;
 
+/* ----------------
+ *		CustomScan node
+ *
+ */
+typedef struct CustomScan
+{
+	Scan		scan;
+	const char *custom_name;
+	uint		custom_flags;
+	List	   *custom_private;
+	List	   *custom_exprs;
+} CustomScan;
 
 /*
  * ==========
@@ -570,6 +582,19 @@ typedef struct HashJoin
 } HashJoin;
 
 /* ----------------
+ *		CustomJoin node
+ *
+ */
+typedef struct CustomJoin
+{
+	Join		join;
+	const char *custom_name;
+	uint		custom_flags;
+	List	   *custom_private;
+	List	   *custom_exprs;
+} CustomJoin;
+
+/* ----------------
  *		materialization node
  * ----------------
  */
@@ -750,6 +775,19 @@ typedef struct Limit
 	Node	   *limitCount;		/* COUNT parameter, or NULL if none */
 } Limit;
 
+/*
+ * CustomPlan node
+ *
+ */
+typedef struct CustomPlan
+{
+	Plan		plan;
+	const char *custom_name;
+	uint		custom_flags;
+	List	   *custom_private;
+	List	   *custom_exprs;
+	List	   *custom_subplans;	/* if here is multiple subplans */
+} CustomPlan;
 
 /*
  * RowMarkType -
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index a2853fb..4a3c924 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1036,6 +1036,30 @@ typedef struct HashPath
 } HashPath;
 
 /*
+ * CustomScanPath
+ *
+ */
+typedef struct CustomScanPath
+{
+	Path		path;
+	const char *custom_name;
+	uint32		custom_flags;
+	List	   *custom_private;
+} CustomScanPath;
+
+/*
+ * CustomJoinPath
+ *
+ */
+typedef struct CustomJoinPath
+{
+	JoinPath	jpath;
+	const char *custom_name;
+	uint32		custom_flags;
+	List	   *custom_private;
+} CustomJoinPath;
+
+/*
  * Restriction clause info.
  *
  * We create one of these for each AND sub-clause of a restriction condition
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 9686229..735dbec 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 CustomScanPath *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);
@@ -124,6 +134,22 @@ extern HashPath *create_hashjoin_path(PlannerInfo *root,
 					 Relids required_outer,
 					 List *hashclauses);
 
+extern CustomJoinPath *create_customjoin_path(PlannerInfo *root,
+											  RelOptInfo *joinrel,
+											  JoinType jointype,
+											  double rows,
+											  Cost startup_cost,
+											  Cost total_cost,
+											  SpecialJoinInfo *sjinfo,
+											  Path *outer_path,
+											  Path *inner_path,
+											  List *restrict_clauses,
+											  List *pathkeys,
+											  Relids required_outer,
+											  const char *custom_name,
+											  uint32 custom_flags,
+											  List *custom_private);
+
 extern Path *reparameterize_path(PlannerInfo *root, Path *path,
 					Relids required_outer,
 					double loop_count);
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 9ef93c7..e9aa199 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,
