*
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.105 2010/02/26 02:00:45 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/path/joinrels.c,v 1.106 2010/09/14 23:15:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 static bool has_legal_joinclause(PlannerInfo *root, RelOptInfo *rel);
 static bool is_dummy_rel(RelOptInfo *rel);
 static void mark_dummy_rel(RelOptInfo *rel);
-static bool restriction_is_constant_false(List *restrictlist);
+static bool restriction_is_constant_false(List *restrictlist,
+                                         bool only_pushed_down);
 
 
 /*
     *
     * Also, a provably constant-false join restriction typically means that
     * we can skip evaluating one or both sides of the join.  We do this by
-    * marking the appropriate rel as dummy.
+    * marking the appropriate rel as dummy.  For outer joins, a constant-false
+    * restriction that is pushed down still means the whole join is dummy,
+    * while a non-pushed-down one means that no inner rows will join so we
+    * can treat the inner rel as dummy.
     *
     * We need only consider the jointypes that appear in join_info_list, plus
     * JOIN_INNER.
    {
        case JOIN_INNER:
            if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
-               restriction_is_constant_false(restrictlist))
+               restriction_is_constant_false(restrictlist, false))
            {
                mark_dummy_rel(joinrel);
                break;
                                 restrictlist);
            break;
        case JOIN_LEFT:
-           if (is_dummy_rel(rel1))
+           if (is_dummy_rel(rel1) ||
+               restriction_is_constant_false(restrictlist, true))
            {
                mark_dummy_rel(joinrel);
                break;
            }
-           if (restriction_is_constant_false(restrictlist) &&
+           if (restriction_is_constant_false(restrictlist, false) &&
                bms_is_subset(rel2->relids, sjinfo->syn_righthand))
                mark_dummy_rel(rel2);
            add_paths_to_joinrel(root, joinrel, rel1, rel2,
                                 restrictlist);
            break;
        case JOIN_FULL:
-           if (is_dummy_rel(rel1) && is_dummy_rel(rel2))
+           if ((is_dummy_rel(rel1) && is_dummy_rel(rel2)) ||
+               restriction_is_constant_false(restrictlist, true))
            {
                mark_dummy_rel(joinrel);
                break;
                bms_is_subset(sjinfo->min_righthand, rel2->relids))
            {
                if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
-                   restriction_is_constant_false(restrictlist))
+                   restriction_is_constant_false(restrictlist, false))
                {
                    mark_dummy_rel(joinrel);
                    break;
                create_unique_path(root, rel2, rel2->cheapest_total_path,
                                   sjinfo) != NULL)
            {
+               if (is_dummy_rel(rel1) || is_dummy_rel(rel2) ||
+                   restriction_is_constant_false(restrictlist, false))
+               {
+                   mark_dummy_rel(joinrel);
+                   break;
+               }
                add_paths_to_joinrel(root, joinrel, rel1, rel2,
                                     JOIN_UNIQUE_INNER, sjinfo,
                                     restrictlist);
            }
            break;
        case JOIN_ANTI:
-           if (is_dummy_rel(rel1))
+           if (is_dummy_rel(rel1) ||
+               restriction_is_constant_false(restrictlist, true))
            {
                mark_dummy_rel(joinrel);
                break;
            }
-           if (restriction_is_constant_false(restrictlist) &&
+           if (restriction_is_constant_false(restrictlist, false) &&
                bms_is_subset(rel2->relids, sjinfo->syn_righthand))
                mark_dummy_rel(rel2);
            add_paths_to_joinrel(root, joinrel, rel1, rel2,
  * join situations this will leave us computing cartesian products only to
  * decide there's no match for an outer row, which is pretty stupid.  So,
  * we need to detect the case.
+ *
+ * If only_pushed_down is TRUE, then consider only pushed-down quals.
  */
 static bool
-restriction_is_constant_false(List *restrictlist)
+restriction_is_constant_false(List *restrictlist, bool only_pushed_down)
 {
    ListCell   *lc;
 
        RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc);
 
        Assert(IsA(rinfo, RestrictInfo));
+       if (only_pushed_down && !rinfo->is_pushed_down)
+           continue;
+
        if (rinfo->clause && IsA(rinfo->clause, Const))
        {
            Const      *con = (Const *) rinfo->clause;
 
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/analyzejoins.c,v 1.3 2010/07/06 19:18:56 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/analyzejoins.c,v 1.4 2010/09/14 23:15:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
+#include "optimizer/joininfo.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 
 /* local functions */
 static bool join_is_removable(PlannerInfo *root, SpecialJoinInfo *sjinfo);
-static void remove_rel_from_query(PlannerInfo *root, int relid);
+static void remove_rel_from_query(PlannerInfo *root, int relid,
+                                 Relids joinrelids);
 static List *remove_rel_from_joinlist(List *joinlist, int relid, int *nremoved);
 
 
         */
        innerrelid = bms_singleton_member(sjinfo->min_righthand);
 
-       remove_rel_from_query(root, innerrelid);
+       remove_rel_from_query(root, innerrelid,
+                             bms_union(sjinfo->min_lefthand,
+                                       sjinfo->min_righthand));
 
        /* We verify that exactly one reference gets removed from joinlist */
        nremoved = 0;
    {
        RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);
 
-       /* Ignore clauses not pertinent to this join */
-       if (!bms_is_subset(restrictinfo->required_relids, joinrelids))
-           continue;
-
        /*
-        * If we find a pushed-down clause, it must have come from above the
-        * outer join and it must contain references to the inner rel.  (If it
-        * had only outer-rel variables, it'd have been pushed down into the
-        * outer rel.)  Therefore, we can conclude that join removal is unsafe
-        * without any examination of the clause contents.
+        * If it's not a join clause for this outer join, we can't use it.
+        * Note that if the clause is pushed-down, then it is logically from
+        * above the outer join, even if it references no other rels (it might
+        * be from WHERE, for example).
         */
-       if (restrictinfo->is_pushed_down)
-           return false;
+       if (restrictinfo->is_pushed_down ||
+           !bms_equal(restrictinfo->required_relids, joinrelids))
+       {
+           /*
+            * If such a clause actually references the inner rel then
+            * join removal has to be disallowed.  We have to check this
+            * despite the previous attr_needed checks because of the
+            * possibility of pushed-down clauses referencing the rel.
+            */
+           if (bms_is_member(innerrelid, restrictinfo->clause_relids))
+               return false;
+           continue;           /* else, ignore; not useful here */
+       }
 
        /* Ignore if it's not a mergejoinable clause */
        if (!restrictinfo->can_join ||
  * We are not terribly thorough here.  We must make sure that the rel is
  * no longer treated as a baserel, and that attributes of other baserels
  * are no longer marked as being needed at joins involving this rel.
- * In particular, we don't bother removing join quals involving the rel from
- * the joininfo lists; they'll just get ignored, since we will never form a
- * join relation at which they could be evaluated.
+ * Also, join quals involving the rel have to be removed from the joininfo
+ * lists, but only if they belong to the outer join identified by joinrelids.
  */
 static void
-remove_rel_from_query(PlannerInfo *root, int relid)
+remove_rel_from_query(PlannerInfo *root, int relid, Relids joinrelids)
 {
    RelOptInfo *rel = find_base_rel(root, relid);
+   List       *joininfos;
    Index       rti;
    ListCell   *l;
 
 
        phinfo->ph_needed = bms_del_member(phinfo->ph_needed, relid);
    }
+
+   /*
+    * Remove any joinquals referencing the rel from the joininfo lists.
+    *
+    * In some cases, a joinqual has to be put back after deleting its
+    * reference to the target rel.  This can occur for pseudoconstant and
+    * outerjoin-delayed quals, which can get marked as requiring the rel in
+    * order to force them to be evaluated at or above the join.  We can't
+    * just discard them, though.  Only quals that logically belonged to the
+    * outer join being discarded should be removed from the query.
+    *
+    * We must make a copy of the rel's old joininfo list before starting the
+    * loop, because otherwise remove_join_clause_from_rels would destroy the
+    * list while we're scanning it.
+    */
+   joininfos = list_copy(rel->joininfo);
+   foreach(l, joininfos)
+   {
+       RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
+
+       remove_join_clause_from_rels(root, rinfo, rinfo->required_relids);
+
+       if (rinfo->is_pushed_down ||
+           !bms_equal(rinfo->required_relids, joinrelids))
+       {
+           /* Recheck that qual doesn't actually reference the target rel */
+           Assert(!bms_is_member(relid, rinfo->clause_relids));
+           /*
+            * The required_relids probably aren't shared with anything else,
+            * but let's copy them just to be sure.
+            */
+           rinfo->required_relids = bms_copy(rinfo->required_relids);
+           rinfo->required_relids = bms_del_member(rinfo->required_relids,
+                                                   relid);
+           distribute_restrictinfo_to_rels(root, rinfo);
+       }
+   }
 }
 
 /*
 
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/joininfo.c,v 1.52 2010/01/02 16:57:48 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/joininfo.c,v 1.53 2010/09/14 23:15:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
    }
    bms_free(tmprelids);
 }
+
+/*
+ * 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 relation need not be joined at all.
+ *
+ * '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(PlannerInfo *root,
+                            RestrictInfo *restrictinfo,
+                            Relids join_relids)
+{
+   Relids      tmprelids;
+   int         cur_relid;
+
+   tmprelids = bms_copy(join_relids);
+   while ((cur_relid = bms_first_member(tmprelids)) >= 0)
+   {
+       RelOptInfo *rel = find_base_rel(root, cur_relid);
+
+       /*
+        * Remove the restrictinfo from the list.  Pointer comparison is
+        * sufficient.
+        */
+       Assert(list_member_ptr(rel->joininfo, restrictinfo));
+       rel->joininfo = list_delete_ptr(rel->joininfo, restrictinfo);
+   }
+   bms_free(tmprelids);
+}
 
  * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/joininfo.h,v 1.38 2010/01/02 16:58:07 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/joininfo.h,v 1.39 2010/09/14 23:15:29 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 extern void add_join_clause_to_rels(PlannerInfo *root,
                        RestrictInfo *restrictinfo,
                        Relids join_relids);
+extern void remove_join_clause_from_rels(PlannerInfo *root,
+                            RestrictInfo *restrictinfo,
+                            Relids join_relids);
 
 #endif   /* JOININFO_H */
 
          ->  Seq Scan on child c
 (5 rows)
 
+-- check for a 9.0rc1 bug: join removal breaks pseudoconstant qual handling
+select p.* from
+  parent p left join child c on (p.k = c.k)
+  where p.k = 1 and p.k = 2;
+ k | pd 
+---+----
+(0 rows)
+
+explain (costs off)
+select p.* from
+  parent p left join child c on (p.k = c.k)
+  where p.k = 1 and p.k = 2;
+                   QUERY PLAN                   
+------------------------------------------------
+ Result
+   One-Time Filter: false
+   ->  Index Scan using parent_pkey on parent p
+         Index Cond: (k = 1)
+(4 rows)
+
+select p.* from
+  (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k
+  where p.k = 1 and p.k = 2;
+ k | pd 
+---+----
+(0 rows)
+
+explain (costs off)
+select p.* from
+  (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k
+  where p.k = 1 and p.k = 2;
+        QUERY PLAN        
+--------------------------
+ Result
+   One-Time Filter: false
+(2 rows)
+
 -- bug 5255: this is not optimizable by join removal
 begin;
 CREATE TEMP TABLE a (id int PRIMARY KEY);
 
     left join (select c.*, true as linked from child c) as ss
     on (p.k = ss.k);
 
+-- check for a 9.0rc1 bug: join removal breaks pseudoconstant qual handling
+select p.* from
+  parent p left join child c on (p.k = c.k)
+  where p.k = 1 and p.k = 2;
+explain (costs off)
+select p.* from
+  parent p left join child c on (p.k = c.k)
+  where p.k = 1 and p.k = 2;
+
+select p.* from
+  (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k
+  where p.k = 1 and p.k = 2;
+explain (costs off)
+select p.* from
+  (parent p left join child c on (p.k = c.k)) join parent x on p.k = x.k
+  where p.k = 1 and p.k = 2;
+
 -- bug 5255: this is not optimizable by join removal
 begin;