384 lines
9.4 KiB
C
384 lines
9.4 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* clausesel.c--
|
|
* Routines to compute and set clause selectivities
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.3 1997/09/08 02:24:14 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "nodes/pg_list.h"
|
|
#include "nodes/relation.h"
|
|
#include "nodes/primnodes.h"
|
|
|
|
#include "optimizer/internal.h"
|
|
#include "optimizer/clauses.h"
|
|
#include "optimizer/clauseinfo.h"
|
|
#include "optimizer/cost.h"
|
|
#include "optimizer/plancat.h"
|
|
|
|
#include "parser/parsetree.h" /* for getrelid() */
|
|
|
|
#include "catalog/pg_proc.h"
|
|
#include "catalog/pg_operator.h"
|
|
|
|
#include "utils/elog.h"
|
|
#include "utils/lsyscache.h"
|
|
|
|
static Cost compute_selec(Query * root, List * clauses, List * or_selectivities);
|
|
|
|
/****************************************************************************
|
|
* ROUTINES TO SET CLAUSE SELECTIVITIES
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* set_clause_selectivities -
|
|
* Sets the selectivity field for each of clause in 'clauseinfo-list'
|
|
* to 'new-selectivity'. If the selectivity has already been set, reset
|
|
* it only if the new one is better.
|
|
*
|
|
* Returns nothing of interest.
|
|
*
|
|
*/
|
|
void
|
|
set_clause_selectivities(List * clauseinfo_list, Cost new_selectivity)
|
|
{
|
|
List *temp;
|
|
CInfo *clausenode;
|
|
Cost cost_clause;
|
|
|
|
foreach(temp, clauseinfo_list)
|
|
{
|
|
clausenode = (CInfo *) lfirst(temp);
|
|
cost_clause = clausenode->selectivity;
|
|
if (FLOAT_IS_ZERO(cost_clause) || new_selectivity < cost_clause)
|
|
{
|
|
clausenode->selectivity = new_selectivity;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* product_selec -
|
|
* Multiplies the selectivities of each clause in 'clauseinfo-list'.
|
|
*
|
|
* Returns a flonum corresponding to the selectivity of 'clauseinfo-list'.
|
|
*/
|
|
Cost
|
|
product_selec(List * clauseinfo_list)
|
|
{
|
|
Cost result = 1.0;
|
|
|
|
if (clauseinfo_list != NIL)
|
|
{
|
|
List *xclausenode = NIL;
|
|
Cost temp;
|
|
|
|
foreach(xclausenode, clauseinfo_list)
|
|
{
|
|
temp = ((CInfo *) lfirst(xclausenode))->selectivity;
|
|
result = result * temp;
|
|
}
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
/*
|
|
* set_rest_relselec -
|
|
* Scans through clauses on each relation and assigns a selectivity to
|
|
* those clauses that haven't been assigned a selectivity by an index.
|
|
*
|
|
* Returns nothing of interest.
|
|
* MODIFIES: selectivities of the various rel's clauseinfo
|
|
* slots.
|
|
*/
|
|
void
|
|
set_rest_relselec(Query * root, List * rel_list)
|
|
{
|
|
Rel *rel;
|
|
List *x;
|
|
|
|
foreach(x, rel_list)
|
|
{
|
|
rel = (Rel *) lfirst(x);
|
|
set_rest_selec(root, rel->clauseinfo);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* set_rest_selec -
|
|
* Sets the selectivity fields for those clauses within a single
|
|
* relation's 'clauseinfo-list' that haven't already been set.
|
|
*
|
|
* Returns nothing of interest.
|
|
*
|
|
*/
|
|
void
|
|
set_rest_selec(Query * root, List * clauseinfo_list)
|
|
{
|
|
List *temp = NIL;
|
|
CInfo *clausenode = (CInfo *) NULL;
|
|
Cost cost_clause;
|
|
|
|
foreach(temp, clauseinfo_list)
|
|
{
|
|
clausenode = (CInfo *) lfirst(temp);
|
|
cost_clause = clausenode->selectivity;
|
|
|
|
/*
|
|
* Check to see if the selectivity of this clause or any 'or'
|
|
* subclauses (if any) haven't been set yet.
|
|
*/
|
|
if (valid_or_clause(clausenode) || FLOAT_IS_ZERO(cost_clause))
|
|
{
|
|
clausenode->selectivity =
|
|
compute_clause_selec(root,
|
|
(Node *) clausenode->clause,
|
|
lcons(makeFloat(cost_clause), NIL));
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* ROUTINES TO COMPUTE SELECTIVITIES
|
|
****************************************************************************/
|
|
|
|
/*
|
|
* compute_clause_selec -
|
|
* Given a clause, this routine will compute the selectivity of the
|
|
* clause by calling 'compute_selec' with the appropriate parameters
|
|
* and possibly use that return value to compute the real selectivity
|
|
* of a clause.
|
|
*
|
|
* 'or-selectivities' are selectivities that have already been assigned
|
|
* to subclauses of an 'or' clause.
|
|
*
|
|
* Returns a flonum corresponding to the clause selectivity.
|
|
*
|
|
*/
|
|
Cost
|
|
compute_clause_selec(Query * root, Node * clause, List * or_selectivities)
|
|
{
|
|
if (!is_opclause(clause))
|
|
{
|
|
|
|
/*
|
|
* if it's not an operator clause, then it is a boolean clause
|
|
* -jolly
|
|
*/
|
|
|
|
/*
|
|
* Boolean variables get a selectivity of 1/2.
|
|
*/
|
|
return (0.1);
|
|
}
|
|
else if (not_clause(clause))
|
|
{
|
|
|
|
/*
|
|
* 'not' gets "1.0 - selectivity-of-inner-clause".
|
|
*/
|
|
return (1.000000 - compute_selec(root,
|
|
lcons(get_notclausearg((Expr *) clause),
|
|
NIL),
|
|
or_selectivities));
|
|
}
|
|
else if (or_clause(clause))
|
|
{
|
|
|
|
/*
|
|
* Both 'or' and 'and' clauses are evaluated as described in
|
|
* (compute_selec).
|
|
*/
|
|
return (compute_selec(root,
|
|
((Expr *) clause)->args, or_selectivities));
|
|
}
|
|
else
|
|
{
|
|
return (compute_selec(root,
|
|
lcons(clause, NIL), or_selectivities));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* compute_selec -
|
|
* Computes the selectivity of a clause.
|
|
*
|
|
* If there is more than one clause in the argument 'clauses', then the
|
|
* desired selectivity is that of an 'or' clause. Selectivities for an
|
|
* 'or' clause such as (OR a b) are computed by finding the selectivity
|
|
* of a (s1) and b (s2) and computing s1+s2 - s1*s2.
|
|
*
|
|
* In addition, if the clause is an 'or' clause, individual selectivities
|
|
* may have already been assigned by indices to subclauses. These values
|
|
* are contained in the list 'or-selectivities'.
|
|
*
|
|
* Returns the clause selectivity as a flonum.
|
|
*
|
|
*/
|
|
static Cost
|
|
compute_selec(Query * root, List * clauses, List * or_selectivities)
|
|
{
|
|
Cost s1 = 0;
|
|
List *clause = lfirst(clauses);
|
|
|
|
if (clauses == NULL)
|
|
{
|
|
s1 = 1.0;
|
|
}
|
|
else if (IsA(clause, Param))
|
|
{
|
|
/* XXX How're we handling this before?? -ay */
|
|
s1 = 1.0;
|
|
}
|
|
else if (IsA(clause, Const))
|
|
{
|
|
s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0;
|
|
}
|
|
else if (IsA(clause, Var))
|
|
{
|
|
Oid relid = getrelid(((Var *) clause)->varno,
|
|
root->rtable);
|
|
|
|
/*
|
|
* we have a bool Var. This is exactly equivalent to the clause:
|
|
* reln.attribute = 't' so we compute the selectivity as if that
|
|
* is what we have. The magic #define constants are a hack. I
|
|
* didn't want to have to do system cache look ups to find out all
|
|
* of that info.
|
|
*/
|
|
|
|
s1 = restriction_selectivity(EqualSelectivityProcedure,
|
|
BooleanEqualOperator,
|
|
relid,
|
|
((Var *) clause)->varoattno,
|
|
"t",
|
|
_SELEC_CONSTANT_RIGHT_);
|
|
}
|
|
else if (or_selectivities)
|
|
{
|
|
/* If s1 has already been assigned by an index, use that value. */
|
|
List *this_sel = lfirst(or_selectivities);
|
|
|
|
s1 = floatVal(this_sel);
|
|
}
|
|
else if (is_funcclause((Node *) clause))
|
|
{
|
|
/* this isn't an Oper, it's a Func!! */
|
|
|
|
/*
|
|
* * This is not an operator, so we guess at the selectivity. *
|
|
* THIS IS A HACK TO GET V4 OUT THE DOOR. FUNCS SHOULD BE * ABLE
|
|
* TO HAVE SELECTIVITIES THEMSELVES. * -- JMH 7/9/92
|
|
*/
|
|
s1 = 0.1;
|
|
}
|
|
else if (NumRelids((Node *) clause) == 1)
|
|
{
|
|
|
|
/*
|
|
* ...otherwise, calculate s1 from 'clauses'. The clause is not a
|
|
* join clause, since there is only one relid in the clause. The
|
|
* clause selectivity will be based on the operator selectivity
|
|
* and operand values.
|
|
*/
|
|
Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
|
|
RegProcedure oprrest = get_oprrest(opno);
|
|
Oid relid;
|
|
int relidx;
|
|
AttrNumber attno;
|
|
Datum constval;
|
|
int flag;
|
|
|
|
get_relattval((Node *) clause, &relidx, &attno, &constval, &flag);
|
|
relid = getrelid(relidx, root->rtable);
|
|
|
|
/*
|
|
* if the oprrest procedure is missing for whatever reason, use a
|
|
* selectivity of 0.5
|
|
*/
|
|
if (!oprrest)
|
|
s1 = (Cost) (0.5);
|
|
else if (attno == InvalidAttrNumber)
|
|
{
|
|
|
|
/*
|
|
* attno can be Invalid if the clause had a function in it,
|
|
* i.e. WHERE myFunc(f) = 10
|
|
*/
|
|
/* this should be FIXED somehow to use function selectivity */
|
|
s1 = (Cost) (0.5);
|
|
}
|
|
else
|
|
s1 = (Cost) restriction_selectivity(oprrest,
|
|
opno,
|
|
relid,
|
|
attno,
|
|
(char *) constval,
|
|
flag);
|
|
|
|
}
|
|
else
|
|
{
|
|
|
|
/*
|
|
* The clause must be a join clause. The clause selectivity will
|
|
* be based on the relations to be scanned and the attributes they
|
|
* are to be joined on.
|
|
*/
|
|
Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
|
|
RegProcedure oprjoin = get_oprjoin(opno);
|
|
int relid1,
|
|
relid2;
|
|
AttrNumber attno1,
|
|
attno2;
|
|
|
|
get_rels_atts((Node *) clause, &relid1, &attno1, &relid2, &attno2);
|
|
relid1 = getrelid(relid1, root->rtable);
|
|
relid2 = getrelid(relid2, root->rtable);
|
|
|
|
/*
|
|
* if the oprjoin procedure is missing for whatever reason, use a
|
|
* selectivity of 0.5
|
|
*/
|
|
if (!oprjoin)
|
|
s1 = (Cost) (0.5);
|
|
else
|
|
s1 = (Cost) join_selectivity(oprjoin,
|
|
opno,
|
|
relid1,
|
|
attno1,
|
|
relid2,
|
|
attno2);
|
|
}
|
|
|
|
/*
|
|
* A null clause list eliminates no tuples, so return a selectivity of
|
|
* 1.0. If there is only one clause, the selectivity is not that of
|
|
* an 'or' clause, but rather that of the single clause.
|
|
*/
|
|
|
|
if (length(clauses) < 2)
|
|
{
|
|
return (s1);
|
|
}
|
|
else
|
|
{
|
|
/* Compute selectivity of the 'or'ed subclauses. */
|
|
/* Added check for taking lnext(NIL). -- JMH 3/9/92 */
|
|
Cost s2;
|
|
|
|
if (or_selectivities != NIL)
|
|
s2 = compute_selec(root, lnext(clauses), lnext(or_selectivities));
|
|
else
|
|
s2 = compute_selec(root, lnext(clauses), NIL);
|
|
return (s1 + s2 - s1 * s2);
|
|
}
|
|
}
|