appendStringInfoString(es->str, "-> ");
es->indent += 2;
}
+ if (plan->parallel_aware)
+ appendStringInfoString(es->str, "Parallel ");
appendStringInfoString(es->str, pname);
es->indent++;
}
ExplainPropertyText("Subplan Name", plan_name, es);
if (custom_name)
ExplainPropertyText("Custom Plan Provider", custom_name, es);
+ if (plan->parallel_aware)
+ ExplainPropertyText("Parallel Aware", "true", es);
}
switch (nodeTag(plan))
if (node == NULL)
return false;
+ /*
+ * Parallel-aware nodes return a subset of the tuples in each worker,
+ * and in general we can't expect to have enough bookkeeping state to
+ * know which ones we returned in this worker as opposed to some other
+ * worker.
+ */
+ if (node->parallel_aware)
+ return false;
+
switch (nodeTag(node))
{
case T_Result:
#include "executor/execParallel.h"
#include "executor/executor.h"
+#include "executor/nodeSeqscan.h"
#include "executor/tqueue.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/planmain.h"
/* 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);
}
/* 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);
}
instrumentation);
}
+/*
+ * Initialize the PlanState and its descendents with the information
+ * retrieved from shared memory. This has to be done once the PlanState
+ * is allocated and initialized by executor; that is, 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:
+ ExecSeqScanInitializeWorker((SeqScanState *) planstate, toc);
+ break;
+ default:
+ break;
+ }
+
+ return planstate_tree_walker(planstate, ExecParallelInitializeWorker, toc);
+}
+
/*
* Main entrypoint for parallel query worker processes.
*
/* 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);
* ExecInitSeqScan creates and initializes a seqscan node.
* ExecEndSeqScan releases any storage allocated.
* ExecReScanSeqScan rescans the relation
+ *
+ * ExecSeqScanEstimate estimates DSM space needed for parallel scan
+ * ExecSeqScanInitializeDSM initialize DSM for parallel scan
+ * ExecSeqScanInitializeWorker attach to DSM info in parallel worker
*/
#include "postgres.h"
/*
* 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;
+
+ if (scandesc == NULL)
+ {
+ /*
+ * We reach here if the scan is not parallel, or if we're executing
+ * a scan that was intended to be parallel serially.
+ */
+ scandesc = heap_beginscan(node->ss.ss_currentRelation,
+ estate->es_snapshot,
+ 0, NULL);
+ node->ss.ss_currentScanDesc = scandesc;
+ }
/*
* get the next tuple from the table
InitScanRelation(SeqScanState *node, EState *estate, int eflags)
{
Relation currentRelation;
- HeapScanDesc currentScanDesc;
/*
* 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->ps.plan)->scanrelid,
+ ((SeqScan *) node->ss.ps.plan)->scanrelid,
eflags);
- /* initialize a heapscan */
- currentScanDesc = heap_beginscan(currentRelation,
- estate->es_snapshot,
- 0,
- NULL);
-
- 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));
}
* 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;
}
/*
* 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 != NULL)
+ heap_endscan(scanDesc);
/*
* close the heap relation.
{
HeapScanDesc scan;
- scan = node->ss_currentScanDesc;
+ scan = node->ss.ss_currentScanDesc;
- heap_rescan(scan, /* scan desc */
- NULL); /* new scan keys */
+ if (scan != NULL)
+ 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);
+ shm_toc_estimate_keys(&pcxt->estimator, 1);
+}
+
+/* ----------------------------------------------------------------
+ * ExecSeqScanInitializeDSM
+ *
+ * Set up a parallel heap scan descriptor.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqScanInitializeDSM(SeqScanState *node,
+ ParallelContext *pcxt)
+{
+ EState *estate = node->ss.ps.state;
+ ParallelHeapScanDesc pscan;
+
+ pscan = shm_toc_allocate(pcxt->toc, node->pscan_len);
+ heap_parallelscan_initialize(pscan,
+ node->ss.ss_currentRelation,
+ estate->es_snapshot);
+ shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan);
+ node->ss.ss_currentScanDesc =
+ heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+}
+
+/* ----------------------------------------------------------------
+ * ExecSeqScanInitializeWorker
+ *
+ * Copy relevant information from TOC into planstate.
+ * ----------------------------------------------------------------
+ */
+void
+ExecSeqScanInitializeWorker(SeqScanState *node, shm_toc *toc)
+{
+ ParallelHeapScanDesc pscan;
+
+ pscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id);
+ node->ss.ss_currentScanDesc =
+ heap_beginscan_parallel(node->ss.ss_currentRelation, pscan);
+}
COPY_SCALAR_FIELD(total_cost);
COPY_SCALAR_FIELD(plan_rows);
COPY_SCALAR_FIELD(plan_width);
+ COPY_SCALAR_FIELD(parallel_aware);
COPY_SCALAR_FIELD(plan_node_id);
COPY_NODE_FIELD(targetlist);
COPY_NODE_FIELD(qual);
WRITE_FLOAT_FIELD(total_cost, "%.2f");
WRITE_FLOAT_FIELD(plan_rows, "%.0f");
WRITE_INT_FIELD(plan_width);
+ WRITE_BOOL_FIELD(parallel_aware);
WRITE_INT_FIELD(plan_node_id);
WRITE_NODE_FIELD(targetlist);
WRITE_NODE_FIELD(qual);
_outBitmapset(str, node->param_info->ppi_req_outer);
else
_outBitmapset(str, NULL);
+ WRITE_BOOL_FIELD(parallel_aware);
WRITE_FLOAT_FIELD(rows, "%.0f");
WRITE_FLOAT_FIELD(startup_cost, "%.2f");
WRITE_FLOAT_FIELD(total_cost, "%.2f");
READ_FLOAT_FIELD(total_cost);
READ_FLOAT_FIELD(plan_rows);
READ_INT_FIELD(plan_width);
+ READ_BOOL_FIELD(parallel_aware);
READ_INT_FIELD(plan_node_id);
READ_NODE_FIELD(targetlist);
READ_NODE_FIELD(qual);
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 index scans */
create_index_paths(root, rel);
*
* '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;
cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
run_cost += cpu_per_tuple * baserel->tuples;
+ /*
+ * Primitive parallel cost model. Assume the leader will do half as much
+ * work as a regular worker, because it will also need to read the tuples
+ * returned by the workers when they percolate up to the gather ndoe.
+ * This is almost certainly not exactly the right way to model this, so
+ * this will probably need to be changed at some point...
+ */
+ if (nworkers > 0)
+ run_cost = run_cost / (nworkers + 0.5);
+
path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost;
}
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
static List *get_switched_clauses(List *clauses, Relids outerrelids);
static List *order_qual_clauses(PlannerInfo *root, List *clauses);
-static void copy_path_costsize(Plan *dest, Path *src);
+static void copy_generic_path_info(Plan *dest, Path *src);
static void copy_plan_costsize(Plan *dest, Plan *src);
static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
static SampleScan *make_samplescan(List *qptlist, List *qpqual, Index scanrelid,
* prepare_sort_from_pathkeys on it before we do so on the individual
* child plans, to make cross-checking the sort info easier.
*/
- copy_path_costsize(plan, (Path *) best_path);
+ copy_generic_path_info(plan, (Path *) best_path);
plan->targetlist = tlist;
plan->qual = NIL;
plan->lefttree = NULL;
plan = make_material(subplan);
- copy_path_costsize(&plan->plan, (Path *) best_path);
+ copy_generic_path_info(&plan->plan, (Path *) best_path);
return plan;
}
best_path->single_copy,
subplan);
- copy_path_costsize(&gather_plan->plan, &best_path->path);
+ copy_generic_path_info(&gather_plan->plan, &best_path->path);
/* use parallel mode for parallel plans. */
root->glob->parallelModeNeeded = true;
scan_clauses,
scan_relid);
- copy_path_costsize(&scan_plan->plan, best_path);
+ copy_generic_path_info(&scan_plan->plan, best_path);
return scan_plan;
}
scan_relid,
tsc);
- copy_path_costsize(&scan_plan->scan.plan, best_path);
+ copy_generic_path_info(&scan_plan->scan.plan, best_path);
return scan_plan;
}
indexorderbyops,
best_path->indexscandir);
- copy_path_costsize(&scan_plan->plan, &best_path->path);
+ copy_generic_path_info(&scan_plan->plan, &best_path->path);
return scan_plan;
}
bitmapqualorig,
baserelid);
- copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);
return scan_plan;
}
scan_relid,
tidquals);
- copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);
return scan_plan;
}
scan_relid,
best_path->parent->subplan);
- copy_path_costsize(&scan_plan->scan.plan, best_path);
+ copy_generic_path_info(&scan_plan->scan.plan, best_path);
return scan_plan;
}
scan_plan = make_functionscan(tlist, scan_clauses, scan_relid,
functions, rte->funcordinality);
- copy_path_costsize(&scan_plan->scan.plan, best_path);
+ copy_generic_path_info(&scan_plan->scan.plan, best_path);
return scan_plan;
}
scan_plan = make_valuesscan(tlist, scan_clauses, scan_relid,
values_lists);
- copy_path_costsize(&scan_plan->scan.plan, best_path);
+ copy_generic_path_info(&scan_plan->scan.plan, best_path);
return scan_plan;
}
scan_plan = make_ctescan(tlist, scan_clauses, scan_relid,
plan_id, cte_param_id);
- copy_path_costsize(&scan_plan->scan.plan, best_path);
+ copy_generic_path_info(&scan_plan->scan.plan, best_path);
return scan_plan;
}
scan_plan = make_worktablescan(tlist, scan_clauses, scan_relid,
cteroot->wt_param_id);
- copy_path_costsize(&scan_plan->scan.plan, best_path);
+ copy_generic_path_info(&scan_plan->scan.plan, best_path);
return scan_plan;
}
tlist, scan_clauses);
/* Copy cost data from Path to Plan; no need to make FDW do this */
- copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
+ copy_generic_path_info(&scan_plan->scan.plan, &best_path->path);
/* Copy foreign server OID; likewise, no need to make FDW do this */
scan_plan->fs_server = rel->serverid;
* Copy cost data from Path to Plan; no need to make custom-plan providers
* do this
*/
- copy_path_costsize(&cplan->scan.plan, &best_path->path);
+ copy_generic_path_info(&cplan->scan.plan, &best_path->path);
/* Likewise, copy the relids that are represented by this custom scan */
cplan->custom_relids = best_path->path.parent->relids;
inner_plan,
best_path->jointype);
- copy_path_costsize(&join_plan->join.plan, &best_path->path);
+ copy_generic_path_info(&join_plan->join.plan, &best_path->path);
return join_plan;
}
best_path->jpath.jointype);
/* Costs of sort and material steps are included in path cost already */
- copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
+ copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
return join_plan;
}
(Plan *) hash_plan,
best_path->jpath.jointype);
- copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
+ copy_generic_path_info(&join_plan->join.plan, &best_path->jpath.path);
return join_plan;
}
/*
* 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.
+ *
+ * Also copy the parallel-aware flag, which the executor will use.
*/
static void
-copy_path_costsize(Plan *dest, Path *src)
+copy_generic_path_info(Plan *dest, Path *src)
{
if (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
{
dest->total_cost = 0;
dest->plan_rows = 0;
dest->plan_width = 0;
+ dest->parallel_aware = false;
}
}
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);
* 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);
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->parallel_aware = nworkers > 0 ? true : false;
pathnode->pathkeys = NIL; /* seqscan has unordered result */
- cost_seqscan(pathnode, root, rel, pathnode->param_info);
+ cost_seqscan(pathnode, root, rel, pathnode->param_info, nworkers);
return pathnode;
}
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->parallel_aware = false;
pathnode->pathkeys = NIL; /* samplescan has unordered result */
cost_samplescan(pathnode, root, rel, pathnode->param_info);
pathnode->path.parent = rel;
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = pathkeys;
/* Convert clauses to indexquals the executor can handle */
pathnode->path.parent = rel;
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = NIL; /* always unordered */
pathnode->bitmapqual = bitmapqual;
pathnode->path.pathtype = T_BitmapAnd;
pathnode->path.parent = rel;
pathnode->path.param_info = NULL; /* not used in bitmap trees */
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = NIL; /* always unordered */
pathnode->bitmapquals = bitmapquals;
pathnode->path.pathtype = T_BitmapOr;
pathnode->path.parent = rel;
pathnode->path.param_info = NULL; /* not used in bitmap trees */
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = NIL; /* always unordered */
pathnode->bitmapquals = bitmapquals;
pathnode->path.parent = rel;
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = NIL; /* always unordered */
pathnode->tidquals = tidquals;
pathnode->path.parent = rel;
pathnode->path.param_info = get_appendrel_parampathinfo(rel,
required_outer);
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = NIL; /* result is always considered
* unsorted */
pathnode->subpaths = subpaths;
pathnode->path.parent = rel;
pathnode->path.param_info = get_appendrel_parampathinfo(rel,
required_outer);
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = pathkeys;
pathnode->subpaths = subpaths;
pathnode->path.pathtype = T_Result;
pathnode->path.parent = NULL;
pathnode->path.param_info = NULL; /* there are no other rels... */
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = NIL;
pathnode->quals = quals;
pathnode->path.pathtype = T_Material;
pathnode->path.parent = rel;
pathnode->path.param_info = subpath->param_info;
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = subpath->pathkeys;
pathnode->subpath = subpath;
pathnode->path.pathtype = T_Unique;
pathnode->path.parent = rel;
pathnode->path.param_info = subpath->param_info;
+ pathnode->path.parallel_aware = false;
/*
* Assume the output is unsorted, since we don't necessarily have pathkeys
pathnode->path.parent = rel;
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = NIL; /* Gather has unordered result */
pathnode->subpath = subpath;
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->parallel_aware = false;
pathnode->pathkeys = pathkeys;
cost_subqueryscan(pathnode, root, rel, pathnode->param_info);
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->parallel_aware = false;
pathnode->pathkeys = pathkeys;
cost_functionscan(pathnode, root, rel, pathnode->param_info);
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->parallel_aware = false;
pathnode->pathkeys = NIL; /* result is always unordered */
cost_valuesscan(pathnode, root, rel, pathnode->param_info);
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->parallel_aware = false;
pathnode->pathkeys = NIL; /* XXX for now, result is always unordered */
cost_ctescan(pathnode, root, rel, pathnode->param_info);
pathnode->parent = rel;
pathnode->param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->parallel_aware = false;
pathnode->pathkeys = NIL; /* result is always unordered */
/* Cost is the same as for a regular CTE scan */
pathnode->path.parent = rel;
pathnode->path.param_info = get_baserel_parampathinfo(root, rel,
required_outer);
+ pathnode->path.parallel_aware = false;
pathnode->path.rows = rows;
pathnode->path.startup_cost = startup_cost;
pathnode->path.total_cost = total_cost;
sjinfo,
required_outer,
&restrict_clauses);
+ pathnode->path.parallel_aware = false;
pathnode->path.pathkeys = pathkeys;
pathnode->jointype = jointype;
pathnode->outerjoinpath = outer_path;
sjinfo,
required_outer,
&restrict_clauses);
+ pathnode->jpath.path.parallel_aware = false;
pathnode->jpath.path.pathkeys = pathkeys;
pathnode->jpath.jointype = jointype;
pathnode->jpath.outerjoinpath = outer_path;
sjinfo,
required_outer,
&restrict_clauses);
+ pathnode->jpath.path.parallel_aware = false;
/*
* A hashjoin never has pathkeys, since its output ordering is
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:
#ifndef NODESEQSCAN_H
#define NODESEQSCAN_H
+#include "access/parallel.h"
#include "nodes/execnodes.h"
extern SeqScanState *ExecInitSeqScan(SeqScan *node, EState *estate, int eflags);
extern void ExecEndSeqScan(SeqScanState *node);
extern void ExecReScanSeqScan(SeqScanState *node);
+/* parallel scan support */
+extern void ExecSeqScanEstimate(SeqScanState *node, ParallelContext *pcxt);
+extern void ExecSeqScanInitializeDSM(SeqScanState *node, ParallelContext *pcxt);
+extern void ExecSeqScanInitializeWorker(SeqScanState *node, shm_toc *toc);
+
#endif /* NODESEQSCAN_H */
TupleTableSlot *ss_ScanTupleSlot;
} ScanState;
-/*
- * SeqScan uses a bare ScanState as its state node, since it needs
- * no additional fields.
+/* ----------------
+ * SeqScanState information
+ * ----------------
*/
-typedef ScanState SeqScanState;
+typedef struct SeqScanState
+{
+ ScanState ss; /* its first field is NodeTag */
+ Size pscan_len; /* size of parallel heap scan descriptor */
+} SeqScanState;
/* ----------------
* SampleScanState information
double plan_rows; /* number of rows plan is expected to emit */
int plan_width; /* average row width in bytes */
+ /*
+ * information needed for parallel query
+ */
+ bool parallel_aware; /* engage parallel-aware logic? */
+
/*
* Common structural data for all Plan types.
*/
RelOptInfo *parent; /* the relation this path can build */
ParamPathInfo *param_info; /* parameterization info, or NULL if none */
+ bool parallel_aware; /* engage parallel-aware logic? */
/* estimated size/costs for path (see costsize.c for more info) */
double rows; /* estimated number of result tuples */
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,
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,