cost into account while planning.
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: execAmi.c,v 1.64 2002/06/20 20:29:27 momjian Exp $
+ * $Id: execAmi.c,v 1.65 2002/11/30 05:21:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
}
-/* ----------------------------------------------------------------
- * ExecMarkPos
- *
- * Marks the current scan position.
+/*
+ * ExecMarkPos
*
- * XXX Needs to be extended to include all the node types,
- * or at least all the ones that can be directly below a mergejoin.
- * ----------------------------------------------------------------
+ * Marks the current scan position.
*/
void
ExecMarkPos(Plan *node)
ExecIndexMarkPos((IndexScan *) node);
break;
+ case T_TidScan:
+ ExecTidMarkPos((TidScan *) node);
+ break;
+
case T_FunctionScan:
ExecFunctionMarkPos((FunctionScan *) node);
break;
ExecSortMarkPos((Sort *) node);
break;
- case T_TidScan:
- ExecTidMarkPos((TidScan *) node);
- break;
-
default:
/* don't make hard error unless caller asks to restore... */
elog(LOG, "ExecMarkPos: node type %d not supported",
}
}
-/* ----------------------------------------------------------------
- * ExecRestrPos
- *
- * restores the scan position previously saved with ExecMarkPos()
+/*
+ * ExecRestrPos
*
- * XXX Needs to be extended to include all the node types,
- * or at least all the ones that can be directly below a mergejoin.
- * ----------------------------------------------------------------
+ * restores the scan position previously saved with ExecMarkPos()
*/
void
ExecRestrPos(Plan *node)
ExecIndexRestrPos((IndexScan *) node);
break;
+ case T_TidScan:
+ ExecTidRestrPos((TidScan *) node);
+ break;
+
case T_FunctionScan:
ExecFunctionRestrPos((FunctionScan *) node);
break;
break;
}
}
+
+/*
+ * ExecSupportsMarkRestore - does a plan type support mark/restore?
+ *
+ * XXX Ideally, all plan node types would support mark/restore, and this
+ * wouldn't be needed. For now, this had better match the routines above.
+ */
+bool
+ExecSupportsMarkRestore(NodeTag plantype)
+{
+ switch (plantype)
+ {
+ case T_SeqScan:
+ case T_IndexScan:
+ case T_TidScan:
+ case T_FunctionScan:
+ case T_Material:
+ case T_Sort:
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.37 2002/09/04 20:31:18 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.38 2002/11/30 05:21:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* ExecInitSeqScan creates and initializes a seqscan node.
* ExecEndSeqScan releases any storage allocated.
* ExecSeqReScan rescans the relation
- * ExecMarkPos marks scan position
- * ExecRestrPos restores scan position
- *
+ * ExecSeqMarkPos marks scan position
+ * ExecSeqRestrPos restores scan position
*/
#include "postgres.h"
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.26 2002/09/04 20:31:18 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.27 2002/11/30 05:21:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
*
- * ExecTidScan scans a relation using tids
+ * ExecTidScan scans a relation using tids
* ExecInitTidScan creates and initializes state info.
* ExecTidReScan rescans the tid relation.
* ExecEndTidScan releases all storage.
* ExecTidMarkPos marks scan position.
- *
+ * ExecTidRestrPos restores scan position.
*/
#include "postgres.h"
tidstate->tss_MarkTidPtr = tidstate->tss_TidPtr;
}
-#ifdef NOT_USED
/* ----------------------------------------------------------------
* ExecTidRestrPos
*
tidstate = node->tidstate;
tidstate->tss_TidPtr = tidstate->tss_MarkTidPtr;
}
-#endif
/* ----------------------------------------------------------------
* ExecInitTidScan
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.224 2002/11/30 00:08:16 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.225 2002/11/30 05:21:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
/*
+ * _copyMaterialPath
+ */
+static MaterialPath *
+_copyMaterialPath(MaterialPath *from)
+{
+ MaterialPath *newnode = makeNode(MaterialPath);
+
+ /*
+ * copy node superclass fields
+ */
+ CopyPathFields((Path *) from, (Path *) newnode);
+
+ /*
+ * copy remainder of node
+ */
+ COPY_NODE_FIELD(subpath);
+
+ return newnode;
+}
+
+/*
* CopyJoinPathFields
*
* This function copies the fields of the JoinPath node. It is used by
case T_RelOptInfo:
retval = _copyRelOptInfo(from);
break;
+ case T_IndexOptInfo:
+ retval = _copyIndexOptInfo(from);
+ break;
case T_Path:
retval = _copyPath(from);
break;
case T_ResultPath:
retval = _copyResultPath(from);
break;
+ case T_MaterialPath:
+ retval = _copyMaterialPath(from);
+ break;
case T_NestPath:
retval = _copyNestPath(from);
break;
case T_JoinInfo:
retval = _copyJoinInfo(from);
break;
- case T_IndexOptInfo:
- retval = _copyIndexOptInfo(from);
- break;
case T_InnerIndexscanInfo:
retval = _copyInnerIndexscanInfo(from);
break;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.169 2002/11/25 21:29:36 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.170 2002/11/30 05:21:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
}
static bool
+_equalMaterialPath(MaterialPath *a, MaterialPath *b)
+{
+ if (!_equalPath((Path *) a, (Path *) b))
+ return false;
+ COMPARE_NODE_FIELD(subpath);
+
+ return true;
+}
+
+static bool
_equalJoinPath(JoinPath *a, JoinPath *b)
{
if (!_equalPath((Path *) a, (Path *) b))
case T_RelOptInfo:
retval = _equalRelOptInfo(a, b);
break;
+ case T_IndexOptInfo:
+ retval = _equalIndexOptInfo(a, b);
+ break;
case T_Path:
retval = _equalPath(a, b);
break;
case T_IndexPath:
retval = _equalIndexPath(a, b);
break;
+ case T_TidPath:
+ retval = _equalTidPath(a, b);
+ break;
+ case T_AppendPath:
+ retval = _equalAppendPath(a, b);
+ break;
+ case T_ResultPath:
+ retval = _equalResultPath(a, b);
+ break;
+ case T_MaterialPath:
+ retval = _equalMaterialPath(a, b);
+ break;
case T_NestPath:
retval = _equalNestPath(a, b);
break;
case T_InnerIndexscanInfo:
retval = _equalInnerIndexscanInfo(a, b);
break;
- case T_TidPath:
- retval = _equalTidPath(a, b);
- break;
- case T_AppendPath:
- retval = _equalAppendPath(a, b);
- break;
- case T_ResultPath:
- retval = _equalResultPath(a, b);
- break;
- case T_IndexOptInfo:
- retval = _equalIndexOptInfo(a, b);
- break;
case T_List:
{
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.184 2002/11/30 00:08:16 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.185 2002/11/30 05:21:02 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
}
static void
+_outMaterialPath(StringInfo str, MaterialPath *node)
+{
+ WRITE_NODE_TYPE("MATERIALPATH");
+
+ _outPathInfo(str, (Path *) node);
+
+ WRITE_NODE_FIELD(subpath);
+}
+
+static void
_outNestPath(StringInfo str, NestPath *node)
{
WRITE_NODE_TYPE("NESTPATH");
case T_ResultPath:
_outResultPath(str, obj);
break;
+ case T_MaterialPath:
+ _outMaterialPath(str, obj);
+ break;
case T_NestPath:
_outNestPath(str, obj);
break;
IndexPath - index scans
TidPath - scan by CTID
AppendPath - append multiple subpaths together
- ResultPath - a Result plan (used for variable-free tlist or qual)
+ ResultPath - a Result plan node (used for variable-free tlist or qual)
+ MaterialPath - a Material plan node
NestPath - nested-loop joins
MergePath - merge joins
HashPath - hash joins
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.92 2002/11/13 00:39:47 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.93 2002/11/30 05:21:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
print_path(Query *root, Path *path, int indent)
{
const char *ptype;
- bool join;
+ bool join = false;
+ Path *subpath = NULL;
int i;
switch (nodeTag(path))
{
case T_Path:
ptype = "SeqScan";
- join = false;
break;
case T_IndexPath:
ptype = "IdxScan";
- join = false;
break;
case T_TidPath:
ptype = "TidScan";
- join = false;
break;
case T_AppendPath:
ptype = "Append";
- join = false;
break;
case T_ResultPath:
ptype = "Result";
- join = false;
+ subpath = ((ResultPath *) path)->subpath;
+ break;
+ case T_MaterialPath:
+ ptype = "Material";
+ subpath = ((MaterialPath *) path)->subpath;
break;
case T_NestPath:
- ptype = "Nestloop";
+ ptype = "NestLoop";
join = true;
break;
case T_MergePath:
break;
default:
ptype = "???Path";
- join = false;
break;
}
print_path(root, jp->outerjoinpath, indent + 1);
print_path(root, jp->innerjoinpath, indent + 1);
}
+
+ if (subpath)
+ print_path(root, subpath, indent + 1);
}
void
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.92 2002/11/30 00:08:16 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.93 2002/11/30 05:21:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Assert(length(baserel->relids) == 1);
Assert(baserel->rtekind == RTE_RELATION);
- if (!enable_indexscan && !is_injoin)
+ if (!enable_indexscan)
startup_cost += disable_cost;
/*
}
/*
+ * cost_material
+ * Determines and returns the cost of materializing a relation, including
+ * the cost of reading the input data.
+ *
+ * If the total volume of data to materialize exceeds SortMem, we will need
+ * to write it to disk, so the cost is much higher in that case.
+ */
+void
+cost_material(Path *path,
+ Cost input_cost, double tuples, int width)
+{
+ Cost startup_cost = input_cost;
+ Cost run_cost = 0;
+ double nbytes = relation_byte_size(tuples, width);
+ long sortmembytes = SortMem * 1024L;
+
+ /* disk costs */
+ if (nbytes > sortmembytes)
+ {
+ double npages = ceil(nbytes / BLCKSZ);
+
+ /* We'll write during startup and read during retrieval */
+ startup_cost += npages;
+ run_cost += npages;
+ }
+
+ /*
+ * Also charge a small amount per extracted tuple. We use cpu_tuple_cost
+ * so that it doesn't appear worthwhile to materialize a bare seqscan.
+ */
+ run_cost += cpu_tuple_cost * tuples;
+
+ path->startup_cost = startup_cost;
+ path->total_cost = startup_cost + run_cost;
+}
+
+/*
* cost_agg
* Determines and returns the cost of performing an Agg plan node,
* including the cost of its input.
* before we can start returning tuples, so the join's startup cost is
* their sum. What's not so clear is whether the inner path's
* startup_cost must be paid again on each rescan of the inner path.
- * This is not true if the inner path is materialized, but probably is
- * true otherwise. Since we don't yet have clean handling of the
- * decision whether to materialize a path, we can't tell here which
- * will happen. As a compromise, charge 50% of the inner startup cost
- * for each restart.
+ * This is not true if the inner path is materialized or is a hashjoin,
+ * but probably is true otherwise.
*/
startup_cost += outer_path->startup_cost + inner_path->startup_cost;
run_cost += outer_path->total_cost - outer_path->startup_cost;
run_cost += outer_path->parent->rows *
(inner_path->total_cost - inner_path->startup_cost);
- if (outer_path->parent->rows > 1)
- run_cost += (outer_path->parent->rows - 1) *
- inner_path->startup_cost * 0.5;
+ if (!(IsA(inner_path, MaterialPath) ||
+ IsA(inner_path, HashPath)) &&
+ outer_path->parent->rows > 1)
+ run_cost += (outer_path->parent->rows - 1) * inner_path->startup_cost;
/*
* Number of tuples processed (not number emitted!). If inner path is
static double
relation_byte_size(double tuples, int width)
{
- return tuples * ((double) MAXALIGN(width + sizeof(HeapTupleData)));
+ return tuples * (MAXALIGN(width) + MAXALIGN(sizeof(HeapTupleData)));
}
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.73 2002/11/30 00:08:16 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.74 2002/11/30 05:21:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* only outer paths that are already ordered well enough for merging).
*
* We always generate a nestloop path for each available outer path.
- * In fact we may generate as many as three: one on the cheapest-total-cost
- * inner path, one on the cheapest-startup-cost inner path (if different),
+ * In fact we may generate as many as four: one on the cheapest-total-cost
+ * inner path, one on the same with materialization, one on the
+ * cheapest-startup-cost inner path (if different),
* and one on the best inner-indexscan path (if any).
*
* We also consider mergejoins if mergejoin clauses are available. We have
{
bool nestjoinOK;
bool useallclauses;
- Path *bestinnerjoin;
+ Path *matpath = NULL;
+ Path *bestinnerjoin = NULL;
List *i;
/*
break;
}
- /*
- * Get the best innerjoin indexpath (if any) for this outer rel. It's
- * the same for all outer paths.
- */
- bestinnerjoin = best_inner_indexscan(root, innerrel,
- outerrel->relids, jointype);
+ if (nestjoinOK)
+ {
+ /*
+ * If the cheapest inner path is a join or seqscan, we should consider
+ * materializing it. (This is a heuristic: we could consider it
+ * always, but for inner indexscans it's probably a waste of time.)
+ */
+ if (!(IsA(innerrel->cheapest_total_path, IndexPath) ||
+ IsA(innerrel->cheapest_total_path, TidPath)))
+ matpath = (Path *)
+ create_material_path(innerrel,
+ innerrel->cheapest_total_path);
+
+ /*
+ * Get the best innerjoin indexpath (if any) for this outer rel. It's
+ * the same for all outer paths.
+ */
+ bestinnerjoin = best_inner_indexscan(root, innerrel,
+ outerrel->relids, jointype);
+ }
foreach(i, outerrel->pathlist)
{
{
/*
* Always consider a nestloop join with this outer and
- * cheapest-total-cost inner. Consider nestloops using the
- * cheapest-startup-cost inner as well, and the best innerjoin
+ * cheapest-total-cost inner. When appropriate, also consider
+ * using the materialized form of the cheapest inner, the
+ * cheapest-startup-cost inner path, and the best innerjoin
* indexpath.
*/
add_path(joinrel, (Path *)
innerrel->cheapest_total_path,
restrictlist,
merge_pathkeys));
+ if (matpath != NULL)
+ add_path(joinrel, (Path *)
+ create_nestloop_path(root,
+ joinrel,
+ jointype,
+ outerpath,
+ matpath,
+ restrictlist,
+ merge_pathkeys));
if (innerrel->cheapest_startup_path !=
innerrel->cheapest_total_path)
add_path(joinrel, (Path *)
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.125 2002/11/30 00:08:17 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.126 2002/11/30 05:21:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static Join *create_join_plan(Query *root, JoinPath *best_path);
static Append *create_append_plan(Query *root, AppendPath *best_path);
static Result *create_result_plan(Query *root, ResultPath *best_path);
+static Material *create_material_plan(Query *root, MaterialPath *best_path);
static SeqScan *create_seqscan_plan(Path *best_path, List *tlist,
List *scan_clauses);
static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
plan = (Plan *) create_result_plan(root,
(ResultPath *) best_path);
break;
+ case T_Material:
+ plan = (Plan *) create_material_plan(root,
+ (MaterialPath *) best_path);
+ break;
default:
elog(ERROR, "create_plan: unknown pathtype %d",
best_path->pathtype);
return plan;
}
+/*
+ * create_material_plan
+ * Create a Material plan for 'best_path' and (recursively) plans
+ * for its subpaths.
+ *
+ * Returns a Plan node.
+ */
+static Material *
+create_material_plan(Query *root, MaterialPath *best_path)
+{
+ Material *plan;
+ Plan *subplan;
+
+ subplan = create_plan(root, best_path->subpath);
+
+ plan = make_material(best_path->path.parent->targetlist, subplan);
+
+ copy_path_costsize(&plan->plan, (Path *) best_path);
+
+ return plan;
+}
+
/*****************************************************************************
*
inner_tlist,
innerscan->scan.scanrelid);
}
- else if (IsA_Join(inner_plan))
- {
- /*
- * Materialize the inner join for speed reasons.
- *
- * XXX It is probably *not* always fastest to materialize an inner
- * join --- how can we estimate whether this is a good thing to
- * do?
- */
- inner_plan = (Plan *) make_material(inner_tlist,
- inner_plan);
- }
/*
* Set quals to contain INNER/OUTER var references.
best_path->innersortkeys);
/*
- * The executor requires the inner side of a mergejoin to support
- * "mark" and "restore" operations. Not all plan types do, so we must
- * be careful not to generate an invalid plan. If necessary, an
- * invalid inner plan can be handled by inserting a Materialize node.
- *
- * Since the inner side must be ordered, and only Sorts and IndexScans
- * can create order to begin with, you might think there's no problem
- * --- but you'd be wrong. Nestloop and merge joins can *preserve*
- * the order of their inputs, so they can be selected as the input of
- * a mergejoin, and that won't work in the present executor.
- *
- * Doing this here is a bit of a kluge since the cost of the Materialize
- * wasn't taken into account in our earlier decisions. But
- * Materialize is hard to estimate a cost for, and the above
- * consideration shows that this is a rare case anyway, so this seems
- * an acceptable way to proceed.
- *
- * This check must agree with ExecMarkPos/ExecRestrPos in
- * executor/execAmi.c!
- */
- switch (nodeTag(inner_plan))
- {
- case T_SeqScan:
- case T_IndexScan:
- case T_FunctionScan:
- case T_Material:
- case T_Sort:
- /* OK, these inner plans support mark/restore */
- break;
-
- default:
- /* Ooops, need to materialize the inner plan */
- inner_plan = (Plan *) make_material(inner_tlist,
- inner_plan);
- break;
- }
-
- /*
* Now we can build the mergejoin node.
*/
join_plan = make_mergejoin(tlist,
Material *node = makeNode(Material);
Plan *plan = &node->plan;
- copy_plan_costsize(plan, lefttree);
-
- /*
- * For plausibility, make startup & total costs equal total cost of
- * input plan; this only affects EXPLAIN display not decisions.
- *
- * XXX shouldn't we charge some additional cost for materialization?
- */
- plan->startup_cost = plan->total_cost;
+ /* cost should be inserted by caller */
plan->state = (EState *) NULL;
plan->targetlist = tlist;
plan->qual = NIL;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.57 2002/11/30 00:08:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.58 2002/11/30 05:21:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (use_material)
{
Plan *matplan;
+ Path matpath; /* dummy for result of cost_material */
matplan = (Plan *) make_material(plan->targetlist, plan);
- /* kluge --- see comments above */
+ /* need to calculate costs */
+ cost_material(&matpath,
+ plan->total_cost,
+ plan->plan_rows,
+ plan->plan_width);
+ matplan->startup_cost = matpath.startup_cost;
+ matplan->total_cost = matpath.total_cost;
+ /* parameter kluge --- see comments above */
matplan->extParam = listCopy(plan->extParam);
matplan->locParam = listCopy(plan->locParam);
node->plan = plan = matplan;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.81 2002/11/30 00:08:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.82 2002/11/30 05:21:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include <math.h>
+#include "executor/executor.h"
#include "nodes/plannodes.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
pathnode->subpath = subpath;
pathnode->constantqual = constantqual;
+ /* Ideally should define cost_result(), but I'm too lazy */
if (subpath)
{
pathnode->path.startup_cost = subpath->startup_cost;
}
/*
+ * create_material_path
+ * Creates a path corresponding to a Material plan, returning the
+ * pathnode.
+ */
+MaterialPath *
+create_material_path(RelOptInfo *rel, Path *subpath)
+{
+ MaterialPath *pathnode = makeNode(MaterialPath);
+
+ pathnode->path.pathtype = T_Material;
+ pathnode->path.parent = rel;
+
+ pathnode->path.pathkeys = subpath->pathkeys;
+
+ pathnode->subpath = subpath;
+
+ cost_material(&pathnode->path,
+ subpath->total_cost,
+ rel->rows,
+ rel->width);
+
+ return pathnode;
+}
+
+/*
* create_subqueryscan_path
* Creates a path corresponding to a sequential scan of a subquery,
* returning the pathnode.
if (innersortkeys &&
pathkeys_contained_in(innersortkeys, inner_path->pathkeys))
innersortkeys = NIL;
+ /*
+ * If we are not sorting the inner path, we may need a materialize
+ * node to ensure it can be marked/restored. (Sort does support
+ * mark/restore, so no materialize is needed in that case.)
+ *
+ * Since the inner side must be ordered, and only Sorts and IndexScans
+ * can create order to begin with, you might think there's no problem
+ * --- but you'd be wrong. Nestloop and merge joins can *preserve*
+ * the order of their inputs, so they can be selected as the input of
+ * a mergejoin, and they don't support mark/restore at present.
+ */
+ if (innersortkeys == NIL &&
+ !ExecSupportsMarkRestore(inner_path->pathtype))
+ inner_path = (Path *)
+ create_material_path(inner_path->parent, inner_path);
pathnode->jpath.path.pathtype = T_MergeJoin;
pathnode->jpath.path.parent = joinrel;
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: executor.h,v 1.78 2002/09/04 20:31:42 momjian Exp $
+ * $Id: executor.h,v 1.79 2002/11/30 05:21:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent);
extern void ExecMarkPos(Plan *node);
extern void ExecRestrPos(Plan *node);
+extern bool ExecSupportsMarkRestore(NodeTag plantype);
/*
* prototypes from functions in execJunk.c
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: nodeTidscan.h,v 1.9 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeTidscan.h,v 1.10 2002/11/30 05:21:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent);
extern void ExecEndTidScan(TidScan *node);
extern void ExecTidMarkPos(TidScan *node);
+extern void ExecTidRestrPos(TidScan *node);
extern bool ExecInitTidScan(TidScan *node, EState *estate, Plan *parent);
extern int ExecCountSlotsTidScan(TidScan *node);
extern void ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent);
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: nodes.h,v 1.124 2002/11/24 21:52:14 tgl Exp $
+ * $Id: nodes.h,v 1.125 2002/11/30 05:21:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* TAGS FOR PLANNER NODES (relation.h)
*/
T_RelOptInfo = 200,
+ T_IndexOptInfo,
T_Path,
T_IndexPath,
T_NestPath,
T_TidPath,
T_AppendPath,
T_ResultPath,
+ T_MaterialPath,
T_PathKeyItem,
T_RestrictInfo,
T_JoinInfo,
- T_IndexOptInfo,
T_InnerIndexscanInfo,
/*
#define IsA(nodeptr,_type_) (nodeTag(nodeptr) == T_##_type_)
/* ----------------------------------------------------------------
- * IsA functions (no inheritance any more)
- * ----------------------------------------------------------------
- */
-#define IsA_JoinPath(jp) \
- (IsA(jp, NestPath) || IsA(jp, MergePath) || IsA(jp, HashPath))
-
-#define IsA_Join(jp) \
- (IsA(jp, Join) || IsA(jp, NestLoop) || \
- IsA(jp, MergeJoin) || IsA(jp, HashJoin))
-
-/* ----------------------------------------------------------------
* extern declarations follow
* ----------------------------------------------------------------
*/
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: relation.h,v 1.71 2002/11/30 00:08:22 tgl Exp $
+ * $Id: relation.h,v 1.72 2002/11/30 05:21:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
} ResultPath;
/*
+ * MaterialPath represents use of a Material plan node, i.e., caching of
+ * the output of its subpath. This is used when the subpath is expensive
+ * and needs to be scanned repeatedly, or when we need mark/restore ability
+ * and the subpath doesn't have it.
+ */
+typedef struct MaterialPath
+{
+ Path path;
+ Path *subpath;
+} MaterialPath;
+
+/*
* All join-type paths share these fields.
*/
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: cost.h,v 1.48 2002/11/21 00:42:19 tgl Exp $
+ * $Id: cost.h,v 1.49 2002/11/30 05:21:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
RelOptInfo *baserel);
extern void cost_sort(Path *path, Query *root,
List *pathkeys, Cost input_cost, double tuples, int width);
+extern void cost_material(Path *path,
+ Cost input_cost, double tuples, int width);
extern void cost_agg(Path *path, Query *root,
AggStrategy aggstrategy, int numAggs,
int numGroupCols, double numGroups,
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pathnode.h,v 1.45 2002/11/06 00:00:45 tgl Exp $
+ * $Id: pathnode.h,v 1.46 2002/11/30 05:21:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
extern ResultPath *create_result_path(RelOptInfo *rel, Path *subpath,
List *constantqual);
+extern MaterialPath *create_material_path(RelOptInfo *rel, Path *subpath);
extern Path *create_subqueryscan_path(RelOptInfo *rel);
extern Path *create_functionscan_path(Query *root, RelOptInfo *rel);