diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 7fb8a14..3d158d1 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -848,7 +848,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
 			sname = "Hash Join";
 			break;
 		case T_SeqScan:
-			pname = sname = "Seq Scan";
+			sname = "Seq Scan";
+			if (plan->parallel_aware)
+				pname = "Parallel Seq Scan";
+			else
+				pname = sname;
 			break;
 		case T_SampleScan:
 			pname = sname = "Sample Scan";
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 163650c..580c654 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.c
@@ -462,6 +462,10 @@ ExecSupportsBackwardScan(Plan *node)
 			}
 
 		case T_SeqScan:
+			{
+				if (node->parallel_aware)
+					return false;
+			}
 		case T_TidScan:
 		case T_FunctionScan:
 		case T_ValuesScan:
diff --git a/src/backend/executor/execParallel.c b/src/backend/executor/execParallel.c
index 99a9de3..c4954b6 100644
--- a/src/backend/executor/execParallel.c
+++ b/src/backend/executor/execParallel.c
@@ -25,6 +25,7 @@
 
 #include "executor/execParallel.h"
 #include "executor/executor.h"
+#include "executor/nodeSeqscan.h"
 #include "executor/tqueue.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/planmain.h"
@@ -167,10 +168,16 @@ ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e)
 	/* Count this node. */
 	e->nnodes++;
 
-	/*
-	 * XXX. Call estimators for parallel-aware nodes here, when we have
-	 * some.
-	 */
+	/* Call estimators for parallel-aware nodes. */
+	switch (nodeTag(planstate))
+	{
+		case T_SeqScanState:
+			ExecSeqScanEstimate((SeqScanState *) planstate,
+								e->pcxt);
+			break;
+		default:
+			break;
+	}
 
 	return planstate_tree_walker(planstate, ExecParallelEstimate, e);
 }
@@ -205,10 +212,16 @@ ExecParallelInitializeDSM(PlanState *planstate,
 	/* Count this node. */
 	d->nnodes++;
 
-	/*
-	 * XXX. Call initializers for parallel-aware plan nodes, when we have
-	 * some.
-	 */
+	/* Call initializers for parallel-aware plan nodes. */
+	switch (nodeTag(planstate))
+	{
+		case T_SeqScanState:
+			ExecSeqScanInitializeDSM((SeqScanState *) planstate,
+									 d->pcxt);
+			break;
+		default:
+			break;
+	}
 
 	return planstate_tree_walker(planstate, ExecParallelInitializeDSM, d);
 }
@@ -575,6 +588,32 @@ ExecParallelReportInstrumentation(PlanState *planstate,
 }
 
 /*
+ * Initialize the PlanState and it's descendents with the information
+ * retrieved from shared memory.  This has to be done once the PlanState
+ * is allocated and initialized by executor for each node aka after
+ * ExecutorStart().
+ */
+static bool
+ExecParallelInitializeWorker(PlanState *planstate, shm_toc *toc)
+{
+	if (planstate == NULL)
+		return false;
+
+	/* Call initializers for parallel-aware plan nodes. */
+	switch (nodeTag(planstate))
+	{
+		case T_SeqScanState:
+			ExecSeqScanInitParallelScanDesc((SeqScanState *) planstate,
+											toc);
+			break;
+		default:
+			break;
+	}
+
+	return planstate_tree_walker(planstate, ExecParallelInitializeWorker, toc);
+}
+
+/*
  * Main entrypoint for parallel query worker processes.
  *
  * We reach this function from ParallelMain, so the setup necessary to create
@@ -610,6 +649,7 @@ ParallelQueryMain(dsm_segment *seg, shm_toc *toc)
 
 	/* Start up the executor, have it run the plan, and then shut it down. */
 	ExecutorStart(queryDesc, 0);
+	ExecParallelInitializeWorker(queryDesc->planstate, toc);
 	ExecutorRun(queryDesc, ForwardScanDirection, 0L);
 	ExecutorFinish(queryDesc);
 
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 3cb81fc..2e46745 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -1,11 +1,18 @@
 /*-------------------------------------------------------------------------
  *
  * nodeSeqscan.c
- *	  Support routines for sequential scans of relations.
+ *	  Support routines for sequential and parallel sequential scans
+ * of relations.
  *
  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
+ * A seqscan node can be used to perform the sequential and parallel scans
+ * of relations.  Parallel aware seqscan node can be executed either in
+ * master backend or by a worker backend and it initializes the scan on
+ * first execution as it needs parallel heap scan descriptor which gets
+ * initialized during first execution of gather node.  The functionality
+ * to parallelize the scan is encapsulated in heap layer (refer heapam.c).
  *
  * IDENTIFICATION
  *	  src/backend/executor/nodeSeqscan.c
@@ -19,6 +26,9 @@
  *		ExecInitSeqScan			creates and initializes a seqscan node.
  *		ExecEndSeqScan			releases any storage allocated.
  *		ExecReScanSeqScan		rescans the relation
+ *		ExecSeqScanEstimate		estimates the space to serialize seqscan node
+ *		ExecSeqScanInitializeDSM Initializes the DSM to perform parallel scan
+ *		ExecSeqScanInitParallelScanDesc Initializes the parallel scan descriptor
  */
 #include "postgres.h"
 
@@ -53,10 +63,10 @@ SeqNext(SeqScanState *node)
 	/*
 	 * get information from the estate and scan state
 	 */
-	scandesc = node->ss_currentScanDesc;
-	estate = node->ps.state;
+	scandesc = node->ss.ss_currentScanDesc;
+	estate = node->ss.ps.state;
 	direction = estate->es_direction;
-	slot = node->ss_ScanTupleSlot;
+	slot = node->ss.ss_ScanTupleSlot;
 
 	/*
 	 * get the next tuple from the table
@@ -108,6 +118,18 @@ SeqRecheck(SeqScanState *node, TupleTableSlot *slot)
 TupleTableSlot *
 ExecSeqScan(SeqScanState *node)
 {
+	/*
+	 * For parallel aware scan, initialize the scan on first execution,
+	 * normally we initialize it during ExecutorStart phase, however we need
+	 * ParallelHeapScanDesc to initialize the scan in case of this node and
+	 * the same is initialized by the Gather node during ExecutorRun phase.
+	 */
+	if (node->ss.ps.plan->parallel_aware && !node->ss.ss_currentScanDesc)
+	{
+		node->ss.ss_currentScanDesc =
+			heap_beginscan_parallel(node->ss.ss_currentRelation, node->pscan);
+	}
+
 	return ExecScan((ScanState *) node,
 					(ExecScanAccessMtd) SeqNext,
 					(ExecScanRecheckMtd) SeqRecheck);
@@ -130,20 +152,23 @@ InitScanRelation(SeqScanState *node, EState *estate, int eflags)
 	 * open that relation and acquire appropriate lock on it.
 	 */
 	currentRelation = ExecOpenScanRelation(estate,
-									  ((SeqScan *) node->ps.plan)->scanrelid,
+									  ((SeqScan *) node->ss.ps.plan)->scanrelid,
 										   eflags);
 
-	/* initialize a heapscan */
-	currentScanDesc = heap_beginscan(currentRelation,
-									 estate->es_snapshot,
-									 0,
-									 NULL);
+	/* initialize a heapscan for non-parallel aware scan */
+	if (!node->ss.ps.plan->parallel_aware)
+	{
+		currentScanDesc = heap_beginscan(currentRelation,
+										 estate->es_snapshot,
+										 0,
+										 NULL);
+		node->ss.ss_currentScanDesc = currentScanDesc;
+	}
 
-	node->ss_currentRelation = currentRelation;
-	node->ss_currentScanDesc = currentScanDesc;
+	node->ss.ss_currentRelation = currentRelation;
 
 	/* and report the scan tuple slot's rowtype */
-	ExecAssignScanType(node, RelationGetDescr(currentRelation));
+	ExecAssignScanType(&node->ss, RelationGetDescr(currentRelation));
 }
 
 
@@ -167,44 +192,44 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	 * create state structure
 	 */
 	scanstate = makeNode(SeqScanState);
-	scanstate->ps.plan = (Plan *) node;
-	scanstate->ps.state = estate;
+	scanstate->ss.ps.plan = (Plan *) node;
+	scanstate->ss.ps.state = estate;
 
 	/*
 	 * Miscellaneous initialization
 	 *
 	 * create expression context for node
 	 */
-	ExecAssignExprContext(estate, &scanstate->ps);
+	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
 	/*
 	 * initialize child expressions
 	 */
-	scanstate->ps.targetlist = (List *)
+	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ps.qual = (List *)
+	scanstate->ss.ps.qual = (List *)
 		ExecInitExpr((Expr *) node->plan.qual,
 					 (PlanState *) scanstate);
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &scanstate->ps);
-	ExecInitScanTupleSlot(estate, scanstate);
+	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &scanstate->ss);
 
 	/*
 	 * initialize scan relation
 	 */
 	InitScanRelation(scanstate, estate, eflags);
 
-	scanstate->ps.ps_TupFromTlist = false;
+	scanstate->ss.ps.ps_TupFromTlist = false;
 
 	/*
 	 * Initialize result tuple type and projection info.
 	 */
-	ExecAssignResultTypeFromTL(&scanstate->ps);
-	ExecAssignScanProjectionInfo(scanstate);
+	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
+	ExecAssignScanProjectionInfo(&scanstate->ss);
 
 	return scanstate;
 }
@@ -224,24 +249,25 @@ ExecEndSeqScan(SeqScanState *node)
 	/*
 	 * get information from node
 	 */
-	relation = node->ss_currentRelation;
-	scanDesc = node->ss_currentScanDesc;
+	relation = node->ss.ss_currentRelation;
+	scanDesc = node->ss.ss_currentScanDesc;
 
 	/*
 	 * Free the exprcontext
 	 */
-	ExecFreeExprContext(&node->ps);
+	ExecFreeExprContext(&node->ss.ps);
 
 	/*
 	 * clean out the tuple table
 	 */
-	ExecClearTuple(node->ps.ps_ResultTupleSlot);
-	ExecClearTuple(node->ss_ScanTupleSlot);
+	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
 	/*
 	 * close heap scan
 	 */
-	heap_endscan(scanDesc);
+	if (scanDesc)
+		heap_endscan(scanDesc);
 
 	/*
 	 * close the heap relation.
@@ -265,10 +291,74 @@ ExecReScanSeqScan(SeqScanState *node)
 {
 	HeapScanDesc scan;
 
-	scan = node->ss_currentScanDesc;
+	scan = node->ss.ss_currentScanDesc;
 
-	heap_rescan(scan,			/* scan desc */
-				NULL);			/* new scan keys */
+	if (scan)
+		heap_rescan(scan,			/* scan desc */
+					NULL);			/* new scan keys */
 
 	ExecScanReScan((ScanState *) node);
 }
+
+/* ----------------------------------------------------------------
+ *						Parallel Scan Support
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------------------------------------------------------
+ *		ExecSeqScanEstimate
+ *
+ *		estimates the space required to serialize seqscan node.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqScanEstimate(SeqScanState *node,
+					ParallelContext *pcxt)
+{
+	EState	   *estate = node->ss.ps.state;
+
+	node->pscan_len = heap_parallelscan_estimate(estate->es_snapshot);
+	shm_toc_estimate_chunk(&pcxt->estimator, node->pscan_len);
+
+	/* key for partial scan information. */
+	shm_toc_estimate_keys(&pcxt->estimator, 1);
+}
+
+/* ----------------------------------------------------------------
+ *		ExecSeqScanInitializeDSM
+ *
+ *		Initialize the DSM with the contents required to perform
+ *		parallel seqscan.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqScanInitializeDSM(SeqScanState *node,
+						 ParallelContext *pcxt)
+{
+	EState	   *estate = node->ss.ps.state;
+
+	/*
+	 * Store parallel heap scan descriptor in dynamic shared memory.
+	 */
+	node->pscan = shm_toc_allocate(pcxt->toc, node->pscan_len);
+	heap_parallelscan_initialize(node->pscan,
+								 node->ss.ss_currentRelation,
+								 estate->es_snapshot);
+	shm_toc_insert(pcxt->toc,
+				   node->ss.ps.plan->plan_node_id,
+				   node->pscan);
+}
+
+/* ----------------------------------------------------------------
+ *		ExecPartialSeqScanInitParallelDesc
+ *
+ *		Retrieve the contents from DSM related to seq scan node
+ *		and initialize the seqscan node.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqScanInitParallelScanDesc(SeqScanState *node,
+								shm_toc *toc)
+{
+	node->pscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id);
+}
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index c176ff9..bb5cfca 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -113,6 +113,7 @@ CopyPlanFields(const Plan *from, Plan *newnode)
 	COPY_SCALAR_FIELD(plan_rows);
 	COPY_SCALAR_FIELD(plan_width);
 	COPY_SCALAR_FIELD(plan_node_id);
+	COPY_SCALAR_FIELD(parallel_aware);
 	COPY_NODE_FIELD(targetlist);
 	COPY_NODE_FIELD(qual);
 	COPY_NODE_FIELD(lefttree);
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3e75cd1..23ce9fe 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -272,6 +272,7 @@ _outPlanInfo(StringInfo str, const Plan *node)
 	WRITE_FLOAT_FIELD(plan_rows, "%.0f");
 	WRITE_INT_FIELD(plan_width);
 	WRITE_INT_FIELD(plan_node_id);
+	WRITE_BOOL_FIELD(parallel_aware);
 	WRITE_NODE_FIELD(targetlist);
 	WRITE_NODE_FIELD(qual);
 	WRITE_NODE_FIELD(lefttree);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 94ba6dc..7781ec0 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -1413,6 +1413,7 @@ ReadCommonPlan(Plan *local_node)
 	READ_FLOAT_FIELD(plan_rows);
 	READ_INT_FIELD(plan_width);
 	READ_INT_FIELD(plan_node_id);
+	READ_BOOL_FIELD(parallel_aware);
 	READ_NODE_FIELD(targetlist);
 	READ_NODE_FIELD(qual);
 	READ_NODE_FIELD(lefttree);
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 8fc1cfd..8a00d7a 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -475,7 +475,10 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
 	required_outer = rel->lateral_relids;
 
 	/* Consider sequential scan */
-	add_path(rel, create_seqscan_path(root, rel, required_outer));
+	add_path(rel, create_seqscan_path(root, rel, required_outer, 0));
+
+	/* Consider parallel scans */
+	create_parallelscan_paths(root, rel, required_outer);
 
 	/* 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 1b61fd9..7b82d7b 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -181,10 +181,13 @@ clamp_row_est(double nrows)
  *
  * 'baserel' is the relation to be scanned
  * 'param_info' is the ParamPathInfo if this is a parameterized path, else NULL
+ * 'nworkers' are the number of workers among which the work will be
+ *			distributed if the scan is parallel scan
  */
 void
 cost_seqscan(Path *path, PlannerInfo *root,
-			 RelOptInfo *baserel, ParamPathInfo *param_info)
+			 RelOptInfo *baserel, ParamPathInfo *param_info,
+			 int nworkers)
 {
 	Cost		startup_cost = 0;
 	Cost		run_cost = 0;
@@ -222,6 +225,26 @@ cost_seqscan(Path *path, PlannerInfo *root,
 	cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
 	run_cost += cpu_per_tuple * baserel->tuples;
 
+	/* account for the parallel workers */
+	if (nworkers > 0)
+	{
+		/*
+		 * Account small cost for communication related to scan via the
+		 * ParallelHeapScanDesc.
+		 */
+		run_cost += 0.01;
+
+		/*
+		 * Runtime cost will be equally shared by all workers. Here assumption is
+		 * that disk access cost will also be equally shared between workers which
+		 * is generally true unless there are too many workers working on a
+		 * relatively lesser number of blocks.  If we come across any such case,
+		 * then we can think of changing the current cost model for parallel
+		 * sequiantial scan.
+		 */
+		run_cost = run_cost / (nworkers + 1);
+	}
+
 	path->startup_cost = startup_cost;
 	path->total_cost = startup_cost + run_cost;
 }
diff --git a/src/backend/optimizer/path/parallelpath.c b/src/backend/optimizer/path/parallelpath.c
new file mode 100644
index 0000000..1e84bdc
--- /dev/null
+++ b/src/backend/optimizer/path/parallelpath.c
@@ -0,0 +1,132 @@
+/*-------------------------------------------------------------------------
+ *
+ * parallelpath.c
+ *	  Routines to determine parallel paths for scanning a given relation.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *	  src/backend/optimizer/path/parallelpath.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/heapam.h"
+#include "optimizer/clauses.h"
+#include "optimizer/cost.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/paths.h"
+#include "parser/parsetree.h"
+#include "utils/rel.h"
+
+
+/*
+ * expr_is_parallel_safe
+ *	  is a paraticular expression parallel safe
+ *
+ * Conditions checked here:
+ *
+ * 1. The expresion must not contain any parallel unsafe or parallel
+ * restricted functions.
+ *
+ * 2. The expression must not contain any initplan or subplan.  We can
+ * probably remove this restriction once we have support of infrastructure
+ * for execution of initplans and subplans at parallel (Gather) nodes.
+ */
+bool
+expr_is_parallel_safe(Node *node)
+{
+	if (check_parallel_safety(node, false))
+		return false;
+
+	if (contain_subplans_or_initplans(node))
+		return false;
+
+	return true;
+}
+
+/*
+ * create_parallelscan_paths
+ *	  Create paths corresponding to parallel scans of the given rel.
+ *	  Currently we only support partial sequential scan.
+ *
+ *	  Candidate paths are added to the rel's pathlist (using add_path).
+ */
+void
+create_parallelscan_paths(PlannerInfo *root, RelOptInfo *rel,
+						  Relids required_outer)
+{
+	int			num_parallel_workers = 0;
+	int			estimated_parallel_workers = 0;
+	Oid			reloid;
+	Relation	relation;
+	Path	   *subpath;
+	ListCell   *l;
+
+	/*
+	 * parallel scan is possible only if user has set parallel_seqscan_degree
+	 * to value greater than 0 and the query is parallel-safe.
+	 */
+	if (max_parallel_degree <= 0 || !root->glob->parallelModeOK)
+		return;
+
+	/*
+	 * There should be atleast a thousand pages to scan for each worker. This
+	 * number is somewhat arbitratry, however we don't want to spawn workers
+	 * to scan smaller relations as that will be costly.
+	 */
+	estimated_parallel_workers = rel->pages / 1000;
+
+	if (estimated_parallel_workers <= 0)
+		return;
+
+	reloid = planner_rt_fetch(rel->relid, root)->relid;
+
+	relation = heap_open(reloid, NoLock);
+
+	/*
+	 * Temporary relations can't be scanned by parallel workers as they are
+	 * visible only to local sessions.
+	 */
+	if (RelationUsesLocalBuffers(relation))
+	{
+		heap_close(relation, NoLock);
+		return;
+	}
+
+	heap_close(relation, NoLock);
+
+	/*
+	 * Allow parallel paths only if all the clauses for relation are parallel
+	 * safe.  We can allow execution of parallel restricted clauses in master
+	 * backend, but for that planner should have infrastructure to pull all
+	 * the parallel restricted clauses from below nodes to the Gather node
+	 * which will then execute such clauses in master backend.
+	 */
+	foreach(l, rel->baserestrictinfo)
+	{
+		RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+
+		if (!expr_is_parallel_safe((Node *) rinfo->clause))
+			return;
+	}
+
+	num_parallel_workers = Min(max_parallel_degree,
+							   estimated_parallel_workers);
+
+	/*
+	 * Create the partial scan path which each worker backend needs to
+	 * execute.
+	 */
+	subpath = create_seqscan_path(root, rel, required_outer,
+								  num_parallel_workers);
+
+	/* Create the gather path which master backend needs to execute. */
+	add_path(rel, (Path *) create_gather_path(root, rel, subpath,
+											  required_outer,
+											  num_parallel_workers));
+}
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 791b64e..ead928e 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -3408,8 +3408,9 @@ order_qual_clauses(PlannerInfo *root, List *clauses)
 }
 
 /*
- * Copy cost and size info from a Path node to the Plan node created from it.
- * The executor usually won't use this info, but it's needed by EXPLAIN.
+ * Copy cost, size and parallel mode info from a Path node to the Plan node
+ * created from it. The executor usually won't use cost and size info, but
+ * that is needed by EXPLAIN.
  */
 static void
 copy_path_costsize(Plan *dest, Path *src)
@@ -3420,6 +3421,7 @@ copy_path_costsize(Plan *dest, Path *src)
 		dest->total_cost = src->total_cost;
 		dest->plan_rows = src->rows;
 		dest->plan_width = src->parent->width;
+		dest->parallel_aware = src->parallel_aware;
 	}
 	else
 	{
@@ -3427,6 +3429,7 @@ copy_path_costsize(Plan *dest, Path *src)
 		dest->total_cost = 0;
 		dest->plan_rows = 0;
 		dest->plan_width = 0;
+		dest->parallel_aware = 0;
 	}
 }
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 536b55e..880bdbd 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -202,13 +202,13 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	glob->hasRowSecurity = false;
 
 	/*
-	 * Assess whether it's feasible to use parallel mode for this query.
-	 * We can't do this in a standalone backend, or if the command will
-	 * try to modify any data, or if this is a cursor operation, or if any
+	 * Assess whether it's feasible to use parallel mode for this query. We
+	 * can't do this in a standalone backend, or if the command will try to
+	 * modify any data, or if this is a cursor operation, or if any
 	 * parallel-unsafe functions are present in the query tree.
 	 *
-	 * For now, we don't try to use parallel mode if we're running inside
-	 * a parallel worker.  We might eventually be able to relax this
+	 * For now, we don't try to use parallel mode if we're running inside a
+	 * parallel worker.  We might eventually be able to relax this
 	 * restriction, but for now it seems best not to have parallel workers
 	 * trying to create their own parallel workers.
 	 *
@@ -225,7 +225,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 		parse->commandType == CMD_SELECT && !parse->hasModifyingCTE &&
 		parse->utilityStmt == NULL && !IsParallelWorker() &&
 		!IsolationIsSerializable() &&
-		!contain_parallel_unsafe((Node *) parse);
+		!check_parallel_safety((Node *) parse, true);
 
 	/*
 	 * glob->parallelModeOK should tell us whether it's necessary to impose
@@ -238,9 +238,9 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
 	 *
 	 * (It's been suggested that we should always impose these restrictions
 	 * whenever glob->parallelModeOK is true, so that it's easier to notice
-	 * incorrectly-labeled functions sooner.  That might be the right thing
-	 * to do, but for now I've taken this approach.  We could also control
-	 * this with a GUC.)
+	 * incorrectly-labeled functions sooner.  That might be the right thing to
+	 * do, but for now I've taken this approach.  We could also control this
+	 * with a GUC.)
 	 *
 	 * FIXME: It's assumed that code further down will set parallelModeNeeded
 	 * to true if a parallel path is actually chosen.  Since the core
@@ -4690,7 +4690,7 @@ plan_cluster_use_sort(Oid tableOid, Oid indexOid)
 	comparisonCost = 2.0 * (indexExprCost.startup + indexExprCost.per_tuple);
 
 	/* Estimate the cost of seq scan + sort */
-	seqScanPath = create_seqscan_path(root, rel, NULL);
+	seqScanPath = create_seqscan_path(root, rel, NULL, 0);
 	cost_sort(&seqScanAndSortPath, root, NIL,
 			  seqScanPath->total_cost, rel->tuples, rel->width,
 			  comparisonCost, maintenance_work_mem, -1.0);
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index f2c8551..2355cc6 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -87,16 +87,25 @@ typedef struct
 	char	   *prosrc;
 } inline_error_callback_arg;
 
+typedef struct
+{
+	bool		allow_restricted;
+}	check_parallel_safety_arg;
+
 static bool contain_agg_clause_walker(Node *node, void *context);
 static bool count_agg_clauses_walker(Node *node,
 						 count_agg_clauses_context *context);
 static bool find_window_functions_walker(Node *node, WindowFuncLists *lists);
 static bool expression_returns_set_rows_walker(Node *node, double *count);
 static bool contain_subplans_walker(Node *node, void *context);
+static bool contain_subplans_or_initplans_walker(Node *node, void *context);
 static bool contain_mutable_functions_walker(Node *node, void *context);
 static bool contain_volatile_functions_walker(Node *node, void *context);
 static bool contain_volatile_functions_not_nextval_walker(Node *node, void *context);
-static bool contain_parallel_unsafe_walker(Node *node, void *context);
+static bool check_parallel_safety_walker(Node *node,
+							 check_parallel_safety_arg * context);
+static bool parallel_too_dangerous(char proparallel,
+					   check_parallel_safety_arg * context);
 static bool contain_nonstrict_functions_walker(Node *node, void *context);
 static bool contain_leaked_vars_walker(Node *node, void *context);
 static Relids find_nonnullable_rels_walker(Node *node, bool top_level);
@@ -1204,13 +1213,16 @@ contain_volatile_functions_not_nextval_walker(Node *node, void *context)
  *****************************************************************************/
 
 bool
-contain_parallel_unsafe(Node *node)
+check_parallel_safety(Node *node, bool allow_restricted)
 {
-	return contain_parallel_unsafe_walker(node, NULL);
+	check_parallel_safety_arg context;
+
+	context.allow_restricted = allow_restricted;
+	return check_parallel_safety_walker(node, &context);
 }
 
 static bool
-contain_parallel_unsafe_walker(Node *node, void *context)
+check_parallel_safety_walker(Node *node, check_parallel_safety_arg * context)
 {
 	if (node == NULL)
 		return false;
@@ -1218,7 +1230,7 @@ contain_parallel_unsafe_walker(Node *node, void *context)
 	{
 		FuncExpr   *expr = (FuncExpr *) node;
 
-		if (func_parallel(expr->funcid) == PROPARALLEL_UNSAFE)
+		if (parallel_too_dangerous(func_parallel(expr->funcid), context))
 			return true;
 		/* else fall through to check args */
 	}
@@ -1227,7 +1239,7 @@ contain_parallel_unsafe_walker(Node *node, void *context)
 		OpExpr	   *expr = (OpExpr *) node;
 
 		set_opfuncid(expr);
-		if (func_parallel(expr->opfuncid) == PROPARALLEL_UNSAFE)
+		if (parallel_too_dangerous(func_parallel(expr->opfuncid), context))
 			return true;
 		/* else fall through to check args */
 	}
@@ -1236,7 +1248,7 @@ contain_parallel_unsafe_walker(Node *node, void *context)
 		DistinctExpr *expr = (DistinctExpr *) node;
 
 		set_opfuncid((OpExpr *) expr);	/* rely on struct equivalence */
-		if (func_parallel(expr->opfuncid) == PROPARALLEL_UNSAFE)
+		if (parallel_too_dangerous(func_parallel(expr->opfuncid), context))
 			return true;
 		/* else fall through to check args */
 	}
@@ -1245,7 +1257,7 @@ contain_parallel_unsafe_walker(Node *node, void *context)
 		NullIfExpr *expr = (NullIfExpr *) node;
 
 		set_opfuncid((OpExpr *) expr);	/* rely on struct equivalence */
-		if (func_parallel(expr->opfuncid) == PROPARALLEL_UNSAFE)
+		if (parallel_too_dangerous(func_parallel(expr->opfuncid), context))
 			return true;
 		/* else fall through to check args */
 	}
@@ -1254,7 +1266,7 @@ contain_parallel_unsafe_walker(Node *node, void *context)
 		ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
 
 		set_sa_opfuncid(expr);
-		if (func_parallel(expr->opfuncid) == PROPARALLEL_UNSAFE)
+		if (parallel_too_dangerous(func_parallel(expr->opfuncid), context))
 			return true;
 		/* else fall through to check args */
 	}
@@ -1268,12 +1280,12 @@ contain_parallel_unsafe_walker(Node *node, void *context)
 		/* check the result type's input function */
 		getTypeInputInfo(expr->resulttype,
 						 &iofunc, &typioparam);
-		if (func_parallel(iofunc) == PROPARALLEL_UNSAFE)
+		if (parallel_too_dangerous(func_parallel(iofunc), context))
 			return true;
 		/* check the input type's output function */
 		getTypeOutputInfo(exprType((Node *) expr->arg),
 						  &iofunc, &typisvarlena);
-		if (func_parallel(iofunc) == PROPARALLEL_UNSAFE)
+		if (parallel_too_dangerous(func_parallel(iofunc), context))
 			return true;
 		/* else fall through to check args */
 	}
@@ -1282,7 +1294,7 @@ contain_parallel_unsafe_walker(Node *node, void *context)
 		ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
 
 		if (OidIsValid(expr->elemfuncid) &&
-			func_parallel(expr->elemfuncid) == PROPARALLEL_UNSAFE)
+			parallel_too_dangerous(func_parallel(expr->elemfuncid), context))
 			return true;
 		/* else fall through to check args */
 	}
@@ -1294,28 +1306,77 @@ contain_parallel_unsafe_walker(Node *node, void *context)
 
 		foreach(opid, rcexpr->opnos)
 		{
-			if (op_volatile(lfirst_oid(opid)) == PROPARALLEL_UNSAFE)
+			if (parallel_too_dangerous(op_volatile(lfirst_oid(opid)), context))
 				return true;
 		}
 		/* else fall through to check args */
 	}
 	else if (IsA(node, Query))
 	{
-		Query *query = (Query *) node;
+		Query	   *query = (Query *) node;
 
 		if (query->rowMarks != NULL)
 			return true;
 
 		/* Recurse into subselects */
 		return query_tree_walker(query,
-								 contain_parallel_unsafe_walker,
+								 check_parallel_safety_walker,
 								 context, 0);
 	}
 	return expression_tree_walker(node,
-								  contain_parallel_unsafe_walker,
+								  check_parallel_safety_walker,
 								  context);
 }
 
+static bool
+parallel_too_dangerous(char proparallel, check_parallel_safety_arg * context)
+{
+	if (context->allow_restricted)
+		return proparallel == PROPARALLEL_UNSAFE;
+	else
+		return proparallel != PROPARALLEL_SAFE;
+}
+
+/*
+ * contain_subplans_or_initplans
+ *	  Recursively search for initplan or subplan nodes within a clause.
+ *
+ * A special purpose function for prohibiting subplan or initplan clauses
+ * in parallel query constructs.
+ *
+ * If we see any form of SubPlan node, we will return TRUE.  For InitPlan's,
+ * we return true when we see the Param node, apart from that InitPlan
+ * can contain a simple NULL constant for MULTIEXPR subquery (see comments
+ * in make_subplan), however it is okay not to care about the same as that
+ * is only possible for Update statement which is anyway prohibited.
+ *
+ * Returns true if any subplan or initplan is found.
+ */
+bool
+contain_subplans_or_initplans(Node *clause)
+{
+	return contain_subplans_or_initplans_walker(clause, NULL);
+}
+
+static bool
+contain_subplans_or_initplans_walker(Node *node, void *context)
+{
+	if (node == NULL)
+		return false;
+	if (IsA(node, SubPlan) ||
+		IsA(node, AlternativeSubPlan) ||
+		IsA(node, SubLink))
+		return true;			/* abort the tree traversal and return true */
+	else if (IsA(node, Param))
+	{
+		Param	   *paramval = (Param *) node;
+
+		if (paramval->paramkind == PARAM_EXEC)
+			return true;
+	}
+	return expression_tree_walker(node, contain_subplans_or_initplans_walker, context);
+}
+
 /*****************************************************************************
  *		Check clauses for nonstrict functions
  *****************************************************************************/
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 1895a68..342020d 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -696,7 +696,8 @@ add_path_precheck(RelOptInfo *parent_rel,
  *	  pathnode.
  */
 Path *
-create_seqscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
+create_seqscan_path(PlannerInfo *root, RelOptInfo *rel,
+					Relids required_outer, int nworkers)
 {
 	Path	   *pathnode = makeNode(Path);
 
@@ -706,7 +707,10 @@ create_seqscan_path(PlannerInfo *root, RelOptInfo *rel, Relids required_outer)
 													 required_outer);
 	pathnode->pathkeys = NIL;	/* seqscan has unordered result */
 
-	cost_seqscan(pathnode, root, rel, pathnode->param_info);
+	/* presence of workers indicate that this is parallel path */
+	pathnode->parallel_aware = nworkers ? true : false;
+
+	cost_seqscan(pathnode, root, rel, pathnode->param_info, nworkers);
 
 	return pathnode;
 }
@@ -1798,7 +1802,7 @@ reparameterize_path(PlannerInfo *root, Path *path,
 	switch (path->pathtype)
 	{
 		case T_SeqScan:
-			return create_seqscan_path(root, rel, required_outer);
+			return create_seqscan_path(root, rel, required_outer, 0);
 		case T_SampleScan:
 			return (Path *) create_samplescan_path(root, rel, required_outer);
 		case T_IndexScan:
diff --git a/src/include/executor/nodeSeqscan.h b/src/include/executor/nodeSeqscan.h
index 39d12a6..d0dc1f6 100644
--- a/src/include/executor/nodeSeqscan.h
+++ b/src/include/executor/nodeSeqscan.h
@@ -20,5 +20,8 @@ extern SeqScanState *ExecInitSeqScan(SeqScan *node, EState *estate, int eflags);
 extern TupleTableSlot *ExecSeqScan(SeqScanState *node);
 extern void ExecEndSeqScan(SeqScanState *node);
 extern void ExecReScanSeqScan(SeqScanState *node);
+extern void ExecSeqScanEstimate(SeqScanState *node, ParallelContext *pcxt);
+extern void ExecSeqScanInitializeDSM(SeqScanState *node, ParallelContext *pcxt);
+extern void ExecSeqScanInitParallelScanDesc(SeqScanState *node, shm_toc *toc);
 
 #endif   /* NODESEQSCAN_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 58ec889..1f52132 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -16,6 +16,7 @@
 
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "access/parallel.h"
 #include "executor/instrument.h"
 #include "lib/pairingheap.h"
 #include "nodes/params.h"
@@ -1249,10 +1250,28 @@ typedef struct ScanState
 } ScanState;
 
 /*
- * SeqScan uses a bare ScanState as its state node, since it needs
- * no additional fields.
+ * SeqScanState extends ScanState by storing additional information
+ * related to parallel scan for parallel-aware sequential scan plan.
  */
-typedef ScanState SeqScanState;
+typedef struct SeqScanState
+{
+	ScanState	ss;				/* its first field is NodeTag */
+	ParallelHeapScanDesc	pscan;	/* parallel heap scan descriptor
+									 * for partial scan */
+	Size		pscan_len;		/* size of parallel heap scan descriptor */
+} SeqScanState;
+
+/*
+ * PartialSeqScanState extends ScanState by storing additional information
+ * related to scan.
+ */
+typedef struct PartialSeqScanState
+{
+	ScanState	ss;				/* its first field is NodeTag */
+	ParallelHeapScanDesc	pscan;	/* parallel heap scan descriptor
+									 * for partial scan */
+	Size		pscan_len;		/* size of parallel heap scan descriptor */
+} PartialSeqScanState;
 
 /* ----------------
  *	 SampleScanState information
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 6b28c8e..ed79dae 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -72,7 +72,7 @@ typedef struct PlannedStmt
 
 	bool		hasRowSecurity; /* row security applied? */
 
-	bool		parallelModeNeeded; /* parallel mode required to execute? */
+	bool		parallelModeNeeded;		/* parallel mode required to execute? */
 } PlannedStmt;
 
 /* macro for fetching the Plan associated with a SubPlan node */
@@ -112,6 +112,7 @@ typedef struct Plan
 	 * Common structural data for all Plan types.
 	 */
 	int			plan_node_id;	/* unique across entire final plan tree */
+	bool		parallel_aware; /* is this plan node parallel-aware? */
 	List	   *targetlist;		/* target list to be computed at this node */
 	List	   *qual;			/* implicitly-ANDed qual conditions */
 	struct Plan *lefttree;		/* input plan tree(s) */
@@ -287,6 +288,12 @@ typedef struct Scan
 typedef Scan SeqScan;
 
 /* ----------------
+ *		partial sequential scan node
+ * ----------------
+ */
+typedef SeqScan PartialSeqScan;
+
+/* ----------------
  *		table sample scan node
  * ----------------
  */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 6cf2e24..f11099f 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -754,6 +754,8 @@ typedef struct Path
 	RelOptInfo *parent;			/* the relation this path can build */
 	ParamPathInfo *param_info;	/* parameterization info, or NULL if none */
 
+	bool		parallel_aware; /* is this path parallel-aware? */
+
 	/* estimated size/costs for path (see costsize.c for more info) */
 	double		rows;			/* estimated number of result tuples */
 	Cost		startup_cost;	/* cost expended before fetching any tuples */
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index 5ac79b1..747b05b 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -62,7 +62,8 @@ extern bool contain_subplans(Node *clause);
 extern bool contain_mutable_functions(Node *clause);
 extern bool contain_volatile_functions(Node *clause);
 extern bool contain_volatile_functions_not_nextval(Node *clause);
-extern bool contain_parallel_unsafe(Node *node);
+extern bool check_parallel_safety(Node *node, bool allow_restricted);
+extern bool contain_subplans_or_initplans(Node *clause);
 extern bool contain_nonstrict_functions(Node *clause);
 extern bool contain_leaked_vars(Node *clause);
 
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 25a7303..ac21a3a 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -72,7 +72,7 @@ extern double clamp_row_est(double nrows);
 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);
+			 ParamPathInfo *param_info, int nworkers);
 extern void cost_samplescan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 				ParamPathInfo *param_info);
 extern void cost_index(IndexPath *path, PlannerInfo *root,
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 7a4940c..f28b4e2 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -31,7 +31,7 @@ extern bool add_path_precheck(RelOptInfo *parent_rel,
 				  List *pathkeys, Relids required_outer);
 
 extern Path *create_seqscan_path(PlannerInfo *root, RelOptInfo *rel,
-					Relids required_outer);
+					Relids required_outer, int nworkers);
 extern Path *create_samplescan_path(PlannerInfo *root, RelOptInfo *rel,
 					   Relids required_outer);
 extern IndexPath *create_index_path(PlannerInfo *root,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index 87123a5..6cd4479 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -55,6 +55,15 @@ 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,
+						  Relids required_outer);
+extern bool expr_is_parallel_safe(Node *node);
+
+/*
  * indxpath.c
  *	  routines to generate index paths
  */
