]> granicus.if.org Git - postgresql/commitdiff
Modify planner's implied-equality-deduction code so that when a set
authorTom Lane <tgl@sss.pgh.pa.us>
Fri, 24 Jan 2003 03:58:44 +0000 (03:58 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Fri, 24 Jan 2003 03:58:44 +0000 (03:58 +0000)
of known-equal expressions includes any constant expressions (including
Params from outer queries), we actively suppress any 'var = var'
clauses that are or could be deduced from the set, generating only the
deducible 'var = const' clauses instead.  The idea here is to push down
the restrictions implied by the equality set to base relations whenever
possible.  Once we have applied the 'var = const' clauses, the 'var = var'
clauses are redundant, and should be suppressed both to save work at
execution and to avoid double-counting restrictivity.

12 files changed:
src/backend/nodes/list.c
src/backend/optimizer/path/indxpath.c
src/backend/optimizer/path/pathkeys.c
src/backend/optimizer/plan/initsplan.c
src/backend/optimizer/util/joininfo.c
src/backend/optimizer/util/relnode.c
src/backend/optimizer/util/restrictinfo.c
src/backend/utils/adt/selfuncs.c
src/include/nodes/pg_list.h
src/include/optimizer/joininfo.h
src/include/optimizer/paths.h
src/include/optimizer/planmain.h

index e896b479018b718d2094e8d6d8a53c45e25e35ec..bf9e5c10d6f0c3f6531a71643275b733d9126259 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.44 2003/01/20 18:54:47 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.45 2003/01/24 03:58:34 tgl Exp $
  *
  * NOTES
  *       XXX a few of the following functions are duplicated to handle
@@ -357,6 +357,7 @@ set_union(List *l1, List *l2)
        return retval;
 }
 
+/* set_union for integer lists */
 List *
 set_unioni(List *l1, List *l2)
 {
@@ -371,6 +372,21 @@ set_unioni(List *l1, List *l2)
        return retval;
 }
 
+/* set_union when pointer-equality comparison is sufficient */
+List *
+set_ptrUnion(List *l1, List *l2)
+{
+       List       *retval = listCopy(l1);
+       List       *i;
+
+       foreach(i, l2)
+       {
+               if (!ptrMember(lfirst(i), retval))
+                       retval = lappend(retval, lfirst(i));
+       }
+       return retval;
+}
+
 /*
  * Generate the intersection of two lists,
  * ie, all members of both l1 and l2.
index 02a92fd99605d5ef26dc13c0e50dc3b288d45b10..443d54c64733ba0ed20edaf1151257e5460e6a49 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.132 2003/01/20 18:54:49 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.133 2003/01/24 03:58:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1596,12 +1596,14 @@ make_innerjoin_index_path(Query *root,
         * nconc the two lists; then we might have some restriction
         * clauses appearing twice, which'd mislead
         * restrictlist_selectivity into double-counting their
-        * selectivity.)
+        * selectivity.  However, since RestrictInfo nodes aren't copied when
+        * linking them into different lists, it should be sufficient to use
+        * pointer comparison to remove duplicates.)
         */
        pathnode->rows = rel->tuples *
                restrictlist_selectivity(root,
-                                                                set_union(rel->baserestrictinfo,
-                                                                                  clausegroup),
+                                                                set_ptrUnion(rel->baserestrictinfo,
+                                                                                         clausegroup),
                                                                 lfirsti(rel->relids));
        /* Like costsize.c, force estimate to be at least one row */
        if (pathnode->rows < 1.0)
index 194bdddc2f09c1b151b3f64053331e9c3b0e7157..b99a44a440420d99b0c995fa8ea04ac20e486999 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.44 2003/01/15 19:35:40 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.45 2003/01/24 03:58:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 #include "optimizer/tlist.h"
+#include "optimizer/var.h"
 #include "parser/parsetree.h"
 #include "parser/parse_func.h"
 #include "utils/lsyscache.h"
@@ -147,14 +148,30 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
  * generate_implied_equalities
  *       Scan the completed equi_key_list for the query, and generate explicit
  *       qualifications (WHERE clauses) for all the pairwise equalities not
- *       already mentioned in the quals.  This is useful because the additional
- *       clauses help the selectivity-estimation code, and in fact it's
- *       *necessary* to ensure that sort keys we think are equivalent really
- *       are (see src/backend/optimizer/README for more info).
+ *       already mentioned in the quals; or remove qualifications found to be
+ *       redundant.
+ *
+ * Adding deduced equalities is useful because the additional clauses help
+ * the selectivity-estimation code and may allow better joins to be chosen;
+ * and in fact it's *necessary* to ensure that sort keys we think are
+ * equivalent really are (see src/backend/optimizer/README for more info).
+ *
+ * If an equi_key_list set includes any constants then we adopt a different
+ * strategy: we record all the "var = const" deductions we can make, and
+ * actively remove all the "var = var" clauses that are implied by the set
+ * (including the clauses that originally gave rise to the set!).  The reason
+ * is that given input like "a = b AND b = 42", once we have deduced "a = 42"
+ * there is no longer any need to apply the clause "a = b"; not only is
+ * it a waste of time to check it, but we will misestimate selectivity if the
+ * clause is left in.  So we must remove it.  For this purpose, any pathkey
+ * item that mentions no Vars of the current level can be taken as a constant.
+ * (The only case where this would be risky is if the item contains volatile
+ * functions; but we will never consider such an expression to be a pathkey
+ * at all, because check_mergejoinable() will reject it.)
  *
  * This routine just walks the equi_key_list to find all pairwise equalities.
- * We call process_implied_equality (in plan/initsplan.c) to determine whether
- * each is already known and add it to the proper restrictinfo list if not.
+ * We call process_implied_equality (in plan/initsplan.c) to adjust the
+ * restrictinfo datastructures for each pair.
  */
 void
 generate_implied_equalities(Query *root)
@@ -164,35 +181,119 @@ generate_implied_equalities(Query *root)
        foreach(cursetlink, root->equi_key_list)
        {
                List       *curset = lfirst(cursetlink);
+               int                     nitems = length(curset);
+               Relids     *relids;
+               bool            have_consts;
                List       *ptr1;
+               int                     i1;
 
                /*
                 * A set containing only two items cannot imply any equalities
                 * beyond the one that created the set, so we can skip it.
                 */
-               if (length(curset) < 3)
+               if (nitems < 3)
                        continue;
 
+               /*
+                * Collect info about relids mentioned in each item.  For this
+                * routine we only really care whether there are any at all in
+                * each item, but process_implied_equality() needs the exact
+                * lists, so we may as well pull them here.
+                */
+               relids = (Relids *) palloc(nitems * sizeof(Relids));
+               have_consts = false;
+               i1 = 0;
+               foreach(ptr1, curset)
+               {
+                       PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
+
+                       relids[i1] = pull_varnos(item1->key);
+                       if (relids[i1] == NIL)
+                               have_consts = true;
+                       i1++;
+               }
+
                /*
                 * Match each item in the set with all that appear after it (it's
                 * sufficient to generate A=B, need not process B=A too).
                 */
+               i1 = 0;
                foreach(ptr1, curset)
                {
                        PathKeyItem *item1 = (PathKeyItem *) lfirst(ptr1);
                        List       *ptr2;
+                       int                     i2 = i1 + 1;
 
                        foreach(ptr2, lnext(ptr1))
                        {
                                PathKeyItem *item2 = (PathKeyItem *) lfirst(ptr2);
 
-                               process_implied_equality(root, item1->key, item2->key,
-                                                                                item1->sortop, item2->sortop);
+                               /*
+                                * If it's "const = const" then just ignore it altogether.
+                                * There is no place in the restrictinfo structure to store
+                                * it.  (If the two consts are in fact unequal, then
+                                * propagating the comparison to Vars will cause us to
+                                * produce zero rows out, as expected.)
+                                */
+                               if (relids[i1] != NIL || relids[i2] != NIL)
+                               {
+                                       /*
+                                        * Tell process_implied_equality to delete the clause,
+                                        * not add it, if it's "var = var" and we have constants
+                                        * present in the list.
+                                        */
+                                       bool    delete_it = (have_consts &&
+                                                                                relids[i1] != NIL &&
+                                                                                relids[i2] != NIL);
+                                       process_implied_equality(root,
+                                                                                        item1->key, item2->key,
+                                                                                        item1->sortop, item2->sortop,
+                                                                                        relids[i1], relids[i2],
+                                                                                        delete_it);
+                               }
+                               i2++;
                        }
+                       i1++;
+               }
+       }
+}
+
+/*
+ * exprs_known_equal
+ *       Detect whether two expressions are known equal due to equijoin clauses.
+ *
+ * Note: does not bother to check for "equal(item1, item2)"; caller must
+ * check that case if it's possible to pass identical items.
+ */
+bool
+exprs_known_equal(Query *root, Node *item1, Node *item2)
+{
+       List       *cursetlink;
+
+       foreach(cursetlink, root->equi_key_list)
+       {
+               List       *curset = lfirst(cursetlink);
+               bool            item1member = false;
+               bool            item2member = false;
+               List       *ptr;
+
+               foreach(ptr, curset)
+               {
+                       PathKeyItem *pitem = (PathKeyItem *) lfirst(ptr);
+
+                       if (equal(item1, pitem->key))
+                               item1member = true;
+                       else if (equal(item2, pitem->key))
+                               item2member = true;
+                       /* Exit as soon as equality is proven */
+                       if (item1member && item2member)
+                               return true;
                }
        }
+       return false;
 }
 
+
 /*
  * make_canonical_pathkey
  *       Given a PathKeyItem, find the equi_key_list subset it is a member of,
index 037ed3314cf866e6087164187c6a36fa4587f7e9..3a824d55d72b7bd1c6d2d06bd33f96770efb118f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.82 2003/01/20 18:54:52 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.83 2003/01/24 03:58:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,8 +40,6 @@ static void distribute_qual_to_rels(Query *root, Node *clause,
                                                bool isouterjoin,
                                                bool isdeduced,
                                                Relids qualscope);
-static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
-                                         Relids join_relids);
 static void add_vars_to_targetlist(Query *root, List *vars);
 static bool qual_is_redundant(Query *root, RestrictInfo *restrictinfo,
                                  List *restrictlist);
@@ -539,7 +537,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
                /*
                 * Add clause to the join lists of all the relevant relations.
                 */
-               add_join_info_to_rels(root, restrictinfo, relids);
+               add_join_clause_to_rels(root, restrictinfo, relids);
 
                /*
                 * Add vars used in the join clause to targetlists of their
@@ -572,79 +570,96 @@ distribute_qual_to_rels(Query *root, Node *clause,
                add_equijoined_keys(root, restrictinfo);
 }
 
-/*
- * add_join_info_to_rels
- *       For every relation participating in a join clause, add 'restrictinfo' to
- *       the appropriate joininfo list (creating a new list and adding it to the
- *       appropriate rel node if necessary).
- *
- * Note that the same copy of the restrictinfo node is linked to by all the
- * lists it is in.  This allows us to exploit caching of information about
- * the restriction clause (but we must be careful that the information does
- * not depend on context).
- *
- * '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,
-                                         Relids join_relids)
-{
-       List       *join_relid;
-
-       /* For every relid, find the joininfo, and add the proper join entries */
-       foreach(join_relid, join_relids)
-       {
-               int                     cur_relid = lfirsti(join_relid);
-               Relids          unjoined_relids = NIL;
-               JoinInfo   *joininfo;
-               List       *otherrel;
-
-               /* Get the relids not equal to the current relid */
-               foreach(otherrel, join_relids)
-               {
-                       if (lfirsti(otherrel) != cur_relid)
-                               unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel));
-               }
-               Assert(unjoined_relids != NIL);
-
-               /*
-                * Find or make the joininfo node for this combination of rels,
-                * and add the restrictinfo node to it.
-                */
-               joininfo = make_joininfo_node(find_base_rel(root, cur_relid),
-                                                                         unjoined_relids);
-               joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
-                                                                                          restrictinfo);
-       }
-}
-
 /*
  * process_implied_equality
  *       Check to see whether we already have a restrictinfo item that says
- *       item1 = item2, and create one if not.  This is a consequence of
- *       transitivity of mergejoin equality: if we have mergejoinable
- *       clauses A = B and B = C, we can deduce A = C (where = is an
- *       appropriate mergejoinable operator).
+ *       item1 = item2, and create one if not; or if delete_it is true,
+ *       remove any such restrictinfo item.
+ *
+ * This processing is a consequence of transitivity of mergejoin equality:
+ * if we have mergejoinable clauses A = B and B = C, we can deduce A = C
+ * (where = is an appropriate mergejoinable operator).  See path/pathkeys.c
+ * for more details.
  */
 void
-process_implied_equality(Query *root, Node *item1, Node *item2,
-                                                Oid sortop1, Oid sortop2)
+process_implied_equality(Query *root,
+                                                Node *item1, Node *item2,
+                                                Oid sortop1, Oid sortop2,
+                                                Relids item1_relids, Relids item2_relids,
+                                                bool delete_it)
 {
+       Relids          relids;
+       RelOptInfo *rel1;
+       List       *restrictlist;
+       List       *itm;
        Oid                     ltype,
                                rtype;
        Operator        eq_operator;
        Form_pg_operator pgopform;
        Expr       *clause;
 
+       /* Get list of relids referenced in the two expressions */
+       relids = set_unioni(item1_relids, item2_relids);
+
        /*
-        * Forget it if this equality is already recorded.
-        *
-        * Note: if only a single relation is involved, we may fall through
-        * here and end up rejecting the equality later on in qual_is_redundant.
-        * This is a tad slow but should be okay.
+        * generate_implied_equalities() shouldn't call me on two constants.
+        */
+       Assert(relids != NIL);
+
+       /*
+        * If the exprs involve a single rel, we need to look at that rel's
+        * baserestrictinfo list.  If multiple rels, any one will have a
+        * joininfo node for the rest, and we can scan any of 'em.
         */
-       if (exprs_known_equal(root, item1, item2))
+       rel1 = find_base_rel(root, lfirsti(relids));
+       if (lnext(relids) == NIL)
+               restrictlist = rel1->baserestrictinfo;
+       else
+       {
+               JoinInfo   *joininfo = find_joininfo_node(rel1, lnext(relids));
+
+               restrictlist = joininfo ? joininfo->jinfo_restrictinfo : NIL;
+       }
+
+       /*
+        * Scan to see if equality is already known.  If so, we're done in
+        * the add case, and done after removing it in the delete case.
+        */
+       foreach(itm, restrictlist)
+       {
+               RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(itm);
+               Node       *left,
+                                  *right;
+
+               if (restrictinfo->mergejoinoperator == InvalidOid)
+                       continue;                       /* ignore non-mergejoinable clauses */
+               /* We now know the restrictinfo clause is a binary opclause */
+               left = get_leftop(restrictinfo->clause);
+               right = get_rightop(restrictinfo->clause);
+               if ((equal(item1, left) && equal(item2, right)) ||
+                       (equal(item2, left) && equal(item1, right)))
+               {
+                       /* found a matching clause */
+                       if (delete_it)
+                       {
+                               if (lnext(relids) == NIL)
+                               {
+                                       /* delete it from local restrictinfo list */
+                                       rel1->baserestrictinfo = lremove(restrictinfo,
+                                                                                                        rel1->baserestrictinfo);
+                               }
+                               else
+                               {
+                                       /* let joininfo.c do it */
+                                       remove_join_clause_from_rels(root, restrictinfo, relids);
+                               }
+                       }
+                       return;                         /* done */
+               }
+       }
+
+       /* Didn't find it.  Done if deletion requested */
+       if (delete_it)
                return;
 
        /*
@@ -692,73 +707,7 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
         */
        distribute_qual_to_rels(root, (Node *) clause,
                                                        true, false, true,
-                                                       pull_varnos((Node *) clause));
-}
-
-/*
- * exprs_known_equal
- *       Detect whether two expressions are known equal due to equijoin clauses.
- *
- * This is not completely accurate since we avoid adding redundant restriction
- * clauses to individual base rels (see qual_is_redundant).  However, after
- * the implied-equality-deduction phase, it is complete for expressions
- * involving Vars of multiple rels; that's sufficient for planned uses.
- */
-bool
-exprs_known_equal(Query *root, Node *item1, Node *item2)
-{
-       List       *relids;
-       RelOptInfo *rel1;
-       List       *restrictlist;
-       List       *itm;
-
-       /* Get list of relids referenced in the two expressions */
-       relids = set_unioni(pull_varnos(item1), pull_varnos(item2));
-
-       /*
-        * If there are no Vars at all, say "true".  This prevents
-        * process_implied_equality from trying to store "const = const"
-        * deductions.
-        */
-       if (relids == NIL)
-               return true;
-
-       /*
-        * If the exprs involve a single rel, we need to look at that rel's
-        * baserestrictinfo list.  If multiple rels, any one will have a
-        * joininfo node for the rest, and we can scan any of 'em.
-        */
-       rel1 = find_base_rel(root, lfirsti(relids));
-       relids = lnext(relids);
-       if (relids == NIL)
-               restrictlist = rel1->baserestrictinfo;
-       else
-       {
-               JoinInfo   *joininfo = find_joininfo_node(rel1, relids);
-
-               restrictlist = joininfo ? joininfo->jinfo_restrictinfo : NIL;
-       }
-
-       /*
-        * Scan to see if equality is known.
-        */
-       foreach(itm, restrictlist)
-       {
-               RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(itm);
-               Node       *left,
-                                  *right;
-
-               if (restrictinfo->mergejoinoperator == InvalidOid)
-                       continue;                       /* ignore non-mergejoinable clauses */
-               /* We now know the restrictinfo clause is a binary opclause */
-               left = get_leftop(restrictinfo->clause);
-               right = get_rightop(restrictinfo->clause);
-               if ((equal(item1, left) && equal(item2, right)) ||
-                       (equal(item2, left) && equal(item1, right)))
-                       return true;            /* found a matching clause */
-       }
-
-       return false;
+                                                       relids);
 }
 
 /*
@@ -770,19 +719,32 @@ exprs_known_equal(Query *root, Node *item1, Node *item2)
  *                             SELECT * FROM tab WHERE f1 = f2 AND f2 = f3;
  *       We need to suppress the redundant condition to avoid computing
  *       too-small selectivity, not to mention wasting time at execution.
+ *
+ * Note: quals of the form "var = const" are never considered redundant,
+ * only those of the form "var = var".  This is needed because when we
+ * have constants in an implied-equality set, we use a different strategy
+ * that suppresses all "var = var" deductions.  We must therefore keep
+ * all the "var = const" quals.
  */
 static bool
 qual_is_redundant(Query *root,
                                  RestrictInfo *restrictinfo,
                                  List *restrictlist)
 {
-       List       *oldquals;
-       List       *olditem;
        Node       *newleft;
        Node       *newright;
+       List       *oldquals;
+       List       *olditem;
        List       *equalexprs;
        bool            someadded;
 
+       newleft = get_leftop(restrictinfo->clause);
+       newright = get_rightop(restrictinfo->clause);
+
+       /* Never redundant unless vars appear on both sides */
+       if (!contain_var_clause(newleft) || !contain_var_clause(newright))
+               return false;
+
        /*
         * Set cached pathkeys.  NB: it is okay to do this now because this
         * routine is only invoked while we are generating implied equalities.
@@ -822,8 +784,6 @@ qual_is_redundant(Query *root,
         * we find we can reach the right-side expr of the new qual, we are
         * done.  We give up when we can't expand the equalexprs list any more.
         */
-       newleft = get_leftop(restrictinfo->clause);
-       newright = get_rightop(restrictinfo->clause);
        equalexprs = makeList1(newleft);
        do
        {
index c202615b1f5d8304edfd1fe3f6f9976cad80000a..79a9f7a3bac35d7d67dc21f228324c60fcdc3678 100644 (file)
@@ -8,13 +8,14 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.32 2003/01/20 18:54:56 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.33 2003/01/24 03:58:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "optimizer/joininfo.h"
+#include "optimizer/pathnode.h"
 
 
 /*
@@ -63,3 +64,110 @@ make_joininfo_node(RelOptInfo *this_rel, Relids join_relids)
        }
        return joininfo;
 }
+
+
+/*
+ * add_join_clause_to_rels
+ *       For every relation participating in a join clause, add 'restrictinfo' to
+ *       the appropriate joininfo list (creating a new list and adding it to the
+ *       appropriate rel node if necessary).
+ *
+ * Note that the same copy of the restrictinfo node is linked to by all the
+ * lists it is in.  This allows us to exploit caching of information about
+ * the restriction clause (but we must be careful that the information does
+ * not depend on context).
+ *
+ * 'restrictinfo' describes the join clause
+ * 'join_relids' is the list of relations participating in the join clause
+ *                              (there must be more than one)
+ */
+void
+add_join_clause_to_rels(Query *root,
+                                               RestrictInfo *restrictinfo,
+                                               Relids join_relids)
+{
+       List       *join_relid;
+
+       /* For every relid, find the joininfo, and add the proper join entries */
+       foreach(join_relid, join_relids)
+       {
+               int                     cur_relid = lfirsti(join_relid);
+               Relids          unjoined_relids = NIL;
+               JoinInfo   *joininfo;
+               List       *otherrel;
+
+               /* Get the relids not equal to the current relid */
+               foreach(otherrel, join_relids)
+               {
+                       if (lfirsti(otherrel) != cur_relid)
+                               unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel));
+               }
+               Assert(unjoined_relids != NIL);
+
+               /*
+                * Find or make the joininfo node for this combination of rels,
+                * and add the restrictinfo node to it.
+                */
+               joininfo = make_joininfo_node(find_base_rel(root, cur_relid),
+                                                                         unjoined_relids);
+               joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
+                                                                                          restrictinfo);
+               /*
+                * Can't freeList(unjoined_relids) because new joininfo node may
+                * link to it.  We could avoid leaking memory by doing listCopy()
+                * in make_joininfo_node, but for now speed seems better.
+                */
+       }
+}
+
+/*
+ * remove_join_clause_from_rels
+ *       Delete 'restrictinfo' from all the joininfo lists it is in
+ *
+ * This reverses the effect of add_join_clause_to_rels.  It's used when we
+ * discover that a join clause is redundant.
+ *
+ * 'restrictinfo' describes the join clause
+ * 'join_relids' is the list of relations participating in the join clause
+ *                              (there must be more than one)
+ */
+void
+remove_join_clause_from_rels(Query *root,
+                                                        RestrictInfo *restrictinfo,
+                                                        Relids join_relids)
+{
+       List       *join_relid;
+
+       /* For every relid, find the joininfo */
+       foreach(join_relid, join_relids)
+       {
+               int                     cur_relid = lfirsti(join_relid);
+               Relids          unjoined_relids = NIL;
+               JoinInfo   *joininfo;
+               List       *otherrel;
+
+               /* Get the relids not equal to the current relid */
+               foreach(otherrel, join_relids)
+               {
+                       if (lfirsti(otherrel) != cur_relid)
+                               unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel));
+               }
+               Assert(unjoined_relids != NIL);
+
+               /*
+                * Find the joininfo node for this combination of rels; it should
+                * exist already, if add_join_clause_to_rels was called.
+                */
+               joininfo = find_joininfo_node(find_base_rel(root, cur_relid),
+                                                                         unjoined_relids);
+               Assert(joininfo);
+               /*
+                * Remove the restrictinfo from the list.  Pointer comparison
+                * is sufficient.
+                */
+               Assert(ptrMember(restrictinfo, joininfo->jinfo_restrictinfo));
+               joininfo->jinfo_restrictinfo = lremove(restrictinfo,
+                                                                                          joininfo->jinfo_restrictinfo);
+               freeList(unjoined_relids);
+       }
+}
index 144fac75501947bfd5c511489f26e7eceb934319..06a73bf4e9e3a26021dfde3d6490c579feb26dc7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.44 2003/01/20 18:54:56 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.45 2003/01/24 03:58:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -549,14 +549,19 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
                        /*
                         * These clauses are still join clauses at this level, so find
                         * or make the appropriate JoinInfo item for the joinrel, and
-                        * add the clauses to it (eliminating duplicates).
+                        * add the clauses to it, eliminating duplicates.  (Since
+                        * RestrictInfo nodes are normally multiply-linked rather than
+                        * copied, pointer equality should be a sufficient test.  If
+                        * two equal() nodes should happen to sneak in, no great harm
+                        * is done --- they'll be detected by redundant-clause testing
+                        * when they reach a restriction list.)
                         */
                        JoinInfo   *new_joininfo;
 
                        new_joininfo = make_joininfo_node(joinrel, new_unjoined_relids);
                        new_joininfo->jinfo_restrictinfo =
-                               set_union(new_joininfo->jinfo_restrictinfo,
-                                                 joininfo->jinfo_restrictinfo);
+                               set_ptrUnion(new_joininfo->jinfo_restrictinfo,
+                                                        joininfo->jinfo_restrictinfo);
                }
        }
 }
index bc1fcc36464207c76fe16b177384c9174939d28f..bdcc338d609a22fa50f24fb409cdcc896a52b9e1 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.15 2002/11/24 21:52:14 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.16 2003/01/24 03:58:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 #include "optimizer/clauses.h"
 #include "optimizer/paths.h"
 #include "optimizer/restrictinfo.h"
+#include "optimizer/var.h"
 
 
 /*
@@ -101,6 +102,13 @@ get_actual_join_clauses(List *restrictinfo_list,
  * equality between any set member on the left and any member on the right;
  * by transitivity, all the rest are then equal.
  *
+ * However, clauses that are of the form "var expr = const expr" cannot be
+ * eliminated as redundant.  This is because when there are const expressions
+ * in a pathkey set, generate_implied_equalities() suppresses "var = var"
+ * clauses in favor of "var = const" clauses.  We cannot afford to drop any
+ * of the latter, even though they might seem redundant by the pathkey
+ * membership test.
+ *
  * Weird special case: if we have two clauses that seem redundant
  * except one is pushed down into an outer join and the other isn't,
  * then they're not really redundant, because one constrains the
@@ -120,7 +128,7 @@ remove_redundant_join_clauses(Query *root, List *restrictinfo_list,
        {
                RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
 
-               /* eliminate duplicates */
+               /* always eliminate duplicates */
                if (member(rinfo, result))
                        continue;
 
@@ -132,6 +140,7 @@ remove_redundant_join_clauses(Query *root, List *restrictinfo_list,
 
                        cache_mergeclause_pathkeys(root, rinfo);
 
+                       /* do the cheap tests first */
                        foreach(olditem, result)
                        {
                                RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
@@ -148,7 +157,20 @@ remove_redundant_join_clauses(Query *root, List *restrictinfo_list,
                        }
 
                        if (redundant)
-                               continue;
+                       {
+                               /*
+                                * It looks redundant, now check for "var = const" case.
+                                * If left_relids/right_relids are set, then there are
+                                * definitely vars on both sides; else we must check the
+                                * hard way.
+                                */
+                               if (rinfo->left_relids)
+                                       continue;       /* var = var, so redundant */
+                               if (contain_var_clause(get_leftop(rinfo->clause)) &&
+                                       contain_var_clause(get_rightop(rinfo->clause)))
+                                       continue;       /* var = var, so redundant */
+                               /* else var = const, not redundant */
+                       }
                }
 
                /* otherwise, add it to result list */
index 20d353a0a5099f88efd517e2d50c4d6bdb390459..62e0b8b32a9c2f5e8b94fe09d215265678484dc4 100644 (file)
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.128 2003/01/22 20:16:42 tgl Exp $
+ *       $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.129 2003/01/24 03:58:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -84,8 +84,8 @@
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
+#include "optimizer/paths.h"
 #include "optimizer/plancat.h"
-#include "optimizer/planmain.h"
 #include "optimizer/prep.h"
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
index d3b01b7fed0ad00f4c7bc5ca06ae077d38303ac8..56a6640916113066d9053378be3f569a923c4f19 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_list.h,v 1.31 2003/01/20 18:55:04 tgl Exp $
+ * $Id: pg_list.h,v 1.32 2003/01/24 03:58:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -141,6 +141,7 @@ extern List *set_differencei(List *list1, List *list2);
 extern List *lreverse(List *l);
 extern List *set_union(List *list1, List *list2);
 extern List *set_unioni(List *list1, List *list2);
+extern List *set_ptrUnion(List *list1, List *list2);
 extern List *set_intersecti(List *list1, List *list2);
 
 extern bool equali(List *list1, List *list2);
index 37131b722d2799567e9182c7320f17d1f4c93ffd..6fd806bbaf1bb571a6302abbf29171ccebb19f05 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: joininfo.h,v 1.22 2003/01/20 18:55:04 tgl Exp $
+ * $Id: joininfo.h,v 1.23 2003/01/24 03:58:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "nodes/relation.h"
 
-extern JoinInfo *find_joininfo_node(RelOptInfo *this_rel, List *join_relids);
-extern JoinInfo *make_joininfo_node(RelOptInfo *this_rel, List *join_relids);
+
+extern JoinInfo *find_joininfo_node(RelOptInfo *this_rel, Relids join_relids);
+extern JoinInfo *make_joininfo_node(RelOptInfo *this_rel, Relids join_relids);
+
+extern void add_join_clause_to_rels(Query *root,
+                                                                       RestrictInfo *restrictinfo,
+                                                                       Relids join_relids);
+extern void remove_join_clause_from_rels(Query *root,
+                                                                                RestrictInfo *restrictinfo,
+                                                                                Relids join_relids);
 
 #endif   /* JOININFO_H */
index 7ed5b403a87c1bc1f2b1798b412c7c35f01d5a4b..76285bac408c63d53bde46e517fd935f2ac7b304 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: paths.h,v 1.63 2002/12/16 21:30:30 tgl Exp $
+ * $Id: paths.h,v 1.64 2003/01/24 03:58:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,6 +17,7 @@
 
 #include "nodes/relation.h"
 
+
 /* default GEQO threshold (default value for geqo_rels) */
 /* If you change this, update backend/utils/misc/postgresql.sample.conf */
 #define DEFAULT_GEQO_RELS 11
@@ -92,6 +93,7 @@ typedef enum
 } PathKeysComparison;
 
 extern void add_equijoined_keys(Query *root, RestrictInfo *restrictinfo);
+extern bool exprs_known_equal(Query *root, Node *item1, Node *item2);
 extern void generate_implied_equalities(Query *root);
 extern List *canonicalize_pathkeys(Query *root, List *pathkeys);
 extern PathKeysComparison compare_pathkeys(List *keys1, List *keys2);
index cf9c2ddeb64315b0e8d2e2b69fab1318075c6b43..399b3bb1310eaafd512c1f33a21cedae04c9dacd 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: planmain.h,v 1.67 2003/01/20 18:55:05 tgl Exp $
+ * $Id: planmain.h,v 1.68 2003/01/24 03:58:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,9 +57,11 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
 extern void add_base_rels_to_query(Query *root, Node *jtnode);
 extern void build_base_rel_tlists(Query *root, List *tlist);
 extern Relids distribute_quals_to_rels(Query *root, Node *jtnode);
-extern void process_implied_equality(Query *root, Node *item1, Node *item2,
-                                                Oid sortop1, Oid sortop2);
-extern bool exprs_known_equal(Query *root, Node *item1, Node *item2);
+extern void process_implied_equality(Query *root,
+                                                                        Node *item1, Node *item2,
+                                                                        Oid sortop1, Oid sortop2,
+                                                                        Relids item1_relids, Relids item2_relids,
+                                                                        bool delete_it);
 
 /*
  * prototypes for plan/setrefs.c