Modify planner's implied-equality-deduction code so that when a set
authorTom Lane
Fri, 24 Jan 2003 03:58:44 +0000 (03:58 +0000)
committerTom Lane
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