Index: src/backend/optimizer/plan/initsplan.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v
retrieving revision 1.123.2.3
diff -c -r1.123.2.3 initsplan.c
*** src/backend/optimizer/plan/initsplan.c	13 Feb 2007 02:31:12 -0000	1.123.2.3
--- src/backend/optimizer/plan/initsplan.c	16 Feb 2007 20:42:45 -0000
***************
*** 45,51 ****
  				   Relids left_rels, Relids right_rels,
  				   bool is_full_join, Node *clause);
  static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
- 						bool is_pushed_down,
  						bool is_deduced,
  						bool below_outer_join,
  						Relids qualscope,
--- 45,50 ----
***************
*** 286,297 ****
  		}
  
  		/*
! 		 * Now process the top-level quals.  These are always marked as
! 		 * "pushed down", since they clearly didn't come from a JOIN expr.
  		 */
  		foreach(l, (List *) f->quals)
  			distribute_qual_to_rels(root, (Node *) lfirst(l),
! 									true, false, below_outer_join,
  									*qualscope, NULL, NULL);
  	}
  	else if (IsA(jtnode, JoinExpr))
--- 285,295 ----
  		}
  
  		/*
! 		 * Now process the top-level quals.
  		 */
  		foreach(l, (List *) f->quals)
  			distribute_qual_to_rels(root, (Node *) lfirst(l),
! 									false, below_outer_join,
  									*qualscope, NULL, NULL);
  	}
  	else if (IsA(jtnode, JoinExpr))
***************
*** 392,398 ****
  		/* Process the qual clauses */
  		foreach(qual, (List *) j->quals)
  			distribute_qual_to_rels(root, (Node *) lfirst(qual),
! 									false, false, below_outer_join,
  									*qualscope, ojscope, nonnullable_rels);
  
  		/* Now we can add the OuterJoinInfo to oj_info_list */
--- 390,396 ----
  		/* Process the qual clauses */
  		foreach(qual, (List *) j->quals)
  			distribute_qual_to_rels(root, (Node *) lfirst(qual),
! 									false, below_outer_join,
  									*qualscope, ojscope, nonnullable_rels);
  
  		/* Now we can add the OuterJoinInfo to oj_info_list */
***************
*** 603,610 ****
   *	  equijoined vars.
   *
   * 'clause': the qual clause to be distributed
-  * 'is_pushed_down': if TRUE, force the clause to be marked 'is_pushed_down'
-  *		(this indicates the clause came from a FromExpr, not a JoinExpr)
   * 'is_deduced': TRUE if the qual came from implied-equality deduction
   * 'below_outer_join': TRUE if the qual is from a JOIN/ON that is below the
   *		nullable side of a higher-level outer join.
--- 601,606 ----
***************
*** 622,628 ****
   */
  static void
  distribute_qual_to_rels(PlannerInfo *root, Node *clause,
- 						bool is_pushed_down,
  						bool is_deduced,
  						bool below_outer_join,
  						Relids qualscope,
--- 618,623 ----
***************
*** 630,635 ****
--- 625,631 ----
  						Relids outerjoin_nonnullable)
  {
  	Relids		relids;
+ 	bool		is_pushed_down;
  	bool		outerjoin_delayed;
  	bool		pseudoconstant = false;
  	bool		maybe_equijoin;
***************
*** 697,713 ****
  				root->hasPseudoConstantQuals = true;
  				/* if not below outer join, push it to top of tree */
  				if (!below_outer_join)
- 				{
  					relids = get_relids_in_jointree((Node *) root->parse->jointree);
- 					is_pushed_down = true;
- 				}
  			}
  		}
  	}
  
! 	/*
  	 * Check to see if clause application must be delayed by outer-join
  	 * considerations.
  	 */
  	if (is_deduced)
  	{
--- 693,729 ----
  				root->hasPseudoConstantQuals = true;
  				/* if not below outer join, push it to top of tree */
  				if (!below_outer_join)
  					relids = get_relids_in_jointree((Node *) root->parse->jointree);
  			}
  		}
  	}
  
! 	/*----------
  	 * Check to see if clause application must be delayed by outer-join
  	 * considerations.
+ 	 *
+ 	 * A word about is_pushed_down: we mark the qual as "pushed down" if
+ 	 * it is (potentially) applicable at a level different from its original
+ 	 * syntactic level.  This flag is used to distinguish OUTER JOIN ON quals
+ 	 * from other quals pushed down to the same joinrel.  The rules are:
+ 	 *		WHERE quals and INNER JOIN quals: is_pushed_down = true.
+ 	 *		Non-degenerate OUTER JOIN quals: is_pushed_down = false.
+ 	 *		Degenerate OUTER JOIN quals: is_pushed_down = true.
+ 	 * A "degenerate" OUTER JOIN qual is one that doesn't mention the
+ 	 * non-nullable side, and hence can be pushed down into the nullable side
+ 	 * without changing the join result.  It is correct to treat it as a
+ 	 * regular filter condition at the level where it is evaluated.
+ 	 *
+ 	 * Note: it is not immediately obvious that a simple boolean is enough
+ 	 * for this: if for some reason we were to attach a degenerate qual to
+ 	 * its original join level, it would need to be treated as an outer join
+ 	 * qual there.  However, this cannot happen, because all the rels the
+ 	 * clause mentions must be in the outer join's min_righthand, therefore
+ 	 * the join it needs must be formed before the outer join; and we always
+ 	 * attach quals to the lowest level where they can be evaluated.  But
+ 	 * if we were ever to re-introduce a mechanism for delaying evaluation
+ 	 * of "expensive" quals, this area would need work.
+ 	 *----------
  	 */
  	if (is_deduced)
  	{
***************
*** 720,725 ****
--- 736,742 ----
  		Assert(bms_equal(relids, qualscope));
  		Assert(!ojscope);
  		Assert(!pseudoconstant);
+ 		is_pushed_down = true;
  		/* Needn't feed it back for more deductions */
  		outerjoin_delayed = false;
  		maybe_equijoin = false;
***************
*** 729,747 ****
  	{
  		/*
  		 * The qual is attached to an outer join and mentions (some of the)
! 		 * rels on the nonnullable side.  Force the qual to be evaluated
! 		 * exactly at the level of joining corresponding to the outer join. We
! 		 * cannot let it get pushed down into the nonnullable side, since then
! 		 * we'd produce no output rows, rather than the intended single
! 		 * null-extended row, for any nonnullable-side rows failing the qual.
! 		 *
! 		 * Note: an outer-join qual that mentions only nullable-side rels can
! 		 * be pushed down into the nullable side without changing the join
! 		 * result, so we treat it the same as an ordinary inner-join qual,
! 		 * except for not setting maybe_equijoin (see below).
  		 */
  		Assert(ojscope);
  		relids = ojscope;
  		outerjoin_delayed = true;
  		Assert(!pseudoconstant);
  
--- 746,761 ----
  	{
  		/*
  		 * The qual is attached to an outer join and mentions (some of the)
! 		 * rels on the nonnullable side, so it's not degenerate.  Force the
! 		 * qual to be evaluated exactly at the level of joining corresponding
! 		 * to the outer join. We cannot let it get pushed down into the
! 		 * nonnullable side, since then we'd produce no output rows, rather
! 		 * than the intended single null-extended row, for any
! 		 * nonnullable-side rows failing the qual.
  		 */
  		Assert(ojscope);
  		relids = ojscope;
+ 		is_pushed_down = false;
  		outerjoin_delayed = true;
  		Assert(!pseudoconstant);
  
***************
*** 760,766 ****
  	else
  	{
  		/*
! 		 * For a non-outer-join qual, we can evaluate the qual as soon as (1)
  		 * we have all the rels it mentions, and (2) we are at or above any
  		 * outer joins that can null any of these rels and are below the
  		 * syntactic location of the given qual.  We must enforce (2) because
--- 774,783 ----
  	else
  	{
  		/*
! 		 * Normal qual clause or degenerate outer-join clause.  Either way,
! 		 * we can mark it as pushed-down.
! 		 *
! 		 * For a pushed-down qual, we can evaluate the qual as soon as (1)
  		 * we have all the rels it mentions, and (2) we are at or above any
  		 * outer joins that can null any of these rels and are below the
  		 * syntactic location of the given qual.  We must enforce (2) because
***************
*** 784,789 ****
--- 801,807 ----
  		 */
  		bool		found_some;
  
+ 		is_pushed_down = true;
  		outerjoin_delayed = false;
  		do {
  			ListCell   *l;
***************
*** 847,863 ****
  	}
  
  	/*
- 	 * Mark the qual as "pushed down" if it can be applied at a level below
- 	 * its original syntactic level.  This allows us to distinguish original
- 	 * JOIN/ON quals from higher-level quals pushed down to the same joinrel.
- 	 * A qual originating from WHERE is always considered "pushed down". Note
- 	 * that for an outer-join qual, we have to compare to ojscope not
- 	 * qualscope.
- 	 */
- 	if (!is_pushed_down)
- 		is_pushed_down = !bms_equal(relids, ojscope ? ojscope : qualscope);
- 
- 	/*
  	 * Build the RestrictInfo node itself.
  	 */
  	restrictinfo = make_restrictinfo((Expr *) clause,
--- 865,870 ----
***************
*** 1161,1172 ****
  
  	/*
  	 * Push the new clause into all the appropriate restrictinfo lists.
- 	 *
- 	 * Note: we mark the qual "pushed down" to ensure that it can never be
- 	 * taken for an original JOIN/ON clause.
  	 */
  	distribute_qual_to_rels(root, (Node *) clause,
! 							true, true, false, relids, NULL, NULL);
  }
  
  /*
--- 1168,1176 ----
  
  	/*
  	 * Push the new clause into all the appropriate restrictinfo lists.
  	 */
  	distribute_qual_to_rels(root, (Node *) clause,
! 							true, false, relids, NULL, NULL);
  }
  
  /*
Index: src/include/nodes/relation.h
===================================================================
RCS file: /cvsroot/pgsql/src/include/nodes/relation.h,v
retrieving revision 1.128
diff -c -r1.128 relation.h
*** src/include/nodes/relation.h	4 Oct 2006 00:30:09 -0000	1.128
--- src/include/nodes/relation.h	16 Feb 2007 20:42:46 -0000
***************
*** 697,710 ****
   * join, we prevent it from being evaluated below the outer join's joinrel.
   * When we do form the outer join's joinrel, we still need to distinguish
   * those quals that are actually in that join's JOIN/ON condition from those
!  * that appeared higher in the tree and were pushed down to the join rel
   * because they used no other rels.  That's what the is_pushed_down flag is
!  * for; it tells us that a qual came from a point above the join of the
!  * set of base rels listed in required_relids.	A clause that originally came
!  * from WHERE will *always* have its is_pushed_down flag set; a clause that
!  * came from an INNER JOIN condition, but doesn't use all the rels being
!  * joined, will also have is_pushed_down set because it will get attached to
!  * some lower joinrel.
   *
   * When application of a qual must be delayed by outer join, we also mark it
   * with outerjoin_delayed = true.  This isn't redundant with required_relids
--- 697,710 ----
   * join, we prevent it from being evaluated below the outer join's joinrel.
   * When we do form the outer join's joinrel, we still need to distinguish
   * those quals that are actually in that join's JOIN/ON condition from those
!  * that appeared elsewhere in the tree and were pushed down to the join rel
   * because they used no other rels.  That's what the is_pushed_down flag is
!  * for; it tells us that a qual is not an OUTER JOIN qual for the set of base
!  * rels listed in required_relids.  A clause that originally came from WHERE
!  * or an INNER JOIN condition will *always* have its is_pushed_down flag set.
!  * It's possible for an OUTER JOIN clause to be marked is_pushed_down too,
!  * if we decide that it can be pushed down into the nullable side of the join.
!  * In that case it acts as a plain filter qual for wherever it gets evaluated.
   *
   * When application of a qual must be delayed by outer join, we also mark it
   * with outerjoin_delayed = true.  This isn't redundant with required_relids
