]> granicus.if.org Git - postgresql/commitdiff
Clean up messy clause-selectivity code in clausesel.c; repair bug
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 24 Jul 1999 23:21:14 +0000 (23:21 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 24 Jul 1999 23:21:14 +0000 (23:21 +0000)
identified by Hiroshi (incorrect cost attributed to OR clauses
after multiple passes through set_rest_selec()).  I think the code
was trying to allow selectivities of OR subclauses to be passed in
from outside, but noplace was actually passing any useful data, and
set_rest_selec() was passing wrong data.

Restructure representation of "indexqual" in IndexPath nodes so that
it is the same as for indxqual in completed IndexScan nodes: namely,
a toplevel list with an entry for each pass of the index scan, having
sublists that are implicitly-ANDed index qual conditions for that pass.
You don't want to know what the old representation was :-(

Improve documentation of OR-clause indexscan functions.

Remove useless 'notclause' field from RestrictInfo nodes.  (This might
force an initdb for anyone who has stored rules containing RestrictInfos,
but I do not think that RestrictInfo ever appears in completed plans.)

17 files changed:
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/clausesel.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/orindxpath.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/util/clauses.c
src/backend/optimizer/util/pathnode.c
src/backend/optimizer/util/restrictinfo.c
src/include/nodes/relation.h
src/include/optimizer/clauses.h
src/include/optimizer/cost.h
src/include/optimizer/restrictinfo.h

index 217bfc54471cd97df0dc61cacb6cbe45774a32cf..ba412332058f79af0ae60ce02ff5d4b578eb4076 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.86 1999/07/17 20:17:05 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.87 1999/07/24 23:21:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1317,7 +1317,6 @@ _copyRestrictInfo(RestrictInfo *from)
        Node_Copy(from, newnode, clause);
 
        newnode->selectivity = from->selectivity;
-       newnode->notclause = from->notclause;
 
        Node_Copy(from, newnode, indexids);
        Node_Copy(from, newnode, mergejoinorder);
index 9f16875f343fc94cfeb5f589b5eac11a65907699..5560deaea53a5569099118f9a120ab7cf8efc1da 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.43 1999/07/17 20:17:05 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.44 1999/07/24 23:21:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -305,8 +305,6 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
                return false;
        if (a->selectivity != b->selectivity)
                return false;
-       if (a->notclause != b->notclause)
-               return false;
 #ifdef EqualMergeOrderExists
        if (!EqualMergeOrder(a->mergejoinorder, b->mergejoinorder))
                return false;
index 23c10fed5ed75071bfd554a891495a283de9c90c..4272bf04cc5208e6e0b6d5858d18a5618761c850 100644 (file)
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *     $Id: outfuncs.c,v 1.90 1999/07/18 19:02:49 tgl Exp $
+ *     $Id: outfuncs.c,v 1.91 1999/07/24 23:21:07 tgl Exp $
  *
  * NOTES
  *       Every (plan) node in POSTGRES has an associated "out" routine which
@@ -1110,9 +1110,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
        _outNode(str, node->clause);
 
        appendStringInfo(str,
-                                        " :selectivity %f :notclause %s :indexids ",
-                                        node->selectivity,
-                                        node->notclause ? "true" : "false");
+                                        " :selectivity %f :indexids ",
+                                        node->selectivity);
        _outNode(str, node->indexids);
 
        appendStringInfo(str, " :mergejoinorder ");
index 9d46b7e39da2df8616618e92b13b5a1b4b38d4b6..e16398fb85481b10b9dc20d3d162094bec38133e 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.69 1999/07/17 20:17:08 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.70 1999/07/24 23:21:08 tgl Exp $
  *
  * NOTES
  *       Most of the read functions for plan nodes are tested. (In fact, they
@@ -1856,14 +1856,6 @@ _readRestrictInfo()
 
        local_node->selectivity = atof(token);
 
-       token = lsptok(NULL, &length);          /* get :notclause */
-       token = lsptok(NULL, &length);          /* now read it */
-
-       if (!strncmp(token, "true", 4))
-               local_node->notclause = true;
-       else
-               local_node->notclause = false;
-
        token = lsptok(NULL, &length);          /* get :indexids */
        local_node->indexids = nodeRead(true);          /* now read it */
 
index 220b3cd0471e6be40392614f495fa1ea4ebf0edd..373d982c497825a705595b5c63a205bb04caab81 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.50 1999/07/17 20:17:11 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.51 1999/07/24 23:21:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,8 +86,8 @@ make_one_rel(Query *root, List *rels)
  * set_base_rel_pathlist
  *       Finds all paths available for scanning each relation entry in
  *       'rels'.  Sequential scan and any available indices are considered
- *       if possible(indices are not considered for lower nesting levels).
- *       All unique paths are attached to the relation's 'pathlist' field.
+ *       if possible (indices are not considered for lower nesting levels).
+ *       All useful paths are attached to the relation's 'pathlist' field.
  *
  *       MODIFIES: rels
  */
@@ -98,21 +98,32 @@ set_base_rel_pathlist(Query *root, List *rels)
 
        foreach(temp, rels)
        {
+               RelOptInfo *rel = (RelOptInfo *) lfirst(temp);
+               List       *indices = find_relation_indices(root, rel);
                List       *sequential_scan_list;
                List       *rel_index_scan_list;
                List       *or_index_scan_list;
-               RelOptInfo *rel = (RelOptInfo *) lfirst(temp);
 
                sequential_scan_list = lcons(create_seqscan_path(rel), NIL);
 
                rel_index_scan_list = create_index_paths(root,
                                                                                                 rel,
-                                                                               find_relation_indices(root, rel),
+                                                                                                indices,
                                                                                                 rel->restrictinfo,
                                                                                                 rel->joininfo);
 
-               or_index_scan_list = create_or_index_paths(root, rel, rel->restrictinfo);
+               /* Note: create_or_index_paths depends on create_index_paths
+                * to have marked OR restriction clauses with relevant indices;
+                * this is why it doesn't need to be given the full list of indices.
+                */
 
+               or_index_scan_list = create_or_index_paths(root, rel,
+                                                                                                  rel->restrictinfo);
+
+               /* add_pathlist will discard any paths that are dominated by
+                * another available path, keeping only those paths that are
+                * superior along at least one dimension of cost or sortedness.
+                */
                rel->pathlist = add_pathlist(rel,
                                                                         sequential_scan_list,
                                                                         nconc(rel_index_scan_list,
@@ -128,7 +139,6 @@ set_base_rel_pathlist(Query *root, List *rels)
                rel->size = compute_rel_size(rel);
                rel->width = compute_rel_width(rel);
        }
-       return;
 }
 
 /*
index df0b4aabb4c5df4f14bd0b28a0736139a5f52e22..00d3780a352d5827cc19c65d986a4ad7cecee57b 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.23 1999/07/16 04:59:14 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.24 1999/07/24 23:21:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #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 'restrictinfo-list'
- *       to 'new-selectivity'.  If the selectivity has already been set, reset
- *       it only if the new one is better.
- *
- * Returns nothing of interest.
- *
+ *       Sets the selectivity field for each clause in 'restrictinfo-list'
+ *       to 'new-selectivity'.  If the selectivity has already been set,
+ *       change it only if the new one is better.
  */
 void
 set_clause_selectivities(List *restrictinfo_list, Cost new_selectivity)
 {
-       List       *temp;
-       RestrictInfo *clausenode;
-       Cost            cost_clause;
+       List       *rlist;
 
-       foreach(temp, restrictinfo_list)
+       foreach(rlist, restrictinfo_list)
        {
-               clausenode = (RestrictInfo *) lfirst(temp);
-               cost_clause = clausenode->selectivity;
+               RestrictInfo *clausenode = (RestrictInfo *) lfirst(rlist);
+               Cost            cost_clause = clausenode->selectivity;
+
                if (cost_clause <= 0 || new_selectivity < cost_clause)
                        clausenode->selectivity = new_selectivity;
        }
@@ -63,18 +57,12 @@ set_clause_selectivities(List *restrictinfo_list, Cost new_selectivity)
 Cost
 product_selec(List *restrictinfo_list)
 {
-       Cost            result = 1.0;
+       Cost            result = (Cost) 1.0;
+       List       *rlist;
 
-       if (restrictinfo_list != NIL)
+       foreach(rlist, restrictinfo_list)
        {
-               List       *xclausenode = NIL;
-               Cost            temp;
-
-               foreach(xclausenode, restrictinfo_list)
-               {
-                       temp = ((RestrictInfo *) lfirst(xclausenode))->selectivity;
-                       result = result * temp;
-               }
+               result *= ((RestrictInfo *) lfirst(rlist))->selectivity;
        }
        return result;
 }
@@ -84,19 +72,16 @@ product_selec(List *restrictinfo_list)
  *       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 restrictinfo
- *               slots.
+ * MODIFIES: selectivities of the various rel's restrictinfo slots.
  */
 void
 set_rest_relselec(Query *root, List *rel_list)
 {
-       RelOptInfo *rel;
        List       *x;
 
        foreach(x, rel_list)
        {
-               rel = (RelOptInfo *) lfirst(x);
+               RelOptInfo *rel = (RelOptInfo *) lfirst(x);
                set_rest_selec(root, rel->restrictinfo);
        }
 }
@@ -105,31 +90,20 @@ set_rest_relselec(Query *root, List *rel_list)
  * set_rest_selec -
  *       Sets the selectivity fields for those clauses within a single
  *       relation's 'restrictinfo-list' that haven't already been set.
- *
- * Returns nothing of interest.
- *
  */
 void
 set_rest_selec(Query *root, List *restrictinfo_list)
 {
-       List       *temp = NIL;
-       RestrictInfo *clausenode = (RestrictInfo *) NULL;
-       Cost            cost_clause;
+       List       *rlist;
 
-       foreach(temp, restrictinfo_list)
+       foreach(rlist, restrictinfo_list)
        {
-               clausenode = (RestrictInfo *) lfirst(temp);
-               cost_clause = clausenode->selectivity;
+               RestrictInfo *clause = (RestrictInfo *) lfirst(rlist);
 
-               /*
-                * Check to see if the selectivity of this clause or any 'or'
-                * subclauses (if any) haven't been set yet.
-                */
-               if (cost_clause <= 0 || valid_or_clause(clausenode))
+               if (clause->selectivity <= 0)
                {
-                       clausenode->selectivity = compute_clause_selec(root,
-                                                                                        (Node *) clausenode->clause,
-                                                                        lcons(makeFloat(cost_clause), NIL));
+                       clause->selectivity =
+                               compute_clause_selec(root, (Node *) clause->clause);
                }
        }
 }
@@ -140,89 +114,29 @@ set_rest_selec(Query *root, List *restrictinfo_list)
 
 /*
  * 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))
-               return compute_selec(root, lcons(clause, NIL), or_selectivities);
-       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
+compute_clause_selec(Query *root, Node *clause)
 {
-       Cost            s1 = 0;
-       List       *clause = lfirst(clauses);
+       Cost            s1 = 1.0;               /* default for any unhandled clause type */
 
        if (clause == 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))
+               return s1;
+       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.
+                *
+                * XXX why are we using varno and varoattno?  Seems like it should
+                * be varno/varattno or varnoold/varoattno, not mix & match...
                 */
+               Oid                     relid = getrelid(((Var *) clause)->varno,
+                                                                        root->rtable);
 
                s1 = restriction_selectivity(F_EQSEL,
                                                                         BooleanEqualOperator,
@@ -231,134 +145,141 @@ compute_selec(Query *root, List *clauses, List *or_selectivities)
                                                                         "t",
                                                                         _SELEC_CONSTANT_RIGHT_);
        }
-       else if (or_selectivities)
+       else if (IsA(clause, Param))
        {
-               /* If s1 has already been assigned by an index, use that value. */
-               List       *this_sel = lfirst(or_selectivities);
-
-               s1 = floatVal(this_sel);
+               /* XXX any way to do better? */
+               s1 = 1.0;
        }
-       else if (is_funcclause((Node *) clause))
+       else if (IsA(clause, Const))
+       {
+               /* bool constant is pretty easy... */
+               s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0;
+       }
+       else if (not_clause(clause))
+       {
+               /* inverse of the selectivity of the underlying clause */
+               s1 = 1.0 - compute_clause_selec(root,
+                                                                               (Node *) get_notclausearg((Expr *) clause));
+       }
+       else if (and_clause(clause))
+       {
+               /* Use the product of the selectivities of the subclauses.
+                * XXX this is probably too optimistic, since the subclauses
+                * are very likely not independent...
+                */
+               List   *arg;
+               s1 = 1.0;
+               foreach(arg, ((Expr *) clause)->args)
+               {
+                       Cost            s2 = compute_clause_selec(root, (Node *) lfirst(arg));
+                       s1 = s1 * s2;
+               }
+       }
+       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?
+                */
+               List   *arg;
+               s1 = 0.0;
+               foreach(arg, ((Expr *) clause)->args)
+               {
+                       Cost            s2 = compute_clause_selec(root, (Node *) lfirst(arg));
+                       s1 = s1 + s2 - s1 * s2;
+               }
+       }
+       else if (is_funcclause(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;
+               s1 = (Cost) 0.3333333;
        }
-       else if (not_clause((Node *) clause))
+       else if (is_subplan(clause))
        {
-               /* negate this baby */
-               return 1 - compute_selec(root, ((Expr *) clause)->args, or_selectivities);
-       }
-       else if (is_subplan((Node *) clause))
-       {
-
                /*
                 * Just for the moment! FIX ME! - vadim 02/04/98
                 */
                s1 = 1.0;
        }
-       else if (NumRelids((Node *) clause) == 1)
+       else if (is_opclause(clause))
        {
-
-               /*
-                * ...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)
+               if (NumRelids(clause) == 1)
                {
+                       /* 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(clause, &relidx, &attno, &constval, &flag);
+                       relid = getrelid(relidx, root->rtable);
 
                        /*
-                        * attno can be Invalid if the clause had a function in it,
-                        * i.e.   WHERE myFunc(f) = 10
+                        * if the oprrest procedure is missing for whatever reason, use a
+                        * selectivity of 0.5
                         */
-                       /* this should be FIXED somehow to use function selectivity */
-                       s1 = (Cost) (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
-                       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;
+               {
+                       /*
+                        * 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);
+                       get_rels_atts(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);
+                       /*
+                        * 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 (lnext(clauses) == NIL)
-               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;
-       }
+       return s1;
 }
index c539b1f90517475e7eca05f127cabcf2df520ec1..d2da20a5b347a20e8c06d952a651a04a7fe0e781 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.62 1999/07/23 03:34:49 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.63 1999/07/24 23:21:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -109,12 +109,25 @@ create_index_paths(Query *root,
                                continue;
 
                /*
-                * 1. Try matching the index against subclauses of an 'or' clause.
-                * The fields of the restrictinfo nodes are marked with lists of
-                * the matching indices.  No paths are actually created.  We
-                * currently only look to match the first key.  We don't find
-                * multi-key index cases where an AND matches the first key, and
-                * the OR matches the second key.
+                * 1. Try matching the index against subclauses of restriction 'or'
+                * clauses (ie, 'or' clauses that reference only this relation).
+                * The restrictinfo nodes for the 'or' clauses are marked with lists
+                * of the matching indices.  No paths are actually created now;
+                * that will be done in orindxpath.c after all indexes for the rel
+                * have been examined.  (We need to do it that way because we can
+                * potentially use a different index for each subclause of an 'or',
+                * so we can't build a path for an 'or' clause until all indexes have
+                * been matched against it.)
+                *
+                * We currently only look to match the first key of each index against
+                * 'or' subclauses.  There are cases where a later key of a multi-key
+                * index could be used (if other top-level clauses match earlier keys
+                * of the index), but our poor brains are hurting already...
+                *
+                * We don't even think about special handling of 'or' clauses that
+                * involve more than one relation, since they can't be processed by
+                * a single indexscan path anyway.  Currently, cnfify() is certain
+                * to have restructured any such toplevel 'or' clauses anyway.
                 */
                match_index_orclauses(rel,
                                                          index,
@@ -123,7 +136,7 @@ create_index_paths(Query *root,
                                                          restrictinfo_list);
 
                /*
-                * 2. If the keys of this index match any of the available
+                * 2. If the keys of this index match any of the available non-'or'
                 * restriction clauses, then create a path using those clauses
                 * as indexquals.
                 */
@@ -179,11 +192,14 @@ create_index_paths(Query *root,
 /*
  * match_index_orclauses
  *       Attempt to match an index against subclauses within 'or' clauses.
- *       If the index does match, then the clause is marked with information
- *       about the index.
+ *       Each subclause that does match is marked with the index's node.
  *
- *       Essentially, this adds 'index' to the list of indices in the
- *       RestrictInfo field of each of the clauses which it matches.
+ *       Essentially, this adds 'index' to the list of subclause indices in
+ *       the RestrictInfo field of each of the 'or' clauses where it matches.
+ *       NOTE: we can use storage in the RestrictInfo for this purpose because
+ *       this processing is only done on single-relation restriction clauses.
+ *       Therefore, we will never have indexes for more than one relation
+ *       mentioned in the same RestrictInfo node's list.
  *
  * 'rel' is the node of the relation on which the index is defined.
  * 'index' is the index node.
@@ -204,12 +220,11 @@ match_index_orclauses(RelOptInfo *rel,
        {
                RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
 
-               if (valid_or_clause(restrictinfo))
+               if (restriction_is_or_clause(restrictinfo))
                {
                        /*
-                        * Mark the 'or' clause with a list of indices which match
-                        * each of its subclauses.      We add entries to the existing
-                        * list, if any.
+                        * Add this index to the subclause index list for each
+                        * subclause that it matches.
                         */
                        restrictinfo->indexids =
                                match_index_orclause(rel, index,
@@ -253,7 +268,9 @@ match_index_orclause(RelOptInfo *rel,
        List       *index_list;
        List       *clist;
 
-       /* first time through, we create empty list of same length as OR clause */
+       /* first time through, we create list of same length as OR clause,
+        * containing an empty sublist for each subclause.
+        */
        if (!other_matching_indices)
        {
                matching_indices = NIL;
@@ -1186,9 +1203,13 @@ index_innerjoin(Query *root, RelOptInfo *rel, List *clausegroup_list,
                pathnode->path.pathorder->ord.sortop = index->ordering;
                pathnode->path.pathkeys = NIL;
 
+               /* Note that we are making a pathnode for a single-scan indexscan;
+                * therefore, both indexid and indexqual should be single-element
+                * lists.
+                */
                pathnode->indexid = index->relids;
                pathnode->indexkeys = index->indexkeys;
-               pathnode->indexqual = clausegroup;
+               pathnode->indexqual = lcons(get_actual_clauses(clausegroup), NIL);
 
                pathnode->path.joinid = ((RestrictInfo *) lfirst(clausegroup))->restrictinfojoinid;
 
index ceb8c3eb47f9a899bd6434236684b36c2256eff3..4a511372a53d8deb989a4c44a64fd3c528cdc97b 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.28 1999/07/16 04:59:15 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.29 1999/07/24 23:21:10 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "parser/parsetree.h"
 
 
-static void best_or_subclause_indices(Query *root, RelOptInfo *rel, List *subclauses,
-                               List *indices, List **indexids, Cost *cost, Cost *selec);
-static void best_or_subclause_index(Query *root, RelOptInfo *rel, Expr *subclause,
-                                  List *indices, int *indexid, Cost *cost, Cost *selec);
+static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
+                                                                         List *subclauses, List *indices,
+                                                                         List **indexids,
+                                                                         Cost *cost, Cost *selec);
+static void best_or_subclause_index(Query *root, RelOptInfo *rel,
+                                                                       Expr *subclause, List *indices,
+                                                                       int *indexid, Cost *cost, Cost *selec);
 
 
 /*
  * create_or_index_paths
  *       Creates index paths for indices that match 'or' clauses.
+ *       create_index_paths() must already have been called.
  *
  * 'rel' is the relation entry for which the paths are to be defined on
  * 'clauses' is the list of available restriction clause nodes
  *
- * Returns a list of these index path nodes.
+ * Returns a list of index path nodes.
  *
  */
 List *
 create_or_index_paths(Query *root,
                                          RelOptInfo *rel, List *clauses)
 {
-       List       *t_list = NIL;
+       List       *path_list = NIL;
        List       *clist;
 
        foreach(clist, clauses)
        {
-               RestrictInfo *clausenode = (RestrictInfo *) (lfirst(clist));
+               RestrictInfo *clausenode = (RestrictInfo *) lfirst(clist);
 
                /*
                 * Check to see if this clause is an 'or' clause, and, if so,
                 * whether or not each of the subclauses within the 'or' clause
-                * has been matched by an index (the 'Index field was set in
-                * (match_or)  if no index matches a given subclause, one of the
-                * lists of index nodes returned by (get_index) will be 'nil').
+                * has been matched by an index.  The information used was
+                * saved by create_index_paths().
                 */
-               if (valid_or_clause(clausenode) &&
+               if (restriction_is_or_clause(clausenode) &&
                        clausenode->indexids)
                {
-                       List       *temp = NIL;
-                       List       *index_list = NIL;
-                       bool            index_flag = true;
+                       bool            all_indexable = true;
+                       List       *temp;
 
-                       index_list = clausenode->indexids;
-                       foreach(temp, index_list)
+                       foreach(temp, clausenode->indexids)
                        {
-                               if (!lfirst(temp))
+                               if (lfirst(temp) == NIL)
                                {
-                                       index_flag = false;
+                                       all_indexable = false;
                                        break;
                                }
                        }
-                       /* do they all have indexes? */
-                       if (index_flag)
-                       {                                       /* used to be a lisp every function */
+                       if (all_indexable)
+                       {
+                               /*
+                                * OK, build an IndexPath for this OR clause, using the
+                                * best available index for each subclause.
+                                */
                                IndexPath  *pathnode = makeNode(IndexPath);
-                               List       *indexids = NIL;
+                               List       *indexids;
+                               List       *orclause;
                                Cost            cost;
                                Cost            selec;
 
@@ -98,16 +103,35 @@ create_or_index_paths(Query *root,
                                pathnode->path.pathorder->ordtype = SORTOP_ORDER;
 
                                /*
-                                * This is an IndexScan, but it does index lookups based
-                                * on the order of the fields specified in the WHERE
-                                * clause, not in any order, so the sortop is NULL.
+                                * This is an IndexScan, but the overall result will consist
+                                * of tuples extracted in multiple passes (one for each
+                                * subclause of the OR), so the result cannot be claimed
+                                * to have any particular ordering.
                                 */
                                pathnode->path.pathorder->ord.sortop = NULL;
                                pathnode->path.pathkeys = NIL;
 
-                               pathnode->indexqual = lcons(clausenode, NIL);
+                               /*
+                                * Generate an indexqual list from the OR clause's args.
+                                * We want two levels of sublist: the first is implicit OR
+                                * and the second is implicit AND.  (Currently, we will never
+                                * see a sub-AND-clause because of cnfify(), but someday maybe
+                                * the code below will do something useful...)
+                                */
+                               pathnode->indexqual = NIL;
+                               foreach(orclause, clausenode->clause->args)
+                               {
+                                       List    *sublist;
+                                       if (and_clause(lfirst(orclause)))
+                                               sublist = ((Expr *) lfirst(orclause))->args;
+                                       else
+                                               sublist = lcons(lfirst(orclause), NIL);
+                                       pathnode->indexqual = lappend(pathnode->indexqual,
+                                                                                                 sublist);
+                               }
                                pathnode->indexid = indexids;
                                pathnode->path.path_cost = cost;
+                               clausenode->selectivity = (Cost) selec;
 
                                /*
                                 * copy restrictinfo list into path for expensive function
@@ -121,33 +145,28 @@ create_or_index_paths(Query *root,
                                if (XfuncMode != XFUNC_OFF)
                                        ((Path *) pathnode)->path_cost += xfunc_get_path_cost((Path) pathnode);
 #endif
-                               clausenode->selectivity = (Cost) selec;
-                               t_list = lappend(t_list, pathnode);
+                               path_list = lappend(path_list, pathnode);
                        }
                }
        }
 
-       return t_list;
+       return path_list;
 }
 
 /*
  * best_or_subclause_indices
  *       Determines the best index to be used in conjunction with each subclause
  *       of an 'or' clause and the cost of scanning a relation using these
- *       indices.      The cost is the sum of the individual index costs.
+ *       indices.      The cost is the sum of the individual index costs, since
+ *       the executor will perform a scan for each subclause of the 'or'.
  *
- * 'rel' is the node of the relation on which the index is defined
+ * 'rel' is the node of the relation on which the indexes are defined
  * 'subclauses' are the subclauses of the 'or' clause
- * 'indices' are those index nodes that matched subclauses of the 'or'
- *             clause
- * 'examined_indexids' is a list of those index ids to be used with
- *             subclauses that have already been examined
- * 'subcost' is the cost of using the indices in 'examined_indexids'
- * 'selec' is a list of all subclauses that have already been examined
- *
- * Returns a list of the indexids, cost, and selectivities of each
- * subclause, e.g., ((i1 i2 i3) cost (s1 s2 s3)), where 'i' is an OID,
- * 'cost' is a flonum, and 's' is a flonum.
+ * 'indices' is a list of sublists of the index nodes that matched each
+ *             subclause of the 'or' clause
+ * '*indexids' gets a list of the best index ID to use for each subclause
+ * '*cost' gets the total cost of the path
+ * '*selec' gets the total selectivity of the path.
  */
 static void
 best_or_subclause_indices(Query *root,
@@ -155,11 +174,12 @@ best_or_subclause_indices(Query *root,
                                                  List *subclauses,
                                                  List *indices,
                                                  List **indexids,              /* return value */
-                                                 Cost *cost,   /* return value */
-                                                 Cost *selec)  /* return value */
+                                                 Cost *cost,                   /* return value */
+                                                 Cost *selec)                  /* return value */
 {
        List       *slist;
 
+       *indexids = NIL;
        *selec = (Cost) 0.0;
        *cost = (Cost) 0.0;
 
@@ -180,8 +200,6 @@ best_or_subclause_indices(Query *root,
 
                indices = lnext(indices);
        }
-
-       return;
 }
 
 /*
@@ -193,10 +211,9 @@ best_or_subclause_indices(Query *root,
  * 'rel' is the node of the relation on which the index is defined
  * 'subclause' is the subclause
  * 'indices' is a list of index nodes that match the subclause
- *
- * Returns a list (index_id index_subcost index_selectivity)
- * (a fixnum, a fixnum, and a flonum respectively).
- *
+ * '*retIndexid' gets the ID of the best index
+ * '*retCost' gets the cost of a scan with that index
+ * '*retSelec' gets the selectivity of that scan
  */
 static void
 best_or_subclause_index(Query *root,
@@ -207,49 +224,60 @@ best_or_subclause_index(Query *root,
                                                Cost *retCost,  /* return value */
                                                Cost *retSelec) /* return value */
 {
-       List       *ilist;
+       Oid                     relid = getrelid(lfirsti(rel->relids),
+                                                                root->rtable);
+       Oid                     opno = ((Oper *) subclause->oper)->opno;
+       AttrNumber      attno = (get_leftop(subclause))->varattno;
+       bool            constant_on_right = non_null((Expr *) get_rightop(subclause));
+       Datum           value;
+       int                     flag;
+       List       *opnos,
+                          *attnos,
+                          *values,
+                          *flags;
        bool            first_run = true;
+       List       *ilist;
 
        /* if we don't match anything, return zeros */
        *retIndexid = 0;
-       *retCost = 0.0;
-       *retSelec = 0.0;
+       *retCost = (Cost) 0.0;
+       *retSelec = (Cost) 0.0;
+
+       if (constant_on_right)          /* XXX looks pretty bogus ... tgl */
+               value = ((Const *) get_rightop(subclause))->constvalue;
+       else
+               value = NameGetDatum("");
+       if (constant_on_right)
+               flag = (_SELEC_IS_CONSTANT_ || _SELEC_CONSTANT_RIGHT_);
+       else
+               flag = _SELEC_CONSTANT_RIGHT_;
+
+       /* prebuild lists since we will pass same list to each index */
+       opnos = lconsi(opno, NIL);
+       attnos = lconsi(attno, NIL);
+       values = lconsi(value, NIL);
+       flags = lconsi(flag, NIL);
 
        foreach(ilist, indices)
        {
                RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
-
-               Datum           value;
-               int                     flag = 0;
+               Oid                     indexid = (Oid) lfirsti(index->relids);
                Cost            subcost;
-               AttrNumber      attno = (get_leftop(subclause))->varattno;
-               Oid                     opno = ((Oper *) subclause->oper)->opno;
-               bool            constant_on_right = non_null((Expr *) get_rightop(subclause));
                float           npages,
                                        selec;
 
-               if (constant_on_right)
-                       value = ((Const *) get_rightop(subclause))->constvalue;
-               else
-                       value = NameGetDatum("");
-               if (constant_on_right)
-                       flag = (_SELEC_IS_CONSTANT_ || _SELEC_CONSTANT_RIGHT_);
-               else
-                       flag = _SELEC_CONSTANT_RIGHT_;
-
-               index_selectivity(lfirsti(index->relids),
+               index_selectivity(indexid,
                                                  index->classlist,
-                                                 lconsi(opno, NIL),
-                                                 getrelid(lfirsti(rel->relids),
-                                                                  root->rtable),
-                                                 lconsi(attno, NIL),
-                                                 lconsi(value, NIL),
-                                                 lconsi(flag, NIL),
+                                                 opnos,
+                                                 relid,
+                                                 attnos,
+                                                 values,
+                                                 flags,
                                                  1,
                                                  &npages,
                                                  &selec);
 
-               subcost = cost_index((Oid) lfirsti(index->relids),
+               subcost = cost_index(indexid,
                                                         (int) npages,
                                                         (Cost) selec,
                                                         rel->pages,
@@ -260,12 +288,11 @@ best_or_subclause_index(Query *root,
 
                if (first_run || subcost < *retCost)
                {
-                       *retIndexid = lfirsti(index->relids);
+                       *retIndexid = indexid;
                        *retCost = subcost;
                        *retSelec = selec;
                        first_run = false;
                }
        }
 
-       return;
 }
index 1dc0e9a0d5d39273f62b10557dab544b4681f256..0148d4912e198feca8c57c9dfa6470d42f3111ee 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.62 1999/07/17 20:17:13 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.63 1999/07/24 23:21:11 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -301,54 +301,30 @@ create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
  * create_indexscan_node
  *       Returns a indexscan node for the base relation scanned by 'best_path'
  *       with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ *
+ * If an 'or' clause is to be used with this index, the indxqual field
+ * will contain a list of the 'or' clause arguments, e.g., the
+ * clause(OR a b c) will generate: ((a) (b) (c)).  Otherwise, the
+ * indxqual will simply contain one conjunctive qualification: ((a)).
  */
 static IndexScan *
 create_indexscan_node(IndexPath *best_path,
                                          List *tlist,
                                          List *scan_clauses)
 {
-
-       /*
-        * Extract the(first if conjunct, only if disjunct) clause from the
-        * restrictinfo list.
-        */
-       Expr       *index_clause = (Expr *) NULL;
-       List       *indxqual = NIL;
-       List       *qpqual = NIL;
-       List       *fixed_indxqual = NIL;
+       List       *indxqual = best_path->indexqual;
+       List       *qpqual;
+       List       *fixed_indxqual;
        List       *ixid;
-       IndexScan  *scan_node = (IndexScan *) NULL;
-       bool            lossy = FALSE;
-       HeapTuple       indexTuple;
-       Form_pg_index index;
-
-       /*
-        * If an 'or' clause is to be used with this index, the indxqual field
-        * will contain a list of the 'or' clause arguments, e.g., the
-        * clause(OR a b c) will generate: ((a) (b) (c)).  Otherwise, the
-        * indxqual will simply contain one conjunctive qualification: ((a)).
-        */
-       if (best_path->indexqual != NULL)
-               /* added call to fix_opids, JMH 6/23/92 */
-               index_clause = (Expr *)
-                       lfirst(fix_opids(get_actual_clauses(best_path->indexqual)));
-
-       if (or_clause((Node *) index_clause))
-       {
-               List       *temp = NIL;
-
-               foreach(temp, index_clause->args)
-                       indxqual = lappend(indxqual, lcons(lfirst(temp), NIL));
-       }
-       else
-       {
-               indxqual = lcons(get_actual_clauses(best_path->indexqual),
-                                                NIL);
-       }
+       IndexScan  *scan_node;
+       bool            lossy = false;
 
        /* check and see if any indices are lossy */
        foreach(ixid, best_path->indexid)
        {
+               HeapTuple       indexTuple;
+               Form_pg_index index;
+
                indexTuple = SearchSysCacheTuple(INDEXRELID,
                                                                                 ObjectIdGetDatum(lfirsti(ixid)),
                                                                                 0, 0, 0);
@@ -356,34 +332,70 @@ create_indexscan_node(IndexPath *best_path,
                        elog(ERROR, "create_plan: index %u not found", lfirsti(ixid));
                index = (Form_pg_index) GETSTRUCT(indexTuple);
                if (index->indislossy)
-                       lossy = TRUE;
+               {
+                       lossy = true;
+                       break;
+               }
        }
 
-
        /*
-        * The qpqual field contains all restrictions not automatically
+        * The qpqual list must contain all restrictions not automatically
         * handled by the index.  Note that for non-lossy indices, the
         * predicates in the indxqual are handled by the index, while for
         * lossy indices the indxqual predicates need to be double-checked
         * after the index fetches the best-guess tuples.
+        *
+        * There should not be any clauses in scan_clauses that duplicate
+        * expressions checked by the index, but just in case, we will
+        * get rid of them via set_difference.
         */
-       if (or_clause((Node *) index_clause))
+       if (length(indxqual) > 1)
        {
+               /*
+                * Build an expression representation of the indexqual, expanding
+                * the implicit OR and AND semantics of the first- and second-level
+                * lists.  XXX Is it really necessary to do a deep copy here?
+                */
+               List       *orclauses = NIL;
+               List       *orclause;
+               Expr       *indxqual_expr;
+
+               foreach(orclause, indxqual)
+               {
+                       orclauses = lappend(orclauses,
+                                                               make_ands_explicit((List *) copyObject(lfirst(orclause))));
+               }
+               indxqual_expr = make_orclause(orclauses);
+
+               /* this set_difference is almost certainly a waste of time... */
                qpqual = set_difference(scan_clauses,
-                                                               lcons(index_clause, NIL));
+                                                               lcons(indxqual_expr, NIL));
 
                if (lossy)
-                       qpqual = lappend(qpqual, (List *) copyObject(index_clause));
+                       qpqual = lappend(qpqual, indxqual_expr);
        }
-       else
+       else if (indxqual != NIL)
        {
+               /* Here, we can simply treat the first sublist as an independent
+                * set of qual expressions, since there is no top-level OR behavior.
+                */
                qpqual = set_difference(scan_clauses, lfirst(indxqual));
                if (lossy)
-                       qpqual = nconc(qpqual,
-                                                  (List *) copyObject(lfirst(indxqual)));
+                       qpqual = nconc(qpqual, (List *) copyObject(lfirst(indxqual)));
        }
+       else
+               qpqual = NIL;
+
+       /*
+        * Fix opids in the completed indxqual.  We don't want to do this sooner
+        * since it would screw up the set_difference calcs above.  Really,
+        * this ought to only happen at final exit from the planner...
+        */
+       indxqual = fix_opids(indxqual);
 
-       fixed_indxqual = (List *) fix_indxqual_references((Node *) indxqual, (Path *) best_path);
+       /* The executor needs a copy with index attrs substituted for table ones */
+       fixed_indxqual = (List *) fix_indxqual_references((Node *) indxqual,
+                                                                                                         (Path *) best_path);
 
        scan_node = make_indexscan(tlist,
                                                           qpqual,
index 2270a0e4eab4aec684acb7f0172ce43a3dd2e3ab..08ed6a7316341f4e19d4684f052b4bfa118186d4 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.34 1999/07/16 04:59:19 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.35 1999/07/24 23:21:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "optimizer/var.h"
 #include "utils/lsyscache.h"
 
-extern int     Quiet;
 
-static void add_restrict_and_join_to_rel(Query *root, List *clause);
+static void add_restrict_and_join_to_rel(Query *root, Node *clause);
 static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
                                          Relids join_relids);
-static void add_vars_to_targetlist(Query *root, List *vars, Relids join_relids);
+static void add_vars_to_targetlist(Query *root, List *vars);
 
 static MergeOrder *mergejoinop(Expr *clause);
 static Oid     hashjoinop(Expr *clause);
@@ -129,7 +128,7 @@ add_missing_vars_to_tlist(Query *root, List *tlist)
  *       relations appearing within clauses.  Creates new relation entries if
  *       necessary, adding them to *query_relation_list*.
  *
- *       Returns nothing of interest.
+ * 'clauses': the list of clauses in the cnfify'd query qualification.
  */
 void
 add_restrict_and_join_to_rels(Query *root, List *clauses)
@@ -137,95 +136,71 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
        List       *clause;
 
        foreach(clause, clauses)
-               add_restrict_and_join_to_rel(root, lfirst(clause));
-       return;
+               add_restrict_and_join_to_rel(root, (Node*) lfirst(clause));
 }
 
 /*
  * add_restrict_and_join_to_rel-
  *       Add clause information to either the 'RestrictInfo' or 'JoinInfo' field
- *       of a relation entry(depending on whether or not the clause is a join)
+ *       of a relation entry (depending on whether or not the clause is a join)
  *       by creating a new RestrictInfo node and setting appropriate fields
  *       within the nodes.
- *
- *       Returns nothing of interest.
  */
 static void
-add_restrict_and_join_to_rel(Query *root, List *clause)
+add_restrict_and_join_to_rel(Query *root, Node *clause)
 {
+       RestrictInfo *restrictinfo = makeNode(RestrictInfo);
        Relids          relids;
        List       *vars;
-       RestrictInfo *restrictinfo = makeNode(RestrictInfo);
-
-       /*
-        * Retrieve all relids and vars contained within the clause.
-        */
-       clause_get_relids_vars((Node *) clause, &relids, &vars);
 
        restrictinfo->clause = (Expr *) clause;
-       restrictinfo->notclause = contains_not((Node *) clause);
-       restrictinfo->selectivity = 0;
        restrictinfo->indexids = NIL;
        restrictinfo->mergejoinorder = (MergeOrder *) NULL;
        restrictinfo->hashjoinoperator = (Oid) 0;
 
+       /*
+        * The selectivity of the clause must be computed regardless of
+        * whether it's a restriction or a join clause
+        */
+       restrictinfo->selectivity = compute_clause_selec(root, clause);
+
+       /*
+        * Retrieve all relids and vars contained within the clause.
+        */
+       clause_get_relids_vars(clause, &relids, &vars);
+
        if (length(relids) == 1)
        {
-
                /*
                 * There is only one relation participating in 'clause', so
-                * 'clause' must be a restriction clause.
+                * 'clause' must be a restriction clause for that relation.
                 */
                RelOptInfo *rel = get_base_rel(root, lfirsti(relids));
 
-               /*
-                * The selectivity of the clause must be computed regardless of
-                * whether it's a restriction or a join clause
-                */
-               if (is_funcclause((Node *) clause))
-
-                       /*
-                        * XXX If we have a func clause set selectivity to 1/3, really
-                        * need a true selectivity function.
-                        */
-                       restrictinfo->selectivity = (Cost) 0.3333333;
-               else
-                       restrictinfo->selectivity = compute_clause_selec(root, (Node *) clause, NIL);
-
                rel->restrictinfo = lcons(restrictinfo, rel->restrictinfo);
        }
        else
        {
-
                /*
                 * 'clause' is a join clause, since there is more than one atom in
-                * the relid list.
+                * the relid list.  Add it to the join lists of all the relevant
+                * relations.  (If, perchance, 'clause' contains NO vars, then
+                * nothing will happen...)
                 */
-               if (is_funcclause((Node *) clause))
-
-                       /*
-                        * XXX If we have a func clause set selectivity to 1/3, really
-                        * need a true selectivity function.
-                        */
-                       restrictinfo->selectivity = (Cost) 0.3333333;
-               else
-                       restrictinfo->selectivity = compute_clause_selec(root, (Node *) clause, NIL);
-
                add_join_info_to_rels(root, restrictinfo, relids);
-               /* we are going to be doing a join, so add var to targetlist */
-               add_vars_to_targetlist(root, vars, relids);
+               /* we are going to be doing a join, so add vars to targetlists */
+               add_vars_to_targetlist(root, vars);
        }
 }
 
 /*
  * add_join_info_to_rels
  *       For every relation participating in a join clause, add 'restrictinfo' to
- *       the appropriate joininfo node(creating a new one and adding it to the
+ *       the appropriate joininfo node (creating a new one and adding it to the
  *       appropriate rel node if necessary).
  *
  * 'restrictinfo' describes the join clause
  * 'join_relids' is the list of relations participating in the join clause
- *
  */
 static void
 add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
@@ -233,7 +208,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
 {
        List       *join_relid;
 
-       /* For every relid, find the rel, and add the proper join entries */
+       /* For every relid, find the joininfo, and add the proper join entries */
        foreach(join_relid, join_relids)
        {
                JoinInfo   *joininfo;
@@ -247,43 +222,39 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
                                unjoined_relids = lappendi(unjoined_relids, lfirsti(rel));
                }
 
+               /*
+                * Find or make the joininfo node for this combination of rels
+                */
                joininfo = find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
                                                                          unjoined_relids);
+
+               /*
+                * And add the restrictinfo node to it.  NOTE that each joininfo
+                * gets its own copy of the restrictinfo node!  (Is this really
+                * necessary?  Possibly ... later parts of the optimizer destructively
+                * modify restrict/join clauses...)
+                */
                joininfo->jinfo_restrictinfo = lcons(copyObject((void *) restrictinfo),
-                                                                                  joininfo->jinfo_restrictinfo);
+                                                                                        joininfo->jinfo_restrictinfo);
        }
 }
 
 /*
  * add_vars_to_targetlist
- *       For each variable appearing in a clause,
- *       (1) If a targetlist entry for the variable is not already present in
- *               the appropriate relation's target list, add one.
- *       (2) If a targetlist entry is already present, but the var is part of a
- *               join clause, add the relids of the join relations to the JoinList
- *               entry of the targetlist entry.
- *
- *       'vars' is the list of var nodes
- *       'join_relids' is the list of relids appearing in the join clause
- *             (if this is a join clause)
- *
- *       Returns nothing.
+ *       For each variable appearing in a clause, add it to the relation's
+ *       targetlist if not already present.
  */
 static void
-add_vars_to_targetlist(Query *root, List *vars, Relids join_relids)
+add_vars_to_targetlist(Query *root, List *vars)
 {
-       Var                *var;
-       List       *temp = NIL;
-       RelOptInfo *rel = (RelOptInfo *) NULL;
-       TargetEntry *tlistentry;
+       List       *temp;
 
        foreach(temp, vars)
        {
-               var = (Var *) lfirst(temp);
-               rel = get_base_rel(root, var->varno);
-               tlistentry = tlistentry_member(var, rel->targetlist);
-               if (tlistentry == NULL)
-                       /* add a new entry */
+               Var                *var = (Var *) lfirst(temp);
+               RelOptInfo *rel = get_base_rel(root, var->varno);
+
+               if (tlistentry_member(var, rel->targetlist) == NULL)
                        add_var_to_tlist(rel, var);
        }
 }
index 84db3df008d67f355a11c140475ec4fdfcb38a25..5c9e4175adebff97139e9bf13eeb5393cb3af56a 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.40 1999/07/16 04:59:23 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.41 1999/07/24 23:21:13 tgl Exp $
  *
  * HISTORY
  *       AUTHOR                        DATE                    MAJOR EVENT
@@ -290,6 +290,21 @@ make_andclause(List *andclauses)
        return expr;
 }
 
+/*
+ * Sometimes (such as in the result of cnfify), we use lists of expression
+ * nodes with implicit AND semantics.  This function converts back to an
+ * explicit-AND representation.
+ */
+Expr *
+make_ands_explicit(List *andclauses)
+{
+       if (andclauses == NIL)
+               return NULL;
+       else if (length(andclauses) == 1)
+               return (Expr *) lfirst(andclauses);
+       else
+               return make_andclause(andclauses);
+}
 
 /*****************************************************************************
  *             CASE clause functions
@@ -410,38 +425,6 @@ NumRelids(Node *clause)
        return length(var_list);
 }
 
-/*
- * contains_not
- *
- * Returns t iff the clause is a 'not' clause or if any of the
- * subclauses within an 'or' clause contain 'not's.
- *
- * NOTE that only the top-level AND/OR structure is searched for NOTs;
- * we are not interested in buried substructure.
- */
-bool
-contains_not(Node *clause)
-{
-       if (single_node(clause))
-               return false;
-
-       if (not_clause(clause))
-               return true;
-
-       if (or_clause(clause) || and_clause(clause))
-       {
-               List       *a;
-
-               foreach(a, ((Expr *) clause)->args)
-               {
-                       if (contains_not(lfirst(a)))
-                               return true;
-               }
-       }
-
-       return false;
-}
-
 /*
  * is_joinable
  *
index 3c9e0a4dc846fbeb951c88d349b685a9964ac07e..52689d96a4d5184c59627208db77d35910ed7faa 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.46 1999/07/16 04:59:25 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.47 1999/07/24 23:21:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -426,7 +426,8 @@ create_index_path(Query *root,
                /* each clause gets an equal selectivity */
                clausesel = pow(selec, 1.0 / (double) length(restriction_clauses));
 
-               pathnode->indexqual = restriction_clauses;
+               pathnode->indexqual = lcons(get_actual_clauses(restriction_clauses),
+                                                                       NIL);
                pathnode->path.path_cost = cost_index(lfirsti(index->relids),
                                                                                          (int) npages,
                                                                                          selec,
index 12b95a61af37f7456ed21cce7764e9322ca33f47..4dfdf66fcbce676d079e39992c8c8dd0d2a2fbce 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.6 1999/07/16 04:59:27 momjian Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.7 1999/07/24 23:21:14 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "optimizer/restrictinfo.h"
 
 /*
- * valid_or_clause
+ * restriction_is_or_clause
  *
- * Returns t iff the restrictinfo node contains a 'normal' 'or' clause.
+ * Returns t iff the restrictinfo node contains an 'or' clause.
  *
  */
 bool
-valid_or_clause(RestrictInfo *restrictinfo)
+restriction_is_or_clause(RestrictInfo *restrictinfo)
 {
        if (restrictinfo != NULL &&
-               !single_node((Node *) restrictinfo->clause) &&
-               !restrictinfo->notclause &&
                or_clause((Node *) restrictinfo->clause))
                return true;
        else
index e305870aa7b71242169c2bc80f8b14fd07b31b3c..416cdb0c55b2e80e07edef4eab05f1fdf7196aaa 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relation.h,v 1.34 1999/07/15 23:03:56 momjian Exp $
+ * $Id: relation.h,v 1.35 1999/07/24 23:21:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -151,6 +151,23 @@ typedef struct Path
        List       *loc_restrictinfo;
 } Path;
 
+/*----------
+ * IndexPath represents an index scan.  Although an indexscan can only read
+ * a single relation, it can scan it more than once, potentially using a
+ * different index during each scan.  The result is the union (OR) of all the
+ * tuples matched during any scan.  (The executor is smart enough not to return
+ * the same tuple more than once, even if it is matched in multiple scans.)
+ * 'indexid' is a list of index relation OIDs, one per scan to be performed.
+ * 'indexqual' is a list of index qualifications, also one per scan.
+ * Each entry in 'indexqual' is a sublist of qualification expressions with
+ * implicit AND semantics across the sublist items.  Each one of the sublist
+ * items must be an operator expression of the form (var op something) or
+ * (something op var), where the var is a field the associated index keys on
+ * and the op is a member of the operator class of the index.
+ * NOTE that the semantics of the top-level list in 'indexqual' is OR
+ * combination, while the sublists are implicitly AND combinations!
+ *----------
+ */
 typedef struct IndexPath
 {
        Path            path;
@@ -205,16 +222,20 @@ typedef struct JoinKey
 } JoinKey;
 
 /*
- * clause info
+ * Restriction clause info.
+ * We create one of these for each AND sub-clause of a restriction condition
+ * (WHERE clause).  Since the restriction clauses are logically ANDed, we
+ * can use any one of them or any subset of them to filter out tuples,
+ * without having to evaluate the rest.  The RestrictInfo node itself stores
+ * data used by the optimizer while choosing the best query plan.
  */
 
 typedef struct RestrictInfo
 {
        NodeTag         type;
-       Expr       *clause;                     /* should be an OP clause */
-       Cost            selectivity;
-       bool            notclause;
-       List       *indexids;
+       Expr       *clause;                     /* the represented subclause of WHERE cond */
+       Cost            selectivity;    /* estimated selectivity */
+       List       *indexids;           /* subclause index IDs if clause is an OR */
 
        /* mergejoin only */
        MergeOrder *mergejoinorder;
index de135ca1d5397481eeca73ad5bcc5148580de9f1..e362b7c64d9eebeca2d19070e7b4f2fe01af1bff 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.21 1999/07/15 23:03:57 momjian Exp $
+ * $Id: clauses.h,v 1.22 1999/07/24 23:21:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,13 +34,13 @@ extern Expr *get_notclausearg(Expr *notclause);
 
 extern bool and_clause(Node *clause);
 extern Expr *make_andclause(List *andclauses);
+extern Expr *make_ands_explicit(List *andclauses);
 
 extern bool case_clause(Node *clause);
 
 extern List *pull_constant_clauses(List *quals, List **constantQual);
 extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
 extern int     NumRelids(Node *clause);
-extern bool contains_not(Node *clause);
 extern bool is_joinable(Node *clause);
 extern bool qual_clause_p(Node *clause);
 extern void fix_opid(Node *clause);
index b9fc710370692e76ddc6c7bf4bc965f38e9e0770..1dfe975226228cb6915501c6d690b085dc83969c 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: cost.h,v 1.21 1999/07/15 15:21:20 momjian Exp $
+ * $Id: cost.h,v 1.22 1999/07/24 23:21:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -59,7 +59,6 @@ extern void set_clause_selectivities(List *restrictinfo_list, Cost new_selectivi
 extern Cost product_selec(List *restrictinfo_list);
 extern void set_rest_relselec(Query *root, List *rel_list);
 extern void set_rest_selec(Query *root, List *restrictinfo_list);
-extern Cost compute_clause_selec(Query *root,
-                                        Node *clause, List *or_selectivities);
+extern Cost compute_clause_selec(Query *root, Node *clause);
 
 #endif  /* COST_H */
index 2f5c4dca20cd8935eaf83fb6edcd85aad8626e5e..7412ca46fa0bf10aa2b1ff16dc49abb823e1bb88 100644 (file)
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: restrictinfo.h,v 1.5 1999/07/15 15:21:23 momjian Exp $
+ * $Id: restrictinfo.h,v 1.6 1999/07/24 23:21:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,7 +15,7 @@
 
 #include "nodes/relation.h"
 
-extern bool valid_or_clause(RestrictInfo *restrictinfo);
+extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
 extern List *get_actual_clauses(List *restrictinfo_list);
 extern void get_relattvals(List *restrictinfo_list, List **attnos,
                           List **values, List **flags);