* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.142 2001/05/20 20:28:17 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.143 2001/06/05 05:26:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* copy remainder of node
*/
Node_Copy(from, newnode, clause);
- newnode->eval_cost = from->eval_cost;
newnode->ispusheddown = from->ispusheddown;
Node_Copy(from, newnode, subclauseindices);
+ newnode->eval_cost = from->eval_cost;
+ newnode->this_selec = from->this_selec;
newnode->mergejoinoperator = from->mergejoinoperator;
newnode->left_sortop = from->left_sortop;
newnode->right_sortop = from->right_sortop;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.90 2001/05/20 20:28:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.91 2001/06/05 05:26:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
if (!equal(a->clause, b->clause))
return false;
-
- /*
- * ignore eval_cost, left/right_pathkey, and left/right_bucketsize,
- * since they may not be set yet, and should be derivable from the
- * clause anyway
- */
if (a->ispusheddown != b->ispusheddown)
return false;
+ /*
+ * We ignore eval_cost, this_selec, left/right_pathkey, and
+ * left/right_bucketsize, since they may not be set yet, and should be
+ * derivable from the clause anyway. Probably it's not really necessary
+ * to compare any of these remaining fields ...
+ */
if (!equal(a->subclauseindices, b->subclauseindices))
return false;
if (a->mergejoinoperator != b->mergejoinoperator)
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.109 2001/05/20 20:28:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.110 2001/06/05 05:26:04 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
/* eval_cost is not part of saved representation; compute on first use */
local_node->eval_cost = -1;
+ /* ditto for this_selec */
+ local_node->this_selec = -1;
/* ditto for cached pathkeys and bucketsize */
local_node->left_pathkey = NIL;
local_node->right_pathkey = NIL;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.74 2001/05/20 20:28:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.75 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* Consider sequential scan */
- add_path(rel, create_seqscan_path(rel));
+ add_path(rel, create_seqscan_path(root, rel));
/* Consider TID scans */
create_tidscan_paths(root, rel);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.44 2001/05/20 20:28:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.45 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static void addRangeClause(RangeQueryClause **rqlist, Node *clause,
bool varonleft, bool isLTsel, Selectivity s2);
-static Selectivity clause_selectivity(Query *root,
- Node *clause,
- int varRelid);
/****************************************************************************
* When varRelid is 0, all variables are treated as variables. This
* is appropriate for ordinary join clauses and restriction clauses.
*/
-static Selectivity
+Selectivity
clause_selectivity(Query *root,
Node *clause,
int varRelid)
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.74 2001/05/20 20:28:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.75 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool enable_hashjoin = true;
+static Selectivity estimate_hash_bucketsize(Query *root, Var *var);
static bool cost_qual_eval_walker(Node *node, Cost *total);
+static Selectivity approx_selectivity(Query *root, List *quals);
static void set_rel_width(Query *root, RelOptInfo *rel);
static double relation_byte_size(double tuples, int width);
static double page_size(double tuples, int width);
* parameters, even though much of it could be extracted from the Path.
*/
void
-cost_seqscan(Path *path, RelOptInfo *baserel)
+cost_seqscan(Path *path, Query *root,
+ RelOptInfo *baserel)
{
Cost startup_cost = 0;
Cost run_cost = 0;
/*
* cost_tidscan
- * Determines and returns the cost of scanning a relation using tid-s.
+ * Determines and returns the cost of scanning a relation using TIDs.
*/
void
-cost_tidscan(Path *path, RelOptInfo *baserel, List *tideval)
+cost_tidscan(Path *path, Query *root,
+ RelOptInfo *baserel, List *tideval)
{
Cost startup_cost = 0;
Cost run_cost = 0;
* but if it ever does, it should react gracefully to lack of key data.
*/
void
-cost_sort(Path *path, List *pathkeys, double tuples, int width)
+cost_sort(Path *path, Query *root,
+ List *pathkeys, double tuples, int width)
{
Cost startup_cost = 0;
Cost run_cost = 0;
* 'restrictlist' are the RestrictInfo nodes to be applied at the join
*/
void
-cost_nestloop(Path *path,
+cost_nestloop(Path *path, Query *root,
Path *outer_path,
Path *inner_path,
List *restrictlist)
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;
+ run_cost += (outer_path->parent->rows - 1) *
+ inner_path->startup_cost * 0.5;
/*
* Number of tuples processed (not number emitted!). If inner path is
* 'outer_path' is the path for the outer relation
* 'inner_path' is the path for the inner relation
* 'restrictlist' are the RestrictInfo nodes to be applied at the join
+ * 'mergeclauses' are the RestrictInfo nodes to use as merge clauses
+ * (this should be a subset of the restrictlist)
* 'outersortkeys' and 'innersortkeys' are lists of the keys to be used
* to sort the outer and inner relations, or NIL if no explicit
* sort is needed because the source path is already ordered
*/
void
-cost_mergejoin(Path *path,
+cost_mergejoin(Path *path, Query *root,
Path *outer_path,
Path *inner_path,
List *restrictlist,
+ List *mergeclauses,
List *outersortkeys,
List *innersortkeys)
{
{
startup_cost += outer_path->total_cost;
cost_sort(&sort_path,
+ root,
outersortkeys,
outer_path->parent->rows,
outer_path->parent->width);
{
startup_cost += inner_path->total_cost;
cost_sort(&sort_path,
+ root,
innersortkeys,
inner_path->parent->rows,
inner_path->parent->width);
}
/*
- * Estimate the number of tuples to be processed in the mergejoin
- * itself as one per tuple in the two source relations. This could be
- * a drastic underestimate if there are many equal-keyed tuples in
- * either relation, but we have no good way of estimating that...
+ * The number of tuple comparisons needed depends drastically on the
+ * number of equal keys in the two source relations, which we have no
+ * good way of estimating. Somewhat arbitrarily, we charge one
+ * tuple comparison (one cpu_operator_cost) for each tuple in the
+ * two source relations. This is probably a lower bound.
*/
- ntuples = outer_path->parent->rows + inner_path->parent->rows;
+ run_cost += cpu_operator_cost *
+ (outer_path->parent->rows + inner_path->parent->rows);
+
+ /*
+ * For each tuple that gets through the mergejoin proper, we charge
+ * cpu_tuple_cost plus the cost of evaluating additional restriction
+ * clauses that are to be applied at the join. It's OK to use an
+ * approximate selectivity here, since in most cases this is a minor
+ * component of the cost.
+ */
+ ntuples = approx_selectivity(root, mergeclauses) *
+ outer_path->parent->rows * inner_path->parent->rows;
/* CPU costs */
cpu_per_tuple = cpu_tuple_cost + cost_qual_eval(restrictlist);
* 'outer_path' is the path for the outer relation
* 'inner_path' is the path for the inner relation
* 'restrictlist' are the RestrictInfo nodes to be applied at the join
- * 'innerbucketsize' is an estimate of the bucketsize statistic
- * for the inner hash key.
+ * 'hashclauses' is a list of the hash join clause (always a 1-element list)
+ * (this should be a subset of the restrictlist)
*/
void
-cost_hashjoin(Path *path,
+cost_hashjoin(Path *path, Query *root,
Path *outer_path,
Path *inner_path,
List *restrictlist,
- Selectivity innerbucketsize)
+ List *hashclauses)
{
Cost startup_cost = 0;
Cost run_cost = 0;
double innerbytes = relation_byte_size(inner_path->parent->rows,
inner_path->parent->width);
long hashtablebytes = SortMem * 1024L;
+ RestrictInfo *restrictinfo;
+ Var *left,
+ *right;
+ Selectivity innerbucketsize;
if (!enable_hashjoin)
startup_cost += disable_cost;
run_cost += cpu_operator_cost * outer_path->parent->rows;
/*
+ * Determine bucketsize fraction for inner relation. First we have
+ * to figure out which side of the hashjoin clause is the inner side.
+ */
+ Assert(length(hashclauses) == 1);
+ Assert(IsA(lfirst(hashclauses), RestrictInfo));
+ restrictinfo = (RestrictInfo *) lfirst(hashclauses);
+ /* these must be OK, since check_hashjoinable accepted the clause */
+ left = get_leftop(restrictinfo->clause);
+ right = get_rightop(restrictinfo->clause);
+
+ /*
+ * Since we tend to visit the same clauses over and over when
+ * planning a large query, we cache the bucketsize estimate in
+ * the RestrictInfo node to avoid repeated lookups of statistics.
+ */
+ if (intMember(right->varno, inner_path->parent->relids))
+ {
+ /* righthand side is inner */
+ innerbucketsize = restrictinfo->right_bucketsize;
+ if (innerbucketsize < 0)
+ {
+ /* not cached yet */
+ innerbucketsize = estimate_hash_bucketsize(root, right);
+ restrictinfo->right_bucketsize = innerbucketsize;
+ }
+ }
+ else
+ {
+ Assert(intMember(left->varno, inner_path->parent->relids));
+ /* lefthand side is inner */
+ innerbucketsize = restrictinfo->left_bucketsize;
+ if (innerbucketsize < 0)
+ {
+ /* not cached yet */
+ innerbucketsize = estimate_hash_bucketsize(root, left);
+ restrictinfo->left_bucketsize = innerbucketsize;
+ }
+ }
+
+ /*
* The number of tuple comparisons needed is the number of outer
* tuples times the typical number of tuples in a hash bucket,
* which is the inner relation size times its bucketsize fraction.
ceil(inner_path->parent->rows * innerbucketsize);
/*
- * Estimate the number of tuples that get through the hashing filter
- * as one per tuple in the two source relations. This could be a
- * drastic underestimate if there are many equal-keyed tuples in
- * either relation, but we have no simple way of estimating that;
- * and since this is only a second-order parameter, it's probably
- * not worth expending a lot of effort on the estimate.
+ * For each tuple that gets through the hashjoin proper, we charge
+ * cpu_tuple_cost plus the cost of evaluating additional restriction
+ * clauses that are to be applied at the join. It's OK to use an
+ * approximate selectivity here, since in most cases this is a minor
+ * component of the cost.
*/
- ntuples = outer_path->parent->rows + inner_path->parent->rows;
+ ntuples = approx_selectivity(root, hashclauses) *
+ outer_path->parent->rows * inner_path->parent->rows;
/* CPU costs */
cpu_per_tuple = cpu_tuple_cost + cost_qual_eval(restrictlist);
* divided by total tuples in relation) if the specified Var is used
* as a hash key.
*
- * This statistic is used by cost_hashjoin. We split out the calculation
- * because it's useful to cache the result for re-use across multiple path
- * cost calculations.
- *
* XXX This is really pretty bogus since we're effectively assuming that the
* distribution of hash keys will be the same after applying restriction
* clauses as it was in the underlying relation. However, we are not nearly
* which is what we want. We do not want to hash unless we know that the
* inner rel is well-dispersed (or the alternatives seem much worse).
*/
-Selectivity
+static Selectivity
estimate_hash_bucketsize(Query *root, Var *var)
{
Oid relid;
/*
+ * approx_selectivity
+ * Quick-and-dirty estimation of clause selectivities.
+ * The input can be either an implicitly-ANDed list of boolean
+ * expressions, or a list of RestrictInfo nodes (typically the latter).
+ *
+ * The "quick" part comes from caching the selectivity estimates so we can
+ * avoid recomputing them later. (Since the same clauses are typically
+ * examined over and over in different possible join trees, this makes a
+ * big difference.)
+ *
+ * The "dirty" part comes from the fact that the selectivities of multiple
+ * clauses are estimated independently and multiplied together. Currently,
+ * clauselist_selectivity can seldom do any better than that anyhow, but
+ * someday it might be smarter.
+ *
+ * Since we are only using the results to estimate how many potential
+ * output tuples are generated and passed through qpqual checking, it
+ * seems OK to live with the approximation.
+ */
+static Selectivity
+approx_selectivity(Query *root, List *quals)
+{
+ Selectivity total = 1.0;
+ List *l;
+
+ foreach(l, quals)
+ {
+ Node *qual = (Node *) lfirst(l);
+ Selectivity selec;
+
+ /*
+ * RestrictInfo nodes contain a this_selec field reserved for this
+ * routine's use, so that it's not necessary to evaluate the qual
+ * clause's selectivity more than once. If the clause's selectivity
+ * hasn't been computed yet, the field will contain -1.
+ */
+ if (qual && IsA(qual, RestrictInfo))
+ {
+ RestrictInfo *restrictinfo = (RestrictInfo *) qual;
+
+ if (restrictinfo->this_selec < 0)
+ restrictinfo->this_selec =
+ clause_selectivity(root,
+ (Node *) restrictinfo->clause,
+ 0);
+ selec = restrictinfo->this_selec;
+ }
+ else
+ {
+ /* If it's a bare expression, must always do it the hard way */
+ selec = clause_selectivity(root, qual, 0);
+ }
+ total *= selec;
+ }
+ return total;
+}
+
+
+/*
* set_baserel_size_estimates
* Set the size estimates for the given base relation.
*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.64 2001/05/07 00:43:20 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.65 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* paths later, and only if they don't need a sort.
*/
add_path(joinrel, (Path *)
- create_mergejoin_path(joinrel,
+ create_mergejoin_path(root,
+ joinrel,
jointype,
outerrel->cheapest_total_path,
innerrel->cheapest_total_path,
* innerjoin indexpath.
*/
add_path(joinrel, (Path *)
- create_nestloop_path(joinrel,
+ create_nestloop_path(root,
+ joinrel,
jointype,
outerpath,
innerrel->cheapest_total_path,
if (innerrel->cheapest_startup_path !=
innerrel->cheapest_total_path)
add_path(joinrel, (Path *)
- create_nestloop_path(joinrel,
+ create_nestloop_path(root,
+ joinrel,
jointype,
outerpath,
innerrel->cheapest_startup_path,
merge_pathkeys));
if (bestinnerjoin != NULL)
add_path(joinrel, (Path *)
- create_nestloop_path(joinrel,
+ create_nestloop_path(root,
+ joinrel,
jointype,
outerpath,
bestinnerjoin,
* innerrel->cheapest_total_path is already correctly sorted.)
*/
add_path(joinrel, (Path *)
- create_mergejoin_path(joinrel,
+ create_mergejoin_path(root,
+ joinrel,
jointype,
outerpath,
innerrel->cheapest_total_path,
else
newclauses = mergeclauses;
add_path(joinrel, (Path *)
- create_mergejoin_path(joinrel,
+ create_mergejoin_path(root,
+ joinrel,
jointype,
outerpath,
innerpath,
newclauses = mergeclauses;
}
add_path(joinrel, (Path *)
- create_mergejoin_path(joinrel,
+ create_mergejoin_path(root,
+ joinrel,
jointype,
outerpath,
innerpath,
*/
merge_pathkeys = build_join_pathkeys(root, joinrel, outersortkeys);
add_path(joinrel, (Path *)
- create_mergejoin_path(joinrel,
+ create_mergejoin_path(root,
+ joinrel,
jointype,
outerrel->cheapest_total_path,
innerpath,
merge_pathkeys = build_join_pathkeys(root, joinrel,
totalouterpath->pathkeys);
add_path(joinrel, (Path *)
- create_mergejoin_path(joinrel,
+ create_mergejoin_path(root,
+ joinrel,
jointype,
totalouterpath,
innerpath,
merge_pathkeys = build_join_pathkeys(root, joinrel,
startupouterpath->pathkeys);
add_path(joinrel, (Path *)
- create_mergejoin_path(joinrel,
+ create_mergejoin_path(root,
+ joinrel,
jointype,
startupouterpath,
innerpath,
foreach(i, restrictlist)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
- Expr *clause;
Var *left,
*right;
- Selectivity innerbucketsize;
List *hashclauses;
if (restrictinfo->hashjoinoperator == InvalidOid)
if (isouterjoin && restrictinfo->ispusheddown)
continue;
- clause = restrictinfo->clause;
/* these must be OK, since check_hashjoinable accepted the clause */
- left = get_leftop(clause);
- right = get_rightop(clause);
+ left = get_leftop(restrictinfo->clause);
+ right = get_rightop(restrictinfo->clause);
/*
- * Check if clause is usable with these sub-rels, find inner side,
- * estimate bucketsize of inner var for costing purposes.
- *
- * Since we tend to visit the same clauses over and over when
- * planning a large query, we cache the bucketsize estimates in
- * the RestrictInfo node to avoid repeated lookups of statistics.
+ * Check if clause is usable with these input rels.
*/
if (intMember(left->varno, outerrelids) &&
intMember(right->varno, innerrelids))
{
/* righthand side is inner */
- innerbucketsize = restrictinfo->right_bucketsize;
- if (innerbucketsize < 0)
- {
- /* not cached yet */
- innerbucketsize = estimate_hash_bucketsize(root, right);
- restrictinfo->right_bucketsize = innerbucketsize;
- }
}
else if (intMember(left->varno, innerrelids) &&
intMember(right->varno, outerrelids))
{
/* lefthand side is inner */
- innerbucketsize = restrictinfo->left_bucketsize;
- if (innerbucketsize < 0)
- {
- /* not cached yet */
- innerbucketsize = estimate_hash_bucketsize(root, left);
- restrictinfo->left_bucketsize = innerbucketsize;
- }
}
else
continue; /* no good for these input relations */
* any but the cheapest-total-cost inner path, however.
*/
add_path(joinrel, (Path *)
- create_hashjoin_path(joinrel,
+ create_hashjoin_path(root,
+ joinrel,
jointype,
outerrel->cheapest_total_path,
innerrel->cheapest_total_path,
restrictlist,
- hashclauses,
- innerbucketsize));
+ hashclauses));
if (outerrel->cheapest_startup_path != outerrel->cheapest_total_path)
add_path(joinrel, (Path *)
- create_hashjoin_path(joinrel,
+ create_hashjoin_path(root,
+ joinrel,
jointype,
outerrel->cheapest_startup_path,
innerrel->cheapest_total_path,
restrictlist,
- hashclauses,
- innerbucketsize));
+ hashclauses));
}
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.8 2001/01/24 19:42:58 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.9 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "parser/parse_coerce.h"
#include "utils/lsyscache.h"
-static void create_tidscan_joinpaths(RelOptInfo *rel);
+static void create_tidscan_joinpaths(Query *root, RelOptInfo *rel);
static List *TidqualFromRestrictinfo(List *relids, List *restrictinfo);
static bool isEvaluable(int varno, Node *node);
static Node *TidequalClause(int varno, Expr *node);
* XXX does this actually work?
*/
static void
-create_tidscan_joinpaths(RelOptInfo *rel)
+create_tidscan_joinpaths(Query *root, RelOptInfo *rel)
{
List *rlst = NIL,
*lst;
pathnode->tideval = tideval;
pathnode->unjoined_relids = joininfo->unjoined_relids;
- cost_tidscan(&pathnode->path, rel, tideval);
+ cost_tidscan(&pathnode->path, root, rel, tideval);
rlst = lappend(rlst, pathnode);
}
rel->baserestrictinfo);
if (tideval)
- add_path(rel, (Path *) create_tidscan_path(rel, tideval));
- create_tidscan_joinpaths(rel);
+ add_path(rel, (Path *) create_tidscan_path(root, rel, tideval));
+ create_tidscan_joinpaths(root, rel);
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.106 2001/05/20 20:28:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.107 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
-static MergeJoin *create_mergejoin_plan(MergePath *best_path, List *tlist,
+static MergeJoin *create_mergejoin_plan(Query *root,
+ MergePath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
switch (best_path->path.pathtype)
{
case T_MergeJoin:
- plan = (Join *) create_mergejoin_plan((MergePath *) best_path,
+ plan = (Join *) create_mergejoin_plan(root,
+ (MergePath *) best_path,
join_tlist,
joinclauses,
otherclauses,
}
static MergeJoin *
-create_mergejoin_plan(MergePath *best_path,
+create_mergejoin_plan(Query *root,
+ MergePath *best_path,
List *tlist,
List *joinclauses,
List *otherclauses,
*/
if (best_path->outersortkeys)
outer_plan = (Plan *)
- make_sort_from_pathkeys(outer_tlist,
+ make_sort_from_pathkeys(root,
+ outer_tlist,
outer_plan,
best_path->outersortkeys);
if (best_path->innersortkeys)
inner_plan = (Plan *)
- make_sort_from_pathkeys(inner_tlist,
+ make_sort_from_pathkeys(root,
+ inner_tlist,
inner_plan,
best_path->innersortkeys);
* each key number from 1 to keycount), or the executor will get confused!
*/
Sort *
-make_sort(List *tlist, Plan *lefttree, int keycount)
+make_sort(Query *root, List *tlist, Plan *lefttree, int keycount)
{
Sort *node = makeNode(Sort);
Plan *plan = &node->plan;
Path sort_path; /* dummy for result of cost_sort */
copy_plan_costsize(plan, lefttree); /* only care about copying size */
- cost_sort(&sort_path, NIL, lefttree->plan_rows, lefttree->plan_width);
+ cost_sort(&sort_path, root, NIL,
+ lefttree->plan_rows, lefttree->plan_width);
plan->startup_cost = sort_path.startup_cost + lefttree->total_cost;
plan->total_cost = sort_path.total_cost + lefttree->total_cost;
plan->state = (EState *) NULL;
* of resdom nodes in the sort plan's target list.
*/
Sort *
-make_sort_from_pathkeys(List *tlist, Plan *lefttree, List *pathkeys)
+make_sort_from_pathkeys(Query *root, List *tlist,
+ Plan *lefttree, List *pathkeys)
{
List *sort_tlist;
List *i;
Assert(numsortkeys > 0);
- return make_sort(sort_tlist, lefttree, numsortkeys);
+ return make_sort(root, sort_tlist, lefttree, numsortkeys);
}
-Material *
+Material *
make_material(List *tlist, Plan *lefttree)
{
Material *node = makeNode(Material);
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.62 2001/05/20 20:28:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.63 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
bool can_be_equijoin;
restrictinfo->clause = (Expr *) clause;
- restrictinfo->eval_cost = -1; /* not computed until needed */
restrictinfo->subclauseindices = NIL;
+ restrictinfo->eval_cost = -1; /* not computed until needed */
+ restrictinfo->this_selec = -1; /* not computed until needed */
restrictinfo->mergejoinoperator = InvalidOid;
restrictinfo->left_sortop = InvalidOid;
restrictinfo->right_sortop = InvalidOid;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.65 2001/05/20 20:28:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.66 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Path sort_path; /* dummy for result of cost_sort */
- cost_sort(&sort_path, root->query_pathkeys,
+ cost_sort(&sort_path, root, root->query_pathkeys,
final_rel->rows, final_rel->width);
sort_path.startup_cost += cheapestpath->total_cost;
sort_path.total_cost += cheapestpath->total_cost;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.107 2001/05/20 20:28:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.108 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static Plan *grouping_planner(Query *parse, double tuple_fraction);
static List *make_subplanTargetList(Query *parse, List *tlist,
AttrNumber **groupColIdx);
-static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
+static Plan *make_groupplan(Query *parse,
+ List *group_tlist, bool tuplePerGroup,
List *groupClause, AttrNumber *grpColIdx,
bool is_presorted, Plan *subplan);
static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
current_pathkeys = group_pathkeys;
}
- result_plan = make_groupplan(group_tlist,
+ result_plan = make_groupplan(parse,
+ group_tlist,
tuplePerGroup,
parse->groupClause,
groupColIdx,
if (parse->sortClause)
{
if (!pathkeys_contained_in(sort_pathkeys, current_pathkeys))
- result_plan = make_sortplan(tlist, result_plan,
+ result_plan = make_sortplan(parse, tlist, result_plan,
parse->sortClause);
}
* first add an explicit Sort node.
*/
static Plan *
-make_groupplan(List *group_tlist,
+make_groupplan(Query *parse,
+ List *group_tlist,
bool tuplePerGroup,
List *groupClause,
AttrNumber *grpColIdx,
Assert(keyno > 0);
- subplan = (Plan *) make_sort(sort_tlist, subplan, keyno);
+ subplan = (Plan *) make_sort(parse, sort_tlist, subplan, keyno);
}
return (Plan *) make_group(group_tlist, tuplePerGroup, numCols,
* Add a Sort node to implement an explicit ORDER BY clause.
*/
Plan *
-make_sortplan(List *tlist, Plan *plannode, List *sortcls)
+make_sortplan(Query *parse, List *tlist, Plan *plannode, List *sortcls)
{
List *sort_tlist;
List *i;
Assert(keyno > 0);
- return (Plan *) make_sort(sort_tlist, plannode, keyno);
+ return (Plan *) make_sort(parse, sort_tlist, plannode, keyno);
}
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.64 2001/05/20 20:28:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.65 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
tlist = new_unsorted_tlist(plan->targetlist);
sortList = addAllTargetsToSortList(NIL, tlist);
- plan = make_sortplan(tlist, plan, sortList);
+ plan = make_sortplan(parse, tlist, plan, sortList);
plan = (Plan *) make_unique(tlist, plan, copyObject(sortList));
}
return plan;
*/
tlist = new_unsorted_tlist(plan->targetlist);
sortList = addAllTargetsToSortList(NIL, tlist);
- plan = make_sortplan(tlist, plan, sortList);
+ plan = make_sortplan(parse, tlist, plan, sortList);
switch (op->op)
{
case SETOP_INTERSECT:
adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
newinfo->subclauseindices = NIL;
- newinfo->eval_cost = -1; /* reset this too */
+ newinfo->eval_cost = -1; /* reset these too */
+ newinfo->this_selec = -1;
newinfo->left_pathkey = NIL; /* and these */
newinfo->right_pathkey = NIL;
newinfo->left_bucketsize = -1;
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.73 2001/05/20 20:28:19 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.74 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* pathnode.
*/
Path *
-create_seqscan_path(RelOptInfo *rel)
+create_seqscan_path(Query *root, RelOptInfo *rel)
{
Path *pathnode = makeNode(Path);
pathnode->parent = rel;
pathnode->pathkeys = NIL; /* seqscan has unordered result */
- cost_seqscan(pathnode, rel);
+ cost_seqscan(pathnode, root, rel);
return pathnode;
}
* create_tidscan_path
* Creates a path corresponding to a tid_direct scan, returning the
* pathnode.
- *
*/
-TidPath *
-create_tidscan_path(RelOptInfo *rel, List *tideval)
+TidPath *
+create_tidscan_path(Query *root, RelOptInfo *rel, List *tideval)
{
TidPath *pathnode = makeNode(TidPath);
* necessary? */
pathnode->unjoined_relids = NIL;
- cost_tidscan(&pathnode->path, rel, tideval);
+ cost_tidscan(&pathnode->path, root, rel, tideval);
/*
* divide selectivity for each clause to get an equal selectivity as
* 'pathkeys' are the path keys of the new join path
*
* Returns the resulting path node.
- *
*/
-NestPath *
-create_nestloop_path(RelOptInfo *joinrel,
+NestPath *
+create_nestloop_path(Query *root,
+ RelOptInfo *joinrel,
JoinType jointype,
Path *outer_path,
Path *inner_path,
pathnode->joinrestrictinfo = restrict_clauses;
pathnode->path.pathkeys = pathkeys;
- cost_nestloop(&pathnode->path, outer_path, inner_path, restrict_clauses);
+ cost_nestloop(&pathnode->path, root, outer_path, inner_path,
+ restrict_clauses);
return pathnode;
}
* (this should be a subset of the restrict_clauses list)
* 'outersortkeys' are the sort varkeys for the outer relation
* 'innersortkeys' are the sort varkeys for the inner relation
- *
*/
-MergePath *
-create_mergejoin_path(RelOptInfo *joinrel,
+MergePath *
+create_mergejoin_path(Query *root,
+ RelOptInfo *joinrel,
JoinType jointype,
Path *outer_path,
Path *inner_path,
pathnode->innersortkeys = innersortkeys;
cost_mergejoin(&pathnode->jpath.path,
+ root,
outer_path,
inner_path,
restrict_clauses,
+ mergeclauses,
outersortkeys,
innersortkeys);
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
* 'hashclauses' is a list of the hash join clause (always a 1-element list)
* (this should be a subset of the restrict_clauses list)
- * 'innerbucketsize' is an estimate of the bucketsize of the inner hash key
- *
*/
-HashPath *
-create_hashjoin_path(RelOptInfo *joinrel,
+HashPath *
+create_hashjoin_path(Query *root,
+ RelOptInfo *joinrel,
JoinType jointype,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
- List *hashclauses,
- Selectivity innerbucketsize)
+ List *hashclauses)
{
HashPath *pathnode = makeNode(HashPath);
pathnode->path_hashclauses = hashclauses;
cost_hashjoin(&pathnode->jpath.path,
+ root,
outer_path,
inner_path,
restrict_clauses,
- innerbucketsize);
+ hashclauses);
return pathnode;
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.91 2001/05/27 17:37:48 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.92 2001/06/05 05:26:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
totalsel2 += otherfreq2 * (otherfreq1 + unmatchfreq1) /
(nd1 - nmatches);
/*
- * For robustness, we average the two estimates. (Can a case
- * be made for taking the min or max instead?)
+ * Use the smaller of the two estimates. This can be justified
+ * in essentially the same terms as given below for the no-stats
+ * case: to a first approximation, we are estimating from the
+ * point of view of the relation with smaller nd.
*/
- selec = (totalsel1 + totalsel2) * 0.5;
+ selec = (totalsel1 < totalsel2) ? totalsel1 : totalsel2;
}
else
{
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: relation.h,v 1.56 2001/05/20 20:28:20 tgl Exp $
+ * $Id: relation.h,v 1.57 2001/06/05 05:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* path is responsible for identifying the restrict clauses it can use
* and ignoring the rest. Clauses not implemented by an indexscan,
* mergejoin, or hashjoin will be placed in the plan qual or joinqual field
- * of the final Plan node, where they will be enforced by general-purpose
+ * of the finished Plan node, where they will be enforced by general-purpose
* qual-expression-evaluation code. (But we are still entitled to count
* their selectivity when estimating the result tuple count, if we
* can guess what it is...)
Expr *clause; /* the represented clause of WHERE or JOIN */
- Cost eval_cost; /* eval cost of clause; -1 if not yet set */
-
bool ispusheddown; /* TRUE if clause was pushed down in level */
/* only used if clause is an OR clause: */
List *subclauseindices; /* indexes matching subclauses */
/* subclauseindices is a List of Lists of IndexOptInfos */
+ /* cache space for costs (currently only used for join clauses) */
+ Cost eval_cost; /* eval cost of clause; -1 if not yet set */
+ Selectivity this_selec; /* selectivity; -1 if not yet set */
+
/* valid if clause is mergejoinable, else InvalidOid: */
Oid mergejoinoperator; /* copy of clause operator */
Oid left_sortop; /* leftside sortop needed for mergejoin */
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: cost.h,v 1.39 2001/05/07 00:43:26 tgl Exp $
+ * $Id: cost.h,v 1.40 2001/06/05 05:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern bool enable_mergejoin;
extern bool enable_hashjoin;
-extern void cost_seqscan(Path *path, RelOptInfo *baserel);
+extern void cost_seqscan(Path *path, Query *root,
+ RelOptInfo *baserel);
extern void cost_index(Path *path, Query *root,
- RelOptInfo *baserel, IndexOptInfo *index,
- List *indexQuals, bool is_injoin);
-extern void cost_tidscan(Path *path, RelOptInfo *baserel, List *tideval);
-extern void cost_sort(Path *path, List *pathkeys, double tuples, int width);
-extern void cost_nestloop(Path *path, Path *outer_path, Path *inner_path,
- List *restrictlist);
-extern void cost_mergejoin(Path *path, Path *outer_path, Path *inner_path,
- List *restrictlist,
- List *outersortkeys, List *innersortkeys);
-extern void cost_hashjoin(Path *path, Path *outer_path, Path *inner_path,
- List *restrictlist, Selectivity innerbucketsize);
-extern Selectivity estimate_hash_bucketsize(Query *root, Var *var);
+ RelOptInfo *baserel, IndexOptInfo *index,
+ List *indexQuals, bool is_injoin);
+extern void cost_tidscan(Path *path, Query *root,
+ RelOptInfo *baserel, List *tideval);
+extern void cost_sort(Path *path, Query *root,
+ List *pathkeys, double tuples, int width);
+extern void cost_nestloop(Path *path, Query *root,
+ Path *outer_path, Path *inner_path,
+ List *restrictlist);
+extern void cost_mergejoin(Path *path, Query *root,
+ Path *outer_path, Path *inner_path,
+ List *restrictlist,
+ List *mergeclauses,
+ List *outersortkeys, List *innersortkeys);
+extern void cost_hashjoin(Path *path, Query *root,
+ Path *outer_path, Path *inner_path,
+ List *restrictlist,
+ List *hashclauses);
extern Cost cost_qual_eval(List *quals);
extern void set_baserel_size_estimates(Query *root, RelOptInfo *rel);
extern void set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
extern Selectivity clauselist_selectivity(Query *root,
List *clauses,
int varRelid);
+extern Selectivity clause_selectivity(Query *root,
+ Node *clause,
+ int varRelid);
#endif /* COST_H */
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: pathnode.h,v 1.37 2001/05/20 20:28:20 tgl Exp $
+ * $Id: pathnode.h,v 1.38 2001/06/05 05:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern void set_cheapest(RelOptInfo *parent_rel);
extern void add_path(RelOptInfo *parent_rel, Path *new_path);
-extern Path *create_seqscan_path(RelOptInfo *rel);
+extern Path *create_seqscan_path(Query *root, RelOptInfo *rel);
extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
IndexOptInfo *index,
List *restriction_clauses,
List *pathkeys,
ScanDirection indexscandir);
-extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval);
+extern TidPath *create_tidscan_path(Query *root, RelOptInfo *rel,
+ List *tideval);
extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
extern Path *create_subqueryscan_path(RelOptInfo *rel);
-extern NestPath *create_nestloop_path(RelOptInfo *joinrel,
- JoinType jointype,
- Path *outer_path,
- Path *inner_path,
- List *restrict_clauses,
- List *pathkeys);
+extern NestPath *create_nestloop_path(Query *root,
+ RelOptInfo *joinrel,
+ JoinType jointype,
+ Path *outer_path,
+ Path *inner_path,
+ List *restrict_clauses,
+ List *pathkeys);
-extern MergePath *create_mergejoin_path(RelOptInfo *joinrel,
- JoinType jointype,
- Path *outer_path,
- Path *inner_path,
- List *restrict_clauses,
- List *pathkeys,
- List *mergeclauses,
- List *outersortkeys,
- List *innersortkeys);
+extern MergePath *create_mergejoin_path(Query *root,
+ RelOptInfo *joinrel,
+ JoinType jointype,
+ Path *outer_path,
+ Path *inner_path,
+ List *restrict_clauses,
+ List *pathkeys,
+ List *mergeclauses,
+ List *outersortkeys,
+ List *innersortkeys);
-extern HashPath *create_hashjoin_path(RelOptInfo *joinrel,
- JoinType jointype,
- Path *outer_path,
- Path *inner_path,
- List *restrict_clauses,
- List *hashclauses,
- Selectivity innerbucketsize);
+extern HashPath *create_hashjoin_path(Query *root,
+ RelOptInfo *joinrel,
+ JoinType jointype,
+ Path *outer_path,
+ Path *inner_path,
+ List *restrict_clauses,
+ List *hashclauses);
/*
* prototypes for relnode.c
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: planmain.h,v 1.50 2001/03/22 04:00:55 momjian Exp $
+ * $Id: planmain.h,v 1.51 2001/06/05 05:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
Index scanrelid, Plan *subplan);
extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
-extern Sort *make_sort(List *tlist, Plan *lefttree, int keycount);
-extern Sort *make_sort_from_pathkeys(List *tlist, Plan *lefttree,
- List *pathkeys);
+extern Sort *make_sort(Query *root, List *tlist,
+ Plan *lefttree, int keycount);
+extern Sort *make_sort_from_pathkeys(Query *root, List *tlist,
+ Plan *lefttree, List *pathkeys);
extern Agg *make_agg(List *tlist, List *qual, Plan *lefttree);
extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
AttrNumber *grpColIdx, Plan *lefttree);
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: planner.h,v 1.19 2001/01/24 19:43:26 momjian Exp $
+ * $Id: planner.h,v 1.20 2001/06/05 05:26:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Plan *planner(Query *parse);
extern Plan *subquery_planner(Query *parse, double tuple_fraction);
-extern Plan *make_sortplan(List *tlist, Plan *plannode, List *sortcls);
+extern Plan *make_sortplan(Query *parse, List *tlist,
+ Plan *plannode, List *sortcls);
#endif /* PLANNER_H */