Index: src/backend/optimizer/path/costsize.c
===================================================================
RCS file: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v
retrieving revision 1.167
diff -c -r1.167 costsize.c
*** src/backend/optimizer/path/costsize.c	4 Oct 2006 00:29:53 -0000	1.167
--- src/backend/optimizer/path/costsize.c	10 Nov 2006 01:16:15 -0000
***************
*** 1948,1954 ****
  						   JoinType jointype,
  						   List *restrictlist)
  {
! 	Selectivity selec;
  	double		nrows;
  	UniquePath *upath;
  
--- 1948,1955 ----
  						   JoinType jointype,
  						   List *restrictlist)
  {
! 	Selectivity jselec;
! 	Selectivity pselec;
  	double		nrows;
  	UniquePath *upath;
  
***************
*** 1957,1976 ****
  	 * clauses that become restriction clauses at this join level; we are not
  	 * double-counting them because they were not considered in estimating the
  	 * sizes of the component rels.
  	 */
! 	selec = clauselist_selectivity(root,
! 								   restrictlist,
! 								   0,
! 								   jointype);
  
  	/*
  	 * Basically, we multiply size of Cartesian product by selectivity.
  	 *
! 	 * If we are doing an outer join, take that into account: the output must
! 	 * be at least as large as the non-nullable input.	(Is there any chance
! 	 * of being even smarter?)	(XXX this is not really right, because it
! 	 * assumes all the restriction clauses are join clauses; we should figure
! 	 * pushed-down clauses separately.)
  	 *
  	 * For JOIN_IN and variants, the Cartesian product is figured with respect
  	 * to a unique-ified input, and then we can clamp to the size of the other
--- 1958,2017 ----
  	 * clauses that become restriction clauses at this join level; we are not
  	 * double-counting them because they were not considered in estimating the
  	 * sizes of the component rels.
+ 	 *
+ 	 * For an outer join, we have to distinguish the selectivity of the
+ 	 * join's own clauses (JOIN/ON conditions) from any clauses that were
+ 	 * "pushed down".  For inner joins we just count them all as joinclauses.
  	 */
! 	if (IS_OUTER_JOIN(jointype))
! 	{
! 		List	   *joinquals = NIL;
! 		List	   *pushedquals = NIL;
! 		ListCell   *l;
! 
! 		/* Grovel through the clauses to separate into two lists */
! 		foreach(l, restrictlist)
! 		{
! 			RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);
! 
! 			Assert(IsA(rinfo, RestrictInfo));
! 			if (rinfo->is_pushed_down)
! 				pushedquals = lappend(pushedquals, rinfo);
! 			else
! 				joinquals = lappend(joinquals, rinfo);
! 		}
! 
! 		/* Get the separate selectivities */
! 		jselec = clauselist_selectivity(root,
! 										joinquals,
! 										0,
! 										jointype);
! 		pselec = clauselist_selectivity(root,
! 										pushedquals,
! 										0,
! 										jointype);
! 
! 		/* Avoid leaking a lot of ListCells */
! 		list_free(joinquals);
! 		list_free(pushedquals);
! 	}
! 	else
! 	{
! 		jselec = clauselist_selectivity(root,
! 										restrictlist,
! 										0,
! 										jointype);
! 		pselec = 0.0;			/* not used, keep compiler quiet */
! 	}
  
  	/*
  	 * Basically, we multiply size of Cartesian product by selectivity.
  	 *
! 	 * If we are doing an outer join, take that into account: the joinqual
! 	 * selectivity has to be clamped using the knowledge that the output must
! 	 * be at least as large as the non-nullable input.  However, any
! 	 * pushed-down quals are applied after the outer join, so their
! 	 * selectivity applies fully.
  	 *
  	 * For JOIN_IN and variants, the Cartesian product is figured with respect
  	 * to a unique-ified input, and then we can clamp to the size of the other
***************
*** 1979,2008 ****
  	switch (jointype)
  	{
  		case JOIN_INNER:
! 			nrows = outer_rel->rows * inner_rel->rows * selec;
  			break;
  		case JOIN_LEFT:
! 			nrows = outer_rel->rows * inner_rel->rows * selec;
  			if (nrows < outer_rel->rows)
  				nrows = outer_rel->rows;
  			break;
  		case JOIN_RIGHT:
! 			nrows = outer_rel->rows * inner_rel->rows * selec;
  			if (nrows < inner_rel->rows)
  				nrows = inner_rel->rows;
  			break;
  		case JOIN_FULL:
! 			nrows = outer_rel->rows * inner_rel->rows * selec;
  			if (nrows < outer_rel->rows)
  				nrows = outer_rel->rows;
  			if (nrows < inner_rel->rows)
  				nrows = inner_rel->rows;
  			break;
  		case JOIN_IN:
  		case JOIN_UNIQUE_INNER:
  			upath = create_unique_path(root, inner_rel,
  									   inner_rel->cheapest_total_path);
! 			nrows = outer_rel->rows * upath->rows * selec;
  			if (nrows > outer_rel->rows)
  				nrows = outer_rel->rows;
  			break;
--- 2020,2052 ----
  	switch (jointype)
  	{
  		case JOIN_INNER:
! 			nrows = outer_rel->rows * inner_rel->rows * jselec;
  			break;
  		case JOIN_LEFT:
! 			nrows = outer_rel->rows * inner_rel->rows * jselec;
  			if (nrows < outer_rel->rows)
  				nrows = outer_rel->rows;
+ 			nrows *= pselec;
  			break;
  		case JOIN_RIGHT:
! 			nrows = outer_rel->rows * inner_rel->rows * jselec;
  			if (nrows < inner_rel->rows)
  				nrows = inner_rel->rows;
+ 			nrows *= pselec;
  			break;
  		case JOIN_FULL:
! 			nrows = outer_rel->rows * inner_rel->rows * jselec;
  			if (nrows < outer_rel->rows)
  				nrows = outer_rel->rows;
  			if (nrows < inner_rel->rows)
  				nrows = inner_rel->rows;
+ 			nrows *= pselec;
  			break;
  		case JOIN_IN:
  		case JOIN_UNIQUE_INNER:
  			upath = create_unique_path(root, inner_rel,
  									   inner_rel->cheapest_total_path);
! 			nrows = outer_rel->rows * upath->rows * jselec;
  			if (nrows > outer_rel->rows)
  				nrows = outer_rel->rows;
  			break;
***************
*** 2010,2016 ****
  		case JOIN_UNIQUE_OUTER:
  			upath = create_unique_path(root, outer_rel,
  									   outer_rel->cheapest_total_path);
! 			nrows = upath->rows * inner_rel->rows * selec;
  			if (nrows > inner_rel->rows)
  				nrows = inner_rel->rows;
  			break;
--- 2054,2060 ----
  		case JOIN_UNIQUE_OUTER:
  			upath = create_unique_path(root, outer_rel,
  									   outer_rel->cheapest_total_path);
! 			nrows = upath->rows * inner_rel->rows * jselec;
  			if (nrows > inner_rel->rows)
  				nrows = inner_rel->rows;
  			break;
