aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/path/clausesel.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/path/clausesel.c')
-rw-r--r--src/backend/optimizer/path/clausesel.c331
1 files changed, 331 insertions, 0 deletions
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
new file mode 100644
index 00000000000..634e1130794
--- /dev/null
+++ b/src/backend/optimizer/path/clausesel.c
@@ -0,0 +1,331 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.1.1.1 1996/07/09 06:21:35 scrappy 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);
+ }
+}
+