* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.271 2004/01/04 00:07:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.272 2004/01/04 03:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
COPY_NODE_FIELD(clause);
COPY_SCALAR_FIELD(ispusheddown);
COPY_SCALAR_FIELD(canjoin);
+ COPY_BITMAPSET_FIELD(clause_relids);
COPY_BITMAPSET_FIELD(left_relids);
COPY_BITMAPSET_FIELD(right_relids);
COPY_NODE_FIELD(orclause);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.224 2004/01/04 00:07:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.225 2004/01/04 03:51:52 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
WRITE_NODE_FIELD(clause);
WRITE_BOOL_FIELD(ispusheddown);
WRITE_BOOL_FIELD(canjoin);
+ WRITE_BITMAPSET_FIELD(clause_relids);
WRITE_BITMAPSET_FIELD(left_relids);
WRITE_BITMAPSET_FIELD(right_relids);
WRITE_NODE_FIELD(orclause);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.62 2003/12/29 21:44:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.63 2004/01/04 03:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* ROUTINES TO COMPUTE SELECTIVITIES
****************************************************************************/
-/*
- * restrictlist_selectivity -
- * Compute the selectivity of an implicitly-ANDed list of RestrictInfo
- * clauses.
- *
- * This is the same as clauselist_selectivity except for the representation
- * of the clause list.
- */
-Selectivity
-restrictlist_selectivity(Query *root,
- List *restrictinfo_list,
- int varRelid,
- JoinType jointype)
-{
- List *clauselist = get_actual_clauses(restrictinfo_list);
- Selectivity result;
-
- result = clauselist_selectivity(root, clauselist, varRelid, jointype);
- freeList(clauselist);
- return result;
-}
-
/*
* clauselist_selectivity -
* Compute the selectivity of an implicitly-ANDed list of boolean
* expression clauses. The list can be empty, in which case 1.0
- * must be returned.
+ * must be returned. List elements may be either RestrictInfos
+ * or bare expression clauses --- the former is preferred since
+ * it allows caching of results.
*
* See clause_selectivity() for the meaning of the additional parameters.
*
foreach(clist, clauses)
{
Node *clause = (Node *) lfirst(clist);
+ RestrictInfo *rinfo;
Selectivity s2;
+ /* Always compute the selectivity using clause_selectivity */
+ s2 = clause_selectivity(root, clause, varRelid, jointype);
+
+ /*
+ * Check for being passed a RestrictInfo.
+ */
+ if (IsA(clause, RestrictInfo))
+ {
+ rinfo = (RestrictInfo *) clause;
+ clause = (Node *) rinfo->clause;
+ }
+ else
+ rinfo = NULL;
+
/*
* See if it looks like a restriction clause with a pseudoconstant
* on one side. (Anything more complicated than that might not
- * behave in the simple way we are expecting.)
- *
- * NB: for consistency of results, this fragment of code had better
- * match what clause_selectivity() would do in the cases it
- * handles.
+ * behave in the simple way we are expecting.) Most of the tests
+ * here can be done more efficiently with rinfo than without.
*/
- if (is_opclause(clause) &&
- (varRelid != 0 || NumRelids(clause) == 1))
+ if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2)
{
OpExpr *expr = (OpExpr *) clause;
+ bool varonleft = true;
+ bool ok;
- if (length(expr->args) == 2)
+ if (rinfo)
{
- bool varonleft = true;
+ ok = (bms_membership(rinfo->clause_relids) == BMS_SINGLETON) &&
+ (is_pseudo_constant_clause_relids(lsecond(expr->args),
+ rinfo->right_relids) ||
+ (varonleft = false,
+ is_pseudo_constant_clause_relids(lfirst(expr->args),
+ rinfo->left_relids)));
+ }
+ else
+ {
+ ok = (NumRelids(clause) == 1) &&
+ (is_pseudo_constant_clause(lsecond(expr->args)) ||
+ (varonleft = false,
+ is_pseudo_constant_clause(lfirst(expr->args))));
+ }
- if (is_pseudo_constant_clause(lsecond(expr->args)) ||
- (varonleft = false,
- is_pseudo_constant_clause(lfirst(expr->args))))
+ if (ok)
+ {
+ /*
+ * If it's not a "<" or ">" operator, just merge the
+ * selectivity in generically. But if it's the
+ * right oprrest, add the clause to rqlist for later
+ * processing.
+ */
+ switch (get_oprrest(expr->opno))
{
- Oid opno = expr->opno;
- RegProcedure oprrest = get_oprrest(opno);
-
- s2 = restriction_selectivity(root, opno,
- expr->args, varRelid);
-
- /*
- * If we reach here, we have computed the same result
- * that clause_selectivity would, so we can just use
- * s2 if it's the wrong oprrest. But if it's the
- * right oprrest, add the clause to rqlist for later
- * processing.
- */
- switch (oprrest)
- {
- case F_SCALARLTSEL:
- addRangeClause(&rqlist, clause,
- varonleft, true, s2);
- break;
- case F_SCALARGTSEL:
- addRangeClause(&rqlist, clause,
- varonleft, false, s2);
- break;
- default:
- /* Just merge the selectivity in generically */
- s1 = s1 * s2;
- break;
- }
- continue; /* drop to loop bottom */
+ case F_SCALARLTSEL:
+ addRangeClause(&rqlist, clause,
+ varonleft, true, s2);
+ break;
+ case F_SCALARGTSEL:
+ addRangeClause(&rqlist, clause,
+ varonleft, false, s2);
+ break;
+ default:
+ /* Just merge the selectivity in generically */
+ s1 = s1 * s2;
+ break;
}
+ continue; /* drop to loop bottom */
}
}
+
/* Not the right form, so treat it generically. */
- s2 = clause_selectivity(root, clause, varRelid, jointype);
s1 = s1 * s2;
}
*rqlist = rqelem;
}
+/*
+ * bms_is_subset_singleton
+ *
+ * Same result as bms_is_subset(s, bms_make_singleton(x)),
+ * but a little faster and doesn't leak memory.
+ *
+ * Is this of use anywhere else? If so move to bitmapset.c ...
+ */
+static bool
+bms_is_subset_singleton(const Bitmapset *s, int x)
+{
+ switch (bms_membership(s))
+ {
+ case BMS_EMPTY_SET:
+ return true;
+ case BMS_SINGLETON:
+ return bms_is_member(x, s);
+ case BMS_MULTIPLE:
+ return false;
+ }
+ /* can't get here... */
+ return false;
+}
+
/*
* clause_selectivity -
* Compute the selectivity of a general boolean expression clause.
*
+ * The clause can be either a RestrictInfo or a plain expression. If it's
+ * a RestrictInfo, we try to cache the selectivity for possible re-use,
+ * so passing RestrictInfos is preferred.
+ *
* varRelid is either 0 or a rangetable index.
*
* When varRelid is not 0, only variables belonging to that relation are
JoinType jointype)
{
Selectivity s1 = 1.0; /* default for any unhandled clause type */
+ RestrictInfo *rinfo = NULL;
+ bool cacheable = false;
- if (clause == NULL)
+ if (clause == NULL) /* can this still happen? */
return s1;
+
+ if (IsA(clause, RestrictInfo))
+ {
+ rinfo = (RestrictInfo *) clause;
+
+ /*
+ * If possible, cache the result of the selectivity calculation for
+ * the clause. We can cache if varRelid is zero or the clause
+ * contains only vars of that relid --- otherwise varRelid will affect
+ * the result, so mustn't cache. We ignore the possibility that
+ * jointype will affect the result, which should be okay because outer
+ * join clauses will always be examined with the same jointype value.
+ */
+ if (varRelid == 0 ||
+ bms_is_subset_singleton(rinfo->clause_relids, varRelid))
+ {
+ /* Cacheable --- do we already have the result? */
+ if (rinfo->this_selec >= 0)
+ return rinfo->this_selec;
+ cacheable = true;
+ }
+
+ /* Proceed with examination of contained clause */
+ clause = (Node *) rinfo->clause;
+ }
+
if (IsA(clause, Var))
{
Var *var = (Var *) clause;
else if (or_clause(clause))
{
/*
- * Selectivities for an 'or' clause are computed as s1+s2 - s1*s2
- * to account for the probable overlap of selected tuple sets. XXX
- * is this too conservative?
+ * Selectivities for an OR clause are computed as s1+s2 - s1*s2
+ * to account for the probable overlap of selected tuple sets.
+ *
+ * XXX is this too conservative?
*/
List *arg;
{
/*
* Otherwise, it's a join if there's more than one relation
- * used.
+ * used. We can optimize this calculation if an rinfo was passed.
*/
- is_join_clause = (NumRelids(clause) > 1);
+ if (rinfo)
+ is_join_clause = (bms_membership(rinfo->clause_relids) ==
+ BMS_MULTIPLE);
+ else
+ is_join_clause = (NumRelids(clause) > 1);
}
if (is_join_clause)
jointype);
}
+ /* Cache the result if possible */
+ if (cacheable)
+ rinfo->this_selec = s1;
+
#ifdef SELECTIVITY_DEBUG
elog(DEBUG4, "clause_selectivity: s1 %f", s1);
#endif /* SELECTIVITY_DEBUG */
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.118 2003/12/18 03:46:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.119 2004/01/04 03:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* 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. Now
+ * This is quick-and-dirty because we bypass clauselist_selectivity, and
+ * simply multiply the independent clause selectivities together. Now
* clauselist_selectivity often can't do any better than that anyhow, but
- * for some situations (such as range constraints) it is smarter.
+ * for some situations (such as range constraints) it is smarter. However,
+ * we can't effectively cache the results of clauselist_selectivity, whereas
+ * the individual clause selectivities can be and are cached.
*
* Since we are only using the results to estimate how many potential
* output tuples are generated and passed through qpqual checking, it
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,
- jointype);
- selec = restrictinfo->this_selec;
- }
- else
- {
- /* If it's a bare expression, must always do it the hard way */
- selec = clause_selectivity(root, qual, 0, jointype);
- }
- total *= selec;
+ /* Note that clause_selectivity will be able to cache its result */
+ total *= clause_selectivity(root, qual, 0, jointype);
}
return total;
}
Assert(rel->relid > 0);
temp = rel->tuples *
- restrictlist_selectivity(root,
- rel->baserestrictinfo,
- rel->relid,
- JOIN_INNER);
+ clauselist_selectivity(root,
+ rel->baserestrictinfo,
+ 0,
+ JOIN_INNER);
/*
* Force estimate to be at least one row, to make explain output look
* not double-counting them because they were not considered in
* estimating the sizes of the component rels.
*/
- selec = restrictlist_selectivity(root,
- restrictlist,
- 0,
- jointype);
+ selec = clauselist_selectivity(root,
+ restrictlist,
+ 0,
+ jointype);
/*
* Basically, we multiply size of Cartesian product by selectivity.
/* Now estimate number of output rows */
temp = rel->tuples *
- restrictlist_selectivity(root,
- rel->baserestrictinfo,
- rel->relid,
- JOIN_INNER);
+ clauselist_selectivity(root,
+ rel->baserestrictinfo,
+ 0,
+ JOIN_INNER);
/*
* Force estimate to be at least one row, to make explain output look
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.152 2004/01/04 00:07:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.153 2004/01/04 03:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* the full set of clauses that must be considered to compute the
* correct selectivity. (Without the union operation, we might have
* some restriction clauses appearing twice, which'd mislead
- * restrictlist_selectivity into double-counting their selectivity.
+ * clauselist_selectivity into double-counting their selectivity.
* However, since RestrictInfo nodes aren't copied when linking them
* into different lists, it should be sufficient to use pointer
* comparison to remove duplicates.)
*/
allclauses = set_ptrUnion(rel->baserestrictinfo, allclauses);
pathnode->rows = rel->tuples *
- restrictlist_selectivity(root,
- allclauses,
- rel->relid,
- JOIN_INNER);
+ clauselist_selectivity(root,
+ allclauses,
+ rel->relid, /* do not use 0! */
+ JOIN_INNER);
/* Like costsize.c, force estimate to be at least one row */
if (pathnode->rows < 1.0)
pathnode->rows = 1.0;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.95 2004/01/04 00:07:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.96 2004/01/04 03:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
Relids qualscope)
{
Relids relids;
- List *vars;
bool can_be_equijoin;
RestrictInfo *restrictinfo;
RelOptInfo *rel;
+ List *vars;
/*
- * Retrieve all relids and vars contained within the clause.
+ * Retrieve all relids mentioned within the clause.
*/
- clause_get_relids_vars(clause, &relids, &vars);
+ relids = pull_varnos(clause);
/*
* Cross-check: clause should contain no relids not within its scope.
* that scan those relations (else they won't be available at
* the join node!).
*/
+ vars = pull_var_clause(clause, false);
add_vars_to_targetlist(root, vars, relids);
+ freeList(vars);
break;
default:
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.106 2004/01/04 00:07:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.107 2004/01/04 03:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
newinfo->orclause = (Expr *)
adjust_inherited_attrs_mutator((Node *) oldinfo->orclause, context);
- /*
- * Adjust left/right relid sets too.
- */
+ /* adjust relid sets too */
+ newinfo->clause_relids = adjust_relid_set(oldinfo->clause_relids,
+ context->old_rt_index,
+ context->new_rt_index);
newinfo->left_relids = adjust_relid_set(oldinfo->left_relids,
context->old_rt_index,
context->new_rt_index);
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.158 2003/12/30 23:53:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.159 2004/01/04 03:51:52 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
* *
*****************************************************************************/
-/*
- * clause_get_relids_vars
- * Retrieves distinct relids and vars appearing within a clause.
- *
- * '*relids' is set to the set of all distinct "varno"s appearing
- * in Vars within the clause.
- * '*vars' is set to a list of all distinct Vars appearing within the clause.
- * Var nodes are considered distinct if they have different varno
- * or varattno values. If there are several occurrences of the same
- * varno/varattno, you get a randomly chosen one...
- *
- * Note that upper-level vars are ignored, since they normally will
- * become Params with respect to this query level.
- */
-void
-clause_get_relids_vars(Node *clause, Relids *relids, List **vars)
-{
- List *clvars = pull_var_clause(clause, false);
- Relids varnos = NULL;
- List *var_list = NIL;
- List *i;
-
- foreach(i, clvars)
- {
- Var *var = (Var *) lfirst(i);
- List *vi;
-
- varnos = bms_add_member(varnos, var->varno);
- foreach(vi, var_list)
- {
- Var *in_list = (Var *) lfirst(vi);
-
- if (in_list->varno == var->varno &&
- in_list->varattno == var->varattno)
- break;
- }
- if (vi == NIL)
- var_list = lcons(var, var_list);
- }
- freeList(clvars);
-
- *relids = varnos;
- *vars = var_list;
-}
-
/*
* NumRelids
* (formerly clause_relids)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.22 2004/01/04 00:07:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.23 2004/01/04 03:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* If it's a binary opclause, set up left/right relids info.
+ * In any case set up the total clause relids info.
*/
if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2)
{
restrictinfo->left_relids = pull_varnos(get_leftop(clause));
restrictinfo->right_relids = pull_varnos(get_rightop(clause));
+ restrictinfo->clause_relids = bms_union(restrictinfo->left_relids,
+ restrictinfo->right_relids);
+
/*
* Does it look like a normal join clause, i.e., a binary operator
* relating expressions that come from distinct relations? If so
}
else
{
- /* Not a binary opclause, so mark both relid sets as empty */
+ /* Not a binary opclause, so mark left/right relid sets as empty */
restrictinfo->left_relids = NULL;
restrictinfo->right_relids = NULL;
+ /* and get the total relid set the hard way */
+ restrictinfo->clause_relids = pull_varnos((Node *) clause);
}
/*
bool redundant = false;
List *refitem;
+ /* do the cheap test first: is it a "var = const" clause? */
+ if (bms_is_empty(rinfo->left_relids) ||
+ bms_is_empty(rinfo->right_relids))
+ return false; /* var = const, so not redundant */
+
cache_mergeclause_pathkeys(root, rinfo);
- /* do the cheap tests first */
foreach(refitem, reference_list)
{
RestrictInfo *refrinfo = (RestrictInfo *) lfirst(refitem);
}
if (redundant)
- {
- /* It looks redundant, but check for "var = const" case */
- if (!bms_is_empty(rinfo->left_relids) &&
- !bms_is_empty(rinfo->right_relids))
- return true; /* var = var, so redundant */
- /* else var = const, not redundant */
- }
+ return true; /* var = var, so redundant */
}
/* otherwise, not redundant */
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.89 2004/01/04 00:07:32 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.90 2004/01/04 03:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
bool canjoin;
+ /* The set of relids (varnos) referenced in the clause: */
+ Relids clause_relids;
+
/* These fields are set for any binary opclause: */
Relids left_relids; /* relids in left side of clause */
Relids right_relids; /* relids in right side of clause */
/* This field is NULL unless clause is an OR clause: */
Expr *orclause; /* modified clause with RestrictInfos */
- /* cache space for costs (currently only used for join clauses) */
+ /* cache space for cost and selectivity */
QualCost eval_cost; /* eval cost of clause; -1 if not yet set */
Selectivity this_selec; /* selectivity; -1 if not yet set */
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.70 2003/12/30 23:53:15 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.71 2004/01/04 03:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern bool has_distinct_on_clause(Query *query);
-extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
extern int NumRelids(Node *clause);
extern void CommuteClause(OpExpr *clause);
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.58 2003/11/29 22:41:07 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.59 2004/01/04 03:51:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* prototypes for clausesel.c
* routines to compute clause selectivities
*/
-extern Selectivity restrictlist_selectivity(Query *root,
- List *restrictinfo_list,
- int varRelid,
- JoinType jointype);
extern Selectivity clauselist_selectivity(Query *root,
List *clauses,
int varRelid,