diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index baed981..639451a 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -26,9 +26,9 @@
 
 static void printtup_startup(DestReceiver *self, int operation,
 				 TupleDesc typeinfo);
-static void printtup(TupleTableSlot *slot, DestReceiver *self);
-static void printtup_20(TupleTableSlot *slot, DestReceiver *self);
-static void printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
+static bool printtup(TupleTableSlot *slot, DestReceiver *self);
+static bool printtup_20(TupleTableSlot *slot, DestReceiver *self);
+static bool printtup_internal_20(TupleTableSlot *slot, DestReceiver *self);
 static void printtup_shutdown(DestReceiver *self);
 static void printtup_destroy(DestReceiver *self);
 
@@ -299,7 +299,7 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
  *		printtup --- print a tuple in protocol 3.0
  * ----------------
  */
-static void
+static bool
 printtup(TupleTableSlot *slot, DestReceiver *self)
 {
 	TupleDesc	typeinfo = slot->tts_tupleDescriptor;
@@ -376,13 +376,15 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
 	/* Return to caller's context, and flush row's temporary memory */
 	MemoryContextSwitchTo(oldcontext);
 	MemoryContextReset(myState->tmpcontext);
+
+	return true;
 }
 
 /* ----------------
  *		printtup_20 --- print a tuple in protocol 2.0
  * ----------------
  */
-static void
+static bool
 printtup_20(TupleTableSlot *slot, DestReceiver *self)
 {
 	TupleDesc	typeinfo = slot->tts_tupleDescriptor;
@@ -452,6 +454,8 @@ printtup_20(TupleTableSlot *slot, DestReceiver *self)
 	/* Return to caller's context, and flush row's temporary memory */
 	MemoryContextSwitchTo(oldcontext);
 	MemoryContextReset(myState->tmpcontext);
+
+	return true;
 }
 
 /* ----------------
@@ -528,7 +532,7 @@ debugStartup(DestReceiver *self, int operation, TupleDesc typeinfo)
  *		debugtup - print one tuple for an interactive backend
  * ----------------
  */
-void
+bool
 debugtup(TupleTableSlot *slot, DestReceiver *self)
 {
 	TupleDesc	typeinfo = slot->tts_tupleDescriptor;
@@ -553,6 +557,8 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
 		printatt((unsigned) i + 1, typeinfo->attrs[i], value);
 	}
 	printf("\t----\n");
+
+	return true;
 }
 
 /* ----------------
@@ -564,7 +570,7 @@ debugtup(TupleTableSlot *slot, DestReceiver *self)
  * This is largely same as printtup_20, except we use binary formatting.
  * ----------------
  */
-static void
+static bool
 printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
 {
 	TupleDesc	typeinfo = slot->tts_tupleDescriptor;
@@ -636,4 +642,6 @@ printtup_internal_20(TupleTableSlot *slot, DestReceiver *self)
 	/* Return to caller's context, and flush row's temporary memory */
 	MemoryContextSwitchTo(oldcontext);
 	MemoryContextReset(myState->tmpcontext);
+
+	return true;
 }
diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c
index 86a2e6b..f5242a4 100644
--- a/src/backend/access/heap/heapam.c
+++ b/src/backend/access/heap/heapam.c
@@ -63,6 +63,7 @@
 #include "storage/predicate.h"
 #include "storage/procarray.h"
 #include "storage/smgr.h"
+#include "storage/spin.h"
 #include "storage/standby.h"
 #include "utils/datum.h"
 #include "utils/inval.h"
@@ -80,9 +81,11 @@ bool		synchronize_seqscans = true;
 static HeapScanDesc heap_beginscan_internal(Relation relation,
 						Snapshot snapshot,
 						int nkeys, ScanKey key,
+						ParallelHeapScanDesc parallel_scan,
 					  bool allow_strat, bool allow_sync, bool allow_pagemode,
 						bool is_bitmapscan, bool is_samplescan,
 						bool temp_snap);
+static BlockNumber heap_parallelscan_nextpage(ParallelHeapScanDesc);
 static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
 					TransactionId xid, CommandId cid, int options);
 static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
@@ -223,7 +226,10 @@ initscan(HeapScanDesc scan, ScanKey key, bool is_rescan)
 	 * results for a non-MVCC snapshot, the caller must hold some higher-level
 	 * lock that ensures the interesting tuple(s) won't change.)
 	 */
-	scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
+	if (scan->rs_parallel != NULL)
+		scan->rs_nblocks = scan->rs_parallel->phs_nblocks;
+	else
+		scan->rs_nblocks = RelationGetNumberOfBlocks(scan->rs_rd);
 
 	/*
 	 * If the table is large relative to NBuffers, use a bulk-read access
@@ -483,7 +489,18 @@ heapgettup(HeapScanDesc scan,
 				tuple->t_data = NULL;
 				return;
 			}
-			page = scan->rs_startblock; /* first page */
+			if (scan->rs_parallel != NULL)
+			{
+				page = heap_parallelscan_nextpage(scan->rs_parallel);
+				if (page >= scan->rs_nblocks)
+				{
+					Assert(!BufferIsValid(scan->rs_cbuf));
+					tuple->t_data = NULL;
+					return;
+				}
+			}
+			else
+				page = scan->rs_startblock; /* first page */
 			heapgetpage(scan, page);
 			lineoff = FirstOffsetNumber;		/* first offnum */
 			scan->rs_inited = true;
@@ -506,6 +523,9 @@ heapgettup(HeapScanDesc scan,
 	}
 	else if (backward)
 	{
+		/* backward parallel scan not supported */
+		Assert(scan->rs_parallel == NULL);
+
 		if (!scan->rs_inited)
 		{
 			/*
@@ -658,11 +678,19 @@ heapgettup(HeapScanDesc scan,
 		}
 		else
 		{
-			page++;
-			if (page >= scan->rs_nblocks)
-				page = 0;
-			finished = (page == scan->rs_startblock) ||
-				(scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks <= 0 : false);
+			if (scan->rs_parallel != NULL)
+			{
+				page = heap_parallelscan_nextpage(scan->rs_parallel);
+				finished = (page >= scan->rs_nblocks);
+			}
+			else
+			{
+				page++;
+				if (page >= scan->rs_nblocks)
+					page = 0;
+				finished = (page == scan->rs_startblock) ||
+					(scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks <= 0 : false);
+			}
 
 			/*
 			 * Report our new scan position for synchronization purposes. We
@@ -760,7 +788,18 @@ heapgettup_pagemode(HeapScanDesc scan,
 				tuple->t_data = NULL;
 				return;
 			}
-			page = scan->rs_startblock; /* first page */
+			if (scan->rs_parallel != NULL)
+			{
+				page = heap_parallelscan_nextpage(scan->rs_parallel);
+				if (page >= scan->rs_nblocks)
+				{
+					Assert(!BufferIsValid(scan->rs_cbuf));
+					tuple->t_data = NULL;
+					return;
+				}
+			}
+			else
+				page = scan->rs_startblock; /* first page */
 			heapgetpage(scan, page);
 			lineindex = 0;
 			scan->rs_inited = true;
@@ -780,6 +819,9 @@ heapgettup_pagemode(HeapScanDesc scan,
 	}
 	else if (backward)
 	{
+		/* backward parallel scan not supported */
+		Assert(scan->rs_parallel == NULL);
+
 		if (!scan->rs_inited)
 		{
 			/*
@@ -921,11 +963,19 @@ heapgettup_pagemode(HeapScanDesc scan,
 		}
 		else
 		{
-			page++;
-			if (page >= scan->rs_nblocks)
-				page = 0;
-			finished = (page == scan->rs_startblock) ||
-				(scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks <= 0 : false);
+			if (scan->rs_parallel != NULL)
+			{
+				page = heap_parallelscan_nextpage(scan->rs_parallel);
+				finished = (page >= scan->rs_nblocks);
+			}
+			else
+			{
+				page++;
+				if (page >= scan->rs_nblocks)
+					page = 0;
+				finished = (page == scan->rs_startblock) ||
+					(scan->rs_numblocks != InvalidBlockNumber ? --scan->rs_numblocks <= 0 : false);
+			}
 
 			/*
 			 * Report our new scan position for synchronization purposes. We
@@ -1321,7 +1371,7 @@ HeapScanDesc
 heap_beginscan(Relation relation, Snapshot snapshot,
 			   int nkeys, ScanKey key)
 {
-	return heap_beginscan_internal(relation, snapshot, nkeys, key,
+	return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
 								   true, true, true, false, false, false);
 }
 
@@ -1331,7 +1381,7 @@ heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
 	Oid			relid = RelationGetRelid(relation);
 	Snapshot	snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
 
-	return heap_beginscan_internal(relation, snapshot, nkeys, key,
+	return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
 								   true, true, true, false, false, true);
 }
 
@@ -1340,7 +1390,7 @@ heap_beginscan_strat(Relation relation, Snapshot snapshot,
 					 int nkeys, ScanKey key,
 					 bool allow_strat, bool allow_sync)
 {
-	return heap_beginscan_internal(relation, snapshot, nkeys, key,
+	return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
 								   allow_strat, allow_sync, true,
 								   false, false, false);
 }
@@ -1349,7 +1399,7 @@ HeapScanDesc
 heap_beginscan_bm(Relation relation, Snapshot snapshot,
 				  int nkeys, ScanKey key)
 {
-	return heap_beginscan_internal(relation, snapshot, nkeys, key,
+	return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
 								   false, false, true, true, false, false);
 }
 
@@ -1358,7 +1408,7 @@ heap_beginscan_sampling(Relation relation, Snapshot snapshot,
 						int nkeys, ScanKey key,
 						bool allow_strat, bool allow_pagemode)
 {
-	return heap_beginscan_internal(relation, snapshot, nkeys, key,
+	return heap_beginscan_internal(relation, snapshot, nkeys, key, NULL,
 								   allow_strat, false, allow_pagemode,
 								   false, true, false);
 }
@@ -1366,6 +1416,7 @@ heap_beginscan_sampling(Relation relation, Snapshot snapshot,
 static HeapScanDesc
 heap_beginscan_internal(Relation relation, Snapshot snapshot,
 						int nkeys, ScanKey key,
+						ParallelHeapScanDesc parallel_scan,
 					  bool allow_strat, bool allow_sync, bool allow_pagemode,
 					  bool is_bitmapscan, bool is_samplescan, bool temp_snap)
 {
@@ -1394,6 +1445,7 @@ heap_beginscan_internal(Relation relation, Snapshot snapshot,
 	scan->rs_allow_strat = allow_strat;
 	scan->rs_allow_sync = allow_sync;
 	scan->rs_temp_snap = temp_snap;
+	scan->rs_parallel = parallel_scan;
 
 	/*
 	 * we can use page-at-a-time mode if it's an MVCC-safe snapshot
@@ -1487,6 +1539,94 @@ heap_endscan(HeapScanDesc scan)
 }
 
 /* ----------------
+ *		heap_parallelscan_estimate - estimate storage for ParallelHeapScanDesc
+ *
+ *		Sadly, this doesn't reduce to a constant, because the size required
+ *		to serialize the snapshot can vary.
+ * ----------------
+ */
+Size
+heap_parallelscan_estimate(Snapshot snapshot)
+{
+	return add_size(offsetof(ParallelHeapScanDescData, phs_snapshot_data),
+					EstimateSnapshotSpace(snapshot));
+}
+
+/* ----------------
+ *		heap_parallelscan_initialize - initialize ParallelHeapScanDesc
+ *
+ *		Must allow as many bytes of shared memory as returned by
+ *		heap_parallelscan_estimate.  Call this just once in the leader
+ *		process; then, individual workers attach via heap_beginscan_parallel.
+ * ----------------
+ */
+void
+heap_parallelscan_initialize(ParallelHeapScanDesc target, Relation relation,
+							 Snapshot snapshot)
+{
+	target->phs_relid = RelationGetRelid(relation);
+	target->phs_nblocks = RelationGetNumberOfBlocks(relation);
+	SpinLockInit(&target->phs_mutex);
+	target->phs_cblock = 0;
+	SerializeSnapshot(snapshot, target->phs_snapshot_data);
+}
+/* ----------------
+ *		heap_parallelscan_nextpage - get the next page to scan
+ *
+ *		A return value larger than the number of blocks to be scanned
+ *		indicates end of scan.  Note, however, that other backends could still
+ *		be scanning if they grabbed a page to scan and aren't done with it yet.
+ * ----------------
+ */
+static BlockNumber
+heap_parallelscan_nextpage(ParallelHeapScanDesc parallel_scan)
+{
+	BlockNumber	page = InvalidBlockNumber;
+
+	/* we treat InvalidBlockNumber specially here to avoid overflow */
+	SpinLockAcquire(&parallel_scan->phs_mutex);
+	if (parallel_scan->phs_cblock != InvalidBlockNumber)
+		page = parallel_scan->phs_cblock++;
+	SpinLockRelease(&parallel_scan->phs_mutex);
+
+	return page;
+}
+
+/* ----------------
+ *		heap_beginscan_parallel - join a parallel scan
+ *
+ *		Caller must hold a suitable lock on the correct relation.
+ * ----------------
+ */
+HeapScanDesc
+heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan)
+{
+	Snapshot		snapshot;
+
+	Assert(RelationGetRelid(relation) == parallel_scan->phs_relid);
+	snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data);
+	RegisterSnapshot(snapshot);
+
+	return heap_beginscan_internal(relation, snapshot, 0, NULL, parallel_scan,
+								   true, true, true, false, false, true);
+}
+
+/* ----------------
+ *		heap_parallel_rescan		- restart a parallel relation scan
+ * ----------------
+ */
+void
+heap_parallel_rescan(ParallelHeapScanDesc pscan,
+					 HeapScanDesc scan)
+{
+	if (pscan != NULL)
+		scan->rs_parallel = pscan;
+
+	heap_rescan(scan,			/* scan desc */
+				NULL);			/* new scan keys */
+}
+
+/* ----------------
  *		heap_getnext	- retrieve next tuple in scan
  *
  *		Fix to work with index relations.
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 8904676..47063c7 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -4395,7 +4395,7 @@ copy_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 /*
  * copy_dest_receive --- receive one tuple
  */
-static void
+static bool
 copy_dest_receive(TupleTableSlot *slot, DestReceiver *self)
 {
 	DR_copy    *myState = (DR_copy *) self;
@@ -4407,6 +4407,8 @@ copy_dest_receive(TupleTableSlot *slot, DestReceiver *self)
 	/* And send the data */
 	CopyOneRowTo(cstate, InvalidOid, slot->tts_values, slot->tts_isnull);
 	myState->processed++;
+
+	return true;
 }
 
 /*
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 41183f6..418b0f6 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -62,7 +62,7 @@ typedef struct
 static ObjectAddress CreateAsReladdr = {InvalidOid, InvalidOid, 0};
 
 static void intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo);
-static void intorel_receive(TupleTableSlot *slot, DestReceiver *self);
+static bool intorel_receive(TupleTableSlot *slot, DestReceiver *self);
 static void intorel_shutdown(DestReceiver *self);
 static void intorel_destroy(DestReceiver *self);
 
@@ -482,7 +482,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 /*
  * intorel_receive --- receive one tuple
  */
-static void
+static bool
 intorel_receive(TupleTableSlot *slot, DestReceiver *self)
 {
 	DR_intorel *myState = (DR_intorel *) self;
@@ -507,6 +507,8 @@ intorel_receive(TupleTableSlot *slot, DestReceiver *self)
 				myState->bistate);
 
 	/* We know this is a newly created relation, so there are no indexes */
+
+	return true;
 }
 
 /*
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 2b930f7..9d6b663 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -20,6 +20,7 @@
 #include "commands/defrem.h"
 #include "commands/prepare.h"
 #include "executor/hashjoin.h"
+#include "executor/nodeFunnel.h"
 #include "foreign/fdwapi.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
@@ -730,6 +731,8 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
 	switch (nodeTag(plan))
 	{
 		case T_SeqScan:
+		case T_PartialSeqScan:
+		case T_Funnel:
 		case T_IndexScan:
 		case T_IndexOnlyScan:
 		case T_BitmapHeapScan:
@@ -935,6 +938,12 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_SeqScan:
 			pname = sname = "Seq Scan";
 			break;
+		case T_PartialSeqScan:
+			pname = sname = "Partial Seq Scan";
+			break;
+		case T_Funnel:
+			pname = sname = "Funnel";
+			break;
 		case T_IndexScan:
 			pname = sname = "Index Scan";
 			break;
@@ -1101,6 +1110,8 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	switch (nodeTag(plan))
 	{
 		case T_SeqScan:
+		case T_PartialSeqScan:
+		case T_Funnel:
 		case T_BitmapHeapScan:
 		case T_TidScan:
 		case T_SubqueryScan:
@@ -1248,6 +1259,16 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	}
 
 	/*
+	 * Aggregate instrumentation information of all the backend
+	 * workers for Funnel node.  Though we already accumulate this
+	 * information when last tuple is fetched from Funnel node, this
+	 * is to cover cases when we don't fetch all tuples from a node
+	 * such as for Limit node.
+	 */
+	if (es->analyze && nodeTag(plan) == T_Funnel)
+		FinishParallelSetupAndAccumStats((FunnelState *)planstate);
+
+	/*
 	 * We have to forcibly clean up the instrumentation state because we
 	 * haven't done ExecutorEnd yet.  This is pretty grotty ...
 	 *
@@ -1364,6 +1385,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 				show_tidbitmap_info((BitmapHeapScanState *) planstate, es);
 			break;
 		case T_SeqScan:
+		case T_PartialSeqScan:
 		case T_ValuesScan:
 		case T_CteScan:
 		case T_WorkTableScan:
@@ -1374,6 +1396,14 @@ ExplainNode(PlanState *planstate, List *ancestors,
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
 			break;
+		case T_Funnel:
+			show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
+			if (plan->qual)
+				show_instrumentation_count("Rows Removed by Filter", 1,
+										   planstate, es);
+			ExplainPropertyInteger("Number of Workers",
+				((Funnel *) plan)->num_workers, es);
+			break;
 		case T_FunctionScan:
 			if (es->verbose)
 			{
@@ -2366,6 +2396,8 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
 	switch (nodeTag(plan))
 	{
 		case T_SeqScan:
+		case T_PartialSeqScan:
+		case T_Funnel:
 		case T_IndexScan:
 		case T_IndexOnlyScan:
 		case T_BitmapHeapScan:
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index 5492e59..750a59c 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -56,7 +56,7 @@ typedef struct
 static int	matview_maintenance_depth = 0;
 
 static void transientrel_startup(DestReceiver *self, int operation, TupleDesc typeinfo);
-static void transientrel_receive(TupleTableSlot *slot, DestReceiver *self);
+static bool transientrel_receive(TupleTableSlot *slot, DestReceiver *self);
 static void transientrel_shutdown(DestReceiver *self);
 static void transientrel_destroy(DestReceiver *self);
 static void refresh_matview_datafill(DestReceiver *dest, Query *query,
@@ -422,7 +422,7 @@ transientrel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 /*
  * transientrel_receive --- receive one tuple
  */
-static void
+static bool
 transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
 {
 	DR_transientrel *myState = (DR_transientrel *) self;
@@ -441,6 +441,8 @@ transientrel_receive(TupleTableSlot *slot, DestReceiver *self)
 				myState->bistate);
 
 	/* We know this is a newly created relation, so there are no indexes */
+
+	return true;
 }
 
 /*
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 08cba6f..be1f47e 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -13,17 +13,17 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execProcnode.o execQual.o execScan.o execTuples.o \
+       execMain.o execParallel.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 nodeCustom.o nodeHash.o \
-       nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \
+       nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeCustom.o nodeFunnel.o \
+       nodeHash.o nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \
        nodeLimit.o nodeLockRows.o \
        nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \
-       nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
-       nodeSamplescan.o nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
+       nodeNestloop.o nodeFunctionscan.o nodePartialSeqscan.o nodeRecursiveunion.o \
+       nodeResult.o nodeSamplescan.o nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
        nodeValuesscan.o nodeCtescan.o nodeWorktablescan.o \
        nodeGroup.o nodeSubplan.o nodeSubqueryscan.o nodeTidscan.o \
-       nodeForeignscan.o nodeWindowAgg.o tstoreReceiver.o spi.o
+       nodeForeignscan.o nodeWindowAgg.o tqueue.o tstoreReceiver.o spi.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 04073d3..233e584 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -24,6 +24,7 @@
 #include "executor/nodeCustom.h"
 #include "executor/nodeForeignscan.h"
 #include "executor/nodeFunctionscan.h"
+#include "executor/nodeFunnel.h"
 #include "executor/nodeGroup.h"
 #include "executor/nodeGroup.h"
 #include "executor/nodeHash.h"
@@ -37,6 +38,7 @@
 #include "executor/nodeMergejoin.h"
 #include "executor/nodeModifyTable.h"
 #include "executor/nodeNestloop.h"
+#include "executor/nodePartialSeqscan.h"
 #include "executor/nodeRecursiveunion.h"
 #include "executor/nodeResult.h"
 #include "executor/nodeSamplescan.h"
@@ -160,6 +162,14 @@ ExecReScan(PlanState *node)
 			ExecReScanSampleScan((SampleScanState *) node);
 			break;
 
+		case T_PartialSeqScanState:
+			ExecReScanPartialSeqScan((PartialSeqScanState *) node);
+			break;
+
+		case T_FunnelState:
+			ExecReScanFunnel((FunnelState *) node);
+			break;
+
 		case T_IndexScanState:
 			ExecReScanIndexScan((IndexScanState *) node);
 			break;
@@ -463,6 +473,10 @@ ExecSupportsBackwardScan(Plan *node)
 		case T_CteScan:
 			return TargetListSupportsBackwardScan(node->targetlist);
 
+		case T_Funnel:
+		case T_PartialSeqScan:
+			return false;
+
 		case T_IndexScan:
 			return IndexSupportsBackwardScan(((IndexScan *) node)->indexid) &&
 				TargetListSupportsBackwardScan(node->targetlist);
diff --git a/src/backend/executor/execCurrent.c b/src/backend/executor/execCurrent.c
index bcd287f..7a44462 100644
--- a/src/backend/executor/execCurrent.c
+++ b/src/backend/executor/execCurrent.c
@@ -262,6 +262,8 @@ search_plan_tree(PlanState *node, Oid table_oid)
 			 */
 		case T_SeqScanState:
 		case T_SampleScanState:
+		case T_PartialSeqScanState:
+		case T_FunnelState:
 		case T_IndexScanState:
 		case T_IndexOnlyScanState:
 		case T_BitmapHeapScanState:
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 2e23cc7..e252727 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -45,9 +45,11 @@
 #include "commands/matview.h"
 #include "commands/trigger.h"
 #include "executor/execdebug.h"
+#include "executor/execParallel.h"
 #include "foreign/fdwapi.h"
 #include "mb/pg_wchar.h"
 #include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "parser/parsetree.h"
 #include "storage/bufmgr.h"
@@ -323,6 +325,9 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 	operation = queryDesc->operation;
 	dest = queryDesc->dest;
 
+	/* inform executor to collect buffer usage stats from parallel workers. */
+	estate->total_time = queryDesc->totaltime ? 1 : 0;
+
 	/*
 	 * startup tuple receiver, if we will be emitting tuples
 	 */
@@ -354,7 +359,15 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 		(*dest->rShutdown) (dest);
 
 	if (queryDesc->totaltime)
+	{
+		/*
+		 * Accumulate the stats by parallel workers before stopping the
+		 * node.
+		 */
+		(void) planstate_tree_walker((Node*) queryDesc->planstate,
+									 NULL, ExecParallelBufferUsageAccum, 0);
 		InstrStopNode(queryDesc->totaltime, estate->es_processed);
+	}
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -1581,7 +1594,15 @@ ExecutePlan(EState *estate,
 		 * practice, this is probably always the case at this point.)
 		 */
 		if (sendTuples)
-			(*dest->receiveSlot) (slot, dest);
+		{
+			/*
+			 * If we are not able to send the tuple, then we assume that
+			 * destination has closed and we won't be able to send any more
+			 * tuples so we just end the loop.
+			 */
+			if (!((*dest->receiveSlot) (slot, dest)))
+				break;
+		}
 
 		/*
 		 * Count tuples processed, if this is a SELECT.  (For other operation
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
new file mode 100644
index 0000000..33e83fe
--- /dev/null
+++ b/src/backend/executor/execParallel.c
@@ -0,0 +1,592 @@
+/*-------------------------------------------------------------------------
+ *
+ * execParallel.c
+ *	  Support routines for setting up backend workers for parallel execution.
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/executor/execParallel.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "executor/execParallel.h"
+#include "executor/nodeFunnel.h"
+#include "executor/nodePartialSeqscan.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planmain.h"
+#include "optimizer/planner.h"
+#include "tcop/tcopprot.h"
+
+
+#define PARALLEL_TUPLE_QUEUE_SIZE					65536
+
+static void ParallelQueryMain(dsm_segment *seg, shm_toc *toc);
+static void
+EstimateParallelSupportInfoSpace(ParallelContext *pcxt, ParamListInfo params,
+								 List *serialized_param_exec_vals,
+								 int instOptions, Size *params_size,
+								 Size *params_exec_size);
+static void
+StoreParallelSupportInfo(ParallelContext *pcxt, ParamListInfo params,
+						 List *serialized_param_exec_vals,
+						 int instOptions, Size params_size,
+						 Size params_exec_size,
+						 char **inst_options_space,
+						 char **buffer_usage_space);
+static void
+EstimatePlannedStmtSpace(ParallelContext *pcxt, PlanState* planstate,
+						 char *plannedstmt_str, Size *plannedstmt_len,
+						 Size *pscan_size);
+static void
+StorePlannedStmt(ParallelContext *pcxt, PlanState* planstate,
+				 char *plannedstmt_str, Size plannedstmt_size,
+				 Size pscan_size);
+static void EstimateResponseQueueSpace(ParallelContext *pcxt);
+static void
+StoreResponseQueue(ParallelContext *pcxt,
+				   shm_mq_handle ***responseqp);
+static void
+ExecParallelGetPlannedStmt(shm_toc *toc, PlannedStmt **plannedstmt);
+static void
+GetParallelSupportInfo(shm_toc *toc, ParamListInfo *params,
+					   List **serialized_param_exec_vals,
+					   int *inst_options, char **instrument,
+					   char **buffer_usage);
+static void
+SetupResponseQueue(dsm_segment *seg, shm_toc *toc, shm_mq **mq,
+				   shm_mq_handle **responseq);
+
+
+/*
+ * This is required for parallel plan execution to fetch the
+ * information from dsm.
+ */
+static shm_toc *parallel_shm_toc = NULL;
+
+/*
+ * EstimateParallelSupportInfoSpace
+ *
+ * Estimate the amount of space required to record information of
+ * bind parameters, PARAM_EXEC parameters and instrumentation
+ * information that need to be retrieved from parallel workers.
+ */
+void
+EstimateParallelSupportInfoSpace(ParallelContext *pcxt, ParamListInfo params,
+								 List *serialized_param_exec_vals,
+								 int instOptions, Size *params_size,
+								 Size *params_exec_size)
+{
+	*params_size = EstimateBoundParametersSpace(params);
+	shm_toc_estimate_chunk(&pcxt->estimator, *params_size);
+
+	*params_exec_size = EstimateExecParametersSpace(serialized_param_exec_vals);
+	shm_toc_estimate_chunk(&pcxt->estimator, *params_exec_size);
+
+	/*
+	 * We expect each worker to populate the BufferUsage structure
+	 * allocated by master backend and then master backend will aggregate
+	 * all the usage along with it's own, so account it for each worker.
+	 */
+	shm_toc_estimate_chunk(&pcxt->estimator,
+						   sizeof(BufferUsage) * pcxt->nworkers);
+
+	/* account for instrumentation options. */
+	shm_toc_estimate_chunk(&pcxt->estimator, sizeof(int));
+
+	/*
+	 * We expect each worker to populate the instrumentation structure
+	 * allocated by master backend and then master backend will aggregate
+	 * all the information, so account it for each worker.
+	 */
+	if (instOptions)
+	{
+		shm_toc_estimate_chunk(&pcxt->estimator,
+							   sizeof(Instrumentation) * pcxt->nworkers);
+		/* keys for parallel support information. */
+		shm_toc_estimate_keys(&pcxt->estimator, 1);
+	}
+
+	/* keys for parallel support information. */
+	shm_toc_estimate_keys(&pcxt->estimator, 4);
+}
+
+/*
+ * StoreParallelSupportInfo
+ * 
+ * Sets up the bind parameters, PARAM_EXEC parameters and instrumentation
+ * information required for parallel execution.
+ */
+void
+StoreParallelSupportInfo(ParallelContext *pcxt, ParamListInfo params,
+						 List *serialized_param_exec_vals,
+						 int instOptions, Size params_size,
+						 Size params_exec_size,
+						 char **inst_options_space,
+						 char **buffer_usage_space)
+{
+	char	*paramsdata;
+	char	*paramsexecdata;
+	int		*inst_options;
+
+	/*
+	 * Store bind parameter's list in dynamic shared memory.  This is
+	 * used for parameters in prepared query.
+	 */
+	paramsdata = shm_toc_allocate(pcxt->toc, params_size);
+	SerializeBoundParams(params, params_size, paramsdata);
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_PARAMS, paramsdata);
+
+	/*
+	 * Store PARAM_EXEC parameters list in dynamic shared memory.  This is
+	 * used for evaluation plan->initPlan params.
+	 */
+	paramsexecdata = shm_toc_allocate(pcxt->toc, params_exec_size);
+	SerializeExecParams(serialized_param_exec_vals, params_exec_size, paramsexecdata);
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_PARAMS_EXEC, paramsexecdata);
+
+	/*
+	 * Allocate space for BufferUsage information to be filled by
+	 * each worker.
+	 */
+	*buffer_usage_space =
+			shm_toc_allocate(pcxt->toc, sizeof(BufferUsage) * pcxt->nworkers);
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_BUFF_USAGE, *buffer_usage_space);
+
+	/* Store instrument options in dynamic shared memory. */
+	inst_options = shm_toc_allocate(pcxt->toc, sizeof(int));
+	*inst_options = instOptions;
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_INST_OPTIONS, inst_options);
+
+	/*
+	 * Allocate space for instrumentation information to be filled by
+	 * each worker.
+	 */
+	if (instOptions)
+	{
+		*inst_options_space =
+			shm_toc_allocate(pcxt->toc, sizeof(Instrumentation) * pcxt->nworkers);
+		shm_toc_insert(pcxt->toc, PARALLEL_KEY_INST_INFO, *inst_options_space);
+	}
+}
+
+/*
+ * EstimatePlannedStmtSpace
+ *
+ * Estimate the amount of space required to record information of
+ * planned statement and parallel node specific information that need
+ * to be copied to parallel workers.
+ */
+void
+EstimatePlannedStmtSpace(ParallelContext *pcxt, PlanState* planstate,
+						 char *plannedstmt_str, Size *plannedstmt_len,
+						 Size *pscan_size)
+{
+	/* Estimate space for planned statement. */
+	*plannedstmt_len = strlen(plannedstmt_str) + 1;
+	shm_toc_estimate_chunk(&pcxt->estimator, *plannedstmt_len);
+
+	/* keys for planned statement information. */
+	shm_toc_estimate_keys(&pcxt->estimator, 1);
+
+	(void) planstate_tree_walker((Node*)planstate, pcxt, ExecParallelEstimate,
+								 pscan_size);
+}
+
+/*
+ * StorePlannedStmt
+ * 
+ * Sets up the planned statement and node specific information.
+ */
+void
+StorePlannedStmt(ParallelContext *pcxt, PlanState* planstate,
+				 char *plannedstmt_str, Size plannedstmt_size,
+				 Size pscan_size)
+{
+	char		*plannedstmtdata;
+
+	/* Store planned statement in dynamic shared memory. */
+	plannedstmtdata = shm_toc_allocate(pcxt->toc, plannedstmt_size);
+	memcpy(plannedstmtdata, plannedstmt_str, plannedstmt_size);
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_PLANNEDSTMT, plannedstmtdata);
+
+	(void) planstate_tree_walker((Node*)planstate, pcxt, ExecParallelInitializeDSM,
+								 &pscan_size);
+}
+
+/*
+ * EstimateResponseQueueSpace
+ *
+ * Estimate the amount of space required to record information of
+ * tuple queues that need to be established between parallel workers
+ * and master backend.
+ */
+void
+EstimateResponseQueueSpace(ParallelContext *pcxt)
+{
+	/* Estimate space for parallel seq. scan specific contents. */
+	shm_toc_estimate_chunk(&pcxt->estimator,
+						   (Size) PARALLEL_TUPLE_QUEUE_SIZE * pcxt->nworkers);
+
+	/* keys for response queue. */
+	shm_toc_estimate_keys(&pcxt->estimator, 1);
+}
+
+/*
+ * StoreResponseQueue
+ * 
+ * It sets up the response queue's for backend worker's to
+ * return tuples to the main backend and start the workers.
+ */
+void
+StoreResponseQueue(ParallelContext *pcxt,
+				   shm_mq_handle ***responseqp)
+{
+	shm_mq		*mq;
+	char		*tuple_queue_space;
+	int			i;
+
+	/* Allocate memory for shared memory queue handles. */
+	*responseqp = (shm_mq_handle**) palloc(pcxt->nworkers * sizeof(shm_mq_handle*));
+
+	/*
+	 * Establish one message queue per worker in dynamic shared memory.
+	 * These queues should be used to transmit tuple data.
+	 */
+	tuple_queue_space =
+	   shm_toc_allocate(pcxt->toc, PARALLEL_TUPLE_QUEUE_SIZE * pcxt->nworkers);
+	for (i = 0; i < pcxt->nworkers; ++i)
+	{
+		mq = shm_mq_create(tuple_queue_space + i * PARALLEL_TUPLE_QUEUE_SIZE,
+						   (Size) PARALLEL_TUPLE_QUEUE_SIZE);
+		
+		shm_mq_set_receiver(mq, MyProc);
+
+		/*
+		 * Attach the queue before launching a worker, so that we'll automatically
+		 * detach the queue if we error out.  (Otherwise, the worker might sit
+		 * there trying to write the queue long after we've gone away.)
+		 */
+		(*responseqp)[i] = shm_mq_attach(mq, pcxt->seg, NULL);
+	}
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_TUPLE_QUEUE, tuple_queue_space);
+}
+
+/*
+ *	ExecParallelEstimate
+ *
+ *		Estimate the amount of space required to record information of
+ * parallel node that need to be copied to parallel workers.
+ */
+bool
+ExecParallelEstimate(Node *node, ParallelContext *pcxt,
+					 Size *pscan_size)
+{
+	if (node == NULL)
+		return false;
+
+	switch (nodeTag(node))
+	{
+		case T_ResultState:
+			{
+				PlanState *planstate = ((ResultState*)node)->ps.lefttree;
+
+				return planstate_tree_walker((Node*)planstate, pcxt,
+											 ExecParallelEstimate, pscan_size);
+			}
+		case T_PartialSeqScanState:
+			{
+				EState		*estate = ((PartialSeqScanState*)node)->ss.ps.state;
+
+				*pscan_size = heap_parallelscan_estimate(estate->es_snapshot);
+				shm_toc_estimate_chunk(&pcxt->estimator, *pscan_size);
+
+				/* key for paratial scan information. */
+				shm_toc_estimate_keys(&pcxt->estimator, 1);
+				return true;
+			}
+		default:
+			break;
+	}
+
+	return false;
+}
+
+/*
+ *	ExecParallelInitializeDSM
+ *
+ *		Store the information of parallel node in dsm.
+ */
+bool
+ExecParallelInitializeDSM(Node *node, ParallelContext *pcxt,
+						  Size *pscan_size)
+{
+	ParallelHeapScanDesc pscan;
+
+	if (node == NULL)
+		return false;
+
+	switch (nodeTag(node))
+	{
+		case T_ResultState:
+			{
+				PlanState *planstate = ((ResultState*)node)->ps.lefttree;
+
+				return planstate_tree_walker((Node*)planstate, pcxt,
+											 ExecParallelInitializeDSM, pscan_size);
+			}
+		case T_PartialSeqScanState:
+			{
+				EState	*estate = ((PartialSeqScanState*)node)->ss.ps.state;
+
+				/* Store parallel heap scan descriptor in dynamic shared memory. */
+				pscan = shm_toc_allocate(pcxt->toc, *pscan_size);
+				heap_parallelscan_initialize(pscan, ((PartialSeqScanState*)node)->ss.ss_currentRelation, estate->es_snapshot);
+				shm_toc_insert(pcxt->toc, PARALLEL_KEY_SCAN, pscan);
+				return true;
+			}
+		default:
+			break;
+	}
+
+	return false;
+}
+
+/*
+ * InitializeParallelWorkers
+ *
+ *	Sets up the required infrastructure for backend workers to
+ *	perform execution and return results to the main backend.
+ */
+void
+InitializeParallelWorkers(PlanState *planstate,
+						  List *serialized_param_exec_vals,
+						  EState *estate,
+						  char **inst_options_space,
+						  char **buffer_usage_space,
+						  shm_mq_handle ***responseqp,
+						  ParallelContext **pcxtp,
+						  int nWorkers)
+{
+	Size		params_size, params_exec_size, pscan_size, plannedstmt_size;
+	char		*plannedstmt_str;
+	PlannedStmt	*plannedstmt;
+	ParallelContext *pcxt;
+
+	pcxt = CreateParallelContext(ParallelQueryMain, nWorkers);
+
+	plannedstmt = create_parallel_worker_plannedstmt((PartialSeqScan *)planstate->plan,
+													 estate->es_range_table,
+													 estate->es_plannedstmt->nParamExec);
+	plannedstmt_str = nodeToString(plannedstmt);
+
+	EstimatePlannedStmtSpace(pcxt, planstate, plannedstmt_str,
+							 &plannedstmt_size, &pscan_size);
+	EstimateParallelSupportInfoSpace(pcxt, estate->es_param_list_info,
+									 serialized_param_exec_vals,
+									 estate->es_instrument, &params_size,
+									 &params_exec_size);
+	EstimateResponseQueueSpace(pcxt);
+
+	InitializeParallelDSM(pcxt);
+	
+	StorePlannedStmt(pcxt, planstate, plannedstmt_str,
+					 plannedstmt_size, pscan_size);
+	StoreParallelSupportInfo(pcxt, estate->es_param_list_info,
+							 serialized_param_exec_vals,
+							 estate->es_instrument,
+							 params_size,
+							 params_exec_size,
+							 inst_options_space,
+							 buffer_usage_space);
+	StoreResponseQueue(pcxt, responseqp);
+
+	/* Return results to caller. */
+	*pcxtp = pcxt;
+}
+
+/*
+ * GetParallelSupportInfo
+ *
+ * Look up based on keys in dynamic shared memory segment
+ * and get the bind parameters, PARAM_EXEC parameters and
+ * instrumentation information required to perform parallel
+ * operation.
+ */
+void
+GetParallelSupportInfo(shm_toc *toc, ParamListInfo *params,
+					   List **serialized_param_exec_vals,
+					   int *inst_options, char **instrument,
+					   char **buffer_usage)
+{
+	char		*paramsdata;
+	char		*paramsexecdata;
+	char		*inst_options_space;
+	char		*buffer_usage_space;
+	int			*instoptions;
+
+	if (params)
+	{
+		paramsdata = shm_toc_lookup(toc, PARALLEL_KEY_PARAMS);
+		*params = RestoreBoundParams(paramsdata);
+	}
+
+	if (serialized_param_exec_vals)
+	{
+		paramsexecdata = shm_toc_lookup(toc, PARALLEL_KEY_PARAMS_EXEC);
+		*serialized_param_exec_vals = RestoreExecParams(paramsexecdata);
+	}
+
+	if (inst_options)
+	{
+		instoptions	= shm_toc_lookup(toc, PARALLEL_KEY_INST_OPTIONS);
+		*inst_options = *instoptions;
+		if (inst_options)
+		{
+			inst_options_space = shm_toc_lookup(toc, PARALLEL_KEY_INST_INFO);
+			*instrument = (inst_options_space +
+				ParallelWorkerNumber * sizeof(Instrumentation));
+		}
+	}
+
+	if (buffer_usage)
+	{
+		buffer_usage_space = shm_toc_lookup(toc, PARALLEL_KEY_BUFF_USAGE);
+		*buffer_usage = (buffer_usage_space +
+					 ParallelWorkerNumber * sizeof(BufferUsage));
+	}
+}
+
+/*
+ * ExecParallelGetPlannedStmt
+ *
+ * Look up based on keys in dynamic shared memory segment
+ * and get the planned statement required to perform
+ * parallel operation.
+ */
+void
+ExecParallelGetPlannedStmt(shm_toc *toc, PlannedStmt **plannedstmt)
+{
+	char		*plannedstmtdata;
+
+	plannedstmtdata = shm_toc_lookup(toc, PARALLEL_KEY_PLANNEDSTMT);
+
+	*plannedstmt = (PlannedStmt *) stringToNode(plannedstmtdata);
+
+	/* Fill in opfuncid values if missing */
+	fix_node_funcids((*plannedstmt)->planTree);
+}
+
+/*
+ * SetupResponseQueue
+ *
+ * Look up based on keys in dynamic shared memory segment
+ * and get the tuple queue information for a particular worker,
+ * attach to the queue and redirect all futher responses from
+ * worker backend via that queue.
+ */
+void
+SetupResponseQueue(dsm_segment *seg, shm_toc *toc, shm_mq **mq,
+				   shm_mq_handle **responseq)
+{
+	char		*tuple_queue_space;
+
+	tuple_queue_space = shm_toc_lookup(toc, PARALLEL_KEY_TUPLE_QUEUE);
+	*mq = (shm_mq *) (tuple_queue_space +
+		ParallelWorkerNumber * PARALLEL_TUPLE_QUEUE_SIZE);
+
+	shm_mq_set_sender(*mq, MyProc);
+	*responseq = shm_mq_attach(*mq, seg, NULL);
+}
+
+/*
+ * GetParallelShmToc
+ */
+shm_toc *
+GetParallelShmToc(void)
+{
+	return parallel_shm_toc;
+}
+
+/*
+ * ParallelQueryMain
+ *
+ * Execute the operation to return the tuples or other information
+ * to parallelism driving node.
+ */
+void
+ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
+{
+	shm_mq			*mq;
+	shm_mq_handle	*responseq;
+	PlannedStmt		*plannedstmt;
+	ParamListInfo	params;
+	List			*serialized_param_exec_vals;
+	int				inst_options;
+	char			*instrument = NULL;
+	char			*buffer_usage = NULL;
+	ParallelStmt	*parallelstmt;
+
+	SetupResponseQueue(seg, toc, &mq, &responseq);
+
+	ExecParallelGetPlannedStmt(toc, &plannedstmt);
+	GetParallelSupportInfo(toc, &params, &serialized_param_exec_vals,
+						   &inst_options, &instrument, &buffer_usage);
+
+	parallelstmt = palloc(sizeof(ParallelStmt));
+
+	parallelstmt->plannedstmt = plannedstmt;
+	parallelstmt->params	= params;
+	parallelstmt->serialized_param_exec_vals = serialized_param_exec_vals;
+	parallelstmt->inst_options = inst_options;
+	parallelstmt->instrument = instrument;
+	parallelstmt->buffer_usage = buffer_usage;
+	parallelstmt->responseq = responseq;
+
+	parallel_shm_toc = toc;
+
+	/* Execute the worker command. */
+	exec_parallel_stmt(parallelstmt);
+
+	/*
+	 * Once we are done with sending tuples, detach from
+	 * shared memory message queue used to send tuples.
+	 */
+	shm_mq_detach(mq);
+}
+
+/*
+ * ExecParallelBufferUsageAccum
+ *
+ * Recursively accumulate the stats for all the funnel nodes
+ * in a plan state tree.
+ */
+bool
+ExecParallelBufferUsageAccum(Node *node)
+{
+	if (node == NULL)
+		return false;
+
+	switch (nodeTag(node))
+	{
+		case T_FunnelState:
+			{
+				FinishParallelSetupAndAccumStats((FunnelState*)node);
+				return true;
+			}
+			break;
+		default:
+			break;
+	}
+
+	(void) planstate_tree_walker((Node*)((PlanState *)node)->lefttree, NULL,
+								 ExecParallelBufferUsageAccum, 0);
+	(void) planstate_tree_walker((Node*)((PlanState *)node)->righttree, NULL,
+								 ExecParallelBufferUsageAccum, 0);
+	return false;
+}
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 03c2feb..e24a439 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -100,6 +100,8 @@
 #include "executor/nodeMergejoin.h"
 #include "executor/nodeModifyTable.h"
 #include "executor/nodeNestloop.h"
+#include "executor/nodePartialSeqscan.h"
+#include "executor/nodeFunnel.h"
 #include "executor/nodeRecursiveunion.h"
 #include "executor/nodeResult.h"
 #include "executor/nodeSamplescan.h"
@@ -196,6 +198,16 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 													  estate, eflags);
 			break;
 
+		case T_PartialSeqScan:
+			result = (PlanState *) ExecInitPartialSeqScan((PartialSeqScan *) node,
+														  estate, eflags);
+			break;
+
+		case T_Funnel:
+			result = (PlanState *) ExecInitFunnel((Funnel *) node,
+												  estate, eflags);
+			break;
+
 		case T_IndexScan:
 			result = (PlanState *) ExecInitIndexScan((IndexScan *) node,
 													 estate, eflags);
@@ -416,6 +428,14 @@ ExecProcNode(PlanState *node)
 			result = ExecSampleScan((SampleScanState *) node);
 			break;
 
+		case T_PartialSeqScanState:
+			result = ExecPartialSeqScan((PartialSeqScanState *) node);
+			break;
+
+		case T_FunnelState:
+			result = ExecFunnel((FunnelState *) node);
+			break;
+
 		case T_IndexScanState:
 			result = ExecIndexScan((IndexScanState *) node);
 			break;
@@ -658,6 +678,14 @@ ExecEndNode(PlanState *node)
 			ExecEndSampleScan((SampleScanState *) node);
 			break;
 
+		case T_PartialSeqScanState:
+			ExecEndPartialSeqScan((PartialSeqScanState *) node);
+			break;
+
+		case T_FunnelState:
+			ExecEndFunnel((FunnelState *) node);
+			break;
+
 		case T_IndexScanState:
 			ExecEndIndexScan((IndexScanState *) node);
 			break;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index a05d8b1..d5619bd 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -1313,7 +1313,7 @@ do_tup_output(TupOutputState *tstate, Datum *values, bool *isnull)
 	ExecStoreVirtualTuple(slot);
 
 	/* send the tuple to the receiver */
-	(*tstate->dest->receiveSlot) (slot, tstate->dest);
+	(void) (*tstate->dest->receiveSlot) (slot, tstate->dest);
 
 	/* clean up */
 	ExecClearTuple(slot);
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 3c611b9..27ca0fa 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -976,3 +976,28 @@ ShutdownExprContext(ExprContext *econtext, bool isCommit)
 
 	MemoryContextSwitchTo(oldcontext);
 }
+
+/*
+ * Populate the values of PARAM_EXEC parameters.
+ *
+ * This is used by worker backends to fill in the values
+ * of PARAM_EXEC parameters after fetching the same from
+ * dynamic shared memory.  This needs to be called before
+ * ExecutorRun.
+ */
+void
+PopulateParamExecParams(QueryDesc *queryDesc,
+						List *serialized_param_exec_vals)
+{
+	ListCell	*lparam;
+
+	foreach(lparam, serialized_param_exec_vals)
+	{
+		SerializedParamExecData* param_val = (SerializedParamExecData*) lfirst(lparam);
+
+		queryDesc->estate->es_param_exec_vals[param_val->paramid].value =
+																param_val->value;
+		queryDesc->estate->es_param_exec_vals[param_val->paramid].isnull =
+																param_val->isnull;
+	}
+}
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 812a610..863bd64 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -167,7 +167,7 @@ static Datum postquel_get_single_result(TupleTableSlot *slot,
 static void sql_exec_error_callback(void *arg);
 static void ShutdownSQLFunction(Datum arg);
 static void sqlfunction_startup(DestReceiver *self, int operation, TupleDesc typeinfo);
-static void sqlfunction_receive(TupleTableSlot *slot, DestReceiver *self);
+static bool sqlfunction_receive(TupleTableSlot *slot, DestReceiver *self);
 static void sqlfunction_shutdown(DestReceiver *self);
 static void sqlfunction_destroy(DestReceiver *self);
 
@@ -1903,7 +1903,7 @@ sqlfunction_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
 /*
  * sqlfunction_receive --- receive one tuple
  */
-static void
+static bool
 sqlfunction_receive(TupleTableSlot *slot, DestReceiver *self)
 {
 	DR_sqlfunction *myState = (DR_sqlfunction *) self;
@@ -1913,6 +1913,8 @@ sqlfunction_receive(TupleTableSlot *slot, DestReceiver *self)
 
 	/* Store the filtered tuple into the tuplestore */
 	tuplestore_puttupleslot(myState->tstore, slot);
+
+	return true;
 }
 
 /*
diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c
index f5351eb..283a136 100644
--- a/src/backend/executor/instrument.c
+++ b/src/backend/executor/instrument.c
@@ -19,9 +19,6 @@
 
 BufferUsage pgBufferUsage;
 
-static void BufferUsageAccumDiff(BufferUsage *dst,
-					 const BufferUsage *add, const BufferUsage *sub);
-
 
 /* Allocate new instrumentation structure(s) */
 Instrumentation *
@@ -127,8 +124,30 @@ InstrEndLoop(Instrumentation *instr)
 	instr->tuplecount = 0;
 }
 
+/*
+ * Aggregate the instrumentation information.  This is used
+ * to aggregate the information of worker backends.  We only
+ * need to sum the buffer usage and tuple count statistics as
+ * for other timing related statistics it is sufficient to
+ * have the master backend's information.
+ */
+void
+InstrAggNode(Instrumentation *instr1, Instrumentation *instr2)
+{
+	/* count the returned tuples */
+	instr1->tuplecount += instr2->tuplecount;
+
+	instr1->nfiltered1 += instr2->nfiltered1;
+	instr1->nfiltered2 += instr2->nfiltered2;
+
+	/* Add delta of buffer usage since entry to node's totals */
+	if (instr1->need_bufusage)
+		BufferUsageAdd(&instr1->bufusage, &instr2->bufusage);
+
+}
+
 /* dst += add - sub */
-static void
+void
 BufferUsageAccumDiff(BufferUsage *dst,
 					 const BufferUsage *add,
 					 const BufferUsage *sub)
@@ -148,3 +167,21 @@ BufferUsageAccumDiff(BufferUsage *dst,
 	INSTR_TIME_ACCUM_DIFF(dst->blk_write_time,
 						  add->blk_write_time, sub->blk_write_time);
 }
+
+/* dst += add */
+void
+BufferUsageAdd(BufferUsage *dst, const BufferUsage *add)
+{
+	dst->shared_blks_hit += add->shared_blks_hit;
+	dst->shared_blks_read += add->shared_blks_read;
+	dst->shared_blks_dirtied += add->shared_blks_dirtied;
+	dst->shared_blks_written += add->shared_blks_written;
+	dst->local_blks_hit += add->local_blks_hit;
+	dst->local_blks_read += add->local_blks_read;
+	dst->local_blks_dirtied += add->local_blks_dirtied;
+	dst->local_blks_written += add->local_blks_written;
+	dst->temp_blks_read += add->temp_blks_read;
+	dst->temp_blks_written += add->temp_blks_written;
+	INSTR_TIME_ADD(dst->blk_read_time, add->blk_read_time);
+	INSTR_TIME_ADD(dst->blk_write_time, add->blk_write_time);
+}
diff --git a/src/backend/executor/nodeFunnel.c b/src/backend/executor/nodeFunnel.c
new file mode 100644
index 0000000..3c42f21
--- /dev/null
+++ b/src/backend/executor/nodeFunnel.c
@@ -0,0 +1,436 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeFunnel.c
+ *	  Support routines for parallel sequential scans of relations.
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/executor/nodeFunnel.c
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *		ExecFunnel				scans a relation.
+ *		ExecInitFunnel			creates and initializes a funnel node.
+ *		ExecEndFunnel			releases any storage allocated.
+ *		ExecReScanFunnel		rescans a relation
+ */
+#include "postgres.h"
+
+#include "access/relscan.h"
+#include "executor/execdebug.h"
+#include "executor/execParallel.h"
+#include "executor/nodeFunnel.h"
+#include "executor/nodeSubplan.h"
+#include "utils/rel.h"
+
+
+static TupleTableSlot *funnel_getnext(FunnelState *funnelstate);
+static void ExecAccumulateInstInfo(FunnelState *node);
+static void ExecAccumulateBufUsageInfo(FunnelState *node);
+
+/* ----------------------------------------------------------------
+ *						Scan Support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *		InitFunnel
+ *
+ *		Set up parallel state information
+ * ----------------------------------------------------------------
+ */
+static void
+InitFunnel(FunnelState *node, EState *estate, int eflags)
+{
+	Relation	currentRelation;
+
+	/*
+	 * get the relation object id from the relid'th entry in the range table,
+	 * open that relation and acquire appropriate lock on it.
+	 */
+	currentRelation = ExecOpenScanRelation(estate,
+										   ((SeqScan *) node->ss.ps.plan)->scanrelid,
+										   eflags);
+
+	node->ss.ss_currentRelation = currentRelation;
+
+	/* and report the scan tuple slot's rowtype */
+	ExecAssignScanType(&node->ss, RelationGetDescr(currentRelation));
+}
+
+/* ----------------------------------------------------------------
+ *		ExecInitFunnel
+ * ----------------------------------------------------------------
+ */
+FunnelState *
+ExecInitFunnel(Funnel *node, EState *estate, int eflags)
+{
+	FunnelState *funnelstate;
+
+	 /* Funnel node doesn't have innerPlan node. */
+	Assert(innerPlan(node) == NULL);
+
+	/*
+	 * create state structure
+	 */
+	funnelstate = makeNode(FunnelState);
+	funnelstate->ss.ps.plan = (Plan *) node;
+	funnelstate->ss.ps.state = estate;
+	funnelstate->fs_workersReady = false;
+
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * create expression context for node
+	 */
+	ExecAssignExprContext(estate, &funnelstate->ss.ps);
+
+	/*
+	 * initialize child expressions
+	 */
+	funnelstate->ss.ps.targetlist = (List *)
+		ExecInitExpr((Expr *) node->scan.plan.targetlist,
+					 (PlanState *) funnelstate);
+	funnelstate->ss.ps.qual = (List *)
+		ExecInitExpr((Expr *) node->scan.plan.qual,
+					 (PlanState *) funnelstate);
+
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlot(estate, &funnelstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &funnelstate->ss);
+
+	InitFunnel(funnelstate, estate, eflags);
+
+	/*
+	 * now initialize outer plan
+	 */
+	outerPlanState(funnelstate) = ExecInitNode(outerPlan(node), estate, eflags);
+
+
+	funnelstate->ss.ps.ps_TupFromTlist = false;
+
+	/*
+	 * Initialize result tuple type and projection info.
+	 */
+	ExecAssignResultTypeFromTL(&funnelstate->ss.ps);
+	ExecAssignScanProjectionInfo(&funnelstate->ss);
+
+	return funnelstate;
+}
+
+/* ----------------------------------------------------------------
+ *		ExecFunnel(node)
+ *
+ *		Scans the relation via multiple workers and returns
+ *		the next qualifying tuple.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecFunnel(FunnelState *node)
+{
+	int			i;
+	TupleTableSlot *slot;
+
+	/*
+	 * Initialize the parallel context and workers on first execution.
+	 * We do this on first execution rather than during node initialization,
+	 * as it needs to allocate large dynamic segement, so it is better to 
+	 * do if it is really needed.
+	 */
+	if (!node->pcxt)
+	{
+		EState		*estate = node->ss.ps.state;
+		ExprContext *econtext = node->ss.ps.ps_ExprContext;
+		bool		any_worker_launched = false;
+		List		*serialized_param_exec;
+
+		/*
+		 * Evaluate the InitPlan and pass the PARAM_EXEC params, so that
+		 * values can be shared with worker backend.  This is different
+		 * from the way InitPlans are evaluated (lazy evaluation) at other
+		 * places as instead of sharing the InitPlan to all the workers
+		 * and let them execute, we pass the values which can be directly
+		 * used by worker backends.
+		 */
+		serialized_param_exec = ExecAndFormSerializeParamExec(econtext,
+											node->ss.ps.plan->lefttree->allParam);
+
+		/* Initialize the workers required to execute funnel node. */
+		InitializeParallelWorkers(node->ss.ps.lefttree,
+								  serialized_param_exec,
+								  estate,
+								  &node->inst_options_space,
+								  &node->buffer_usage_space,
+								  &node->responseq,
+								  &node->pcxt,
+								  ((Funnel *)(node->ss.ps.plan))->num_workers);
+
+		outerPlanState(node)->toc = node->pcxt->toc;
+
+		/*
+		 * Register backend workers. If the required number of workers are
+		 * not available then we perform the scan with available workers and
+		 * If there are no more workers available, then the funnel node will
+		 * just scan locally.
+		 */
+		LaunchParallelWorkers(node->pcxt);
+
+		node->funnel = CreateTupleQueueFunnel();
+
+		for (i = 0; i < node->pcxt->nworkers; ++i)
+		{
+			if (node->pcxt->worker[i].bgwhandle)
+			{
+				shm_mq_set_handle((node->responseq)[i], node->pcxt->worker[i].bgwhandle);
+				RegisterTupleQueueOnFunnel(node->funnel, (node->responseq)[i]);
+				any_worker_launched = true;
+			}
+		}
+
+		if (any_worker_launched)
+			node->fs_workersReady = true;
+	}
+	
+	slot = funnel_getnext(node);
+	
+	if (TupIsNull(slot))
+	{
+
+		/*
+		 * Destroy the parallel context once we complete fetching all
+		 * the tuples, this will ensure that if in the same statement
+		 * we need to have Funnel node for multiple parts of statement,
+		 * it won't accumulate lot of dsm's and workers can be made
+		 * available to use by other parts of statement.
+		 */
+		FinishParallelSetupAndAccumStats(node);
+	}
+	return slot;
+}
+
+/* ----------------------------------------------------------------
+ *		ExecEndFunnel
+ *
+ *		frees any storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndFunnel(FunnelState *node)
+{
+	Relation	relation;
+
+	relation = node->ss.ss_currentRelation;
+
+	/*
+	 * Free the exprcontext
+	 */
+	ExecFreeExprContext(&node->ss.ps);
+
+	/*
+	 * clean out the tuple table
+	 */
+	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
+
+	/*
+	 * close the heap relation.
+	 */
+	ExecCloseScanRelation(relation);
+
+	ExecEndNode(outerPlanState(node));
+
+	FinishParallelSetupAndAccumStats(node);
+}
+
+/*
+ * funnel_getnext
+ *
+ *	Get the next tuple from shared memory queue.  This function
+ *	is reponsible for fetching tuples from all the queues associated
+ *	with worker backends used in funnel scan and if there is no
+ *  data available from queues or no worker is available, it does
+ *  fetch the data from local node.
+ */
+TupleTableSlot *
+funnel_getnext(FunnelState *funnelstate)
+{
+	PlanState		*outerPlan;
+	TupleTableSlot	*outerTupleSlot;
+	TupleTableSlot	*slot;
+	HeapTuple		tup;
+
+	if (funnelstate->ss.ps.ps_ProjInfo)
+		slot = funnelstate->ss.ps.ps_ProjInfo->pi_slot;
+	else
+		slot = funnelstate->ss.ss_ScanTupleSlot;
+
+	while ((!funnelstate->all_workers_done  && funnelstate->fs_workersReady) ||
+			!funnelstate->local_scan_done)
+	{
+		if (!funnelstate->all_workers_done && funnelstate->fs_workersReady)
+		{
+			/* wait only if local scan is done */
+			tup = TupleQueueFunnelNext(funnelstate->funnel,
+									   !funnelstate->local_scan_done,
+									   &funnelstate->all_workers_done);
+
+			if (HeapTupleIsValid(tup))
+			{
+				ExecStoreTuple(tup,		/* tuple to store */
+							   slot,	/* slot to store in */
+							   InvalidBuffer, /* buffer associated with this
+											   * tuple */
+							   true);	/* pfree this pointer if not from heap */
+
+				return slot;
+			}
+		}
+		if (!funnelstate->local_scan_done)
+		{
+			outerPlan = outerPlanState(funnelstate);
+
+			outerTupleSlot = ExecProcNode(outerPlan);
+
+			if (!TupIsNull(outerTupleSlot))
+				return outerTupleSlot;
+
+			funnelstate->local_scan_done = true;
+		}
+	}
+
+	return ExecClearTuple(slot);
+}
+
+/* ----------------------------------------------------------------
+ *		FinishParallelSetupAndAccumStats
+ *
+ *		Destroy the setup for parallel workers.  Collect all the
+ *		stats after workers are stopped, else some work done by
+ *		workers won't be accounted.
+ * ----------------------------------------------------------------
+ */
+void
+FinishParallelSetupAndAccumStats(FunnelState *node)
+{
+	if (node->pcxt)
+	{
+		/*
+		 * Ensure all workers have finished before destroying the parallel
+		 * context to ensure a clean exit.
+		 */
+		if (node->fs_workersReady)
+		{
+			TupleQueueFunnelShutdown(node->funnel);
+			WaitForParallelWorkersToFinish(node->pcxt);
+		}
+
+		/* destroy the tuple queue */
+		DestroyTupleQueueFunnel(node->funnel);
+		node->funnel = NULL;
+
+		/*
+		 * Aggregate the buffer usage stats from all workers.  This is
+		 * required by external modules like pg_stat_statements.
+		 */
+		ExecAccumulateBufUsageInfo(node);
+
+		/*
+		 * Aggregate instrumentation information of all the backend
+		 * workers for Funnel node.  This has to be done before we
+		 * destroy the parallel context.
+		 */
+		if (node->ss.ps.state->es_instrument)
+			ExecAccumulateInstInfo(node);
+
+		/* destroy parallel context. */
+		DestroyParallelContext(node->pcxt);
+		node->pcxt = NULL;
+
+		node->fs_workersReady = false;
+		node->all_workers_done = false;
+		node->local_scan_done = false;
+	}
+}
+
+/* ----------------------------------------------------------------
+ *		ExecAccumulateInstInfo
+ *
+ *		Accumulate instrumentation information of all the workers
+ * ----------------------------------------------------------------
+ */
+void ExecAccumulateInstInfo(FunnelState *node)
+{
+	int i;
+	Instrumentation *instrument_worker;
+	int nworkers;
+	char *inst_info_workers;
+	
+	if (node->pcxt)
+	{
+		nworkers = node->pcxt->nworkers;
+		inst_info_workers = node->inst_options_space;
+		for (i = 0; i < nworkers; i++)
+		{
+			instrument_worker = (Instrumentation *)(inst_info_workers + (i * sizeof(Instrumentation)));
+			InstrAggNode(node->ss.ps.instrument, instrument_worker);
+		}
+	}
+}
+
+/* ----------------------------------------------------------------
+ *		ExecAccumulateBufUsageInfo
+ *
+ *		Accumulate buffer usage information of all the workers
+ * ----------------------------------------------------------------
+ */
+void ExecAccumulateBufUsageInfo(FunnelState *node)
+{
+	int i;
+	int nworkers;
+	BufferUsage *buffer_usage_worker;
+	char *buffer_usage;
+
+	if (node->pcxt)
+	{
+		nworkers = node->pcxt->nworkers;
+		buffer_usage = node->buffer_usage_space;
+
+		for (i = 0; i < nworkers; i++)
+		{
+			buffer_usage_worker = (BufferUsage *)(buffer_usage + (i * sizeof(BufferUsage)));
+			BufferUsageAdd(&pgBufferUsage, buffer_usage_worker);
+		}
+	}
+}
+
+/* ----------------------------------------------------------------
+ *						Join Support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *		ExecReScanFunnel
+ *
+ *		Rescans a relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecReScanFunnel(FunnelState *node)
+{
+	/*
+	 * Re-initialize the parallel context and workers to perform
+	 * rescan of relation.  We want to gracefully shutdown all the
+	 * workers so that they should be able to propagate any error
+	 * or other information to master backend before dying.
+	 */
+	FinishParallelSetupAndAccumStats(node);
+
+	ExecReScan(node->ss.ps.lefttree);
+}
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index e66bcda..c447062 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -144,6 +144,7 @@ ExecNestLoop(NestLoopState *node)
 			{
 				NestLoopParam *nlp = (NestLoopParam *) lfirst(lc);
 				int			paramno = nlp->paramno;
+				TupleDesc	tdesc = outerTupleSlot->tts_tupleDescriptor;
 				ParamExecData *prm;
 
 				prm = &(econtext->ecxt_param_exec_vals[paramno]);
@@ -154,6 +155,7 @@ ExecNestLoop(NestLoopState *node)
 				prm->value = slot_getattr(outerTupleSlot,
 										  nlp->paramval->varattno,
 										  &(prm->isnull));
+				prm->ptype = tdesc->attrs[nlp->paramval->varattno-1]->atttypid;
 				/* Flag parameter value as changed */
 				innerPlan->chgParam = bms_add_member(innerPlan->chgParam,
 													 paramno);
diff --git a/src/backend/executor/nodePartialSeqscan.c b/src/backend/executor/nodePartialSeqscan.c
new file mode 100644
index 0000000..09b7e07
--- /dev/null
+++ b/src/backend/executor/nodePartialSeqscan.c
@@ -0,0 +1,308 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodePartialSeqscan.c
+ *	  Support routines for parallel sequential scans of relations.
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/executor/nodePartialSeqscan.c
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *		ExecPartialSeqScan				scans a relation.
+ *		PartialSeqNext					retrieve next tuple from either heap.
+ *		ExecInitPartialSeqScan			creates and initializes a partial seqscan node.
+ *		ExecEndPartialSeqScan			releases any storage allocated.
+ */
+#include "postgres.h"
+
+#include "access/relscan.h"
+#include "executor/execdebug.h"
+#include "executor/execParallel.h"
+#include "executor/nodePartialSeqscan.h"
+#include "utils/rel.h"
+
+
+
+/* ----------------------------------------------------------------
+ *						Scan Support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *		PartialSeqNext
+ *
+ *		This is a workhorse for ExecPartialSeqScan
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+PartialSeqNext(PartialSeqScanState *node)
+{
+	HeapTuple	tuple;
+	HeapScanDesc scandesc;
+	EState	   *estate;
+	ScanDirection direction;
+	TupleTableSlot *slot;
+
+	/*
+	 * get information from the estate and scan state
+	 */
+	scandesc = node->ss.ss_currentScanDesc;
+	estate = node->ss.ps.state;
+	direction = estate->es_direction;
+	slot = node->ss.ss_ScanTupleSlot;
+
+	/*
+	 * get the next tuple from the table
+	 */
+	tuple = heap_getnext(scandesc, direction);
+
+	/*
+	 * save the tuple and the buffer returned to us by the access methods in
+	 * our scan tuple slot and return the slot.  Note: we pass 'false' because
+	 * tuples returned by heap_getnext() are pointers onto disk pages and were
+	 * not created with palloc() and so should not be pfree()'d.  Note also
+	 * that ExecStoreTuple will increment the refcount of the buffer; the
+	 * refcount will not be dropped until the tuple table slot is cleared.
+	 */
+	if (tuple)
+		ExecStoreTuple(tuple,	/* tuple to store */
+					   slot,	/* slot to store in */
+					   scandesc->rs_cbuf,		/* buffer associated with this
+												 * tuple */
+					   false);	/* don't pfree this pointer */
+	else
+		ExecClearTuple(slot);
+
+	return slot;
+}
+
+/*
+ * PartialSeqRecheck -- access method routine to recheck a tuple in EvalPlanQual
+ */
+static bool
+PartialSeqRecheck(PartialSeqScanState *node, TupleTableSlot *slot)
+{
+	/*
+	 * Note that unlike IndexScan, PartialSeqScan never use keys in
+	 * heap_beginscan (and this is very bad) - so, here we do not
+	 * check are keys ok or not.
+	 */
+	return true;
+}
+
+/* ----------------------------------------------------------------
+ *		InitPartialScanRelation
+ *
+ *		Set up to access the scan relation.
+ * ----------------------------------------------------------------
+ */
+static void
+InitPartialScanRelation(PartialSeqScanState *node, EState *estate, int eflags)
+{
+	Relation	currentRelation;
+	shm_toc		*toc;
+
+	/*
+	 * get the relation object id from the relid'th entry in the range table,
+	 * open that relation and acquire appropriate lock on it.
+	 */
+	currentRelation = ExecOpenScanRelation(estate,
+										   ((Scan *) node->ss.ps.plan)->scanrelid,
+										   eflags);
+
+	/*
+	 * Parallel scan descriptor is initialized and stored in dynamic shared
+	 * memory segment by master backend and parallel workers retrieve it
+	 * from shared memory.  We set 'toc' (place to lookup parallel scan
+	 * descriptor) as retrievied by attaching to dsm for parallel workers
+	 * whereas master backend stores it directly in partial scan state node
+	 * after initializing workers. 
+	 */
+	toc = GetParallelShmToc();
+	if (toc)
+		node->ss.ps.toc = toc;
+
+	node->ss.ss_currentRelation = currentRelation;
+
+	/* and report the scan tuple slot's rowtype */
+	ExecAssignScanType(&node->ss, RelationGetDescr(currentRelation));
+}
+
+/* ----------------------------------------------------------------
+ *		ExecInitPartialSeqScan
+ * ----------------------------------------------------------------
+ */
+PartialSeqScanState *
+ExecInitPartialSeqScan(PartialSeqScan *node, EState *estate, int eflags)
+{
+	PartialSeqScanState *scanstate;
+
+	/*
+	 * Once upon a time it was possible to have an outerPlan of a SeqScan, but
+	 * not any more.
+	 */
+	Assert(outerPlan(node) == NULL);
+	Assert(innerPlan(node) == NULL);
+
+	/*
+	 * create state structure
+	 */
+	scanstate = makeNode(PartialSeqScanState);
+	scanstate->ss.ps.plan = (Plan *) node;
+	scanstate->ss.ps.state = estate;
+
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * create expression context for node
+	 */
+	ExecAssignExprContext(estate, &scanstate->ss.ps);
+
+	/*
+	 * initialize child expressions
+	 */
+	scanstate->ss.ps.targetlist = (List *)
+		ExecInitExpr((Expr *) node->plan.targetlist,
+					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual = (List *)
+		ExecInitExpr((Expr *) node->plan.qual,
+					 (PlanState *) scanstate);
+
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &scanstate->ss);
+
+	/*
+	 * initialize scan relation
+	 */
+	InitPartialScanRelation(scanstate, estate, eflags);
+
+	scanstate->ss.ps.ps_TupFromTlist = false;
+
+	/*
+	 * Initialize result tuple type and projection info.
+	 */
+	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
+	ExecAssignScanProjectionInfo(&scanstate->ss);
+
+	return scanstate;
+}
+
+/* ----------------------------------------------------------------
+ *		ExecPartialSeqScan(node)
+ *
+ *		Scans the relation and returns the next qualifying tuple.
+ *		We call the ExecScan() routine and pass it the appropriate
+ *		access method functions.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecPartialSeqScan(PartialSeqScanState *node)
+{
+	/*
+	 * Initialize the scan on first execution, normally we initialize
+	 * it during ExecutorStart phase, however we need ParallelHeapScanDesc
+	 * to initialize the scan in case of this node and the same is
+	 * initialized by the Funnel node during ExecutorRun phase.
+	 */
+	if (!node->scan_initialized)
+	{
+		ParallelHeapScanDesc pscan;
+
+		/*
+		 * Parallel scan descriptor is initialized and stored in dynamic shared
+		 * memory segment by master backend, parallel workers and local scan by
+		 * master backend retrieve it from shared memory.  If the scan descriptor
+		 * is available on first execution, then we need to re-initialize for
+		 * rescan.
+		 */
+		Assert(node->ss.ps.toc);
+	
+		pscan = shm_toc_lookup(node->ss.ps.toc, PARALLEL_KEY_SCAN);
+
+		if (!node->ss.ss_currentScanDesc)
+		{
+			node->ss.ss_currentScanDesc =
+				heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+		}
+		else
+		{
+			heap_parallel_rescan(pscan, node->ss.ss_currentScanDesc);
+		}
+
+		node->scan_initialized = true;
+	}
+
+	return ExecScan((ScanState *) node,
+					(ExecScanAccessMtd) PartialSeqNext,
+					(ExecScanRecheckMtd) PartialSeqRecheck);
+}
+
+/* ----------------------------------------------------------------
+ *		ExecEndPartialSeqScan
+ *
+ *		frees any storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndPartialSeqScan(PartialSeqScanState *node)
+{
+	Relation	relation;
+	HeapScanDesc scanDesc;
+
+	/*
+	 * get information from node
+	 */
+	relation = node->ss.ss_currentRelation;
+	scanDesc = node->ss.ss_currentScanDesc;
+
+	/*
+	 * Free the exprcontext
+	 */
+	ExecFreeExprContext(&node->ss.ps);
+
+	/*
+	 * clean out the tuple table
+	 */
+	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
+
+	/*
+	 * close heap scan
+	 */
+	if (scanDesc)
+		heap_endscan(scanDesc);
+
+	/*
+	 * close the heap relation.
+	 */
+	ExecCloseScanRelation(relation);
+}
+
+/* ----------------------------------------------------------------
+ *						Join Support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *		ExecReScanPartialSeqScan
+ *
+ *		Rescans the relation.
+ * ----------------------------------------------------------------
+ */
+void
+ExecReScanPartialSeqScan(PartialSeqScanState *node)
+{
+	if (node->scan_initialized)
+		node->scan_initialized = false;
+
+	ExecScanReScan((ScanState *) node);
+}
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 8d3dde0..b348bfd 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -75,6 +75,13 @@ ExecResult(ResultState *node)
 	econtext = node->ps.ps_ExprContext;
 
 	/*
+	 * Result node can be added as a gating node on top of PartialSeqScan
+	 * node, so need to percolate toc information to outer node.
+	 */
+	if (node->ps.toc)
+		outerPlanState(node)->toc = node->ps.toc;
+
+	/*
 	 * check constant qualifications like (2 > 1), if not already done
 	 */
 	if (node->rs_checkqual)
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 9eb4d63..6afd55a 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -30,11 +30,14 @@
 #include <math.h>
 
 #include "access/htup_details.h"
+#include "catalog/pg_type.h"
 #include "executor/executor.h"
 #include "executor/nodeSubplan.h"
 #include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "utils/array.h"
+#include "utils/datum.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
 
@@ -281,12 +284,14 @@ ExecScanSubPlan(SubPlanState *node,
 	forboth(l, subplan->parParam, pvar, node->args)
 	{
 		int			paramid = lfirst_int(l);
+		ExprState	*exprstate = (ExprState *) lfirst(pvar);
 		ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
 
-		prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar),
+		prm->value = ExecEvalExprSwitchContext(exprstate,
 											   econtext,
 											   &(prm->isnull),
 											   NULL);
+		prm->ptype = exprType((Node *) exprstate->expr);
 		planstate->chgParam = bms_add_member(planstate->chgParam, paramid);
 	}
 
@@ -399,6 +404,7 @@ ExecScanSubPlan(SubPlanState *node,
 			prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
 			Assert(prmdata->execPlan == NULL);
 			prmdata->value = slot_getattr(slot, col, &(prmdata->isnull));
+			prmdata->ptype = tdesc->attrs[col-1]->atttypid;
 			col++;
 		}
 
@@ -551,6 +557,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
 		 !TupIsNull(slot);
 		 slot = ExecProcNode(planstate))
 	{
+		TupleDesc	tdesc = slot->tts_tupleDescriptor;
 		int			col = 1;
 		ListCell   *plst;
 		bool		isnew;
@@ -568,6 +575,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
 			Assert(prmdata->execPlan == NULL);
 			prmdata->value = slot_getattr(slot, col,
 										  &(prmdata->isnull));
+			prmdata->ptype = tdesc->attrs[col-1]->atttypid;
 			col++;
 		}
 		slot = ExecProject(node->projRight, NULL);
@@ -954,6 +962,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 	ListCell   *l;
 	bool		found = false;
 	ArrayBuildStateAny *astate = NULL;
+	Oid			ptype;
 
 	if (subLinkType == ANY_SUBLINK ||
 		subLinkType == ALL_SUBLINK)
@@ -961,6 +970,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 	if (subLinkType == CTE_SUBLINK)
 		elog(ERROR, "CTE subplans should not be executed via ExecSetParamPlan");
 
+	ptype = exprType((Node *) node->xprstate.expr);
+
 	/* Initialize ArrayBuildStateAny in caller's context, if needed */
 	if (subLinkType == ARRAY_SUBLINK)
 		astate = initArrayResultAny(subplan->firstColType,
@@ -983,12 +994,14 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 	forboth(l, subplan->parParam, pvar, node->args)
 	{
 		int			paramid = lfirst_int(l);
+		ExprState	*exprstate = (ExprState *) lfirst(pvar);
 		ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
 
-		prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar),
+		prm->value = ExecEvalExprSwitchContext(exprstate,
 											   econtext,
 											   &(prm->isnull),
 											   NULL);
+		prm->ptype = exprType((Node *) exprstate->expr);
 		planstate->chgParam = bms_add_member(planstate->chgParam, paramid);
 	}
 
@@ -1011,6 +1024,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 
 			prm->execPlan = NULL;
 			prm->value = BoolGetDatum(true);
+			prm->ptype = ptype;
 			prm->isnull = false;
 			found = true;
 			break;
@@ -1062,6 +1076,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 			prm->execPlan = NULL;
 			prm->value = heap_getattr(node->curTuple, i, tdesc,
 									  &(prm->isnull));
+			prm->ptype = tdesc->attrs[i-1]->atttypid;
 			i++;
 		}
 	}
@@ -1084,6 +1099,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 											true);
 		prm->execPlan = NULL;
 		prm->value = node->curArray;
+		prm->ptype = ptype;
 		prm->isnull = false;
 	}
 	else if (!found)
@@ -1096,6 +1112,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 
 			prm->execPlan = NULL;
 			prm->value = BoolGetDatum(false);
+			prm->ptype = ptype;
 			prm->isnull = false;
 		}
 		else
@@ -1108,6 +1125,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 
 				prm->execPlan = NULL;
 				prm->value = (Datum) 0;
+				prm->ptype = VOIDOID;
 				prm->isnull = true;
 			}
 		}
@@ -1238,3 +1256,47 @@ ExecAlternativeSubPlan(AlternativeSubPlanState *node,
 					   isNull,
 					   isDone);
 }
+
+/*
+ * ExecAndFormSerializeParamExec
+ *
+ * Execute the subplan stored in PARAM_EXEC param if it is not executed
+ * till now and form the serialized structure required for passing to
+ * worker backend.
+ */
+List *
+ExecAndFormSerializeParamExec(ExprContext *econtext, Bitmapset *params)
+{
+	List	*lparam = NIL;
+	SerializedParamExecData *sparamdata;
+	ParamExecData *prm;
+	int		paramid;
+
+	paramid = -1;
+	while ((paramid = bms_next_member(params, paramid)) >= 0)
+	{
+		/*
+		 * PARAM_EXEC params (internal executor parameters) are stored in the
+		 * ecxt_param_exec_vals array, and can be accessed by array index.
+		 */
+		sparamdata = palloc0(sizeof(SerializedParamExecData));
+
+		prm = &(econtext->ecxt_param_exec_vals[paramid]);
+		if (prm->execPlan != NULL)
+		{
+			/* Parameter not evaluated yet, so go do it */
+			ExecSetParamPlan(prm->execPlan, econtext);
+			/* ExecSetParamPlan should have processed this param... */
+			Assert(prm->execPlan == NULL);
+		}
+
+		sparamdata->paramid	= paramid;
+		sparamdata->ptype = prm->ptype;
+		sparamdata->value = prm->value;
+		sparamdata->isnull = prm->isnull;
+
+		lparam = lappend(lparam, sparamdata);
+	}
+
+	return lparam;
+}
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index d544ad9..d8ca074 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1774,7 +1774,7 @@ spi_dest_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
  *		store tuple retrieved by Executor into SPITupleTable
  *		of current SPI procedure
  */
-void
+bool
 spi_printtup(TupleTableSlot *slot, DestReceiver *self)
 {
 	SPITupleTable *tuptable;
@@ -1808,6 +1808,8 @@ spi_printtup(TupleTableSlot *slot, DestReceiver *self)
 	(tuptable->free)--;
 
 	MemoryContextSwitchTo(oldcxt);
+
+	return true;
 }
 
 /*
diff --git a/src/backend/executor/tqueue.c b/src/backend/executor/tqueue.c
new file mode 100644
index 0000000..39acda7
--- /dev/null
+++ b/src/backend/executor/tqueue.c
@@ -0,0 +1,304 @@
+/*-------------------------------------------------------------------------
+ *
+ * tqueue.c
+ *	  Use shm_mq to send & receive tuples between parallel backends
+ *
+ * A DestReceiver of type DestTupleQueue, which is a TQueueDestReciever
+ * under the hood, writes tuples from the executor to a shm_mq.
+ *
+ * A TupleQueueFunnel helps manage the process of reading tuples from
+ * one or more shm_mq objects being used as tuple queues.
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  src/backend/executor/tqueue.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "executor/tqueue.h"
+#include "miscadmin.h"
+
+typedef struct
+{
+	DestReceiver pub;
+	shm_mq_handle *handle;
+} TQueueDestReceiver;
+
+struct TupleQueueFunnel
+{
+	int		nqueues;
+	int		maxqueues;
+	int		nextqueue;
+	shm_mq_handle **queue;
+};
+
+/*
+ * Receive a tuple.
+ */
+static bool
+tqueueReceiveSlot(TupleTableSlot *slot, DestReceiver *self)
+{
+	TQueueDestReceiver *tqueue = (TQueueDestReceiver *) self;
+	HeapTuple	tuple;
+	shm_mq_result	result;
+
+	tuple = ExecMaterializeSlot(slot);
+	result = shm_mq_send(tqueue->handle, tuple->t_len, tuple->t_data, false);
+
+	if (result == SHM_MQ_DETACHED)
+		return false;
+	else if (result != SHM_MQ_SUCCESS)
+		ereport(ERROR,
+				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
+				 errmsg("unable to send tuples")));
+
+	return true;
+}
+
+/*
+ * Prepare to receive tuples from executor.
+ */
+static void
+tqueueStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
+{
+	/* do nothing */
+}
+
+/*
+ * Clean up at end of an executor run
+ */
+static void
+tqueueShutdownReceiver(DestReceiver *self)
+{
+	/* do nothing */
+}
+
+/*
+ * Destroy receiver when done with it
+ */
+static void
+tqueueDestroyReceiver(DestReceiver *self)
+{
+	pfree(self);
+}
+
+/*
+ * Create a DestReceiver that writes tuples to a tuple queue.
+ */
+DestReceiver *
+CreateTupleQueueDestReceiver(void)
+{
+	TQueueDestReceiver *self;
+
+	self = (TQueueDestReceiver *) palloc0(sizeof(TQueueDestReceiver));
+
+	self->pub.receiveSlot = tqueueReceiveSlot;
+	self->pub.rStartup = tqueueStartupReceiver;
+	self->pub.rShutdown = tqueueShutdownReceiver;
+	self->pub.rDestroy = tqueueDestroyReceiver;
+	self->pub.mydest = DestTupleQueue;
+
+	/* private fields will be set by SetTupleQueueDestReceiverParams */
+
+	return (DestReceiver *) self;
+}
+
+/*
+ * Set parameters for a TupleQueueDestReceiver
+ */
+void
+SetTupleQueueDestReceiverParams(DestReceiver *self,
+								shm_mq_handle *handle)
+{
+	TQueueDestReceiver *myState = (TQueueDestReceiver *) self;
+
+	myState->handle = handle;
+}
+
+/*
+ * Create a tuple queue funnel.
+ */
+TupleQueueFunnel *
+CreateTupleQueueFunnel(void)
+{
+	TupleQueueFunnel *funnel = palloc0(sizeof(TupleQueueFunnel));
+
+	funnel->maxqueues = 8;
+	funnel->queue = palloc(funnel->maxqueues * sizeof(shm_mq_handle *));
+
+	return funnel;
+}
+
+/*
+ * Detach all tuple queues that belong to funnel.
+ */
+void
+TupleQueueFunnelShutdown(TupleQueueFunnel *funnel)
+{
+	if (funnel)
+	{
+		int		i;
+		shm_mq_handle *mqh;
+		shm_mq	   *mq;
+		for (i = 0; i < funnel->nqueues; i++)
+		{
+			mqh = funnel->queue[i];
+			mq = shm_mq_get_queue(mqh);
+			shm_mq_detach(mq);
+		}
+	}
+}
+
+/*
+ * Destroy a tuple queue funnel.
+ */
+void
+DestroyTupleQueueFunnel(TupleQueueFunnel *funnel)
+{
+	if (funnel)
+	{
+		pfree(funnel->queue);
+		pfree(funnel);
+	}
+}
+
+/*
+ * Remember the shared memory queue handle in funnel.
+ */
+void
+RegisterTupleQueueOnFunnel(TupleQueueFunnel *funnel, shm_mq_handle *handle)
+{
+	if (funnel->nqueues < funnel->maxqueues)
+	{
+		funnel->queue[funnel->nqueues++] = handle;
+		return;
+	}
+
+	if (funnel->nqueues >= funnel->maxqueues)
+	{
+		int newsize = funnel->nqueues * 2;
+
+		Assert(funnel->nqueues == funnel->maxqueues);
+
+		funnel->queue = repalloc(funnel->queue,
+								 newsize * sizeof(shm_mq_handle *));
+		funnel->maxqueues = newsize;
+	}
+
+	funnel->queue[funnel->nqueues++] = handle;
+}
+
+/*
+ * Fetch a tuple from a tuple queue funnel.
+ *
+ * We try to read from the queues in round-robin fashion so as to avoid
+ * the situation where some workers get their tuples read expediently while
+ * others are barely ever serviced.
+ *
+ * Even when nowait = false, we read from the individual queues in
+ * non-blocking mode.  Even when shm_mq_receive() returns SHM_MQ_WOULD_BLOCK,
+ * it can still accumulate bytes from a partially-read message, so doing it
+ * this way should outperform doing a blocking read on each queue in turn.
+ *
+ * The return value is NULL if there are no remaining queues or if
+ * nowait = true and no queue returned a tuple without blocking.  *done, if
+ * not NULL, is set to true when there are no remaining queues and false in
+ * any other case.
+ */
+HeapTuple
+TupleQueueFunnelNext(TupleQueueFunnel *funnel, bool nowait, bool *done)
+{
+	int	waitpos = funnel->nextqueue;
+
+	/* Corner case: called before adding any queues, or after all are gone. */
+	if (funnel->nqueues == 0)
+	{
+		if (done != NULL)
+			*done = true;
+		return NULL;
+	}
+
+	if (done != NULL)
+		*done = false;
+
+	for (;;)
+	{
+		shm_mq_handle *mqh = funnel->queue[funnel->nextqueue];
+		shm_mq_result result;
+		Size	nbytes;
+		void   *data;
+
+		/* Attempt to read a message. */
+		result = shm_mq_receive(mqh, &nbytes, &data, true);
+
+		/*
+		 * Normally, we advance funnel->nextqueue to the next queue at this
+		 * point, but if we're pointing to a queue that we've just discovered
+		 * is detached, then forget that queue and leave the pointer where it
+		 * is until the number of remaining queues fall below that pointer and
+		 * at that point make the pointer point to the first queue.
+		 */
+		if (result != SHM_MQ_DETACHED)
+			funnel->nextqueue = (funnel->nextqueue + 1) % funnel->nqueues;
+		else
+		{
+			--funnel->nqueues;
+			if (funnel->nqueues == 0)
+			{
+				if (done != NULL)
+					*done = true;
+				return NULL;
+			}
+
+			memmove(&funnel->queue[funnel->nextqueue],
+					&funnel->queue[funnel->nextqueue + 1],
+					sizeof(shm_mq_handle *)
+						* (funnel->nqueues - funnel->nextqueue));
+
+			if (funnel->nextqueue >= funnel->nqueues)
+				funnel->nextqueue = 0;
+
+			if (funnel->nextqueue < waitpos)
+				--waitpos;
+
+			continue;
+		}
+
+		/* If we got a message, return it. */
+		if (result == SHM_MQ_SUCCESS)
+		{
+			HeapTupleData htup;
+
+			/*
+			 * The tuple data we just read from the queue is only valid
+			 * until we again attempt to read from it.  Copy the tuple into
+			 * a single palloc'd chunk as callers will expect.
+			 */
+			ItemPointerSetInvalid(&htup.t_self);
+			htup.t_tableOid = InvalidOid;
+			htup.t_len = nbytes;
+			htup.t_data = data;
+			return heap_copytuple(&htup);
+		}
+
+		/*
+		 * If we've visited all of the queues, then we should either give up
+		 * and return NULL (if we're in non-blocking mode) or wait for the
+		 * process latch to be set (otherwise).
+		 */
+		if (funnel->nextqueue == waitpos)
+		{
+			if (nowait)
+				return NULL;
+			WaitLatch(MyLatch, WL_LATCH_SET, 0);
+			CHECK_FOR_INTERRUPTS();
+			ResetLatch(MyLatch);
+		}
+	}
+}
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index c1fdeb7..b0862ae 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -37,8 +37,8 @@ typedef struct
 } TStoreState;
 
 
-static void tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self);
-static void tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self);
+static bool tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self);
+static bool tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self);
 
 
 /*
@@ -90,19 +90,21 @@ tstoreStartupReceiver(DestReceiver *self, int operation, TupleDesc typeinfo)
  * Receive a tuple from the executor and store it in the tuplestore.
  * This is for the easy case where we don't have to detoast.
  */
-static void
+static bool
 tstoreReceiveSlot_notoast(TupleTableSlot *slot, DestReceiver *self)
 {
 	TStoreState *myState = (TStoreState *) self;
 
 	tuplestore_puttupleslot(myState->tstore, slot);
+
+	return true;
 }
 
 /*
  * Receive a tuple from the executor and store it in the tuplestore.
  * This is for the case where we have to detoast any toasted values.
  */
-static void
+static bool
 tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
 {
 	TStoreState *myState = (TStoreState *) self;
@@ -152,6 +154,8 @@ tstoreReceiveSlot_detoast(TupleTableSlot *slot, DestReceiver *self)
 	/* And release any temporary detoasted values */
 	for (i = 0; i < nfree; i++)
 		pfree(DatumGetPointer(myState->tofree[i]));
+
+	return true;
 }
 
 /*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5bfb7c0..578b39a 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -361,6 +361,43 @@ _copySeqScan(const SeqScan *from)
 }
 
 /*
+ * _copyPartialSeqScan
+ */
+static PartialSeqScan *
+_copyPartialSeqScan(const SeqScan *from)
+{
+	PartialSeqScan    *newnode = makeNode(PartialSeqScan);
+
+	/*
+	 * copy node superclass fields
+	 */
+	CopyScanFields((const Scan *) from, (Scan *) newnode);
+
+	return newnode;
+}
+
+/*
+ * _copyFunnel
+ */
+static Funnel *
+_copyFunnel(const Funnel *from)
+{
+	Funnel    *newnode = makeNode(Funnel);
+
+	/*
+	 * copy node superclass fields
+	 */
+	CopyScanFields((const Scan *) from, (Scan *) newnode);
+
+	/*
+	 * copy remainder of node
+	 */
+	COPY_SCALAR_FIELD(num_workers);
+
+	return newnode;
+}
+
+/*
  * _copyIndexScan
  */
 static IndexScan *
@@ -4226,6 +4263,12 @@ copyObject(const void *from)
 		case T_SeqScan:
 			retval = _copySeqScan(from);
 			break;
+		case T_PartialSeqScan:
+			retval = _copyPartialSeqScan(from);
+			break;
+		case T_Funnel:
+			retval = _copyFunnel(from);
+			break;
 		case T_IndexScan:
 			retval = _copyIndexScan(from);
 			break;
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index b1e3e6e..e610d9d 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -3399,3 +3399,25 @@ raw_expression_tree_walker(Node *node,
 	}
 	return false;
 }
+
+/*
+ * planstate_tree_walker
+ *
+ * This routine will invoke walker on the node passed.  This is a useful
+ * way of starting the recursion when the walker's normal change of state
+ * is not appropriate for the outermost PlanState node.
+ */
+bool
+planstate_tree_walker(Node *node,
+					  ParallelContext *pcxt,
+					  bool (*walker) (),
+					  void *context)
+{
+	if (node == NULL)
+		return false;
+
+	/* Guard against stack overflow due to overly complex plan */
+	check_stack_depth();
+
+	return walker(node, pcxt, context);
+}
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 482354c..f8dee8e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -446,6 +446,24 @@ _outSeqScan(StringInfo str, const SeqScan *node)
 }
 
 static void
+_outPartialSeqScan(StringInfo str, const SeqScan *node)
+{
+	WRITE_NODE_TYPE("PARTIALSEQSCAN");
+
+	_outScanInfo(str, (const Scan *) node);
+}
+
+static void
+_outFunnel(StringInfo str, const Funnel *node)
+{
+	WRITE_NODE_TYPE("FUNNEL");
+
+	_outScanInfo(str, (const Scan *) node);
+
+	WRITE_UINT_FIELD(num_workers);
+}
+
+static void
 _outIndexScan(StringInfo str, const IndexScan *node)
 {
 	WRITE_NODE_TYPE("INDEXSCAN");
@@ -3005,6 +3023,12 @@ _outNode(StringInfo str, const void *obj)
 			case T_SeqScan:
 				_outSeqScan(str, obj);
 				break;
+			case T_PartialSeqScan:
+				_outPartialSeqScan(str, obj);
+				break;
+			case T_Funnel:
+				_outFunnel(str, obj);
+				break;
 			case T_IndexScan:
 				_outIndexScan(str, obj);
 				break;
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c
index fb803f8..0050195 100644
--- a/src/backend/nodes/params.c
+++ b/src/backend/nodes/params.c
@@ -16,9 +16,22 @@
 #include "postgres.h"
 
 #include "nodes/params.h"
+#include "storage/shmem.h"
 #include "utils/datum.h"
 #include "utils/lsyscache.h"
 
+/*
+ * for each bind parameter, pass this structure followed by value
+ * except for pass-by-value parameters.
+ */
+typedef struct SerializedParamExternData
+{
+	Datum		value;			/*pass-by-val are directly stored */
+	Size		length;			/* length of parameter value */
+	bool		isnull;			/* is it NULL? */
+	uint16		pflags;			/* flag bits, same as in original Param */
+	Oid			ptype;			/* parameter's datatype, or 0 */
+} SerializedParamExternData;
 
 /*
  * Copy a ParamListInfo structure.
@@ -73,3 +86,355 @@ copyParamList(ParamListInfo from)
 
 	return retval;
 }
+
+/*
+ * Estimate the amount of space required to serialize the bound
+ * parameters.
+ */
+Size
+EstimateBoundParametersSpace(ParamListInfo paramInfo)
+{
+	Size		size;
+	int			i;
+
+	/* Add space required for saving numParams */
+	size = sizeof(int);
+
+	if (paramInfo)
+	{
+		/* Add space required for saving the param data */
+		for (i = 0; i < paramInfo->numParams; i++)
+		{
+			/*
+			 * for each parameter, calculate the size of fixed part
+			 * of parameter (SerializedParamExternData) and length of
+			 * parameter value.
+			 */
+			ParamExternData *oprm;
+			int16		typLen;
+			bool		typByVal;
+			Size		length;
+
+			length = sizeof(SerializedParamExternData);
+
+			oprm = &paramInfo->params[i];
+
+			get_typlenbyval(oprm->ptype, &typLen, &typByVal);
+
+			/*
+			 * pass-by-value parameters are directly stored in
+			 * SerializedParamExternData, so no need of additional
+			 * space for them.
+			 */
+			if (!(typByVal || oprm->isnull))
+			{
+				length += datumGetSize(oprm->value, typByVal, typLen);
+				size = add_size(size, length);
+
+				/* Allow space for terminating zero-byte */
+				size = add_size(size, 1);
+			}
+			else
+				size = add_size(size, length);
+		}
+	}
+
+	return size;
+}
+
+/*
+ * Serialize the bind parameters into the memory, beginning at start_address.
+ * maxsize should be at least as large as the value returned by
+ * EstimateBoundParametersSpace.
+ */
+void
+SerializeBoundParams(ParamListInfo paramInfo, Size maxsize, char *start_address)
+{
+	char	   *curptr;
+	SerializedParamExternData *retval;
+	int i;
+
+	/*
+	 * First, we store the number of bind parameters, if there is
+	 * no bind parameter then no need to store any more information.
+	 */
+	if (paramInfo && paramInfo->numParams > 0)
+		* (int *) start_address = paramInfo->numParams;
+	else
+	{
+		* (int *) start_address = 0;
+		return;
+	}
+	curptr = start_address + sizeof(int);
+
+
+	for (i = 0; i < paramInfo->numParams; i++)
+	{
+		ParamExternData *oprm;
+		int16		typLen;
+		bool		typByVal;
+		Size		datumlength, length;
+		const char	*s;
+
+		Assert (curptr <= start_address + maxsize);
+		retval = (SerializedParamExternData*) curptr;
+		oprm = &paramInfo->params[i];
+
+		retval->isnull = oprm->isnull;
+		retval->pflags = oprm->pflags;
+		retval->ptype = oprm->ptype;
+		retval->value = oprm->value;
+
+		curptr = curptr + sizeof(SerializedParamExternData);
+
+		if (retval->isnull)
+			continue;
+
+		get_typlenbyval(oprm->ptype, &typLen, &typByVal);
+
+		if (!typByVal)
+		{
+			datumlength = datumGetSize(oprm->value, typByVal, typLen);
+			s = (char *) DatumGetPointer(oprm->value);
+			memcpy(curptr, s, datumlength);
+			length = datumlength;
+			curptr[length] = '\0';
+			retval->length = length;
+			curptr += length + 1;
+		}
+	}
+}
+
+/*
+ * RestoreBoundParams
+ *		Restore bind parameters from the specified address.
+ *
+ * The params are palloc'd in CurrentMemoryContext.
+ */
+ParamListInfo
+RestoreBoundParams(char *start_address)
+{
+	ParamListInfo retval;
+	Size		size;
+	int			num_params,i;
+	char	   *curptr;
+
+	num_params = * (int *) start_address;
+
+	if (num_params <= 0)
+		return NULL;
+
+	size = offsetof(ParamListInfoData, params) +
+						num_params * sizeof(ParamExternData);
+	retval = (ParamListInfo) palloc(size);
+	retval->paramFetch = NULL;
+	retval->paramFetchArg = NULL;
+	retval->parserSetup = NULL;
+	retval->parserSetupArg = NULL;
+	retval->numParams = num_params;
+
+	curptr = start_address + sizeof(int);
+
+	for (i = 0; i < num_params; i++)
+	{
+		SerializedParamExternData *nprm;
+		char	*s;
+		int16		typLen;
+		bool		typByVal;
+
+		nprm = (SerializedParamExternData *) curptr;
+
+		/* copy the parameter info */
+		retval->params[i].isnull = nprm->isnull;
+		retval->params[i].pflags = nprm->pflags;
+		retval->params[i].ptype = nprm->ptype;
+		retval->params[i].value = nprm->value;
+
+		curptr = curptr + sizeof(SerializedParamExternData);
+
+		if (nprm->isnull)
+			continue;
+
+		get_typlenbyval(nprm->ptype, &typLen, &typByVal);
+
+		if (!typByVal)
+		{
+			s = palloc(nprm->length + 1);
+			memcpy(s, curptr, nprm->length + 1);
+			retval->params[i].value = CStringGetDatum(s);
+
+			curptr += nprm->length + 1;
+		}
+	}
+
+	return retval;
+}
+
+/*
+ * Estimate the amount of space required to serialize the PARAM_EXEC
+ * parameters.
+ */
+Size
+EstimateExecParametersSpace(List *serialized_param_exec_vals)
+{
+	Size		size;
+	ListCell	*lparam;
+
+	/*
+	 * Add space required for saving number of PARAM_EXEC parameters
+	 * that needs to be serialized.
+	 */
+	size = sizeof(int);
+
+	foreach(lparam, serialized_param_exec_vals)
+	{
+		int16		typLen;
+		bool		typByVal;
+		Size		length;
+		SerializedParamExecData* param_val = (SerializedParamExecData*) lfirst(lparam);
+
+		length = sizeof(SerializedParamExecData);
+
+		get_typlenbyval(param_val->ptype, &typLen, &typByVal);
+
+		/*
+		 * pass-by-value parameters are directly stored in
+		 * SerializedParamExternData, so no need of additional
+		 * space for them.
+		 */
+		if (!(typByVal || param_val->isnull))
+		{
+			length += datumGetSize(param_val->value, typByVal, typLen);
+			size = add_size(size, length);
+
+			/* Allow space for terminating zero-byte */
+			size = add_size(size, 1);
+		}
+		else
+			size = add_size(size, length);
+	}
+
+	return size;
+}
+
+/*
+ * Serialize the PARAM_EXEC parameters into the memory, beginning at
+ * start_address.  maxsize should be at least as large as the value
+ * returned by EstimateExecParametersSpace.
+ */
+void
+SerializeExecParams(List *serialized_param_exec_vals, Size maxsize,
+					char *start_address)
+{
+	char	   *curptr;
+	SerializedParamExecData *retval;
+	ListCell	*lparam;
+
+	/*
+	 * First, we store the number of PARAM_EXEC parameters that needs to
+	 * be serialized.
+	 */
+	if (serialized_param_exec_vals)
+		* (int *) start_address = list_length(serialized_param_exec_vals);
+	else
+	{
+		* (int *) start_address = 0;
+		return;
+	}
+
+	curptr = start_address + sizeof(int);
+
+	foreach(lparam, serialized_param_exec_vals)
+	{
+		int16		typLen;
+		bool		typByVal;
+		Size		datumlength, length;
+		const char	*s;
+		SerializedParamExecData* param_val = (SerializedParamExecData*) lfirst(lparam);
+
+		retval = (SerializedParamExecData*) curptr;
+
+		retval->paramid	= param_val->paramid;
+		retval->value = param_val->value;
+		retval->isnull = param_val->isnull;
+		retval->ptype = param_val->ptype;
+
+		curptr = curptr + sizeof(SerializedParamExecData);
+
+		if (retval->isnull)
+			continue;
+
+		get_typlenbyval(retval->ptype, &typLen, &typByVal);
+
+		if (!typByVal)
+		{
+			datumlength = datumGetSize(retval->value, typByVal, typLen);
+			s = (char *) DatumGetPointer(retval->value);
+			memcpy(curptr, s, datumlength);
+			length = datumlength;
+			curptr[length] = '\0';
+			retval->length = length;
+			curptr += length + 1;
+		}
+	}
+}
+
+/*
+ * RestoreExecParams
+ *		Restore PARAM_EXEC parameters from the specified address.
+ *
+ * The params are palloc'd in CurrentMemoryContext.
+ */
+List *
+RestoreExecParams(char *start_address)
+{
+	List			*lparamexecvals = NIL;
+	//Size			size;
+	int				num_params,i;
+	char			*curptr;
+
+	num_params = * (int *) start_address;
+
+	if (num_params <= 0)
+		return NULL;
+
+	curptr = start_address + sizeof(int);
+
+	for (i = 0; i < num_params; i++)
+	{
+		SerializedParamExecData *nprm;
+		SerializedParamExecData	*outparam;
+		char	*s;
+		int16		typLen;
+		bool		typByVal;
+
+		nprm = (SerializedParamExecData *) curptr;
+
+		outparam = palloc0(sizeof(SerializedParamExecData));
+
+		/* copy the parameter info */
+		outparam->isnull = nprm->isnull;
+		outparam->value = nprm->value;
+		outparam->paramid = nprm->paramid;
+
+		curptr = curptr + sizeof(SerializedParamExecData);
+
+		if (nprm->isnull)
+			continue;
+
+		get_typlenbyval(nprm->ptype, &typLen, &typByVal);
+
+		if (!typByVal)
+		{
+			s = palloc(nprm->length + 1);
+			memcpy(s, curptr, nprm->length + 1);
+			outparam->value = CStringGetDatum(s);
+
+			curptr += nprm->length + 1;
+		}
+
+		lparamexecvals = lappend(lparamexecvals, outparam);
+	}
+
+	return lparamexecvals;
+}
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index f5a40fb..e3357cc 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -29,6 +29,7 @@
 #include <math.h>
 
 #include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
 #include "nodes/readfuncs.h"
 
 
@@ -1391,6 +1392,124 @@ _readRangeTblFunction(void)
 	READ_DONE();
 }
 
+/*
+ * _readPlanInvalItem
+ */
+static PlanInvalItem *
+_readPlanInvalItem(void)
+{
+	READ_LOCALS(PlanInvalItem);
+
+	READ_INT_FIELD(cacheId);
+	READ_UINT_FIELD(hashValue);
+
+	READ_DONE();
+}
+
+/*
+ * _readPlannedStmt
+ */
+static PlannedStmt *
+_readPlannedStmt(void)
+{
+	READ_LOCALS(PlannedStmt);
+
+	READ_ENUM_FIELD(commandType, CmdType);
+	READ_UINT_FIELD(queryId);
+	READ_BOOL_FIELD(hasReturning);
+	READ_BOOL_FIELD(hasModifyingCTE);
+	READ_BOOL_FIELD(canSetTag);
+	READ_BOOL_FIELD(transientPlan);
+	READ_NODE_FIELD(planTree);
+	READ_NODE_FIELD(rtable);
+	READ_NODE_FIELD(resultRelations);
+	READ_NODE_FIELD(utilityStmt);
+	READ_NODE_FIELD(subplans);
+	READ_BITMAPSET_FIELD(rewindPlanIDs);
+	READ_NODE_FIELD(rowMarks);
+	READ_NODE_FIELD(relationOids);
+	READ_NODE_FIELD(invalItems);
+	READ_INT_FIELD(nParamExec);
+	READ_BOOL_FIELD(hasRowSecurity);
+	READ_BOOL_FIELD(parallelModeNeeded);
+
+	READ_DONE();
+}
+
+/*
+ * _readPlan
+ */
+static Plan *
+_readPlan(void)
+{
+	READ_LOCALS(Plan);
+
+	READ_FLOAT_FIELD(startup_cost);
+	READ_FLOAT_FIELD(total_cost);
+	READ_FLOAT_FIELD(plan_rows);
+	READ_INT_FIELD(plan_width);
+	READ_NODE_FIELD(targetlist);
+	READ_NODE_FIELD(qual);
+	READ_NODE_FIELD(lefttree);
+	READ_NODE_FIELD(righttree);
+	READ_NODE_FIELD(initPlan);
+	READ_BITMAPSET_FIELD(extParam);
+	READ_BITMAPSET_FIELD(allParam);
+
+	READ_DONE();
+}
+
+/*
+ * _readScan
+ */
+static Scan *
+_readScan(void)
+{
+	Plan *local_plan;
+	READ_LOCALS(PartialSeqScan);
+
+	local_plan = _readPlan();
+	local_node->plan.startup_cost = local_plan->startup_cost;
+	local_node->plan.total_cost = local_plan->total_cost;
+	local_node->plan.plan_rows = local_plan->plan_rows;
+	local_node->plan.plan_width = local_plan->plan_width;
+	local_node->plan.targetlist = local_plan->targetlist;
+	local_node->plan.qual = local_plan->qual;
+	local_node->plan.lefttree = local_plan->lefttree;
+	local_node->plan.righttree = local_plan->righttree;
+	local_node->plan.initPlan = local_plan->initPlan;
+	local_node->plan.extParam = local_plan->extParam;
+	local_node->plan.allParam = local_plan->allParam;
+	READ_UINT_FIELD(scanrelid);
+
+	READ_DONE();
+}
+
+/*
+ * _readResult
+ */
+static Result *
+_readResult(void)
+{
+	Plan *local_plan;
+	READ_LOCALS(Result);
+
+	local_plan = _readPlan();
+	local_node->plan.startup_cost = local_plan->startup_cost;
+	local_node->plan.total_cost = local_plan->total_cost;
+	local_node->plan.plan_rows = local_plan->plan_rows;
+	local_node->plan.plan_width = local_plan->plan_width;
+	local_node->plan.targetlist = local_plan->targetlist;
+	local_node->plan.qual = local_plan->qual;
+	local_node->plan.lefttree = local_plan->lefttree;
+	local_node->plan.righttree = local_plan->righttree;
+	local_node->plan.initPlan = local_plan->initPlan;
+	local_node->plan.extParam = local_plan->extParam;
+	local_node->plan.allParam = local_plan->allParam;
+	READ_NODE_FIELD(resconstantqual);
+
+	READ_DONE();
+}
 
 /*
  * parseNodeString
@@ -1532,6 +1651,14 @@ parseNodeString(void)
 		return_value = _readNotifyStmt();
 	else if (MATCH("DECLARECURSOR", 13))
 		return_value = _readDeclareCursorStmt();
+	else if (MATCH("PLANINVALITEM", 13))
+		return_value = _readPlanInvalItem();
+	else if (MATCH("PLANNEDSTMT", 11))
+		return_value = _readPlannedStmt();
+	else if (MATCH("PARTIALSEQSCAN", 14))
+		return_value = _readScan();
+	else if (MATCH("RESULT", 6))
+		return_value = _readResult();
 	else
 	{
 		elog(ERROR, "badly formatted node string \"%.32s\"...", token);
diff --git a/src/backend/optimizer/path/Makefile b/src/backend/optimizer/path/Makefile
index 6864a62..6e462b1 100644
--- a/src/backend/optimizer/path/Makefile
+++ b/src/backend/optimizer/path/Makefile
@@ -13,6 +13,6 @@ top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = allpaths.o clausesel.o costsize.o equivclass.o indxpath.o \
-       joinpath.o joinrels.o pathkeys.o tidpath.o
+       joinpath.o joinrels.o pathkeys.o parallelpath.o tidpath.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 0b83189..fa6bbdf 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -471,6 +471,9 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	/* Consider sequential scan */
 	add_path(rel, create_seqscan_path(root, rel, required_outer));
 
+	/* Consider parallel scans */
+	create_parallelscan_paths(root, rel);
+
 	/* Consider index scans */
 	create_index_paths(root, rel);
 
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 0d302f6..8b6b46c 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -11,6 +11,8 @@
  *	cpu_tuple_cost		Cost of typical CPU time to process a tuple
  *	cpu_index_tuple_cost  Cost of typical CPU time to process an index tuple
  *	cpu_operator_cost	Cost of CPU time to execute an operator or function
+ *  cpu_tuple_comm_cost	Cost of CPU time to pass a tuple from worker to master backend
+ *  parallel_setup_cost Cost of setting up shared memory for parallelism
  *
  * We expect that the kernel will typically do some amount of read-ahead
  * optimization; this in conjunction with seek costs means that seq_page_cost
@@ -101,11 +103,15 @@ double		random_page_cost = DEFAULT_RANDOM_PAGE_COST;
 double		cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST;
 double		cpu_index_tuple_cost = DEFAULT_CPU_INDEX_TUPLE_COST;
 double		cpu_operator_cost = DEFAULT_CPU_OPERATOR_COST;
+double		cpu_tuple_comm_cost = DEFAULT_CPU_TUPLE_COMM_COST;
+double		parallel_setup_cost = DEFAULT_PARALLEL_SETUP_COST;
 
 int			effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;
 
 Cost		disable_cost = 1.0e10;
 
+int	parallel_seqscan_degree = 0;
+
 bool		enable_seqscan = true;
 bool		enable_indexscan = true;
 bool		enable_indexonlyscan = true;
@@ -287,6 +293,86 @@ cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel)
 }
 
 /*
+ * cost_patialseqscan
+ *	  Determines and returns the cost of scanning a relation partially.
+ *
+ * 'baserel' is the relation to be scanned
+ * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
+ * 'nworkers' are the number of workers among which the work will be
+ *			distributed
+ */
+void
+cost_patialseqscan(Path *path, PlannerInfo *root,
+				   RelOptInfo *baserel, ParamPathInfo *param_info,
+				   int nworkers)
+{
+	Cost		startup_cost = 0;
+	Cost		run_cost = 0;
+
+	cost_seqscan(path, root, baserel, param_info);
+
+	startup_cost = path->startup_cost;
+
+	run_cost = path->total_cost - startup_cost;
+
+	/*
+	 * Account for small cost for communication related to scan
+	 * via the ParallelHeapScanDesc.
+	 */
+	run_cost += 0.01;
+
+	/*
+	 * Runtime cost will be equally shared by all workers.
+	 * Here assumption is that disk access cost will also be
+	 * equally shared between workers which is generally true
+	 * unless there are too many workers working on a relatively
+	 * lesser number of blocks.  If we come across any such case,
+	 * then we can think of changing the current cost model for
+	 * partial sequiantial scan.
+	 */
+	run_cost = run_cost / (nworkers + 1);
+
+	path->startup_cost = startup_cost;
+	path->total_cost = startup_cost + run_cost;
+}
+
+/*
+ * cost_funnel
+ *	  Determines and returns the cost of funnel path.
+ *
+ * 'baserel' is the relation to be scanned
+ * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
+ */
+void
+cost_funnel(FunnelPath *path, PlannerInfo *root,
+			RelOptInfo *baserel, ParamPathInfo *param_info)
+{
+	Cost		startup_cost = 0;
+	Cost		run_cost = 0;
+
+	/* Should only be applied to base relations */
+	Assert(baserel->relid > 0);
+	Assert(baserel->rtekind == RTE_RELATION);
+
+	/* Mark the path with the correct row estimate */
+	if (param_info)
+		path->path.rows = param_info->ppi_rows;
+	else
+		path->path.rows = baserel->rows;
+
+	startup_cost = path->subpath->startup_cost;
+
+	run_cost = path->subpath->total_cost - path->subpath->startup_cost;
+
+	/* Parallel setup and communication cost. */
+	startup_cost += parallel_setup_cost;
+	run_cost += cpu_tuple_comm_cost * baserel->tuples;
+
+	path->path.startup_cost = startup_cost;
+	path->path.total_cost = (startup_cost + run_cost);
+}
+
+/*
  * cost_index
  *	  Determines and returns the cost of scanning a relation using an index.
  *
diff --git a/src/backend/optimizer/path/parallelpath.c b/src/backend/optimizer/path/parallelpath.c
new file mode 100644
index 0000000..bc71737
--- /dev/null
+++ b/src/backend/optimizer/path/parallelpath.c
@@ -0,0 +1,89 @@
+/*-------------------------------------------------------------------------
+ *
+ * parallelpath.c
+ *	  Routines to determine which conditions are usable for scanning
+ *	  a given relation, and create ParallelPaths accordingly.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/optimizer/path/parallelpath.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "optimizer/cost.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/paths.h"
+#include "parser/parsetree.h"
+#include "utils/rel.h"
+
+
+/*
+ * create_parallelscan_paths
+ *	  Create paths corresponding to parallel scans of the given rel.
+ *	  Currently we only support parallel sequential scan.
+ *
+ *	  Candidate paths are added to the rel's pathlist (using add_path).
+ */
+void
+create_parallelscan_paths(PlannerInfo *root, RelOptInfo *rel)
+{
+	int			num_parallel_workers = 0;
+	int			estimated_parallel_workers = 0;
+	Oid			reloid;
+	Relation	relation;
+	Path		*subpath;
+
+	/*
+	 * parallel scan is possible only if user has set
+	 * parallel_seqscan_degree to value greater than 0
+	 * and the query is parallel-safe.
+	 */
+	if (parallel_seqscan_degree <= 0 || !root->glob->parallelModeOK)
+		return;
+
+	/*
+	 * There should be atleast thousand pages to scan for each worker.
+	 * This number is somewhat arbitratry, however we don't want to
+	 * spawn workers to scan smaller relations as that will be costly.
+	 */
+	estimated_parallel_workers = rel->pages / 1000;
+	
+	if (estimated_parallel_workers <= 0)
+		return;
+
+	reloid = planner_rt_fetch(rel->relid, root)->relid;
+
+	relation = heap_open(reloid, NoLock);
+
+	/*
+	 * Temporary relations can't be scanned by parallel workers as
+	 * they are visible only to local sessions.
+	 */
+	if (RelationUsesLocalBuffers(relation))
+	{
+		heap_close(relation, NoLock);
+		return;
+	}
+
+	heap_close(relation, NoLock);
+
+	if (parallel_seqscan_degree <= estimated_parallel_workers)
+		num_parallel_workers = parallel_seqscan_degree;
+	else
+		num_parallel_workers = estimated_parallel_workers;
+
+	/* Create the partial scan path which each worker needs to execute. */
+	subpath = create_partialseqscan_path(root, rel, false,
+										 num_parallel_workers);
+
+	/* Create the parallel scan path which master needs to execute. */
+	add_path(rel, (Path *) create_funnel_path(root, rel, subpath,
+											  num_parallel_workers));
+}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index dc2dcbf..1e2b3ba 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -60,6 +60,10 @@ static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path,
 					List *tlist, List *scan_clauses);
 static SampleScan *create_samplescan_plan(PlannerInfo *root, Path *best_path,
 					   List *tlist, List *scan_clauses);
+static Scan *create_partialseqscan_plan(PlannerInfo *root, Path *best_path,
+						List *tlist, List *scan_clauses);
+static Scan *create_funnel_plan(PlannerInfo *root,
+						FunnelPath *best_path);
 static Scan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path,
 					  List *tlist, List *scan_clauses, bool indexonly);
 static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root,
@@ -103,6 +107,11 @@ static void copy_path_costsize(Plan *dest, Path *src);
 static void copy_plan_costsize(Plan *dest, Plan *src);
 static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
 static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid);
+static PartialSeqScan *make_partialseqscan(List *qptlist, List *qpqual,
+									Index scanrelid);
+static Funnel *make_funnel(List *qptlist, List *qpqual,
+					Index scanrelid, int nworkers,
+					Plan *subplan);
 static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
 			   Oid indexid, List *indexqual, List *indexqualorig,
 			   List *indexorderby, List *indexorderbyorig,
@@ -233,6 +242,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
 	{
 		case T_SeqScan:
 		case T_SampleScan:
+		case T_PartialSeqScan:
 		case T_IndexScan:
 		case T_IndexOnlyScan:
 		case T_BitmapHeapScan:
@@ -272,6 +282,10 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
 			plan = create_unique_plan(root,
 									  (UniquePath *) best_path);
 			break;
+		case T_Funnel:
+			plan = (Plan *) create_funnel_plan(root,
+											   (FunnelPath *) best_path);
+			break;
 		default:
 			elog(ERROR, "unrecognized node type: %d",
 				 (int) best_path->pathtype);
@@ -355,6 +369,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
 												   scan_clauses);
 			break;
 
+		case T_PartialSeqScan:
+			plan = (Plan *) create_partialseqscan_plan(root,
+													   best_path,
+													   tlist,
+													   scan_clauses);
+			break;
+
 		case T_IndexScan:
 			plan = (Plan *) create_indexscan_plan(root,
 												  (IndexPath *) best_path,
@@ -559,6 +580,8 @@ disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path)
 	{
 		case T_SeqScan:
 		case T_SampleScan:
+		case T_PartialSeqScan:
+		case T_Funnel:
 		case T_IndexScan:
 		case T_IndexOnlyScan:
 		case T_BitmapHeapScan:
@@ -1186,6 +1209,107 @@ create_samplescan_plan(PlannerInfo *root, Path *best_path,
 }
 
 /*
+ * create_partialseqscan_plan
+ *
+ * Returns a partial seqscan plan for the base relation scanned by
+ * 'best_path' with restriction clauses 'scan_clauses' and targetlist
+ * 'tlist'.
+ */
+static Scan *
+create_partialseqscan_plan(PlannerInfo *root, Path *best_path,
+						   List *tlist, List *scan_clauses)
+{
+	Scan    *scan_plan;
+	Index		scan_relid = best_path->parent->relid;
+
+	/* it should be a base rel... */
+	Assert(scan_relid > 0);
+	Assert(best_path->parent->rtekind == RTE_RELATION);
+
+	/* Sort clauses into best execution order */
+	scan_clauses = order_qual_clauses(root, scan_clauses);
+
+	/* Reduce RestrictInfo list to bare expressions; ignore pseudoconstants */
+	scan_clauses = extract_actual_clauses(scan_clauses, false);
+
+	/* Replace any outer-relation variables with nestloop params */
+	if (best_path->param_info)
+	{
+		scan_clauses = (List *)
+			replace_nestloop_params(root, (Node *) scan_clauses);
+	}
+
+	scan_plan = (Scan *) make_partialseqscan(tlist,
+											 scan_clauses,
+											 scan_relid);
+
+	copy_path_costsize(&scan_plan->plan, best_path);
+
+	return scan_plan;
+}
+
+/*
+ * create_funnel_plan
+ *
+ * Returns a funnel plan for the base relation scanned by
+ * 'best_path'.
+ */
+static Scan *
+create_funnel_plan(PlannerInfo *root, FunnelPath *best_path)
+{
+	Scan    *scan_plan;
+	Plan	*subplan;
+	List	*tlist;
+	RelOptInfo *rel = best_path->path.parent;
+	Index	scan_relid = best_path->path.parent->relid;
+
+	/*
+	 * For table scans, rather than using the relation targetlist (which is
+	 * only those Vars actually needed by the query), we prefer to generate a
+	 * tlist containing all Vars in order.  This will allow the executor to
+	 * optimize away projection of the table tuples, if possible.  (Note that
+	 * planner.c may replace the tlist we generate here, forcing projection to
+	 * occur.)
+	 */
+	if (use_physical_tlist(root, rel))
+	{
+			tlist = build_physical_tlist(root, rel);
+			/* if fail because of dropped cols, use regular method */
+			if (tlist == NIL)
+				tlist = build_path_tlist(root, &best_path->path);
+	}
+	else
+	{
+		tlist = build_path_tlist(root, &best_path->path);
+	}
+
+	/* it should be a base rel... */
+	Assert(scan_relid > 0);
+	Assert(best_path->path.parent->rtekind == RTE_RELATION);
+
+	subplan = create_plan_recurse(root, best_path->subpath);
+
+	/*
+	 * quals for subplan and top level plan are same
+	 * as either all the quals are pushed to subplan
+	 * (partialseqscan plan) or parallel plan won't be
+	 * choosen.
+	 */
+	scan_plan = (Scan *) make_funnel(tlist,
+									 subplan->qual,
+									 scan_relid,
+									 best_path->num_workers,
+									 subplan);
+
+	copy_path_costsize(&scan_plan->plan, &best_path->path);
+
+	/* use parallel mode for parallel plans. */
+	root->glob->parallelModeNeeded = true;
+
+	return scan_plan;
+}
+
+/*
  * create_indexscan_plan
  *	  Returns an indexscan plan for the base relation scanned by 'best_path'
  *	  with restriction clauses 'scan_clauses' and targetlist 'tlist'.
@@ -3452,6 +3576,45 @@ make_samplescan(List *qptlist,
 	return node;
 }
 
+static PartialSeqScan *
+make_partialseqscan(List *qptlist,
+					List *qpqual,
+					Index scanrelid)
+{
+	PartialSeqScan *node = makeNode(PartialSeqScan);
+	Plan	   *plan = &node->plan;
+
+	/* cost should be inserted by caller */
+	plan->targetlist = qptlist;
+	plan->qual = qpqual;
+	plan->lefttree = NULL;
+	plan->righttree = NULL;
+	node->scanrelid = scanrelid;
+
+	return node;
+}
+
+static Funnel *
+make_funnel(List *qptlist,
+			List *qpqual,
+			Index scanrelid,
+			int nworkers,
+			Plan *subplan)
+{
+	Funnel *node = makeNode(Funnel);
+	Plan	   *plan = &node->scan.plan;
+
+	/* cost should be inserted by caller */
+	plan->targetlist = qptlist;
+	plan->qual = qpqual;
+	plan->lefttree = subplan;
+	plan->righttree = NULL;
+	node->scan.scanrelid = scanrelid;
+	node->num_workers = nworkers;
+
+	return node;
+}
+
 static IndexScan *
 make_indexscan(List *qptlist,
 			   List *qpqual,
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 7065e39..641b05f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -293,6 +293,52 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	return result;
 }
 
+PlannedStmt	*
+create_parallel_worker_plannedstmt(PartialSeqScan *partialscan,
+								   List *rangetable,
+								   int num_exec_params)
+{
+	PlannedStmt	*result;
+	ListCell   *tlist;
+
+	/*
+	 * Avoid removing junk entries in worker as those are
+	 * required by upper nodes in master backend.
+	 */
+	foreach(tlist, partialscan->plan.targetlist)
+	{
+		TargetEntry *tle = (TargetEntry *) lfirst(tlist);
+
+		tle->resjunk = false;
+	}
+
+	/* build the PlannedStmt result */
+	result = makeNode(PlannedStmt);
+
+	result->commandType = CMD_SELECT;
+	result->queryId = 0;
+	result->hasReturning = 0;
+	result->hasModifyingCTE = 0;
+	result->canSetTag = 1;
+	result->transientPlan = 0;
+	result->planTree = (Plan*) partialscan;
+	result->rtable = rangetable;
+	result->resultRelations = NIL;
+	result->utilityStmt = NULL;
+	result->subplans = NIL;
+	result->rewindPlanIDs = NULL;
+	result->rowMarks = NIL;
+	result->nParamExec = num_exec_params;
+	/*
+	 * Don't bother to set parameters used for invalidation as
+	 * worker backend plans are not saved, so can't be invalidated.
+	 */
+	result->relationOids = NIL;
+	result->invalItems = NIL;
+	result->hasRowSecurity = false;
+
+	return result;
+}
 
 /*--------------------
  * subquery_planner
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 820f69d..90f4dfb 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -440,6 +440,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 	switch (nodeTag(plan))
 	{
 		case T_SeqScan:
+		case T_PartialSeqScan:
 			{
 				SeqScan    *splan = (SeqScan *) plan;
 
@@ -461,6 +462,26 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 					fix_scan_list(root, splan->plan.qual, rtoffset);
 			}
 			break;
+		case T_Funnel:
+			{
+				Funnel    *splan = (Funnel *) plan;
+
+				/*
+				 * target list for partial sequence scan (leftree of funnel plan)
+				 * should be same as for funnel scan as both nodes need to produce
+				 * same projection.  We don't want to do this assignment after
+				 * fixing references as that will be done separately for partial
+				 * sequence scan node.
+				 */
+				splan->scan.plan.lefttree->targetlist = splan->scan.plan.targetlist;
+
+				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);
+			}
+			break;
 		case T_IndexScan:
 			{
 				IndexScan  *splan = (IndexScan *) plan;
@@ -2259,6 +2280,45 @@ fix_opfuncids_walker(Node *node, void *context)
 }
 
 /*
+ * fix_node_funcids
+ *		Set the opfuncid (procedure OID) in an OpExpr node,
+ *		for plan tree.
+ *
+ * We need it mainly to fix the opfuncid in nodes of plantree
+ * after reading the planned statement by worker backend.
+ * Currently the support of nodes that could be executed by
+ * worker backend are limited, so we can enhance this API based
+ * on it's usage in future.
+ */
+void
+fix_node_funcids(Plan *node)
+{
+	/*
+	 * do nothing when we get to the end of a leaf on tree.
+	 */
+	if (node == NULL)
+		return;
+
+	fix_opfuncids((Node*) node->qual);
+	fix_opfuncids((Node*) node->targetlist);
+
+	switch (nodeTag(node))
+	{
+		case T_Result:
+			fix_opfuncids((Node*) (((Result *)node)->resconstantqual));
+			break;
+		case T_PartialSeqScan:
+			break;
+		default:
+			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
+			break;
+	}
+
+	fix_node_funcids(node->lefttree);
+	fix_node_funcids(node->righttree);
+}
+
+/*
  * set_opfuncid
  *		Set the opfuncid (procedure OID) in an OpExpr node,
  *		if it hasn't been set already.
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 4708b87..92e9326 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2217,6 +2217,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 
 		case T_SeqScan:
 		case T_SampleScan:
+		case T_PartialSeqScan:
+		case T_Funnel:
 			context.paramids = bms_add_members(context.paramids, scan_params);
 			break;
 
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index f7f33bb..81ca06c 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -732,6 +732,54 @@ create_samplescan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer
 }
 
 /*
+ * create_partialseqscan_path
+ *	  Creates a path corresponding to a partial sequential scan, returning the
+ *	  pathnode.
+ */
+Path *
+create_partialseqscan_path(PlannerInfo *root, RelOptInfo *rel,
+						   Relids required_outer, int nworkers)
+{
+	Path	   *pathnode = makeNode(Path);
+
+	pathnode->pathtype = T_PartialSeqScan;
+	pathnode->parent = rel;
+	pathnode->param_info = get_baserel_parampathinfo(root, rel,
+													 false);
+	pathnode->pathkeys = NIL;	/* partialseqscan has unordered result */
+
+	cost_patialseqscan(pathnode, root, rel, pathnode->param_info, nworkers);
+
+	return pathnode;
+}
+
+/*
+ * create_funnel_path
+ *
+ *	  Creates a path corresponding to a funnel scan, returning the
+ *	  pathnode.
+ */
+FunnelPath *
+create_funnel_path(PlannerInfo *root, RelOptInfo *rel,
+				   Path* subpath, int nworkers)
+{
+	FunnelPath	   *pathnode = makeNode(FunnelPath);
+
+	pathnode->path.pathtype = T_Funnel;
+	pathnode->path.parent = rel;
+	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
+													 false);
+	pathnode->path.pathkeys = NIL;	/* seqscan has unordered result */
+
+	pathnode->subpath = subpath;
+	pathnode->num_workers = nworkers;
+
+	cost_funnel(pathnode, root, rel, pathnode->path.param_info);
+
+	return pathnode;
+}
+
+/*
  * create_index_path
  *	  Creates a path node for an index scan.
  *
diff --git a/src/backend/postmaster/Makefile b/src/backend/postmaster/Makefile
index 71c2321..4aec92a 100644
--- a/src/backend/postmaster/Makefile
+++ b/src/backend/postmaster/Makefile
@@ -13,6 +13,7 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = autovacuum.o bgworker.o bgwriter.o checkpointer.o fork_process.o \
-	pgarch.o pgstat.o postmaster.o startup.o syslogger.o walwriter.o
+	pgarch.o pgstat.o postmaster.o startup.o syslogger.o \
+	walwriter.o
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index df8037b..8729bef 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -103,6 +103,7 @@
 #include "miscadmin.h"
 #include "pg_getopt.h"
 #include "pgstat.h"
+#include "optimizer/cost.h"
 #include "postmaster/autovacuum.h"
 #include "postmaster/bgworker_internals.h"
 #include "postmaster/fork_process.h"
@@ -848,6 +849,12 @@ PostmasterMain(int argc, char *argv[])
 		ereport(ERROR,
 				(errmsg("WAL streaming (max_wal_senders > 0) requires wal_level \"archive\", \"hot_standby\", or \"logical\"")));
 
+	if (parallel_seqscan_degree >= MaxConnections)
+	{
+		write_stderr("%s: parallel_scan_degree must be less than max_connections\n", progname);
+		ExitPostmaster(1);
+	}
+
 	/*
 	 * Other one-time internal sanity checks can go here, if they are fast.
 	 * (Put any slow processing further down, after postmaster.pid creation.)
diff --git a/src/backend/storage/ipc/shm_mq.c b/src/backend/storage/ipc/shm_mq.c
index 126cb07..e482269 100644
--- a/src/backend/storage/ipc/shm_mq.c
+++ b/src/backend/storage/ipc/shm_mq.c
@@ -746,6 +746,15 @@ shm_mq_detach(shm_mq *mq)
 }
 
 /*
+ * Get the shm_mq from handle.
+ */
+shm_mq *
+shm_mq_get_queue(shm_mq_handle *mqh)
+{
+	return mqh->mqh_queue;
+}
+
+/*
  * Write bytes into a shared message queue.
  */
 static shm_mq_result
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index bcf3895..57014ee 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -34,6 +34,7 @@
 #include "commands/createas.h"
 #include "commands/matview.h"
 #include "executor/functions.h"
+#include "executor/tqueue.h"
 #include "executor/tstoreReceiver.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
@@ -44,9 +45,10 @@
  *		dummy DestReceiver functions
  * ----------------
  */
-static void
+static bool
 donothingReceive(TupleTableSlot *slot, DestReceiver *self)
 {
+	return true;
 }
 
 static void
@@ -129,6 +131,9 @@ CreateDestReceiver(CommandDest dest)
 
 		case DestTransientRel:
 			return CreateTransientRelDestReceiver(InvalidOid);
+
+		case DestTupleQueue:
+			return CreateTupleQueueDestReceiver();
 	}
 
 	/* should never get here */
@@ -162,6 +167,7 @@ EndCommand(const char *commandTag, CommandDest dest)
 		case DestCopyOut:
 		case DestSQLFunction:
 		case DestTransientRel:
+		case DestTupleQueue:
 			break;
 	}
 }
@@ -204,6 +210,7 @@ NullCommand(CommandDest dest)
 		case DestCopyOut:
 		case DestSQLFunction:
 		case DestTransientRel:
+		case DestTupleQueue:
 			break;
 	}
 }
@@ -248,6 +255,7 @@ ReadyForQuery(CommandDest dest)
 		case DestCopyOut:
 		case DestSQLFunction:
 		case DestTransientRel:
+		case DestTupleQueue:
 			break;
 	}
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 7598318..f1542a0 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -42,6 +42,8 @@
 #include "catalog/pg_type.h"
 #include "commands/async.h"
 #include "commands/prepare.h"
+#include "executor/execParallel.h"
+#include "executor/tqueue.h"
 #include "libpq/libpq.h"
 #include "libpq/pqformat.h"
 #include "libpq/pqsignal.h"
@@ -1192,6 +1194,94 @@ exec_simple_query(const char *query_string)
 }
 
 /*
+ * exec_parallel_stmt
+ *
+ * Execute the plan for backend worker.
+ */
+void
+exec_parallel_stmt(ParallelStmt *parallelstmt)
+{
+	DestReceiver *receiver;
+	QueryDesc	*queryDesc;
+	MemoryContext oldcontext;
+	MemoryContext	plancontext;
+	BufferUsage bufusage_start;
+	BufferUsage bufusage_end = {0};
+
+	set_ps_display("SELECT", false);
+
+	/*
+	 * Unlike exec_simple_query(), in backend worker we won't allow
+	 * transaction control statements, so we can allow plancontext
+	 * to be created in TopTransaction context.
+	 */
+	plancontext = AllocSetContextCreate(CurrentMemoryContext,
+										"worker plan",
+										ALLOCSET_DEFAULT_MINSIZE,
+										ALLOCSET_DEFAULT_INITSIZE,
+										ALLOCSET_DEFAULT_MAXSIZE);
+
+	oldcontext = MemoryContextSwitchTo(plancontext);
+
+	receiver = CreateDestReceiver(DestTupleQueue);
+	SetTupleQueueDestReceiverParams(receiver, parallelstmt->responseq);
+
+	/* Create a QueryDesc for the query */
+	queryDesc = CreateQueryDesc(parallelstmt->plannedstmt, "",
+								GetActiveSnapshot(), InvalidSnapshot,
+								receiver, parallelstmt->params,
+								parallelstmt->inst_options);
+
+	PushActiveSnapshot(queryDesc->snapshot);
+
+	/* call ExecutorStart to prepare the plan for execution */
+	ExecutorStart(queryDesc, 0);
+
+	PopulateParamExecParams(queryDesc, parallelstmt->serialized_param_exec_vals);
+
+	bufusage_start = pgBufferUsage;
+
+	/* run the plan */
+	ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+
+	/*
+	 * Calculate the buffer usage for this statement run, it is required
+	 * by plugins like pg_stat_statements to report the total usage for
+	 * statement execution.
+	 */
+	BufferUsageAccumDiff(&bufusage_end,
+						 &pgBufferUsage, &bufusage_start);
+
+	/* run cleanup too */
+	ExecutorFinish(queryDesc);
+
+	/* copy buffer usage into shared memory. */
+	memcpy(parallelstmt->buffer_usage,
+		   &bufusage_end,
+		   sizeof(BufferUsage));
+
+	/*
+	 * copy intrumentation information into shared memory if requested
+	 * by master backend.
+	 */
+	if (parallelstmt->inst_options)
+		memcpy(parallelstmt->instrument,
+			   queryDesc->planstate->instrument,
+			   sizeof(Instrumentation));
+
+	ExecutorEnd(queryDesc);
+
+	PopActiveSnapshot();
+
+	FreeQueryDesc(queryDesc);
+
+	if (!parallelstmt->inst_options)
+		(*receiver->rDestroy) (receiver);
+
+	MemoryContextSwitchTo(oldcontext);
+}
+
+/*
  * exec_parse_message
  *
  * Execute a "Parse" protocol message.
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 9c14e8a..f2fb638 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -1121,7 +1121,13 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
 			if (!ok)
 				break;
 
-			(*dest->receiveSlot) (slot, dest);
+			/*
+			 * If we are not able to send the tuple, then we assume that
+			 * destination has closed and we won't be able to send any more
+			 * tuples so we just end the loop.
+			 */
+			if (!((*dest->receiveSlot) (slot, dest)))
+				break;
 
 			ExecClearTuple(slot);
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 595a609..c4e9531 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -607,6 +607,8 @@ const char *const config_group_names[] =
 	gettext_noop("Statistics / Query and Index Statistics Collector"),
 	/* AUTOVACUUM */
 	gettext_noop("Autovacuum"),
+	/* PARALLEL_QUERY */
+	gettext_noop("parallel_seqscan_degree"),
 	/* CLIENT_CONN */
 	gettext_noop("Client Connection Defaults"),
 	/* CLIENT_CONN_STATEMENT */
@@ -2545,6 +2547,16 @@ static struct config_int ConfigureNamesInt[] =
 	},
 
 	{
+		{"parallel_seqscan_degree", PGC_SUSET, PARALLEL_QUERY,
+			gettext_noop("Sets the maximum number of simultaneously running backend worker processes."),
+			NULL
+		},
+		&parallel_seqscan_degree,
+		0, 0, MAX_BACKENDS,
+		NULL, NULL, NULL
+	},
+
+	{
 		{"autovacuum_work_mem", PGC_SIGHUP, RESOURCES_MEM,
 			gettext_noop("Sets the maximum memory to be used by each autovacuum worker process."),
 			NULL,
@@ -2732,6 +2744,26 @@ static struct config_real ConfigureNamesReal[] =
 		DEFAULT_CPU_OPERATOR_COST, 0, DBL_MAX,
 		NULL, NULL, NULL
 	},
+	{
+		{"cpu_tuple_comm_cost", PGC_USERSET, QUERY_TUNING_COST,
+			gettext_noop("Sets the planner's estimate of the cost of "
+						 "passing each tuple (row) from worker to master backend."),
+			NULL
+		},
+		&cpu_tuple_comm_cost,
+		DEFAULT_CPU_TUPLE_COMM_COST, 0, DBL_MAX,
+		NULL, NULL, NULL
+	},
+	{
+		{"parallel_setup_cost", PGC_USERSET, QUERY_TUNING_COST,
+			gettext_noop("Sets the planner's estimate of the cost of "
+						 "setting up environment (shared memory) for parallelism."),
+			NULL
+		},
+		&parallel_setup_cost,
+		DEFAULT_PARALLEL_SETUP_COST, 0, DBL_MAX,
+		NULL, NULL, NULL
+	},
 
 	{
 		{"cursor_tuple_fraction", PGC_USERSET, QUERY_TUNING_OTHER,
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 06dfc06..32ff938 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -291,6 +291,8 @@
 #cpu_tuple_cost = 0.01			# same scale as above
 #cpu_index_tuple_cost = 0.005		# same scale as above
 #cpu_operator_cost = 0.0025		# same scale as above
+#cpu_tuple_comm_cost = 0.1		# same scale as above
+#parallel_setup_cost = 0.0	# same scale as above
 #effective_cache_size = 4GB
 
 # - Genetic Query Optimizer -
@@ -501,6 +503,11 @@
 					# autovacuum, -1 means use
 					# vacuum_cost_limit
 
+#------------------------------------------------------------------------------
+# PARALLEL_QUERY PARAMETERS
+#------------------------------------------------------------------------------
+
+#parallel_seqscan_degree = 0		# max number of worker backend subprocesses
 
 #------------------------------------------------------------------------------
 # CLIENT CONNECTION DEFAULTS
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 31139cb..d56e839 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -96,8 +96,9 @@ extern Relation heap_openrv_extended(const RangeVar *relation,
 
 #define heap_close(r,l)  relation_close(r,l)
 
-/* struct definition appears in relscan.h */
+/* struct definitions appear in relscan.h */
 typedef struct HeapScanDescData *HeapScanDesc;
+typedef struct ParallelHeapScanDescData *ParallelHeapScanDesc;
 
 /*
  * HeapScanIsValid
@@ -121,9 +122,15 @@ extern void heap_setscanlimits(HeapScanDesc scan, BlockNumber startBlk,
 				   BlockNumber endBlk);
 extern void heapgetpage(HeapScanDesc scan, BlockNumber page);
 extern void heap_rescan(HeapScanDesc scan, ScanKey key);
+extern void heap_parallel_rescan(ParallelHeapScanDesc pscan, HeapScanDesc scan);
 extern void heap_endscan(HeapScanDesc scan);
 extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction);
 
+extern Size heap_parallelscan_estimate(Snapshot snapshot);
+extern void heap_parallelscan_initialize(ParallelHeapScanDesc target,
+							 Relation relation, Snapshot snapshot);
+extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc);
+
 extern bool heap_fetch(Relation relation, Snapshot snapshot,
 		   HeapTuple tuple, Buffer *userbuf, bool keep_buf,
 		   Relation stats_relation);
diff --git a/src/include/access/printtup.h b/src/include/access/printtup.h
index 46c4148..92ec882 100644
--- a/src/include/access/printtup.h
+++ b/src/include/access/printtup.h
@@ -25,11 +25,11 @@ extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist,
 
 extern void debugStartup(DestReceiver *self, int operation,
 			 TupleDesc typeinfo);
-extern void debugtup(TupleTableSlot *slot, DestReceiver *self);
+extern bool debugtup(TupleTableSlot *slot, DestReceiver *self);
 
 /* XXX these are really in executor/spi.c */
 extern void spi_dest_startup(DestReceiver *self, int operation,
 				 TupleDesc typeinfo);
-extern void spi_printtup(TupleTableSlot *slot, DestReceiver *self);
+extern bool spi_printtup(TupleTableSlot *slot, DestReceiver *self);
 
 #endif   /* PRINTTUP_H */
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index f2482e9..90af7e1 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -20,6 +20,15 @@
 #include "access/itup.h"
 #include "access/tupdesc.h"
 
+/* Struct for parallel scan setup */
+typedef struct ParallelHeapScanDescData
+{
+	Oid			phs_relid;
+	BlockNumber	phs_nblocks;
+	slock_t		phs_mutex;
+	BlockNumber phs_cblock;
+	char		phs_snapshot_data[FLEXIBLE_ARRAY_MEMBER];
+}	ParallelHeapScanDescData;
 
 typedef struct HeapScanDescData
 {
@@ -49,6 +58,7 @@ typedef struct HeapScanDescData
 	BlockNumber rs_cblock;		/* current block # in scan, if any */
 	Buffer		rs_cbuf;		/* current buffer in scan, if any */
 	/* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */
+	ParallelHeapScanDesc rs_parallel; /* parallel scan information */
 
 	/* these fields only used in page-at-a-time mode and for bitmap scans */
 	int			rs_cindex;		/* current tuple's index in vistuples */
diff --git a/src/include/executor/execParallel.h b/src/include/executor/execParallel.h
new file mode 100644
index 0000000..73006a8
--- /dev/null
+++ b/src/include/executor/execParallel.h
@@ -0,0 +1,65 @@
+/*--------------------------------------------------------------------
+ * execParallel.h
+ *		POSTGRES backend workers interface
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *		src/include/executor/execParallel.h
+ *--------------------------------------------------------------------
+ */
+#ifndef EXECPARALLEL_H
+#define EXECPARALLEL_H
+
+/*---------------------------------------------------------------------
+ * External module API.
+ *---------------------------------------------------------------------
+ */
+
+#include "libpq/pqmq.h"
+#include "nodes/execnodes.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+
+/* Table-of-contents constants for our dynamic shared memory segment. */
+#define	PARALLEL_KEY_PLANNEDSTMT	0
+#define	PARALLEL_KEY_PARAMS			1
+#define	PARALLEL_KEY_PARAMS_EXEC	2
+#define PARALLEL_KEY_BUFF_USAGE		3
+#define PARALLEL_KEY_INST_OPTIONS	4
+#define PARALLEL_KEY_INST_INFO		5
+#define PARALLEL_KEY_TUPLE_QUEUE	6
+#define PARALLEL_KEY_SCAN			7
+
+extern int	parallel_seqscan_degree;
+
+/* worker statement required for parallel execution. */
+typedef struct ParallelStmt
+{
+	PlannedStmt		*plannedstmt;
+	ParamListInfo	params;
+	List			*serialized_param_exec_vals;
+	shm_mq_handle	*responseq;
+	int				inst_options;
+	char			*instrument;
+	char			*buffer_usage;
+} ParallelStmt;
+
+extern void InitializeParallelWorkers(PlanState *planstate,
+									  List *serialized_param_exec_vals,
+									  EState *estate,
+									  char **inst_options_space,
+									  char **buffer_usage_space,
+									  shm_mq_handle ***responseqp,
+									  ParallelContext **pcxtp,
+									  int nWorkers);
+extern shm_toc *GetParallelShmToc(void);
+extern bool ExecParallelEstimate(Node *node, ParallelContext *pcxt,
+								 Size *pscan_size);
+extern bool ExecParallelInitializeDSM(Node *node, ParallelContext *pcxt,
+									  Size *pscan_size);
+extern bool ExecParallelBufferUsageAccum(Node *node);
+extern void ExecAssociateBufferStatsToDSM(BufferUsage *buf_usage,
+							  ParallelStmt *parallel_stmt);
+#endif   /* EXECPARALLEL_H */
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 193a654..963e656 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -273,6 +273,8 @@ extern TupleDesc ExecCleanTypeFromTL(List *targetList, bool hasoid);
 extern TupleDesc ExecTypeFromExprList(List *exprList);
 extern void ExecTypeSetColNames(TupleDesc typeInfo, List *namesList);
 extern void UpdateChangedParamSet(PlanState *node, Bitmapset *newchg);
+extern void PopulateParamExecParams(QueryDesc *queryDesc,
+						List *serialized_param_exec_vals);
 
 typedef struct TupOutputState
 {
diff --git a/src/include/executor/instrument.h b/src/include/executor/instrument.h
index c9a2129..0c7847d 100644
--- a/src/include/executor/instrument.h
+++ b/src/include/executor/instrument.h
@@ -69,5 +69,12 @@ extern Instrumentation *InstrAlloc(int n, int instrument_options);
 extern void InstrStartNode(Instrumentation *instr);
 extern void InstrStopNode(Instrumentation *instr, double nTuples);
 extern void InstrEndLoop(Instrumentation *instr);
+extern void InstrAggNode(Instrumentation *instr1, Instrumentation *instr2);
+extern void
+	InstrAggBufferUsage(BufferUsage *buffer_usage_dst, BufferUsage *buffer_usage_add);
+extern void BufferUsageAccumDiff(BufferUsage *dst,
+					 const BufferUsage *add,
+					 const BufferUsage *sub);
+extern void BufferUsageAdd(BufferUsage *dst, const BufferUsage *add);
 
 #endif   /* INSTRUMENT_H */
diff --git a/src/include/executor/nodeFunnel.h b/src/include/executor/nodeFunnel.h
new file mode 100644
index 0000000..27d0b3d
--- /dev/null
+++ b/src/include/executor/nodeFunnel.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeFunnel.h
+ *
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/nodeFunnel.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEFUNNEL_H
+#define NODEFUNNEL_H
+
+#include "nodes/execnodes.h"
+
+extern FunnelState *ExecInitFunnel(Funnel *node, EState *estate, int eflags);
+extern TupleTableSlot *ExecFunnel(FunnelState *node);
+extern void ExecEndFunnel(FunnelState *node);
+extern void FinishParallelSetupAndAccumStats(FunnelState *node);
+extern void ExecReScanFunnel(FunnelState *node);
+
+#endif   /* NODEFUNNEL_H */
diff --git a/src/include/executor/nodePartialSeqscan.h b/src/include/executor/nodePartialSeqscan.h
new file mode 100644
index 0000000..47b8f73
--- /dev/null
+++ b/src/include/executor/nodePartialSeqscan.h
@@ -0,0 +1,25 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodePartialSeqscan.h
+ *
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/nodePartialSeqscan.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEPARTIALSEQSCAN_H
+#define NODEPARTIALSEQSCAN_H
+
+#include "nodes/execnodes.h"
+
+extern PartialSeqScanState *ExecInitPartialSeqScan(PartialSeqScan *node,
+											EState *estate, int eflags);
+extern TupleTableSlot *ExecPartialSeqScan(PartialSeqScanState *node);
+extern void ExecEndPartialSeqScan(PartialSeqScanState *node);
+extern void ExecReScanPartialSeqScan(PartialSeqScanState *node);
+
+#endif   /* NODEPARTIALSEQSCAN_H */
diff --git a/src/include/executor/nodeSubplan.h b/src/include/executor/nodeSubplan.h
index 3732ad4..21c745e 100644
--- a/src/include/executor/nodeSubplan.h
+++ b/src/include/executor/nodeSubplan.h
@@ -24,4 +24,7 @@ extern void ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent);
 
 extern void ExecSetParamPlan(SubPlanState *node, ExprContext *econtext);
 
+extern List *
+ExecAndFormSerializeParamExec(ExprContext *econtext, Bitmapset *params);
+
 #endif   /* NODESUBPLAN_H */
diff --git a/src/include/executor/tqueue.h b/src/include/executor/tqueue.h
new file mode 100644
index 0000000..d2ddb6e
--- /dev/null
+++ b/src/include/executor/tqueue.h
@@ -0,0 +1,35 @@
+/*-------------------------------------------------------------------------
+ *
+ * tqueue.h
+ *	  Use shm_mq to send & receive tuples between parallel backends
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/tqueue.h
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#ifndef TQUEUE_H
+#define TQUEUE_H
+
+#include "storage/shm_mq.h"
+#include "tcop/dest.h"
+
+/* Use this to send tuples to a shm_mq. */
+extern DestReceiver *CreateTupleQueueDestReceiver(void);
+extern void SetTupleQueueDestReceiverParams(DestReceiver *self,
+						shm_mq_handle *handle);
+
+/* Use these to receive tuples from a shm_mq. */
+typedef struct TupleQueueFunnel TupleQueueFunnel;
+extern TupleQueueFunnel *CreateTupleQueueFunnel(void);
+extern void TupleQueueFunnelShutdown(TupleQueueFunnel *funnel);
+extern void DestroyTupleQueueFunnel(TupleQueueFunnel *funnel);
+extern void RegisterTupleQueueOnFunnel(TupleQueueFunnel *, shm_mq_handle *);
+extern HeapTuple TupleQueueFunnelNext(TupleQueueFunnel *, bool nowait,
+					 bool *done);
+
+#endif   /* TQUEUE_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 541ee18..cc1174e 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -16,7 +16,9 @@
 
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "access/parallel.h"
 #include "executor/instrument.h"
+#include "executor/tqueue.h"
 #include "lib/pairingheap.h"
 #include "nodes/params.h"
 #include "nodes/plannodes.h"
@@ -401,6 +403,18 @@ typedef struct EState
 	List	   *es_auxmodifytables;		/* List of secondary ModifyTableStates */
 
 	/*
+	 * This is required for parallel plan execution to fetch the
+	 * information from dsm.
+	 */
+	shm_toc		*toc;
+
+	/*
+	 * This is required to collect buffer usage stats from parallel
+	 * workers when requested by plugins.
+	 */
+	bool		total_time;	/* total time spent in ExecutorRun */
+
+	/*
 	 * this ExprContext is for per-output-tuple operations, such as constraint
 	 * checks and index-value computations.  It will be reset for each output
 	 * tuple.  Note that it will be created only if needed.
@@ -1050,6 +1064,11 @@ typedef struct PlanState
 	 * State for management of parameter-change-driven rescanning
 	 */
 	Bitmapset  *chgParam;		/* set of IDs of changed Params */
+	/*
+	 * This is required for parallel plan execution to fetch the
+	 * information from dsm.
+	 */
+	shm_toc			*toc;
 
 	/*
 	 * Other run-time state needed by most if not all node types.
@@ -1267,6 +1286,45 @@ typedef struct SampleScanState
 } SampleScanState;
 
 /*
+ * PartialSeqScanState extends ScanState by storing additional information
+ * related to scan.
+ */
+typedef struct PartialSeqScanState
+{
+	ScanState		ss;				/* its first field is NodeTag */
+	bool			scan_initialized; /* used to determine if the scan is initialized */
+} PartialSeqScanState;
+
+/*
+ * FunnelState extends ScanState by storing additional information
+ * related to parallel workers.
+ *		pcxt				parallel context for managing generic state information
+ *							required for parallelism.
+ *		responseq			shared memory queues to receive data from workers.
+ *		funnel				maintains the runtime information about queue's used to
+ *							receive data from parallel workers.
+ *		inst_options_space	to accumulate instrumentation information from all
+ *							parallel workers.
+ *		buffer_usage_space	to accumulate buffer usage information from all
+ *							parallel workers.
+ *		fs_workersReady		indicates that workers are launched.
+ *		all_workers_done	indicates that all the data from workers has been received.
+ *		local_scan_done		indicates that local scan is compleleted.
+ */
+typedef struct FunnelState
+{
+	ScanState		ss;				/* its first field is NodeTag */
+	ParallelContext *pcxt;
+	shm_mq_handle	**responseq;
+	TupleQueueFunnel *funnel;
+	char			*inst_options_space;
+	char			*buffer_usage_space;
+	bool			fs_workersReady;
+	bool			all_workers_done;
+	bool			local_scan_done;
+} FunnelState;
+
+/*
  * These structs store information about index quals that don't have simple
  * constant right-hand sides.  See comments for ExecIndexBuildScanKeys()
  * for discussion.
diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h
index 7b1b1d6..df00d3d 100644
--- a/src/include/nodes/nodeFuncs.h
+++ b/src/include/nodes/nodeFuncs.h
@@ -13,6 +13,7 @@
 #ifndef NODEFUNCS_H
 #define NODEFUNCS_H
 
+#include "access/parallel.h"
 #include "nodes/parsenodes.h"
 
 
@@ -63,4 +64,7 @@ extern Node *query_or_expression_tree_mutator(Node *node, Node *(*mutator) (),
 extern bool raw_expression_tree_walker(Node *node, bool (*walker) (),
 												   void *context);
 
+extern bool planstate_tree_walker(Node *node, ParallelContext *pcxt,
+					  bool (*walker) (), void *context);
+
 #endif   /* NODEFUNCS_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 290cdb3..322b5e8 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -51,6 +51,8 @@ typedef enum NodeTag
 	T_BitmapOr,
 	T_Scan,
 	T_SeqScan,
+	T_PartialSeqScan,
+	T_Funnel,
 	T_IndexScan,
 	T_IndexOnlyScan,
 	T_BitmapIndexScan,
@@ -99,6 +101,8 @@ typedef enum NodeTag
 	T_ScanState,
 	T_SeqScanState,
 	T_SampleScanState,
+	T_PartialSeqScanState,
+	T_FunnelState,
 	T_IndexScanState,
 	T_IndexOnlyScanState,
 	T_BitmapIndexScanState,
@@ -223,6 +227,7 @@ typedef enum NodeTag
 	T_IndexOptInfo,
 	T_ParamPathInfo,
 	T_Path,
+	T_FunnelPath,
 	T_IndexPath,
 	T_BitmapHeapPath,
 	T_BitmapAndPath,
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index a0f7dd0..21c6f7a 100644
--- a/src/include/nodes/params.h
+++ b/src/include/nodes/params.h
@@ -14,6 +14,8 @@
 #ifndef PARAMS_H
 #define PARAMS_H
 
+#include "nodes/pg_list.h"
+
 /* To avoid including a pile of parser headers, reference ParseState thus: */
 struct ParseState;
 
@@ -96,11 +98,47 @@ typedef struct ParamExecData
 {
 	void	   *execPlan;		/* should be "SubPlanState *" */
 	Datum		value;
+	/*
+	 * parameter's datatype, or 0.  This is required so that
+	 * datum value can be read and used for other purposes like
+	 * passing it to worker backend via shared memory.  This is
+	 * required only for evaluation of initPlan's, however for
+	 * consistency we set this for Subplan as well.  We left it
+	 * for other cases like CTE or RecursiveUnion cases where this
+	 * structure is not used for evaluation of subplans.
+	 */
+	Oid			ptype;
 	bool		isnull;
 } ParamExecData;
 
+/*
+ * This structure is used to pass PARAM_EXEC parameters to backend
+ * workers.  For each PARAM_EXEC parameter, pass this structure
+ * followed by value except for pass-by-value parameters.
+ */
+typedef struct SerializedParamExecData
+{
+	int			paramid;			/* parameter id of this param */
+	Size		length;			/* length of parameter value */
+	Oid			ptype;			/* parameter's datatype, or 0 */
+	Datum		value;
+	bool		isnull;
+} SerializedParamExecData;
+
 
 /* Functions found in src/backend/nodes/params.c */
 extern ParamListInfo copyParamList(ParamListInfo from);
 
+extern Size
+EstimateBoundParametersSpace(ParamListInfo params);
+extern void
+SerializeBoundParams(ParamListInfo params, Size maxsize, char *start_address);
+extern ParamListInfo RestoreBoundParams(char *start_address);
+extern Size
+EstimateExecParametersSpace(List *serialized_param_exec_vals);
+extern void
+SerializeExecParams(List *serialized_param_exec_vals, Size maxsize,
+					char *start_address);
+List *
+RestoreExecParams(char *start_address);
 #endif   /* PARAMS_H */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 761bdf4..5d705c1 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -292,6 +292,22 @@ typedef Scan SeqScan;
 typedef Scan SampleScan;
 
 /* ----------------
+ *		partial sequential scan node
+ * ----------------
+ */
+typedef SeqScan PartialSeqScan;
+
+/* ----------------
+ *		parallel sequential scan node
+ * ----------------
+ */
+typedef struct Funnel
+{
+	Scan		scan;
+	int			num_workers;
+} Funnel;
+
+/* ----------------
  *		index scan node
  *
  * indexqualorig is an implicitly-ANDed list of index qual expressions, each
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 53a8820..62b498b 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -754,6 +754,13 @@ typedef struct Path
 	/* pathkeys is a List of PathKey nodes; see above */
 } Path;
 
+typedef struct FunnelPath
+{
+	Path		path;
+	Path	    *subpath;	/* path for each worker */
+	int			num_workers;
+} FunnelPath;
+
 /* Macro for extracting a path's parameterization relids; beware double eval */
 #define PATH_REQ_OUTER(path)  \
 	((path)->param_info ? (path)->param_info->ppi_req_outer : (Relids) NULL)
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 24003ae..a1c9f59 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -26,6 +26,13 @@
 #define DEFAULT_CPU_TUPLE_COST	0.01
 #define DEFAULT_CPU_INDEX_TUPLE_COST 0.005
 #define DEFAULT_CPU_OPERATOR_COST  0.0025
+#define DEFAULT_CPU_TUPLE_COMM_COST 0.1
+/*
+ * XXX - We need some experiments to know what could be
+ * appropriate default values for parallel setup and startup
+ * cost.
+ */
+#define	DEFAULT_PARALLEL_SETUP_COST  0.0
 
 #define DEFAULT_EFFECTIVE_CACHE_SIZE  524288	/* measured in pages */
 
@@ -48,8 +55,11 @@ extern PGDLLIMPORT double random_page_cost;
 extern PGDLLIMPORT double cpu_tuple_cost;
 extern PGDLLIMPORT double cpu_index_tuple_cost;
 extern PGDLLIMPORT double cpu_operator_cost;
+extern PGDLLIMPORT double cpu_tuple_comm_cost;
+extern PGDLLIMPORT double parallel_setup_cost;
 extern PGDLLIMPORT int effective_cache_size;
 extern Cost disable_cost;
+extern int	parallel_seqscan_degree;
 extern bool enable_seqscan;
 extern bool enable_indexscan;
 extern bool enable_indexonlyscan;
@@ -69,6 +79,11 @@ extern double index_pages_fetched(double tuples_fetched, BlockNumber pages,
 extern void cost_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 			 ParamPathInfo *param_info);
 extern void cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
+extern void cost_patialseqscan(Path *path, PlannerInfo *root,
+						RelOptInfo *baserel, ParamPathInfo *param_info,
+						int nworkers);
+extern void cost_funnel(FunnelPath *path, PlannerInfo *root,
+					RelOptInfo *baserel, ParamPathInfo *param_info);
 extern void cost_index(IndexPath *path, PlannerInfo *root,
 		   double loop_count);
 extern void cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 161644c..6047fec 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -34,6 +34,10 @@ extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel,
 					Relids required_outer);
 extern Path *create_samplescan_path(PlannerInfo *root, RelOptInfo *rel,
 					   Relids required_outer);
+extern Path *create_partialseqscan_path(PlannerInfo *root, RelOptInfo *rel,
+						Relids required_outer, int nworkers);
+extern FunnelPath *create_funnel_path(PlannerInfo *root,
+						RelOptInfo *rel, Path *subpath, int nworkers);
 extern IndexPath *create_index_path(PlannerInfo *root,
 				  IndexOptInfo *index,
 				  List *indexclauses,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 3e2378a..bd8eb67 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -55,6 +55,13 @@ extern void debug_print_rel(PlannerInfo *root, RelOptInfo *rel);
 #endif
 
 /*
+ * parallelpath.c
+ *	  routines to generate parallel scan paths
+ */
+
+extern void create_parallelscan_paths(PlannerInfo *root, RelOptInfo *rel);
+
+/*
  * indxpath.c
  *	  routines to generate index paths
  */
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 52b077a..67a8582 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -133,6 +133,7 @@ extern bool query_is_distinct_for(Query *query, List *colnos, List *opids);
  */
 extern Plan *set_plan_references(PlannerInfo *root, Plan *plan);
 extern void fix_opfuncids(Node *node);
+extern void fix_node_funcids(Plan *node);
 extern void set_opfuncid(OpExpr *opexpr);
 extern void set_sa_opfuncid(ScalarArrayOpExpr *opexpr);
 extern void record_plan_function_dependency(PlannerInfo *root, Oid funcid);
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index b10a504..8c7ce75 100644
--- a/src/include/optimizer/planner.h
+++ b/src/include/optimizer/planner.h
@@ -14,6 +14,7 @@
 #ifndef PLANNER_H
 #define PLANNER_H
 
+#include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
 #include "nodes/relation.h"
 
@@ -29,6 +30,8 @@ extern PlannedStmt *planner(Query *parse, int cursorOptions,
 		ParamListInfo boundParams);
 extern PlannedStmt *standard_planner(Query *parse, int cursorOptions,
 				 ParamListInfo boundParams);
+extern PlannedStmt	*create_parallel_worker_plannedstmt(PartialSeqScan *partialscan,
+											List *rangetable, int num_exec_params);
 
 extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse,
 				 PlannerInfo *parent_root,
diff --git a/src/include/storage/shm_mq.h b/src/include/storage/shm_mq.h
index 1a2ba04..7621a35 100644
--- a/src/include/storage/shm_mq.h
+++ b/src/include/storage/shm_mq.h
@@ -65,6 +65,9 @@ extern void shm_mq_set_handle(shm_mq_handle *, BackgroundWorkerHandle *);
 /* Break connection. */
 extern void shm_mq_detach(shm_mq *);
 
+/* Get the shm_mq from handle. */
+extern shm_mq *shm_mq_get_queue(shm_mq_handle *mqh);
+
 /* Send or receive messages. */
 extern shm_mq_result shm_mq_send(shm_mq_handle *mqh,
 			Size nbytes, const void *data, bool nowait);
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 5bcca3f..91acd60 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -94,7 +94,8 @@ typedef enum
 	DestIntoRel,				/* results sent to relation (SELECT INTO) */
 	DestCopyOut,				/* results sent to COPY TO code */
 	DestSQLFunction,			/* results sent to SQL-language func mgr */
-	DestTransientRel			/* results sent to transient relation */
+	DestTransientRel,			/* results sent to transient relation */
+	DestTupleQueue				/* results sent to tuple queue */
 } CommandDest;
 
 /* ----------------
@@ -103,7 +104,9 @@ typedef enum
  *		pointers that the executor must call.
  *
  * Note: the receiveSlot routine must be passed a slot containing a TupleDesc
- * identical to the one given to the rStartup routine.
+ * identical to the one given to the rStartup routine.  It returns bool where
+ * a "true" value means "continue processing" and a "false" value means
+ * "stop early, just as if we'd reached the end of the scan".
  * ----------------
  */
 typedef struct _DestReceiver DestReceiver;
@@ -111,7 +114,7 @@ typedef struct _DestReceiver DestReceiver;
 struct _DestReceiver
 {
 	/* Called for each tuple to be output: */
-	void		(*receiveSlot) (TupleTableSlot *slot,
+	bool		(*receiveSlot) (TupleTableSlot *slot,
 											DestReceiver *self);
 	/* Per-executor-run initialization and shutdown: */
 	void		(*rStartup) (DestReceiver *self,
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 96c5b8b..6f319c1 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -19,6 +19,7 @@
 #ifndef TCOPPROT_H
 #define TCOPPROT_H
 
+#include "executor/execParallel.h"
 #include "nodes/params.h"
 #include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
@@ -84,5 +85,6 @@ extern void set_debug_options(int debug_flag,
 extern bool set_plan_disabling_options(const char *arg,
 						   GucContext context, GucSource source);
 extern const char *get_stats_option_name(const char *arg);
+extern void exec_parallel_stmt(ParallelStmt *parallelscan);
 
 #endif   /* TCOPPROT_H */
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index 7a58ddb..3505d31 100644
--- a/src/include/utils/guc_tables.h
+++ b/src/include/utils/guc_tables.h
@@ -85,6 +85,7 @@ enum config_group
 	STATS_MONITORING,
 	STATS_COLLECTOR,
 	AUTOVACUUM,
+	PARALLEL_QUERY,
 	CLIENT_CONN,
 	CLIENT_CONN_STATEMENT,
 	CLIENT_CONN_LOCALE,
