diff --git a/src/backend/access/Makefile b/src/backend/access/Makefile
index 21721b4..823d5c3 100644
--- a/src/backend/access/Makefile
+++ b/src/backend/access/Makefile
@@ -8,6 +8,6 @@ subdir = src/backend/access
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc spgist transam
+SUBDIRS	    = brin common gin gist hash heap index nbtree rmgrdesc shmmq spgist transam
 
 include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index baed981..a032afb 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -82,7 +82,7 @@ printtup_create_DR(CommandDest dest)
 
 	/*
 	 * Send T message automatically if DestRemote, but not if
-	 * DestRemoteExecute
+	 * DestRemoteExecute or DestRemoteBackend
 	 */
 	self->sendDescrip = (dest == DestRemote);
 
@@ -95,7 +95,8 @@ printtup_create_DR(CommandDest dest)
 }
 
 /*
- * Set parameters for a DestRemote (or DestRemoteExecute) receiver
+ * Set parameters for a DestRemote (or DestRemoteExecute or DestRemoteBackend)
+ * receiver
  */
 void
 SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
@@ -103,7 +104,8 @@ SetRemoteDestReceiverParams(DestReceiver *self, Portal portal)
 	DR_printtup *myState = (DR_printtup *) self;
 
 	Assert(myState->pub.mydest == DestRemote ||
-		   myState->pub.mydest == DestRemoteExecute);
+		   myState->pub.mydest == DestRemoteExecute ||
+		   myState->pub.mydest == DestRemoteBackend);
 
 	myState->portal = portal;
 
@@ -243,7 +245,19 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
 				pq_sendint(&buf, 0, 2);
 		}
 	}
-	pq_endmessage(&buf);
+
+	/*
+	 * Send the message via shared-memory tuple queue, if the same
+	 * is enabled.
+	 */
+	if (is_tuple_shm_mq_enabled())
+	{
+		mq_putmessage_direct(buf.cursor, buf.data, buf.len);
+		pfree(buf.data);
+		buf.data = NULL;
+	}
+	else
+		pq_endmessage(&buf);
 }
 
 /*
@@ -252,9 +266,15 @@ SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist, int16 *formats)
 static void
 printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 {
-	int16	   *formats = myState->portal->formats;
+	int16	   *formats;
 	int			i;
 
+	/* Remote backend always uses binary format to communicate. */
+	if (myState->pub.mydest == DestRemoteBackend)
+		formats = NULL;
+	else
+		formats = myState->portal->formats;
+
 	/* get rid of any old data */
 	if (myState->myinfo)
 		pfree(myState->myinfo);
@@ -271,7 +291,12 @@ printtup_prepare_info(DR_printtup *myState, TupleDesc typeinfo, int numAttrs)
 	for (i = 0; i < numAttrs; i++)
 	{
 		PrinttupAttrInfo *thisState = myState->myinfo + i;
-		int16		format = (formats ? formats[i] : 0);
+		int16		format;
+
+		if (myState->pub.mydest == DestRemoteBackend)
+			format = (formats ? formats[i] : 1);
+		else
+			format = (formats ? formats[i] : 0);
 
 		thisState->format = format;
 		if (format == 0)
@@ -371,7 +396,18 @@ printtup(TupleTableSlot *slot, DestReceiver *self)
 		}
 	}
 
-	pq_endmessage(&buf);
+	/*
+	 * Send the message via shared-memory tuple queue, if the same
+	 * is enabled.
+	 */
+	if (is_tuple_shm_mq_enabled())
+	{
+		mq_putmessage_direct(buf.cursor, buf.data, buf.len);
+		pfree(buf.data);
+		buf.data = NULL;
+	}
+	else
+		pq_endmessage(&buf);
 
 	/* Return to caller's context, and flush row's temporary memory */
 	MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/access/shmmq/Makefile b/src/backend/access/shmmq/Makefile
new file mode 100644
index 0000000..aeae8d9
--- /dev/null
+++ b/src/backend/access/shmmq/Makefile
@@ -0,0 +1,17 @@
+#-------------------------------------------------------------------------
+#
+# Makefile--
+#    Makefile for access/shmmq
+#
+# IDENTIFICATION
+#    src/backend/access/shmmq/Makefile
+#
+#-------------------------------------------------------------------------
+
+subdir = src/backend/access/shmmq
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+
+OBJS = shmmqam.o 
+
+include $(top_srcdir)/src/backend/common.mk
diff --git a/src/backend/access/shmmq/shmmqam.c b/src/backend/access/shmmq/shmmqam.c
new file mode 100644
index 0000000..116a717
--- /dev/null
+++ b/src/backend/access/shmmq/shmmqam.c
@@ -0,0 +1,339 @@
+/*-------------------------------------------------------------------------
+ *
+ * shmmqam.c
+ *	  shared memory queue access method code
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/access/shmmq/shmmqam.c
+ *
+ *
+ * INTERFACE ROUTINES
+ *		shm_getnext	- retrieve next tuple in queue
+ *
+ * NOTES
+ *	  This file contains the shmmq_ routines which implement
+ *	  the POSTGRES shared memory access method used for all POSTGRES
+ *	  relations.
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup.h"
+#include "access/htup_details.h"
+#include "access/shmmqam.h"
+#include "access/tupdesc.h"
+#include "fmgr.h"
+#include "libpq/libpq.h"
+#include "libpq/pqformat.h"
+#include "utils/lsyscache.h"
+
+
+static bool
+HandleParallelTupleMessage(worker_result resultState, TupleDesc tupdesc,
+						   StringInfo msg, int queueId);
+static HeapTuple
+form_result_tuple(worker_result resultState, TupleDesc tupdesc,
+				  StringInfo msg, int queueId);
+
+/*
+ * shm_beginscan
+ *
+ * Initializes the shared memory scan descriptor to retrieve tuples
+ * from worker backends. 
+ */
+ShmScanDesc
+shm_beginscan(int num_queues)
+{
+	ShmScanDesc		shmscan;
+
+	shmscan = palloc(sizeof(ShmScanDescData));
+
+	shmscan->num_shm_queues = num_queues;
+	shmscan->ss_cqueue = -1;
+	shmscan->shmscan_inited	= false;
+
+	return shmscan;
+}
+
+/*
+ * ExecInitWorkerResult
+ *
+ * Initializes the result state to retrieve tuples from worker backends. 
+ */
+worker_result
+ExecInitWorkerResult(TupleDesc tupdesc, int nWorkers)
+{
+	worker_result	workerResult;
+	int				i;
+	int	natts = tupdesc->natts;
+
+	workerResult = palloc0(sizeof(worker_result_state));
+	workerResult->receive_functions = palloc(sizeof(FmgrInfo) * natts);
+	workerResult->typioparams = palloc(sizeof(Oid) * natts);
+	workerResult->num_shm_queues = nWorkers;
+	workerResult->queue_detached = palloc0(sizeof(bool) * nWorkers);
+
+	for (i = 0;	i < natts; ++i)
+	{
+		Oid	receive_function_id;
+
+		getTypeBinaryInputInfo(tupdesc->attrs[i]->atttypid,
+							   &receive_function_id,
+							   &workerResult->typioparams[i]);
+		fmgr_info(receive_function_id, &workerResult->receive_functions[i]);
+	}
+
+	return workerResult;
+}
+
+
+/*
+ * shm_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 parallel sequential scan.
+ */
+HeapTuple
+shm_getnext(HeapScanDesc scanDesc, ShmScanDesc shmScan,
+			worker_result resultState, shm_mq_handle **responseq,
+			TupleDesc tupdesc, ScanDirection direction, bool *fromheap)
+{
+	shm_mq_result	res;
+	Size			nbytes;
+	void			*data;
+	StringInfoData	msg;
+	int				queueId = 0;
+
+	/*
+	 * calculate next starting queue used for fetching tuples
+	 */
+	if(!shmScan->shmscan_inited)
+	{
+		shmScan->shmscan_inited = true;
+		Assert(shmScan->num_shm_queues > 0);
+		queueId = 0;
+	}
+	else
+		queueId = shmScan->ss_cqueue;
+
+	/* Read and processes messages from the shared memory queues. */
+	for(;;)
+	{
+		if (!resultState->all_queues_detached)
+		{
+			if (queueId == shmScan->num_shm_queues)
+				queueId = 0;
+
+			/*
+			 * Don't fetch from detached queue.  This loop could continue
+			 * forever, if we reach a situation such that all queue's are
+			 * detached, however we won't reach here if that is the case.
+			 */
+			while (resultState->queue_detached[queueId])
+			{
+				++queueId;
+				if (queueId == shmScan->num_shm_queues)
+					queueId = 0;
+			}
+
+			for (;;)
+			{
+				/*
+				 * mark current queue used for fetching tuples, this is used
+				 * to fetch consecutive tuples from queue used in previous
+				 * fetch.
+				 */
+				shmScan->ss_cqueue = queueId;
+
+				/* Get next message. */
+				res = shm_mq_receive(responseq[queueId], &nbytes, &data, true);
+				if (res == SHM_MQ_DETACHED)
+				{
+					/*
+					 * mark the queue that got detached, so that we don't
+					 * try to fetch from it again.
+					 */
+					resultState->queue_detached[queueId] = true;
+					--resultState->num_shm_queues;
+					/*
+					 * if we have exhausted data from all worker queues, then don't
+					 * process data from queues.
+					 */
+					if (resultState->num_shm_queues <= 0)
+						resultState->all_queues_detached = true;
+					break;
+				}
+				else if (res == SHM_MQ_WOULD_BLOCK)
+					break;
+				else if (res == SHM_MQ_SUCCESS)
+				{
+					bool rettuple;
+					initStringInfo(&msg);
+					appendBinaryStringInfo(&msg, data, nbytes);
+					rettuple = HandleParallelTupleMessage(resultState, tupdesc, &msg, queueId);
+					pfree(msg.data);
+					if (rettuple)
+					{
+						*fromheap = false;
+						return resultState->tuple;
+					}
+				}
+			}
+		}
+
+		/*
+		 * if we have checked all the message queue's and didn't find
+		 * any message or we have already fetched all the data from queue's,
+		 * then it's time to fetch directly from heap.  Reset the current
+		 * queue as the first queue from which we need to receive tuples.
+		 */
+		if ((queueId == shmScan->num_shm_queues - 1 ||
+			 resultState->all_queues_detached) &&
+			 !resultState->all_heap_fetched)
+		{
+			HeapTuple	tuple;
+			shmScan->ss_cqueue = 0;
+			tuple = heap_getnext(scanDesc, direction);
+			if (tuple)
+			{
+				*fromheap = true;
+				return tuple;
+			}
+			else if (tuple == NULL && resultState->all_queues_detached)
+				break;
+			else
+				resultState->all_heap_fetched = true;
+		}
+		else if (resultState->all_queues_detached &&
+				 resultState->all_heap_fetched)
+			break;
+
+		/* check the data in next queue. */
+		++queueId;
+	}
+
+	return NULL;
+}
+
+/*
+ * HandleParallelTupleMessage
+ *
+ * Handle a single tuple related protocol message received from
+ * a single parallel worker.
+ */
+static bool
+HandleParallelTupleMessage(worker_result resultState, TupleDesc tupdesc,
+						   StringInfo msg, int queueId)
+{
+	char	msgtype;
+	bool	rettuple = false;
+
+	msgtype = pq_getmsgbyte(msg);
+
+	/* Dispatch on message type. */
+	switch (msgtype)
+	{
+		case 'D':
+			{
+				/* Handle DataRow message. */
+				resultState->tuple = form_result_tuple(resultState, tupdesc, msg, queueId);
+				rettuple = true;
+				break;
+			}
+		case 'C':
+			{
+				/*
+				 * Handle CommandComplete message. Ignore tags sent by
+				 * worker backend as we are anyway going to use tag of
+				 * master backend for sending the same to client.
+				 */
+				(void) pq_getmsgstring(msg);
+				break;
+			}
+		case 'G':
+		case 'H':
+		case 'W':
+			{
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("COPY protocol not allowed in worker")));
+			}
+		default:
+			elog(WARNING, "unknown message type: %c", msg->data[0]);
+			break;
+	}
+
+	return rettuple;
+}
+
+/*
+ * form_result_tuple
+ *
+ * Parse a DataRow message and form a result tuple.
+ */
+static HeapTuple
+form_result_tuple(worker_result resultState, TupleDesc tupdesc,
+				  StringInfo msg, int queueId)
+{
+	/* Handle DataRow message. */
+	int16	natts = pq_getmsgint(msg, 2);
+	int16	i;
+	Datum  *values = NULL;
+	bool   *isnull = NULL;
+	HeapTuple	tuple;
+	StringInfoData	buf;
+
+	if (natts != tupdesc->natts)
+		elog(ERROR, "malformed DataRow");
+	if (natts > 0)
+	{
+		values = palloc(natts * sizeof(Datum));
+		isnull = palloc(natts * sizeof(bool));
+	}
+	initStringInfo(&buf);
+
+	for (i = 0; i < natts; ++i)
+	{
+		int32	bytes = pq_getmsgint(msg, 4);
+
+		if (bytes < 0)
+		{
+			values[i] = ReceiveFunctionCall(&resultState->receive_functions[i],
+											NULL,
+											resultState->typioparams[i],
+											tupdesc->attrs[i]->atttypmod);
+			isnull[i] = true;
+		}
+		else
+		{
+			resetStringInfo(&buf);
+			appendBinaryStringInfo(&buf, pq_getmsgbytes(msg, bytes), bytes);
+			values[i] = ReceiveFunctionCall(&resultState->receive_functions[i],
+											&buf,
+											resultState->typioparams[i],
+											tupdesc->attrs[i]->atttypmod);
+			isnull[i] = false;
+		}
+	}
+
+	pq_getmsgend(msg);
+
+	tuple = heap_form_tuple(tupdesc, values, isnull);
+
+	/*
+	 * Release locally palloc'd space.  XXX would probably be good to pfree
+	 * values of pass-by-reference datums, as well.
+	 */
+	pfree(values);
+	pfree(isnull);
+
+	pfree(buf.data);
+
+	return tuple;
+}
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 7cfc9bb..8b85e97 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -721,6 +721,7 @@ ExplainPreScanNode(PlanState *planstate, Bitmapset **rels_used)
 	switch (nodeTag(plan))
 	{
 		case T_SeqScan:
+		case T_ParallelSeqScan:
 		case T_IndexScan:
 		case T_IndexOnlyScan:
 		case T_BitmapHeapScan:
@@ -917,6 +918,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
 		case T_SeqScan:
 			pname = sname = "Seq Scan";
 			break;
+		case T_ParallelSeqScan:
+			pname = sname = "Parallel Seq Scan";
+			break;
 		case T_IndexScan:
 			pname = sname = "Index Scan";
 			break;
@@ -1066,6 +1070,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	switch (nodeTag(plan))
 	{
 		case T_SeqScan:
+		case T_ParallelSeqScan:
 		case T_BitmapHeapScan:
 		case T_TidScan:
 		case T_SubqueryScan:
@@ -1207,6 +1212,24 @@ ExplainNode(PlanState *planstate, List *ancestors,
 	}
 
 	/*
+	 * Aggregate instrumentation information of all the backend
+	 * workers for parallel sequence scan.
+	 */
+	if (es->analyze && nodeTag(plan) == T_ParallelSeqScan)
+	{
+		int i;
+		Instrumentation *instrument_worker;
+		int nworkers = ((ParallelSeqScanState *)planstate)->pcxt->nworkers;
+		char *inst_info_workers = ((ParallelSeqScanState *)planstate)->inst_options_space;
+
+		for (i = 0; i < nworkers; i++)
+		{
+			instrument_worker = (Instrumentation *)(inst_info_workers + (i * sizeof(Instrumentation)));
+			InstrAggNode(planstate->instrument, instrument_worker);
+		}
+	}
+
+	/*
 	 * We have to forcibly clean up the instrumentation state because we
 	 * haven't done ExecutorEnd yet.  This is pretty grotty ...
 	 *
@@ -1332,6 +1355,14 @@ ExplainNode(PlanState *planstate, List *ancestors,
 				show_instrumentation_count("Rows Removed by Filter", 1,
 										   planstate, es);
 			break;
+		case T_ParallelSeqScan:
+			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",
+				((ParallelSeqScan *) plan)->num_workers, es);
+			break;
 		case T_FunctionScan:
 			if (es->verbose)
 			{
@@ -2224,6 +2255,7 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
 	switch (nodeTag(plan))
 	{
 		case T_SeqScan:
+		case T_ParallelSeqScan:
 		case T_IndexScan:
 		case T_IndexOnlyScan:
 		case T_BitmapHeapScan:
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index af707b0..9a8ca75 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -21,7 +21,7 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
        nodeLimit.o nodeLockRows.o \
        nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \
        nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
-       nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
+       nodeSeqscan.o nodeParallelSeqscan.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
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 9892499..f77a77f 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -100,6 +100,7 @@
 #include "executor/nodeMergejoin.h"
 #include "executor/nodeModifyTable.h"
 #include "executor/nodeNestloop.h"
+#include "executor/nodeParallelSeqscan.h"
 #include "executor/nodeRecursiveunion.h"
 #include "executor/nodeResult.h"
 #include "executor/nodeSeqscan.h"
@@ -190,6 +191,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 												   estate, eflags);
 			break;
 
+		case T_ParallelSeqScan:
+			result = (PlanState *) ExecInitParallelSeqScan((ParallelSeqScan *) node,
+														   estate, eflags);
+			break;
+
 		case T_IndexScan:
 			result = (PlanState *) ExecInitIndexScan((IndexScan *) node,
 													 estate, eflags);
@@ -406,6 +412,10 @@ ExecProcNode(PlanState *node)
 			result = ExecSeqScan((SeqScanState *) node);
 			break;
 
+		case T_ParallelSeqScanState:
+			result = ExecParallelSeqScan((ParallelSeqScanState *) node);
+			break;
+
 		case T_IndexScanState:
 			result = ExecIndexScan((IndexScanState *) node);
 			break;
@@ -644,6 +654,10 @@ ExecEndNode(PlanState *node)
 			ExecEndSeqScan((SeqScanState *) node);
 			break;
 
+		case T_ParallelSeqScanState:
+			ExecEndParallelSeqScan((ParallelSeqScanState *) node);
+			break;
+
 		case T_IndexScanState:
 			ExecEndIndexScan((IndexScanState *) node);
 			break;
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 3f0d809..229302d 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -191,8 +191,17 @@ ExecScan(ScanState *node,
 		 * check for non-nil qual here to avoid a function call to ExecQual()
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
+		 *
+		 * check for non-heap tuples (can get such tuples from shared memory
+		 * message queue's in case of parallel query), for such tuples no need
+		 * to perform qualification as for them the same is done by worker
+		 * backend.  This case will happen only for parallel query where we push
+		 * down the qualification.
+		 * XXX - We can do this optimization for projection as well, but for
+		 * now it is okay, as we don't allow parallel query if there are
+		 * expressions involved in target list.
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!slot->tts_fromheap || !qual || ExecQual(qual, econtext, false))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 753754d..4c5bd88 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -123,6 +123,7 @@ MakeTupleTableSlot(void)
 	slot->tts_values = NULL;
 	slot->tts_isnull = NULL;
 	slot->tts_mintuple = NULL;
+	slot->tts_fromheap	= true;
 
 	return slot;
 }
@@ -473,6 +474,8 @@ ExecClearTuple(TupleTableSlot *slot)	/* slot in which to store tuple */
 	slot->tts_isempty = true;
 	slot->tts_nvalid = 0;
 
+	slot->tts_fromheap = true;
+
 	return slot;
 }
 
diff --git a/src/backend/executor/instrument.c b/src/backend/executor/instrument.c
index f5351eb..b7898a5 100644
--- a/src/backend/executor/instrument.c
+++ b/src/backend/executor/instrument.c
@@ -21,6 +21,8 @@ BufferUsage pgBufferUsage;
 
 static void BufferUsageAccumDiff(BufferUsage *dst,
 					 const BufferUsage *add, const BufferUsage *sub);
+static void
+BufferUsageAdd(BufferUsage *dst, const BufferUsage *add);
 
 
 /* Allocate new instrumentation structure(s) */
@@ -127,6 +129,28 @@ 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
 BufferUsageAccumDiff(BufferUsage *dst,
@@ -148,3 +172,21 @@ BufferUsageAccumDiff(BufferUsage *dst,
 	INSTR_TIME_ACCUM_DIFF(dst->blk_write_time,
 						  add->blk_write_time, sub->blk_write_time);
 }
+
+/* dst += add */
+static 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);
+}
\ No newline at end of file
diff --git a/src/backend/executor/nodeParallelSeqscan.c b/src/backend/executor/nodeParallelSeqscan.c
new file mode 100644
index 0000000..397a47d
--- /dev/null
+++ b/src/backend/executor/nodeParallelSeqscan.c
@@ -0,0 +1,364 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeParallelSeqscan.c
+ *	  Support routines for parallel sequential scans of relations.
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/executor/nodeParallelSeqscan.c
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *		ExecParallelSeqScan				scans a relation.
+ *		ParallelSeqNext					retrieve next tuple from either heap or shared memory segment.
+ *		ExecInitParallelSeqScan			creates and initializes a parallel seqscan node.
+ *		ExecEndParallelSeqScan			releases any storage allocated.
+ */
+#include "postgres.h"
+
+#include "access/relscan.h"
+#include "access/shmmqam.h"
+#include "access/xact.h"
+#include "commands/dbcommands.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSeqscan.h"
+#include "executor/nodeParallelSeqscan.h"
+#include "postmaster/backendworker.h"
+#include "utils/rel.h"
+
+
+
+/* ----------------------------------------------------------------
+ *						Scan Support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *		ParallelSeqNext
+ *
+ *		This is a workhorse for ExecParallelSeqScan
+ * ----------------------------------------------------------------
+ */
+static TupleTableSlot *
+ParallelSeqNext(ParallelSeqScanState *node)
+{
+	HeapTuple	tuple;
+	HeapScanDesc scandesc;
+	EState	   *estate;
+	ScanDirection direction;
+	TupleTableSlot *slot;
+	bool			fromheap = true;
+
+	/*
+	 * 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;
+
+	if(((ParallelSeqScan*)node->ss.ps.plan)->shm_toc_key)
+	{
+		/*
+		 * get the next tuple from the table
+		 */
+		tuple = heap_getnext(scandesc, direction);
+	}
+	else
+	{
+		/*
+		 * get the next tuple from the table based on result tuple descriptor.
+		 */
+		tuple = shm_getnext(scandesc, node->pss_currentShmScanDesc,
+							node->pss_workerResult,
+							node->responseq,
+							node->ss.ps.ps_ResultTupleSlot->tts_tupleDescriptor,
+							direction, &fromheap);
+	}
+
+	slot->tts_fromheap = fromheap;
+
+	/*
+	 * 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 '!fromheap'
+	 * because tuples returned by shm_getnext() are either pointers that are
+	 * created with palloc() or are pointers onto disk pages and so it should
+	 * be pfree()'d accordingly.  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 */
+					   fromheap ? scandesc->rs_cbuf : InvalidBuffer, /* buffer associated with this
+																	  * tuple */
+					   !fromheap);	/* pfree this pointer if not from heap */
+	else
+		ExecClearTuple(slot);
+
+	return slot;
+}
+
+/*
+ * ParallelSeqRecheck -- access method routine to recheck a tuple in EvalPlanQual
+ */
+static bool
+ParallelSeqRecheck(SeqScanState *node, TupleTableSlot *slot)
+{
+	/*
+	 * Note that unlike IndexScan, ParallelSeqScan never use keys in
+	 * shm_beginscan/heap_beginscan (and this is very bad) - so, here
+	 * we do not check are keys ok or not.
+	 */
+	return true;
+}
+
+/* ----------------------------------------------------------------
+ *		InitParallelScanRelation
+ *
+ *		Set up to access the scan relation.
+ * ----------------------------------------------------------------
+ */
+static void
+InitParallelScanRelation(ParallelSeqScanState *node, EState *estate, int eflags)
+{
+	Relation	currentRelation;
+	HeapScanDesc currentScanDesc;
+	ParallelHeapScanDesc pscan;
+
+	/*
+	 * 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);
+
+	/*
+	 * For Explain statement, we don't want to initialize workers as
+	 * those are maily needed to execute the plan, however scan descriptor
+	 * still needs to be initialized for the purpose of InitNode functionality
+	 * (as EnNode functionality assumes that scan descriptor and scan relation
+	 * must be initialized, probably we can change that but that will make
+	 * the code EndParallelSeqScan look different than other node's end
+	 * functionality.
+	 *
+	 * XXX - If we want executorstart to initilize workers as well, then we
+	 * need to have a provision for waiting till all the workers get started
+	 * otherwise while doing endscan, it will try to wait for termination of
+	 * workers which are not even started (and will neither get started).
+	 */
+	if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
+	{
+		/* initialize a heapscan */
+		currentScanDesc = heap_beginscan(currentRelation,
+										 estate->es_snapshot,
+										 0,
+										 NULL);
+	}
+	else
+	{
+		/*
+		 * Parallel scan descriptor is initialized and stored in dynamic shared
+		 * memory segment by master backend and parallel workers retrieve it
+		 * from shared memory.
+		 */
+		if (((ParallelSeqScan *) node->ss.ps.plan)->shm_toc_key != 0)
+		{
+			Assert(!pscan);
+
+			pscan = shm_toc_lookup(((ParallelSeqScan *) node->ss.ps.plan)->toc,
+								   ((ParallelSeqScan *) node->ss.ps.plan)->shm_toc_key);
+		}
+		else
+		{
+			/* Initialize the workers required to perform parallel scan. */
+			InitializeParallelWorkers(((SeqScan *) node->ss.ps.plan)->scanrelid,
+									  node->ss.ps.plan->targetlist,
+									  node->ss.ps.plan->qual,
+									  estate,
+									  currentRelation,
+									  &node->inst_options_space,
+									  &node->responseq,
+									  &node->pcxt,
+									  &pscan,
+									  ((ParallelSeqScan *)(node->ss.ps.plan))->num_workers);
+		}
+
+		currentScanDesc = heap_beginscan_parallel(currentRelation, pscan);
+	}
+
+	node->ss.ss_currentRelation = currentRelation;
+	node->ss.ss_currentScanDesc = currentScanDesc;
+
+	/* and report the scan tuple slot's rowtype */
+	ExecAssignScanType(&node->ss, RelationGetDescr(currentRelation));
+}
+
+/* ----------------------------------------------------------------
+ *		InitShmScan
+ *
+ *		Set up to access the scan for shared memory segment.
+ * ----------------------------------------------------------------
+ */
+static void
+InitShmScan(ParallelSeqScanState *node)
+{
+	ShmScanDesc			 currentShmScanDesc;
+	worker_result		 workerResult;
+
+	/*
+	 * Shared memory scan needs to be initialized only for
+	 * master backend as worker backend scans only heap.
+	 */
+	if (((ParallelSeqScan *) node->ss.ps.plan)->shm_toc_key == 0)
+	{
+		/*
+		 * Use result tuple descriptor to fetch data from shared memory queues
+		 * as the worker backend's would have put the data after projection.
+		 * Number of queues must be equal to number of worker backend's.
+		 */
+		currentShmScanDesc = shm_beginscan(node->pcxt->nworkers);
+		workerResult = ExecInitWorkerResult(node->ss.ps.ps_ResultTupleSlot->tts_tupleDescriptor,
+											node->pcxt->nworkers);
+
+		node->pss_currentShmScanDesc = currentShmScanDesc;
+		node->pss_workerResult	= workerResult;
+	}
+}
+
+/* ----------------------------------------------------------------
+ *		ExecInitParallelSeqScan
+ * ----------------------------------------------------------------
+ */
+ParallelSeqScanState *
+ExecInitParallelSeqScan(ParallelSeqScan *node, EState *estate, int eflags)
+{
+	ParallelSeqScanState *parallelscanstate;
+
+	/*
+	 * 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
+	 */
+	parallelscanstate = makeNode(ParallelSeqScanState);
+	parallelscanstate->ss.ps.plan = (Plan *) node;
+	parallelscanstate->ss.ps.state = estate;
+
+	/*
+	 * Miscellaneous initialization
+	 *
+	 * create expression context for node
+	 */
+	ExecAssignExprContext(estate, &parallelscanstate->ss.ps);
+
+	/*
+	 * initialize child expressions
+	 */
+	parallelscanstate->ss.ps.targetlist = (List *)
+		ExecInitExpr((Expr *) node->scan.plan.targetlist,
+					 (PlanState *) parallelscanstate);
+	parallelscanstate->ss.ps.qual = (List *)
+		ExecInitExpr((Expr *) node->scan.plan.qual,
+					 (PlanState *) parallelscanstate);
+
+	/*
+	 * tuple table initialization
+	 */
+	ExecInitResultTupleSlot(estate, &parallelscanstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &parallelscanstate->ss);
+
+	InitParallelScanRelation(parallelscanstate, estate, eflags);
+
+	parallelscanstate->ss.ps.ps_TupFromTlist = false;
+
+	/*
+	 * Initialize result tuple type and projection info.
+	 */
+	ExecAssignResultTypeFromTL(&parallelscanstate->ss.ps);
+	ExecAssignScanProjectionInfo(&parallelscanstate->ss);
+
+	/*
+	 * For Explain, we don't initialize the parallel workers, so
+	 * accordingly don't need to initialize the shared memory scan.
+	 */
+	if (!(eflags & EXEC_FLAG_EXPLAIN_ONLY))
+		InitShmScan(parallelscanstate);
+
+	return parallelscanstate;
+}
+
+/* ----------------------------------------------------------------
+ *		ExecParallelSeqScan(node)
+ *
+ *		Scans the relation via multiple workers and returns
+ *		the next qualifying tuple.
+ *		We call the ExecScan() routine and pass it the appropriate
+ *		access method functions.
+ * ----------------------------------------------------------------
+ */
+TupleTableSlot *
+ExecParallelSeqScan(ParallelSeqScanState *node)
+{
+	return ExecScan((ScanState *) &node->ss,
+					(ExecScanAccessMtd) ParallelSeqNext,
+					(ExecScanRecheckMtd) ParallelSeqRecheck);
+}
+
+/* ----------------------------------------------------------------
+ *		ExecEndParallelSeqScan
+ *
+ *		frees any storage allocated through C routines.
+ * ----------------------------------------------------------------
+ */
+void
+ExecEndParallelSeqScan(ParallelSeqScanState *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
+	 */
+	heap_endscan(scanDesc);
+
+	/*
+	 * close the heap relation.
+	 */
+	ExecCloseScanRelation(relation);
+
+	if (node->pcxt)
+	{
+		/* destroy parallel context. */
+		DestroyParallelContext(node->pcxt);
+
+		ExitParallelMode();
+	}
+}
diff --git a/src/backend/libpq/pqmq.c b/src/backend/libpq/pqmq.c
index f12f2d5..cfab8b5 100644
--- a/src/backend/libpq/pqmq.c
+++ b/src/backend/libpq/pqmq.c
@@ -26,6 +26,8 @@ static bool pq_mq_busy = false;
 static pid_t pq_mq_parallel_master_pid = 0;
 static pid_t pq_mq_parallel_master_backend_id = InvalidBackendId;
 
+static shm_mq_handle *pq_mq_tuple_handle = NULL;
+
 static void mq_comm_reset(void);
 static int	mq_flush(void);
 static int	mq_flush_if_writable(void);
@@ -61,6 +63,26 @@ pq_redirect_to_shm_mq(shm_mq *mq, shm_mq_handle *mqh)
 }
 
 /*
+ * Arrange to send some frontend/backend protocol messages to a shared-memory
+ * tuple message queue.
+ */
+void
+pq_redirect_to_tuple_shm_mq(shm_mq_handle *mqh)
+{
+	pq_mq_tuple_handle = mqh;
+}
+
+/*
+ * Check if tuples can be sent through tuple shared-memory
+ * message queue.
+ */
+bool
+is_tuple_shm_mq_enabled(void)
+{
+	return pq_mq_tuple_handle ? true : false;
+}
+
+/*
  * Arrange to SendProcSignal() to the parallel master each time we transmit
  * message data via the shm_mq.
  */
@@ -161,6 +183,42 @@ mq_putmessage(char msgtype, const char *s, size_t len)
 	return 0;
 }
 
+/*
+ * Transmit a libpq protocol message to the shared memory message queue
+ * via pq_mq_tuple_handle.  We don't include a length word, because the
+ * receiver will know the length of the message from shm_mq_receive().
+ */
+int
+mq_putmessage_direct(char msgtype, const char *s, size_t len)
+{
+	shm_mq_iovec	iov[2];
+	shm_mq_result	result;
+
+	iov[0].data = &msgtype;
+	iov[0].len = 1;
+	iov[1].data = s;
+	iov[1].len = len;
+
+	Assert(pq_mq_tuple_handle != NULL);
+
+	for (;;)
+	{
+		result = shm_mq_sendv(pq_mq_tuple_handle, iov, 2, true);
+
+		if (result != SHM_MQ_WOULD_BLOCK)
+			break;
+
+		WaitLatch(&MyProc->procLatch, WL_LATCH_SET, 0);
+		CHECK_FOR_INTERRUPTS();
+		ResetLatch(&MyProc->procLatch);
+	}
+
+	Assert(result == SHM_MQ_SUCCESS || result == SHM_MQ_DETACHED);
+	if (result != SHM_MQ_SUCCESS)
+		return EOF;
+	return 0;
+}
+
 static void
 mq_putmessage_noblock(char msgtype, const char *s, size_t len)
 {
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index f1a24f5..5846f22 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -352,6 +352,27 @@ _copySeqScan(const SeqScan *from)
 }
 
 /*
+ * _copyParallelSeqScan
+ */
+static ParallelSeqScan *
+_copyParallelSeqScan(const ParallelSeqScan *from)
+{
+	ParallelSeqScan    *newnode = makeNode(ParallelSeqScan);
+
+	/*
+	 * copy node superclass fields
+	 */
+	CopyScanFields((const Scan *) from, (Scan *) newnode);
+
+	/*
+	 * copy remainder of node
+	 */
+	COPY_SCALAR_FIELD(num_workers);
+
+	return newnode;
+}
+
+/*
  * _copyIndexScan
  */
 static IndexScan *
@@ -4039,6 +4060,9 @@ copyObject(const void *from)
 		case T_SeqScan:
 			retval = _copySeqScan(from);
 			break;
+		case T_ParallelSeqScan:
+			retval = _copyParallelSeqScan(from);
+			break;
 		case T_IndexScan:
 			retval = _copyIndexScan(from);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index dd1278b..35c2e1e 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -437,6 +437,16 @@ _outSeqScan(StringInfo str, const SeqScan *node)
 }
 
 static void
+_outParallelSeqScan(StringInfo str, const ParallelSeqScan *node)
+{
+	WRITE_NODE_TYPE("PARALLELSEQSCAN");
+
+	_outScanInfo(str, (const Scan *) node);
+
+	WRITE_UINT_FIELD(num_workers);
+}
+
+static void
 _outIndexScan(StringInfo str, const IndexScan *node)
 {
 	WRITE_NODE_TYPE("INDEXSCAN");
@@ -2851,6 +2861,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_SeqScan:
 				_outSeqScan(str, obj);
 				break;
+			case T_ParallelSeqScan:
+				_outParallelSeqScan(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 2f2f5ed..7ecaa7f 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, see above */
+	Oid			ptype;			/* parameter's datatype, or 0 */
+} SerializedParamExternData;
 
 /*
  * Copy a ParamListInfo structure.
@@ -74,3 +87,187 @@ 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;
+
+	/* sizeof(ParamListInfoData) includes the first array element */
+	size = sizeof(ParamListInfoData) +
+		(num_params - 1) * 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;
+}
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 58d78e6..528727c 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -410,6 +410,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 020558b..4abfd25 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -11,6 +11,9 @@
  *	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
+ *  parallel_startup_cost  Cost of starting up parallel workers
  *
  * 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 +104,16 @@ 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;
+double		parallel_startup_cost = DEFAULT_PARALLEL_STARTUP_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;
@@ -219,6 +227,73 @@ cost_seqscan(Path *path, PlannerInfo *root,
 }
 
 /*
+ * cost_parallelseqscan
+ *	  Determines and returns the cost of scanning a relation parallely.
+ *
+ * 'baserel' is the relation to be scanned
+ * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
+ */
+void
+cost_parallelseqscan(ParallelSeqPath *path, PlannerInfo *root,
+			 RelOptInfo *baserel, ParamPathInfo *param_info, int nWorkers)
+{
+	Cost		startup_cost = 0;
+	Cost		run_cost = 0;
+	double		spc_seq_page_cost;
+	QualCost	qpqual_cost;
+	Cost		cpu_per_tuple;
+
+	/* 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;
+
+	if (!enable_seqscan)
+		startup_cost += disable_cost;
+
+	/* fetch estimated page cost for tablespace containing table */
+	get_tablespace_page_costs(baserel->reltablespace,
+							  NULL,
+							  &spc_seq_page_cost);
+
+	/*
+	 * disk costs
+	 */
+	run_cost += spc_seq_page_cost * baserel->pages;
+
+	/* CPU costs */
+	get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
+
+	startup_cost += qpqual_cost.startup;
+	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
+	run_cost += cpu_per_tuple * baserel->tuples;
+
+	/*
+	 * 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
+	 * parallel sequiantial scan.
+	 */
+	run_cost = run_cost / (nWorkers + 1);
+
+	/* Parallel setup and communication cost. */
+	startup_cost += parallel_setup_cost;
+	startup_cost += parallel_startup_cost * nWorkers;
+	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..fda6f40
--- /dev/null
+++ b/src/backend/optimizer/path/parallelpath.c
@@ -0,0 +1,148 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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 "nodes/relation.h"
+#include "optimizer/cost.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/paths.h"
+#include "optimizer/restrictinfo.h"
+#include "optimizer/clauses.h"
+#include "parser/parsetree.h"
+#include "utils/rel.h"
+
+
+/*
+ *	IsTargetListContainNonVars -
+ *		Check if target list contain non-var entries.
+ */
+static bool
+IsTargetListContainNonVars(List *targetlist)
+{
+	ListCell   *l;
+
+	foreach(l, targetlist)
+	{
+		TargetEntry *te = (TargetEntry *) lfirst(l);
+
+		if (!IsA(te, TargetEntry))
+			continue;			/* probably should never happen */
+		if (!IsA(te->expr, Var))
+			return true;
+	}
+	return false;
+}
+
+/*
+ *	check_simple_qual -
+ *		Check if qual is made only of simple things we can
+ *		hand out directly to backend worker for execution.
+ *
+ *		XXX - Currently we don't allow to push an expression
+ *		if it contains volatile function, however eventually we
+ *		need a mechanism (proisparallel) with which we can distinquish
+ *		the functions that can be pushed for execution by parallel
+ *		worker.
+ */
+static bool
+check_simple_qual(Node *node)
+{
+	if (node == NULL)
+		return TRUE;
+
+	if (contain_volatile_functions(node))
+		return FALSE;
+
+	return TRUE;
+}
+
+/*
+ * 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;
+	Oid			reloid;
+	Relation	relation;
+
+	/*
+	 * parallel scan is possible only if user has set
+	 * parallel_seqscan_degree to value greater than 0.
+	 */
+	if (parallel_seqscan_degree <= 0)
+		return;
+
+	/*
+	 * parallel scan is not supported for joins.
+	 */
+	if (root->simple_rel_array_size > 2)
+		return;
+
+	/* parallel scan is supportted only for Select statements. */
+	if (root->parse->commandType != CMD_SELECT)
+		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);
+
+	/*
+	 * parallel scan is not supported for non-var target list.
+	 *
+	 * XXX - This is to keep the implementation simple, we can do this
+	 * in future.  Here we are checking by passing root->parse->targetList
+	 * instead of rel->reltargetlist because rel->targetlist always contains
+	 * Vars (refer build_base_rel_tlists).
+	 */
+	if (IsTargetListContainNonVars(root->parse->targetList))
+	   return;
+
+	/*
+	 * parallel scan is not supported for mutable functions
+	 */
+	if (!check_simple_qual((Node*) extract_actual_clauses(rel->baserestrictinfo, false)))
+		return;
+
+	/*
+	 * There should be atleast one page to scan for each worker.
+	 */
+	if (parallel_seqscan_degree <= rel->pages)
+		num_parallel_workers = parallel_seqscan_degree;
+	else
+		num_parallel_workers = rel->pages;
+
+	add_path(rel, (Path *) create_parallelseqscan_path(root, rel,
+													   num_parallel_workers));
+}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 655be81..a8a626e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -58,6 +58,9 @@ static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path
 static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path);
 static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path,
 					List *tlist, List *scan_clauses);
+static Scan *create_parallelseqscan_plan(PlannerInfo *root,
+										 ParallelSeqPath *best_path,
+										 List *tlist, List *scan_clauses);
 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,
@@ -100,6 +103,9 @@ static List *order_qual_clauses(PlannerInfo *root, List *clauses);
 static void copy_path_costsize(Plan *dest, Path *src);
 static void copy_plan_costsize(Plan *dest, Plan *src);
 static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
+static ParallelSeqScan *make_parallelseqscan(List *qptlist, List *qpqual,
+											 Index scanrelid, int nworkers,
+											 shm_toc *toc, uint64 shm_toc_key);
 static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
 			   Oid indexid, List *indexqual, List *indexqualorig,
 			   List *indexorderby, List *indexorderbyorig,
@@ -228,6 +234,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
 	switch (best_path->pathtype)
 	{
 		case T_SeqScan:
+		case T_ParallelSeqScan:
 		case T_IndexScan:
 		case T_IndexOnlyScan:
 		case T_BitmapHeapScan:
@@ -343,6 +350,13 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
 												scan_clauses);
 			break;
 
+		case T_ParallelSeqScan:
+			plan = (Plan *) create_parallelseqscan_plan(root,
+														(ParallelSeqPath *) best_path,
+														tlist,
+														scan_clauses);
+			break;
+
 		case T_IndexScan:
 			plan = (Plan *) create_indexscan_plan(root,
 												  (IndexPath *) best_path,
@@ -546,6 +560,7 @@ disuse_physical_tlist(PlannerInfo *root, Plan *plan, Path *path)
 	switch (path->pathtype)
 	{
 		case T_SeqScan:
+		case T_ParallelSeqScan:
 		case T_IndexScan:
 		case T_IndexOnlyScan:
 		case T_BitmapHeapScan:
@@ -1133,6 +1148,70 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path,
 }
 
 /*
+ * create_worker_seqscan_plan
+ *
+ * Returns a parallel seqscan plan for the base relation scanned
+ * by worker with restriction clauses 'qual' and targetlist 'tlist'.
+ */
+Scan *
+create_worker_seqscan_plan(ParallelScanStmt *parallelscan)
+{
+	Scan	   *scan_plan;
+
+	scan_plan = (Scan*) make_parallelseqscan(parallelscan->targetList,
+											 parallelscan->qual,
+											 parallelscan->scanrelId,
+											 0,
+											 parallelscan->toc,
+											 parallelscan->shm_toc_scan_key);
+
+	return scan_plan;
+}
+
+/*
+ * create_parallelseqscan_plan
+ *
+ * Returns a parallel seqscan plan for the base relation scanned by
+ * 'best_path' with restriction clauses 'scan_clauses' and targetlist
+ * 'tlist'.
+ */
+static Scan *
+create_parallelseqscan_plan(PlannerInfo *root, ParallelSeqPath *best_path,
+							List *tlist, List *scan_clauses)
+{
+	Scan    *scan_plan;
+	Index		scan_relid = best_path->path.parent->relid;
+
+	/* it should be a base rel... */
+	Assert(scan_relid > 0);
+	Assert(best_path->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->path.param_info)
+	{
+		scan_clauses = (List *)
+			replace_nestloop_params(root, (Node *) scan_clauses);
+	}
+
+	scan_plan = (Scan *) make_parallelseqscan(tlist,
+											  scan_clauses,
+											  scan_relid,
+											  best_path->num_workers,
+											  NULL,
+											  0);
+
+	copy_path_costsize(&scan_plan->plan, &best_path->path);
+
+	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'.
@@ -3318,6 +3397,30 @@ make_seqscan(List *qptlist,
 	return node;
 }
 
+static ParallelSeqScan *
+make_parallelseqscan(List *qptlist,
+			   List *qpqual,
+			   Index scanrelid,
+			   int nworkers,
+			   shm_toc *toc,
+			   uint64 shm_toc_key)
+{
+	ParallelSeqScan *node = makeNode(ParallelSeqScan);
+	Plan	   *plan = &node->scan.plan;
+
+	/* cost should be inserted by caller */
+	plan->targetlist = qptlist;
+	plan->qual = qpqual;
+	plan->lefttree = NULL;
+	plan->righttree = NULL;
+	node->scan.scanrelid = scanrelid;
+	node->num_workers = nworkers;
+	node->toc = toc;
+	node->shm_toc_key = shm_toc_key;
+
+	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 9cbbcfb..f9be27f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -260,6 +260,74 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	return result;
 }
 
+/*
+ * create_worker_seqscan_plannedstmt
+ *
+ *	Returns a planned statement to be used by worker for execution.
+ *  Instead of master backend forming and passing the planned statement
+ *  to each worker, it just passes required information and PlannedStmt
+ *	is then constructed by worker.  The reason for doing so is that
+ *  master backend plan doesn't contain the subplans for each worker.
+ *  In future, if there is a need for doing so, we might want to
+ *  change the implementation master backend will pass the planned
+ *  statement directly.
+ */
+PlannedStmt	*
+create_worker_seqscan_plannedstmt(ParallelScanStmt *parallelscan)
+{
+	Plan    *scan_plan;
+	PlannedStmt	*result;
+	ListCell   *tlist;
+	Oid			reloid;
+
+	/* get the relid to save the same as part of planned statement. */
+	reloid = getrelid(parallelscan->scanrelId, parallelscan->rangetableList);
+
+	/* Fill in opfuncid values if missing */
+	fix_opfuncids((Node*) parallelscan->qual);
+	fix_opfuncids((Node*) parallelscan->targetList);
+
+	/*
+	 * Avoid removing junk entries in worker as those are
+	 * required by upper nodes in master backend.
+	 */
+	foreach(tlist, parallelscan->targetList)
+	{
+		TargetEntry *tle = (TargetEntry *) lfirst(tlist);
+
+		tle->resjunk = false;
+	}
+
+	scan_plan = (Plan*) create_worker_seqscan_plan(parallelscan);
+
+	/* 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*) scan_plan;
+	result->rtable = parallelscan->rangetableList;
+	result->resultRelations = NIL;
+	result->utilityStmt = NULL;
+	result->subplans = NIL;
+	result->rewindPlanIDs = NULL;
+	result->rowMarks = NIL;
+	result->relationOids = lappend_oid(result->relationOids, reloid);
+	result->invalItems = NIL;
+	result->nParamExec = 0;
+	/*
+	 * Don't bother to get hasRowSecurity passed from master
+	 * backend as this is used only for invalidation and in
+	 * worker backend plans are not saved, so can't be invalidated.
+	 */
+	result->hasRowSecurity = false;
+
+	return result;
+}
 
 /*--------------------
  * subquery_planner
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 7703946..3a44aef 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -436,6 +436,7 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset)
 	switch (nodeTag(plan))
 	{
 		case T_SeqScan:
+		case T_ParallelSeqScan:
 			{
 				SeqScan    *splan = (SeqScan *) plan;
 
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 78fb6b1..c35f934 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -2163,6 +2163,7 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
 			break;
 
 		case T_SeqScan:
+		case T_ParallelSeqScan:
 			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 1395a21..ea3b865 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -706,6 +706,30 @@ create_seqscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
 }
 
 /*
+ * create_parallelseqscan_path
+ *
+ *	  Creates a path corresponding to a parallel sequential scan, returning the
+ *	  pathnode.
+ */
+ParallelSeqPath *
+create_parallelseqscan_path(PlannerInfo *root, RelOptInfo *rel, int nWorkers)
+{
+	ParallelSeqPath	   *pathnode = makeNode(ParallelSeqPath);
+
+	pathnode->path.pathtype = T_ParallelSeqScan;
+	pathnode->path.parent = rel;
+	pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
+													 false);
+	pathnode->path.pathkeys = NIL;	/* seqscan has unordered result */
+
+	pathnode->num_workers = nWorkers;
+
+	cost_parallelseqscan(pathnode, root, rel, pathnode->path.param_info, nWorkers);
+
+	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..f056bd5 100644
--- a/src/backend/postmaster/Makefile
+++ b/src/backend/postmaster/Makefile
@@ -12,7 +12,8 @@ subdir = src/backend/postmaster
 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
+OBJS = autovacuum.o backendworker.o bgworker.o bgwriter.o checkpointer.o \
+	fork_process.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/backendworker.c b/src/backend/postmaster/backendworker.c
new file mode 100644
index 0000000..890a0d5
--- /dev/null
+++ b/src/backend/postmaster/backendworker.c
@@ -0,0 +1,562 @@
+/*-------------------------------------------------------------------------
+ *
+ * backendworker.c
+ *	  Support routines for setting up backend workers.
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/postmaster/backendworker.c
+ *
+ *-------------------------------------------------------------------------
+ */
+/*
+ * INTERFACE ROUTINES
+ *		InitializeParallelWorkers				Setup dynamic shared memory and parallel backend workers.
+ */
+#include "postgres.h"
+
+#include "access/xact.h"
+#include "access/parallel.h"
+#include "commands/dbcommands.h"
+#include "commands/async.h"
+#include "executor/nodeParallelSeqscan.h"
+#include "miscadmin.h"
+#include "nodes/parsenodes.h"
+#include "postmaster/backendworker.h"
+#include "storage/ipc.h"
+#include "storage/procsignal.h"
+#include "storage/procarray.h"
+#include "storage/shm_toc.h"
+#include "storage/spin.h"
+#include "tcop/tcopprot.h"
+#include "utils/builtins.h"
+#include "utils/memutils.h"
+#include "utils/resowner.h"
+
+
+#define PARALLEL_TUPLE_QUEUE_SIZE					65536
+
+/* Table-of-contents constants for our dynamic shared memory segment. */
+#define PARALLEL_KEY_SCANRELID		0
+#define PARALLEL_KEY_TARGETLIST		1
+#define PARALLEL_KEY_QUAL			2
+#define	PARALLEL_KEY_RANGETBL		3
+#define	PARALLEL_KEY_PARAMS			4
+#define PARALLEL_KEY_INST_OPTIONS	5
+#define PARALLEL_KEY_INST_INFO		6
+#define PARALLEL_KEY_TUPLE_QUEUE	7
+#define PARALLEL_KEY_SCAN			8
+#define PARALLEL_KEY_OPERATION		9
+
+static void ParallelQueryMain(dsm_segment *seg, shm_toc *toc);
+static void RestoreAndExecuteParallelScan(dsm_segment *seg, shm_toc *toc);
+static void
+EstimateParallelQueryElemsSpace(ParallelContext *pcxt,
+								char *targetlist_str, char *qual_str,
+								Size *targetlist_len, Size *qual_len);
+static void
+StoreParallelQueryElems(ParallelContext *pcxt,
+						char *targetlist_str, char *qual_str,
+						Size targetlist_len, Size qual_len);
+static void
+EstimateParallelSupportInfoSpace(ParallelContext *pcxt, ParamListInfo params,
+								 int instOptions, Size *params_len);
+static void
+StoreParallelSupportInfo(ParallelContext *pcxt, ParamListInfo params,
+						 int instOptions, int params_len,
+						 char **inst_options_space);
+static void
+EstimateParallelSeqScanSpace(ParallelContext *pcxt, EState *estate,
+							 Index scanrelId, char *rangetbl_str,
+							 Size *rangetbl_len, Size *pscan_size);
+static void
+StoreParallelSeqScan(ParallelContext *pcxt, EState *estate, Relation rel,
+					 Index scanrelId, char *rangetbl_str,
+					 ParallelHeapScanDesc *pscan,
+					 Size rangetbl_len, Size pscan_size);
+static void EstimateResponseQueueSpace(ParallelContext *pcxt);
+static void
+StoreResponseQueueAndStartWorkers(ParallelContext *pcxt,
+								  shm_mq_handle ***responseqp);
+static void
+GetParallelQueryElems(shm_toc *toc, List **targetList, List **qual);
+static void
+GetParallelSupportInfo(shm_toc *toc, ParamListInfo *params,
+					   int *inst_options, char **instrument);
+static void
+GetParallelSeqScanInfo(shm_toc *toc, Index *scanrelId,
+					   List **rangeTableList);
+static void
+SetupResponseQueue(dsm_segment *seg, shm_toc *toc, shm_mq **mq);
+
+/*
+ * EstimateParallelQueryElemsSpace
+ *
+ * Estimate the amount of space required to record information of
+ * query elements that need to be copied to parallel workers.
+ */
+void
+EstimateParallelQueryElemsSpace(ParallelContext *pcxt,
+								char *targetlist_str, char *qual_str,
+								Size *targetlist_len, Size *qual_len)
+{
+	*targetlist_len = strlen(targetlist_str) + 1;
+	shm_toc_estimate_chunk(&pcxt->estimator, *targetlist_len);
+
+	*qual_len = strlen(qual_str) + 1;
+	shm_toc_estimate_chunk(&pcxt->estimator, *qual_len);
+
+	/* keys for parallel query elements. */
+	shm_toc_estimate_keys(&pcxt->estimator, 2);
+}
+
+/*
+ * StoreParallelQueryElems
+ * 
+ * Sets up target list and qualification required for parallel
+ * execution.
+ */
+void
+StoreParallelQueryElems(ParallelContext *pcxt,
+						char *targetlist_str, char *qual_str,
+						Size targetlist_len, Size qual_len)
+{
+	char	   *targetlistdata;
+	char	   *qualdata;
+
+	/* Store target list in dynamic shared memory. */
+	targetlistdata = shm_toc_allocate(pcxt->toc, targetlist_len);
+	memcpy(targetlistdata, targetlist_str, targetlist_len);
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_TARGETLIST, targetlistdata);
+
+	/* Store qual list in dynamic shared memory. */
+	qualdata = shm_toc_allocate(pcxt->toc, qual_len);
+	memcpy(qualdata, qual_str, qual_len);
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_QUAL, qualdata);
+}
+
+/*
+ * EstimateParallelSupportInfoSpace
+ *
+ * Estimate the amount of space required to record information of
+ * bind parameters and instrumentation information that need to be
+ * retrieved from parallel workers.
+ */
+void
+EstimateParallelSupportInfoSpace(ParallelContext *pcxt, ParamListInfo params,
+								 int instOptions, Size *params_len)
+{
+	*params_len = EstimateBoundParametersSpace(params);
+	shm_toc_estimate_chunk(&pcxt->estimator, *params_len);
+
+	/* 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, 2);
+	}
+
+	/* keys for parallel support information. */
+	shm_toc_estimate_keys(&pcxt->estimator, 2);
+}
+
+/*
+ * StoreParallelSupportInfo
+ * 
+ * Sets up the bind parameters and instrumentation information
+ * required for parallel execution.
+ */
+void
+StoreParallelSupportInfo(ParallelContext *pcxt, ParamListInfo params,
+						 int instOptions, int params_len,
+						 char **inst_options_space)
+{
+	char	*paramsdata;
+	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_len);
+	SerializeBoundParams(params, params_len, paramsdata);
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_PARAMS, paramsdata);
+
+	/* 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);
+	}
+}
+
+/*
+ * EstimateParallelSeqScanSpace
+ *
+ * Estimate the amount of space required to record information of
+ * scanrelId, rangetable and parallel heap scan descriptor that need
+ * to be copied to parallel workers.
+ */
+void
+EstimateParallelSeqScanSpace(ParallelContext *pcxt, EState *estate,
+							 Index scanrelId, char *rangetbl_str,
+							 Size *rangetbl_len, Size *pscan_size)
+{
+	/* Estimate space for parallel seq. scan specific contents. */
+	shm_toc_estimate_chunk(&pcxt->estimator, sizeof(NodeTag));
+	shm_toc_estimate_chunk(&pcxt->estimator, sizeof(scanrelId));
+
+	*rangetbl_len = strlen(rangetbl_str) + 1;
+	shm_toc_estimate_chunk(&pcxt->estimator, *rangetbl_len);
+
+	*pscan_size = heap_parallelscan_estimate(estate->es_snapshot);
+	shm_toc_estimate_chunk(&pcxt->estimator, *pscan_size);
+
+	/* keys for parallel support information. */
+	shm_toc_estimate_keys(&pcxt->estimator, 4);
+}
+
+/*
+ * StoreParallelSeqScan
+ * 
+ * Sets up the scanrelid, rangetable entries and block range
+ * for parallel sequence scan.
+ */
+void
+StoreParallelSeqScan(ParallelContext *pcxt, EState *estate, Relation rel,
+					 Index scanrelId, char *rangetbl_str,
+					 ParallelHeapScanDesc *pscan,
+					 Size rangetbl_len, Size pscan_size)
+{
+	NodeTag		*nodetype;
+	Oid			*scanreliddata;
+	char		*rangetbldata;
+
+	/* Store sequence scan Nodetag in dynamic shared memory. */
+	nodetype = shm_toc_allocate(pcxt->toc, sizeof(NodeTag));
+	*nodetype = T_ParallelSeqScan;
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_OPERATION, nodetype);
+
+	/* Store scan relation id in dynamic shared memory. */
+	scanreliddata = shm_toc_allocate(pcxt->toc, sizeof(Index));
+	*scanreliddata = scanrelId;
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_SCANRELID, scanreliddata);
+
+	/* Store range table list in dynamic shared memory. */
+	rangetbldata = shm_toc_allocate(pcxt->toc, rangetbl_len);
+	memcpy(rangetbldata, rangetbl_str, rangetbl_len);
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_RANGETBL, rangetbldata);
+
+	/* Store parallel heap scan descriptor in dynamic shared memory. */
+	*pscan = shm_toc_allocate(pcxt->toc, pscan_size);
+	heap_parallelscan_initialize(*pscan, rel, estate->es_snapshot);
+	shm_toc_insert(pcxt->toc, PARALLEL_KEY_SCAN, *pscan);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * StoreResponseQueueAndStartWorkers
+ * 
+ * It sets up the response queue's for backend worker's to
+ * return tuples to the main backend and start the workers.
+ * This function must be called after setting up all the other
+ * necessary parallel execution related information as it start
+ * the workers after which we can't initialize or pass the parallel
+ * state information.
+ */
+void
+StoreResponseQueueAndStartWorkers(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);
+
+	/* Register backend workers. */
+	LaunchParallelWorkers(pcxt);
+
+	for (i = 0; i < pcxt->nworkers; ++i)
+		shm_mq_set_handle((*responseqp)[i], pcxt->worker[i].bgwhandle);
+}
+
+/*
+ * InitializeParallelWorkers
+ *
+ *	Sets up the required infrastructure for backend workers to
+ *	perform execution and return results to the main backend.
+ */
+void
+InitializeParallelWorkers(Index scanrelId, List *targetList, List *qual,
+						  EState *estate, Relation rel, char **inst_options_space,
+						  shm_mq_handle ***responseqp, ParallelContext **pcxtp,
+						  ParallelHeapScanDesc *pscan, int nWorkers)
+{
+	bool		already_in_parallel_mode = IsInParallelMode();
+	Size		targetlist_len, qual_len, rangetbl_len, params_len, pscan_size;
+	char	   *targetlist_str;
+	char	   *qual_str;
+	char	   *rangetbl_str;
+	ParallelContext *pcxt;
+
+	if (!already_in_parallel_mode)
+		EnterParallelMode();
+
+	pcxt = CreateParallelContext(ParallelQueryMain, nWorkers);
+
+	/* Estimate space for parallel seq. scan specific contents. */
+	targetlist_str = nodeToString(targetList);
+	qual_str = nodeToString(qual);
+	EstimateParallelQueryElemsSpace(pcxt, targetlist_str, qual_str,
+									&targetlist_len, &qual_len);
+
+	rangetbl_str = nodeToString(estate->es_range_table);
+	EstimateParallelSeqScanSpace(pcxt, estate, scanrelId, rangetbl_str,
+								 &rangetbl_len, &pscan_size);
+	EstimateParallelSupportInfoSpace(pcxt, estate->es_param_list_info,
+									 estate->es_instrument, &params_len);
+	EstimateResponseQueueSpace(pcxt);
+
+	InitializeParallelDSM(pcxt);
+
+	StoreParallelQueryElems(pcxt, targetlist_str, qual_str,
+							targetlist_len, qual_len);
+	StoreParallelSeqScan(pcxt, estate, rel, scanrelId, rangetbl_str,
+						 pscan, rangetbl_len, pscan_size);
+	StoreParallelSupportInfo(pcxt, estate->es_param_list_info,
+							 estate->es_instrument,
+							 params_len, inst_options_space);
+	StoreResponseQueueAndStartWorkers(pcxt, responseqp);
+
+	/* Return results to caller. */
+	*pcxtp = pcxt;
+}
+
+/*
+ * GetParallelQueryElems
+ *
+ * Look up based on keys in dynamic shared memory segment
+ * and get the targetlist and qualification list required
+ * to perform parallel operation.
+ */
+void
+GetParallelQueryElems(shm_toc *toc, List **targetList, List **qual)
+{
+	char	    *targetlistdata;
+	char		*qualdata;
+
+	targetlistdata = shm_toc_lookup(toc, PARALLEL_KEY_TARGETLIST);
+	qualdata = shm_toc_lookup(toc, PARALLEL_KEY_QUAL);
+
+	*targetList = (List *) stringToNode(targetlistdata);
+	*qual = (List *) stringToNode(qualdata);
+}
+
+/*
+ * GetParallelSupportInfo
+ *
+ * Look up based on keys in dynamic shared memory segment
+ * and get the bind parameter's and instrumentation information
+ * required to perform parallel operation.
+ */
+void
+GetParallelSupportInfo(shm_toc *toc, ParamListInfo *params,
+					   int *inst_options, char **instrument)
+{
+	char		*paramsdata;
+	char		*inst_options_space;
+	int			*instoptions;
+
+	paramsdata = shm_toc_lookup(toc, PARALLEL_KEY_PARAMS);
+	instoptions	= shm_toc_lookup(toc, PARALLEL_KEY_INST_OPTIONS);
+
+	*params = RestoreBoundParams(paramsdata);
+
+	*inst_options = *instoptions;
+	if (inst_options)
+	{
+		inst_options_space = shm_toc_lookup(toc, PARALLEL_KEY_INST_INFO);
+		*instrument = (inst_options_space +
+			ParallelWorkerNumber * sizeof(Instrumentation));
+	}
+}
+
+/*
+ * GetParallelSeqScanInfo
+ *
+ * Look up based on keys in dynamic shared memory segment
+ * and get the scanrelId and rangeTable required to perform
+ * parallel sequential scan.
+ */
+void
+GetParallelSeqScanInfo(shm_toc *toc, Index *scanrelId,
+					   List **rangeTableList)
+{
+	char		*rangetbldata;
+	Index		*scanrel;
+
+	scanrel = shm_toc_lookup(toc, PARALLEL_KEY_SCANRELID);
+	rangetbldata = shm_toc_lookup(toc, PARALLEL_KEY_RANGETBL);
+
+	*scanrelId = *scanrel;
+	*rangeTableList = (List *) stringToNode(rangetbldata);
+}
+
+/*
+ * 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)
+{
+	char		*tuple_queue_space;
+	shm_mq_handle *responseq;
+
+	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);
+
+	/* Redirect protocol messages to responseq. */
+	pq_redirect_to_tuple_shm_mq(responseq);
+}
+
+/*
+ * ParallelQueryMain
+ *
+ * Execute the operation to return the tuples or other information
+ * to parallelism driving node.
+ */
+void
+ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
+{
+	NodeTag		*nodetype;
+
+	nodetype = shm_toc_lookup(toc, PARALLEL_KEY_OPERATION);
+
+	switch (*nodetype)
+	{
+		case T_ParallelSeqScan:
+			RestoreAndExecuteParallelScan(seg, toc);
+			break;
+		default:
+			elog(ERROR, "unrecognized node type: %d", (int) *nodetype);
+			break;
+	}
+}
+
+/*
+ * RestoreAndExecuteParallelScan
+ *
+ * Lookup the parallel sequence scan related parameters
+ * from dynamic shared memory segment and setup the
+ * statement to execute the scan.
+ */
+void
+RestoreAndExecuteParallelScan(dsm_segment *seg, shm_toc *toc)
+{
+	shm_mq		*mq;
+	List		*targetList = NIL;
+	List		*qual = NIL;
+	List		*rangeTableList = NIL;
+	ParamListInfo params;
+	int			inst_options;
+	char		*instrument = NULL;
+	Index		scanrelId;
+	ParallelScanStmt	*parallelscan;
+
+	SetupResponseQueue(seg, toc, &mq);
+
+	GetParallelQueryElems(toc, &targetList, &qual);
+	GetParallelSeqScanInfo(toc, &scanrelId, &rangeTableList);
+	GetParallelSupportInfo(toc, &params, &inst_options, &instrument);
+
+	parallelscan = palloc(sizeof(ParallelScanStmt));
+
+	parallelscan->scanrelId = scanrelId;
+	parallelscan->targetList = targetList;
+	parallelscan->qual = qual;
+	parallelscan->rangetableList = rangeTableList;
+	parallelscan->params	= params;
+	parallelscan->inst_options = inst_options;
+	parallelscan->instrument = instrument;
+	parallelscan->toc = toc;
+	parallelscan->shm_toc_scan_key = PARALLEL_KEY_SCAN;
+
+	/* Execute the worker command. */
+	exec_parallel_scan(parallelscan);
+
+	/*
+	 * Once we are done with sending tuples, detach from
+	 * shared memory message queue used to send tuples.
+	 */
+	shm_mq_detach(mq);
+}
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index ac431e5..4c303dd 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"
@@ -835,6 +836,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/tcop/dest.c b/src/backend/tcop/dest.c
index bcf3895..e7ebc1f 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -104,6 +104,7 @@ CreateDestReceiver(CommandDest dest)
 	{
 		case DestRemote:
 		case DestRemoteExecute:
+		case DestRemoteBackend:
 			return printtup_create_DR(dest);
 
 		case DestNone:
@@ -146,12 +147,22 @@ EndCommand(const char *commandTag, CommandDest dest)
 	{
 		case DestRemote:
 		case DestRemoteExecute:
+		case DestRemoteBackend:
 
 			/*
-			 * We assume the commandTag is plain ASCII and therefore requires
-			 * no encoding conversion.
+			 * Send the message via shared-memory tuple queue, if the same
+			 * is enabled.
 			 */
-			pq_putmessage('C', commandTag, strlen(commandTag) + 1);
+			if (is_tuple_shm_mq_enabled())
+				mq_putmessage_direct('C', commandTag, strlen(commandTag) + 1);
+			else
+			{
+				/*
+				 * We assume the commandTag is plain ASCII and therefore requires
+				 * no encoding conversion.
+				 */
+				pq_putmessage('C', commandTag, strlen(commandTag) + 1);
+			}
 			break;
 
 		case DestNone:
@@ -204,6 +215,7 @@ NullCommand(CommandDest dest)
 		case DestCopyOut:
 		case DestSQLFunction:
 		case DestTransientRel:
+		case DestRemoteBackend:
 			break;
 	}
 }
@@ -248,6 +260,7 @@ ReadyForQuery(CommandDest dest)
 		case DestCopyOut:
 		case DestSQLFunction:
 		case DestTransientRel:
+		case DestRemoteBackend:
 			break;
 	}
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 8899448..2e42aa2 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -55,6 +55,7 @@
 #include "pg_getopt.h"
 #include "postmaster/autovacuum.h"
 #include "postmaster/postmaster.h"
+#include "postmaster/backendworker.h"
 #include "replication/slot.h"
 #include "replication/walsender.h"
 #include "rewrite/rewriteHandler.h"
@@ -1191,6 +1192,90 @@ exec_simple_query(const char *query_string)
 }
 
 /*
+ * execute_worker_stmt
+ *
+ * Execute the plan for backend worker.
+ */
+void
+exec_parallel_scan(ParallelScanStmt *parallelscan)
+{
+	DestReceiver *receiver;
+	QueryDesc	*queryDesc;
+	PlannedStmt	*planned_stmt;
+	MemoryContext oldcontext;
+	MemoryContext	plancontext;
+
+	set_ps_display("SELECT", false);
+	BeginCommand("SELECT", DestNone);
+
+	/*
+	 * 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);
+
+	planned_stmt = create_worker_seqscan_plannedstmt(parallelscan);
+
+	if (parallelscan->inst_options)
+		receiver = None_Receiver;
+	else
+	{
+		receiver = CreateDestReceiver(DestRemoteBackend);
+		SetRemoteDestReceiverParams(receiver, NULL);
+	}
+
+	/* Create a QueryDesc for the query */
+	queryDesc = CreateQueryDesc(planned_stmt, "",
+								GetActiveSnapshot(), InvalidSnapshot,
+								receiver, parallelscan->params,
+								parallelscan->inst_options);
+
+	PushActiveSnapshot(queryDesc->snapshot);
+
+	/* call ExecutorStart to prepare the plan for execution */
+	ExecutorStart(queryDesc, 0);
+
+	/* run the plan */
+	ExecutorRun(queryDesc, ForwardScanDirection, 0L);
+
+	/* run cleanup too */
+	ExecutorFinish(queryDesc);
+
+	/*
+	 * copy intrumentation information into shared memory if requested
+	 * by master backend.
+	 */
+	if (parallelscan->inst_options)
+		memcpy(parallelscan->instrument,
+			   queryDesc->planstate->instrument,
+			   sizeof(Instrumentation));
+
+	ExecutorEnd(queryDesc);
+
+	PopActiveSnapshot();
+
+	FreeQueryDesc(queryDesc);
+
+	if (!parallelscan->inst_options)
+		(*receiver->rDestroy) (receiver);
+
+	/*
+	 * Send appropriate CommandComplete to client.  There is no
+	 * need to send completion tag from worker as that won't be
+	 * of any use considering the completiong tag of master backend
+	 * will be used for sending to client.
+	 */
+	EndCommand("", DestRemoteBackend);
+}
+
+/*
  * exec_parse_message
  *
  * Execute a "Parse" protocol message.
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index de988ba..b348bad 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -622,6 +622,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 */
@@ -2437,6 +2439,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,
@@ -2624,6 +2636,36 @@ 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
+	},
+	{
+		{"parallel_startup_cost", PGC_USERSET, QUERY_TUNING_COST,
+			gettext_noop("Sets the planner's estimate of the cost of "
+						 "starting parallel workers."),
+			NULL
+		},
+		&parallel_startup_cost,
+		DEFAULT_PARALLEL_STARTUP_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 b053659..784cfe0 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -287,6 +287,9 @@
 #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
+#parallel_startup_cost = 0.0	# same scale as above
 #effective_cache_size = 4GB
 
 # - Genetic Query Optimizer -
@@ -497,6 +500,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/parallel.h b/src/include/access/parallel.h
index 0685e64..9d3d5e5 100644
--- a/src/include/access/parallel.h
+++ b/src/include/access/parallel.h
@@ -47,6 +47,8 @@ typedef struct ParallelContext
 extern bool ParallelMessagePending;
 extern int ParallelWorkerNumber;
 
+extern int ParallelWorkerNumber;
+
 extern ParallelContext *CreateParallelContext(parallel_worker_main_type entrypoint, int nworkers);
 extern ParallelContext *CreateParallelContextForExternalFunction(char *library_name, char *function_name, int nworkers);
 extern void InitializeParallelDSM(ParallelContext *);
diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h
index f459020..7a7bf75 100644
--- a/src/include/access/relscan.h
+++ b/src/include/access/relscan.h
@@ -115,4 +115,13 @@ typedef struct SysScanDescData
 	Snapshot	snapshot;		/* snapshot to unregister at end of scan */
 }	SysScanDescData;
 
+/* struct for scanning shared memory queues */
+typedef struct ShmScanDescData
+{
+	/* scan current state */
+	int			num_shm_queues;	/* number of shared memory queues used in scan. */
+	int			ss_cqueue;		/* current queue # in scan, if any */
+	bool		shmscan_inited;		/* false = scan not init'd yet */
+}	ShmScanDescData;
+
 #endif   /* RELSCAN_H */
diff --git a/src/include/access/shmmqam.h b/src/include/access/shmmqam.h
new file mode 100644
index 0000000..f3668ae
--- /dev/null
+++ b/src/include/access/shmmqam.h
@@ -0,0 +1,43 @@
+/*-------------------------------------------------------------------------
+ *
+ * shmmqam.h
+ *	  POSTGRES shared memory queue access method definitions.
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/access/shmmqam.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef SHMMQAM_H
+#define SHMMQAM_H
+
+#include "access/relscan.h"
+#include "libpq/pqmq.h"
+
+
+/* Private state maintained across calls to shm_getnext. */
+typedef struct worker_result_state
+{
+	FmgrInfo   *receive_functions;
+	Oid		   *typioparams;
+	HeapTuple  tuple;
+	int		   num_shm_queues;
+	bool	   *queue_detached;
+	bool	   all_queues_detached;
+	bool	   all_heap_fetched;
+} worker_result_state;
+
+typedef struct worker_result_state *worker_result;
+
+typedef struct ShmScanDescData *ShmScanDesc;
+
+extern worker_result ExecInitWorkerResult(TupleDesc tupdesc, int nWorkers);
+extern ShmScanDesc shm_beginscan(int num_queues);
+extern HeapTuple shm_getnext(HeapScanDesc scanDesc, ShmScanDesc shmScan,
+							 worker_result resultState, shm_mq_handle **responseq,
+							 TupleDesc tupdesc, ScanDirection direction, bool *fromheap);
+
+#endif   /* SHMMQAM_H */
diff --git a/src/include/executor/instrument.h b/src/include/executor/instrument.h
index 1c3b2b0..e8522fe 100644
--- a/src/include/executor/instrument.h
+++ b/src/include/executor/instrument.h
@@ -69,5 +69,6 @@ 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);
 
 #endif   /* INSTRUMENT_H */
diff --git a/src/include/executor/nodeParallelSeqscan.h b/src/include/executor/nodeParallelSeqscan.h
new file mode 100644
index 0000000..b638a24
--- /dev/null
+++ b/src/include/executor/nodeParallelSeqscan.h
@@ -0,0 +1,33 @@
+/*-------------------------------------------------------------------------
+ *
+ * nodeparallelSeqscan.h
+ *
+ *
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/nodeParallelSeqscan.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef NODEPARALLELSEQSCAN_H
+#define NODEPARALLELSEQSCAN_H
+
+#include "nodes/execnodes.h"
+
+extern ParallelSeqScanState *ExecInitParallelSeqScan(ParallelSeqScan *node, EState *estate, int eflags);
+extern TupleTableSlot *ExecParallelSeqScan(ParallelSeqScanState *node);
+extern void ExecEndParallelSeqScan(ParallelSeqScanState *node);
+
+extern Size EstimateScanRelationIdSpace(Oid relId);
+extern void SerializeScanRelationId(Oid relId, Size maxsize,
+									char *start_address);
+extern void RestoreScanRelationId(Oid *relId, char *start_address);
+
+extern Size EstimateTargetListSpace(List *targetList);
+extern void SerializeTargetList(List *targetList, Size maxsize,
+								char *start_address);
+extern void RestoreTargetList(List **targetList, char *start_address);
+
+#endif   /* NODEPARALLELSEQSCAN_H */
diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h
index 48f84bf..e5dec1e 100644
--- a/src/include/executor/tuptable.h
+++ b/src/include/executor/tuptable.h
@@ -127,6 +127,8 @@ typedef struct TupleTableSlot
 	MinimalTuple tts_mintuple;	/* minimal tuple, or NULL if none */
 	HeapTupleData tts_minhdr;	/* workspace for minimal-tuple-only case */
 	long		tts_off;		/* saved state for slot_deform_tuple */
+	bool		tts_fromheap;	/* indicates whether the tuple is fetched from
+								   heap or shrared memory message queue */
 } TupleTableSlot;
 
 #define TTS_HAS_PHYSICAL_TUPLE(slot)  \
diff --git a/src/include/libpq/pqmq.h b/src/include/libpq/pqmq.h
index ad7589d..067edbe 100644
--- a/src/include/libpq/pqmq.h
+++ b/src/include/libpq/pqmq.h
@@ -19,6 +19,13 @@
 extern void	pq_redirect_to_shm_mq(shm_mq *, shm_mq_handle *);
 extern void pq_set_parallel_master(pid_t pid, BackendId backend_id);
 
+extern int
+mq_putmessage_direct(char msgtype, const char *s, size_t len);
+extern void
+pq_redirect_to_tuple_shm_mq(shm_mq_handle *mqh);
+extern bool
+is_tuple_shm_mq_enabled(void);
+
 extern void pq_parse_errornotice(StringInfo str, ErrorData *edata);
 
 #endif   /* PQMQ_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 41288ed..844a9eb 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -16,9 +16,12 @@
 
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "access/parallel.h"
+#include "access/shmmqam.h"
 #include "executor/instrument.h"
 #include "nodes/params.h"
 #include "nodes/plannodes.h"
+#include "storage/shm_mq.h"
 #include "utils/reltrigger.h"
 #include "utils/sortsupport.h"
 #include "utils/tuplestore.h"
@@ -1212,6 +1215,24 @@ typedef struct ScanState
 typedef ScanState SeqScanState;
 
 /*
+ * ParallelScanState extends ScanState by storing additional information
+ * related to parallel workers.
+ *		dsm_segment		dynamic shared memory segment to setup worker queues
+ *		responseq		shared memory queues to receive data from workers
+ */
+typedef struct ParallelScanState
+{
+	ScanState	ss;				/* its first field is NodeTag */
+	ParallelContext *pcxt;
+	shm_mq_handle **responseq;
+	ShmScanDesc pss_currentShmScanDesc;
+	worker_result	pss_workerResult;
+	char	*inst_options_space;
+} ParallelScanState;
+
+typedef ParallelScanState ParallelSeqScanState;
+
+/*
  * 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/nodes.h b/src/include/nodes/nodes.h
index 97ef0fc..b6f1493 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -51,6 +51,7 @@ typedef enum NodeTag
 	T_BitmapOr,
 	T_Scan,
 	T_SeqScan,
+	T_ParallelSeqScan,
 	T_IndexScan,
 	T_IndexOnlyScan,
 	T_BitmapIndexScan,
@@ -97,6 +98,7 @@ typedef enum NodeTag
 	T_BitmapOrState,
 	T_ScanState,
 	T_SeqScanState,
+	T_ParallelSeqScanState,
 	T_IndexScanState,
 	T_IndexOnlyScanState,
 	T_BitmapIndexScanState,
@@ -217,6 +219,7 @@ typedef enum NodeTag
 	T_IndexOptInfo,
 	T_ParamPathInfo,
 	T_Path,
+	T_ParallelSeqPath,
 	T_IndexPath,
 	T_BitmapHeapPath,
 	T_BitmapAndPath,
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index 5b096c5..eb8c86a 100644
--- a/src/include/nodes/params.h
+++ b/src/include/nodes/params.h
@@ -103,4 +103,9 @@ typedef struct ParamExecData
 /* 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);
 #endif   /* PARAMS_H */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b1dfa85..929937d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -20,9 +20,14 @@
 #ifndef PARSENODES_H
 #define PARSENODES_H
 
+#include "executor/instrument.h"
 #include "nodes/bitmapset.h"
+#include "nodes/params.h"
 #include "nodes/primnodes.h"
 #include "nodes/value.h"
+#include "nodes/params.h"
+#include "storage/block.h"
+#include "storage/shm_toc.h"
 #include "utils/lockwaitpolicy.h"
 
 /* Possible sources of a Query */
@@ -156,6 +161,19 @@ typedef struct Query
 								 * depends on to be semantically valid */
 } Query;
 
+/* worker statement required for execution. */
+typedef struct ParallelScanStmt
+{
+	Index		scanrelId;
+	List		*targetList;
+	List		*qual;
+	List		*rangetableList;
+	ParamListInfo params;
+	shm_toc		*toc;
+	uint64		shm_toc_scan_key;
+	int			inst_options;
+	char		*instrument;
+} ParallelScanStmt;
 
 /****************************************************************************
  *	Supporting data structures for Parse Trees
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 316c9ce..2ae52dd 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -18,6 +18,8 @@
 #include "lib/stringinfo.h"
 #include "nodes/bitmapset.h"
 #include "nodes/primnodes.h"
+#include "storage/block.h"
+#include "storage/shm_toc.h"
 #include "utils/lockwaitpolicy.h"
 
 
@@ -278,6 +280,23 @@ typedef struct Scan
 typedef Scan SeqScan;
 
 /* ----------------
+ *		parallel sequential scan node
+ * ----------------
+ */
+typedef struct ParallelSeqScan
+{
+	Scan		scan;
+	/*
+	 * Non-zero values of toc and shm_toc_key indicates that this
+	 * node will be used for execution of parallel scan in worker
+	 * backend.
+	 */
+	shm_toc		*toc;
+	uint64		shm_toc_key;
+	int			num_workers;
+} ParallelSeqScan;
+
+/* ----------------
  *		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 6845a40..c5eb319 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -737,6 +737,12 @@ typedef struct Path
 	/* pathkeys is a List of PathKey nodes; see above */
 } Path;
 
+typedef struct ParallelSeqPath
+{
+	Path		path;
+	int			num_workers;
+} ParallelSeqPath;
+
 /* 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 9c2000b..0b6a469 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -26,6 +26,14 @@
 #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_PARALLEL_STARTUP_COST  0.0
 
 #define DEFAULT_EFFECTIVE_CACHE_SIZE  524288	/* measured in pages */
 
@@ -48,8 +56,12 @@ 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 double parallel_startup_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;
@@ -68,6 +80,8 @@ extern double index_pages_fetched(double tuples_fetched, BlockNumber pages,
 					double index_pages, PlannerInfo *root);
 extern void cost_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 			 ParamPathInfo *param_info);
+extern void cost_parallelseqscan(ParallelSeqPath *path, PlannerInfo *root,
+			 RelOptInfo *baserel, ParamPathInfo *param_info, int nWorkers);
 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 9923f0e..32c3e0d 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -32,6 +32,8 @@ extern bool add_path_precheck(RelOptInfo *parent_rel,
 
 extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel,
 					Relids required_outer);
+extern ParallelSeqPath *create_parallelseqscan_path(PlannerInfo *root,
+					RelOptInfo *rel, 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 6cad92e..391d519 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -46,6 +46,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 082f7d7..eb6be5a 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -41,6 +41,8 @@ extern Plan *optimize_minmax_aggregates(PlannerInfo *root, List *tlist,
  * prototypes for plan/createplan.c
  */
 extern Plan *create_plan(PlannerInfo *root, Path *best_path);
+extern SeqScan *
+create_worker_seqscan_plan(ParallelScanStmt *parallelscan);
 extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
 				  Index scanrelid, Plan *subplan);
 extern ForeignScan *make_foreignscan(List *qptlist, List *qpqual,
diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h
index cd62aec..c2aa875 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_worker_seqscan_plannedstmt(ParallelScanStmt *parallelscan);
 
 extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse,
 				 PlannerInfo *parent_root,
diff --git a/src/include/postmaster/backendworker.h b/src/include/postmaster/backendworker.h
new file mode 100644
index 0000000..6d0b590
--- /dev/null
+++ b/src/include/postmaster/backendworker.h
@@ -0,0 +1,32 @@
+/*--------------------------------------------------------------------
+ * backendworker.h
+ *		POSTGRES backend workers interface
+ *
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *		src/include/postmaster/backendworker.h
+ *--------------------------------------------------------------------
+ */
+#ifndef BACKENDWORKER_H
+#define BACKENDWORKER_H
+
+/*---------------------------------------------------------------------
+ * External module API.
+ *---------------------------------------------------------------------
+ */
+
+#include "libpq/pqmq.h"
+
+extern int	parallel_seqscan_degree;
+
+extern void InitializeParallelWorkers(Index scanrelId, List *targetList,
+									  List *qual, EState *estate,
+									  Relation rel, char **inst_options_space,
+									  shm_mq_handle ***responseqp,
+									  ParallelContext **pcxtp,
+									  ParallelHeapScanDesc *pscan,
+									  int nWorkers);
+
+#endif   /* BACKENDWORKER_H */
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 5bcca3f..dd176b5 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -89,6 +89,7 @@ typedef enum
 	DestDebug,					/* results go to debugging output */
 	DestRemote,					/* results sent to frontend process */
 	DestRemoteExecute,			/* sent to frontend, in Execute command */
+	DestRemoteBackend,			/* parallel worker send results to master backend */
 	DestSPI,					/* results sent to SPI manager */
 	DestTuplestore,				/* results sent to Tuplestore */
 	DestIntoRel,				/* results sent to relation (SELECT INTO) */
diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h
index 3e17770..9eebc51 100644
--- a/src/include/tcop/tcopprot.h
+++ b/src/include/tcop/tcopprot.h
@@ -84,5 +84,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_scan(ParallelScanStmt *parallelscan);
 
 #endif   /* TCOPPROT_H */
diff --git a/src/include/utils/guc_tables.h b/src/include/utils/guc_tables.h
index cf319af..38855e5 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,
