diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 7b126b7f54cccbdcafb8a3776413cf218df87dc7..cbffa3a3a47011f81ad51d71275fbedaa34ed015 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.284 2006/08/25 04:06:50 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.285 2006/09/19 22:49:52 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -1187,6 +1187,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
 	WRITE_NODE_FIELD(query_pathkeys);
 	WRITE_NODE_FIELD(group_pathkeys);
 	WRITE_NODE_FIELD(sort_pathkeys);
+	WRITE_FLOAT_FIELD(total_table_pages, "%.0f");
 	WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
 	WRITE_BOOL_FIELD(hasJoinRTEs);
 	WRITE_BOOL_FIELD(hasOuterJoins);
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 794c14fbbabc2b05f1e468daa607e87a0d5734b2..0c2b6920cd1b5c1fa79f0c2d9a721bfcdbd45f56 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.152 2006/08/19 02:48:53 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.153 2006/09/19 22:49:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -279,13 +279,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 				 errmsg("SELECT FOR UPDATE/SHARE is not supported for inheritance queries")));
 
-	/*
-	 * We might have looked up indexes for the parent rel, but they're
-	 * really not relevant to the appendrel.  Reset the pointer to avoid
-	 * any confusion.
-	 */
-	rel->indexlist = NIL;
-
 	/*
 	 * Initialize to compute size estimates for whole append relation
 	 */
@@ -312,11 +305,11 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
 		childRTindex = appinfo->child_relid;
 
 		/*
-		 * Make a RelOptInfo for the child so we can do planning. Mark it as
-		 * an "other rel" since it will not be part of the main join tree.
+		 * The child rel's RelOptInfo was already created during
+		 * add_base_rels_to_query.
 		 */
-		childrel = build_simple_rel(root, childRTindex,
-									RELOPT_OTHER_MEMBER_REL);
+		childrel = find_base_rel(root, childRTindex);
+		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);
 
 		/*
 		 * Copy the parent's targetlist and quals to the child, with
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index fffa25dd8443bc740a27fc00b5d97513898a7036..cd289423ecdbd95a2bbcd3bbdbc9693161e8cec3 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -54,7 +54,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.165 2006/08/02 01:59:45 joe Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.166 2006/09/19 22:49:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -288,7 +288,8 @@ cost_index(IndexPath *path, PlannerInfo *root,
 
 		pages_fetched = index_pages_fetched(tuples_fetched * num_scans,
 											baserel->pages,
-											index->pages);
+											(double) index->pages,
+											root);
 
 		run_cost += (pages_fetched * random_page_cost) / num_scans;
 	}
@@ -300,7 +301,8 @@ cost_index(IndexPath *path, PlannerInfo *root,
 		 */
 		pages_fetched = index_pages_fetched(tuples_fetched,
 											baserel->pages,
-											index->pages);
+											(double) index->pages,
+											root);
 
 		/* max_IO_cost is for the perfectly uncorrelated case (csquared=0) */
 		max_IO_cost = pages_fetched * random_page_cost;
@@ -369,13 +371,18 @@ cost_index(IndexPath *path, PlannerInfo *root,
  *		b = # buffer pages available (we include kernel space here)
  *
  * We assume that effective_cache_size is the total number of buffer pages
- * available for both table and index, and pro-rate that space between the
- * table and index.  (Ideally other_pages should include all the other
- * tables and indexes used by the query too; but we don't have a good way
- * to get that number here.)
+ * available for the whole query, and pro-rate that space across all the
+ * tables in the query and the index currently under consideration.  (This
+ * ignores space needed for other indexes used by the query, but since we
+ * don't know which indexes will get used, we can't estimate that very well;
+ * and in any case counting all the tables may well be an overestimate, since
+ * depending on the join plan not all the tables may be scanned concurrently.)
  *
  * The product Ns is the number of tuples fetched; we pass in that
- * product rather than calculating it here.
+ * product rather than calculating it here.  "pages" is the number of pages
+ * in the object under consideration (either an index or a table).
+ * "index_pages" is the amount to add to the total table space, which was
+ * computed for us by query_planner.
  *
  * Caller is expected to have ensured that tuples_fetched is greater than zero
  * and rounded to integer (see clamp_row_est).  The result will likewise be
@@ -383,17 +390,23 @@ cost_index(IndexPath *path, PlannerInfo *root,
  */
 double
 index_pages_fetched(double tuples_fetched, BlockNumber pages,
-					BlockNumber other_pages)
+					double index_pages, PlannerInfo *root)
 {
 	double		pages_fetched;
+	double		total_pages;
 	double		T,
 				b;
 
 	/* T is # pages in table, but don't allow it to be zero */
 	T = (pages > 1) ? (double) pages : 1.0;
 
+	/* Compute number of pages assumed to be competing for cache space */
+	total_pages = root->total_table_pages + index_pages;
+	total_pages = Max(total_pages, 1.0);
+	Assert(T <= total_pages);
+
 	/* b is pro-rated share of effective_cache_size */
-	b = (double) effective_cache_size * T / (T + (double) other_pages);
+	b = (double) effective_cache_size * T / total_pages;
 	/* force it positive and integral */
 	if (b <= 1.0)
 		b = 1.0;
@@ -430,6 +443,51 @@ index_pages_fetched(double tuples_fetched, BlockNumber pages,
 	return pages_fetched;
 }
 
+/*
+ * get_indexpath_pages
+ *		Determine the total size of the indexes used in a bitmap index path.
+ *
+ * Note: if the same index is used more than once in a bitmap tree, we will
+ * count it multiple times, which perhaps is the wrong thing ... but it's
+ * not completely clear, and detecting duplicates is difficult, so ignore it
+ * for now.
+ */
+static double
+get_indexpath_pages(Path *bitmapqual)
+{
+	double		result = 0;
+	ListCell   *l;
+
+	if (IsA(bitmapqual, BitmapAndPath))
+	{
+		BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
+
+		foreach(l, apath->bitmapquals)
+		{
+			result += get_indexpath_pages((Path *) lfirst(l));
+		}
+	}
+	else if (IsA(bitmapqual, BitmapOrPath))
+	{
+		BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
+
+		foreach(l, opath->bitmapquals)
+		{
+			result += get_indexpath_pages((Path *) lfirst(l));
+		}
+	}
+	else if (IsA(bitmapqual, IndexPath))
+	{
+		IndexPath  *ipath = (IndexPath *) bitmapqual;
+
+		result = (double) ipath->indexinfo->pages;
+	}
+	else
+		elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual));
+
+	return result;
+}
+
 /*
  * cost_bitmap_heap_scan
  *	  Determines and returns the cost of scanning a relation using a bitmap
@@ -494,7 +552,8 @@ cost_bitmap_heap_scan(Path *path, PlannerInfo *root, RelOptInfo *baserel,
 
 		pages_fetched = index_pages_fetched(tuples_fetched * num_scans,
 											baserel->pages,
-											0 /* XXX total index size? */);
+											get_indexpath_pages(bitmapqual),
+											root);
 		pages_fetched /= num_scans;
 	}
 	else
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index c2a1d3e080bcd0319dcc3f7ea60bc129766be957..8299f6756b8587fb652c0b12d89f21ff2031959a 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.121 2006/09/08 17:49:13 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.122 2006/09/19 22:49:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -76,8 +76,8 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
  * At the end of this process, there should be one baserel RelOptInfo for
  * every non-join RTE that is used in the query.  Therefore, this routine
  * is the only place that should call build_simple_rel with reloptkind
- * RELOPT_BASEREL.  However, otherrels will be built later for append relation
- * members.
+ * RELOPT_BASEREL.  (Note: build_simple_rel recurses internally to build
+ * "other rel" RelOptInfos for the members of any appendrels we find here.)
  */
 void
 add_base_rels_to_query(PlannerInfo *root, Node *jtnode)
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index c8d43a54657388b5dbdd72dcd1201db7e2bf74ba..ae44e2bc35ceef00a1846f7b6c6c61b84d2b2d7e 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.95 2006/07/14 14:52:20 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.96 2006/09/19 22:49:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -85,6 +85,8 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction,
 	RelOptInfo *final_rel;
 	Path	   *cheapestpath;
 	Path	   *sortedpath;
+	Index		rti;
+	double		total_pages;
 
 	/* Make tuple_fraction accessible to lower-level routines */
 	root->tuple_fraction = tuple_fraction;
@@ -122,10 +124,43 @@ query_planner(PlannerInfo *root, List *tlist, double tuple_fraction,
 	root->oj_info_list = NIL;
 
 	/*
-	 * Construct RelOptInfo nodes for all base relations in query.
+	 * Construct RelOptInfo nodes for all base relations in query, and
+	 * indirectly for all appendrel member relations ("other rels").  This
+	 * will give us a RelOptInfo for every "simple" (non-join) rel involved
+	 * in the query.
+	 *
+	 * Note: the reason we find the rels by searching the jointree and
+	 * appendrel list, rather than just scanning the rangetable, is that the
+	 * rangetable may contain RTEs for rels not actively part of the query,
+	 * for example views.  We don't want to make RelOptInfos for them.
 	 */
 	add_base_rels_to_query(root, (Node *) parse->jointree);
 
+	/*
+	 * We should now have size estimates for every actual table involved
+	 * in the query, so we can compute total_table_pages.  Note that
+	 * appendrels are not double-counted here, even though we don't bother
+	 * to distinguish RelOptInfos for appendrel parents, because the parents
+	 * will still have size zero.
+	 *
+	 * XXX if a table is self-joined, we will count it once per appearance,
+	 * which perhaps is the wrong thing ... but that's not completely clear,
+	 * and detecting self-joins here is difficult, so ignore it for now.
+	 */
+	total_pages = 0;
+	for (rti = 1; rti < root->simple_rel_array_size; rti++)
+	{
+		RelOptInfo *brel = root->simple_rel_array[rti];
+
+		if (brel == NULL)
+			continue;
+
+		Assert(brel->relid == rti); /* sanity check on array */
+
+		total_pages += (double) brel->pages;
+	}
+	root->total_table_pages = total_pages;
+
 	/*
 	 * Examine the targetlist and qualifications, adding entries to baserel
 	 * targetlists for all referenced Vars.  Restrict and join clauses are
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 5e3c7d98575d5184722b1dc3e18ea74199c0b83d..aba4b881571effc1109aab826b33fda8e7381e30 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.125 2006/08/25 04:06:50 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.126 2006/09/19 22:49:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -62,9 +62,15 @@ static List *get_relation_constraints(Oid relationObjectId, RelOptInfo *rel);
  * Also, initialize the attr_needed[] and attr_widths[] arrays.  In most
  * cases these are left as zeroes, but sometimes we need to compute attr
  * widths here, and we may as well cache the results for costsize.c.
+ *
+ * If inhparent is true, all we need to do is set up the attr arrays:
+ * the RelOptInfo actually represents the appendrel formed by an inheritance
+ * tree, and so the parent rel's physical size and index information isn't
+ * important for it.
  */
 void
-get_relation_info(PlannerInfo *root, Oid relationObjectId, RelOptInfo *rel)
+get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
+				  RelOptInfo *rel)
 {
 	Index		varno = rel->relid;
 	Relation	relation;
@@ -88,15 +94,21 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, RelOptInfo *rel)
 		palloc0((rel->max_attr - rel->min_attr + 1) * sizeof(int32));
 
 	/*
-	 * Estimate relation size.
+	 * Estimate relation size --- unless it's an inheritance parent, in which
+	 * case the size will be computed later in set_append_rel_pathlist, and
+	 * we must leave it zero for now to avoid bollixing the total_table_pages
+	 * calculation.
 	 */
-	estimate_rel_size(relation, rel->attr_widths - rel->min_attr,
-					  &rel->pages, &rel->tuples);
+	if (!inhparent)
+		estimate_rel_size(relation, rel->attr_widths - rel->min_attr,
+						  &rel->pages, &rel->tuples);
 
 	/*
 	 * Make list of indexes.  Ignore indexes on system catalogs if told to.
+	 * Don't bother with indexes for an inheritance parent, either.
 	 */
-	if (IgnoreSystemIndexes && IsSystemClass(relation->rd_rel))
+	if (inhparent ||
+		(IgnoreSystemIndexes && IsSystemClass(relation->rd_rel)))
 		hasindex = false;
 	else
 		hasindex = relation->rd_rel->relhasindex;
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 545b125197c1f39a47ea8d44f1b7d5925c326ce9..331855f8e9fefcd4012f1de7a5b6f9e1ed9a3fcb 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.81 2006/08/02 01:59:46 joe Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.82 2006/09/19 22:49:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,7 +92,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 	{
 		case RTE_RELATION:
 			/* Table --- retrieve statistics from the system catalogs */
-			get_relation_info(root, rte->relid, rel);
+			get_relation_info(root, rte->relid, rte->inh, rel);
 			break;
 		case RTE_SUBQUERY:
 		case RTE_FUNCTION:
@@ -119,6 +119,29 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
 	/* Save the finished struct in the query's simple_rel_array */
 	root->simple_rel_array[relid] = rel;
 
+	/*
+	 * If this rel is an appendrel parent, recurse to build "other rel"
+	 * RelOptInfos for its children.  They are "other rels" because they are
+	 * not in the main join tree, but we will need RelOptInfos to plan access
+	 * to them.
+	 */
+	if (rte->inh)
+	{
+		ListCell   *l;
+
+		foreach(l, root->append_rel_list)
+		{
+			AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);
+
+			/* append_rel_list contains all append rels; ignore others */
+			if (appinfo->parent_relid != relid)
+				continue;
+
+			(void) build_simple_rel(root, appinfo->child_relid,
+									RELOPT_OTHER_MEMBER_REL);
+		}
+	}
+
 	return rel;
 }
 
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index c46dbd6ece2cea062ab3fd25c43f5f6605d8227b..96d6512ac07e0385994e38c4d27e3f64a2dd5681 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.211 2006/07/26 17:17:28 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.212 2006/09/19 22:49:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -4647,7 +4647,8 @@ genericcostestimate(PlannerInfo *root,
 		/* use Mackert and Lohman formula to adjust for cache effects */
 		pages_fetched = index_pages_fetched(pages_fetched,
 											index->pages,
-											index->rel->pages);
+											(double) index->pages,
+											root);
 
 		/*
 		 * Now compute the total disk access cost, and then report a
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index d7a93f0f6506e95ded42b1765f8153441fb4b119..471d8e209e232dce4a2cc2278e20028b935fc47d 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.126 2006/07/01 18:38:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.127 2006/09/19 22:49:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -109,6 +109,8 @@ typedef struct PlannerInfo
 	List	   *group_pathkeys; /* groupClause pathkeys, if any */
 	List	   *sort_pathkeys;	/* sortClause pathkeys, if any */
 
+	double		total_table_pages;	/* # of pages in all tables of query */
+
 	double		tuple_fraction; /* tuple_fraction passed to query_planner */
 
 	bool		hasJoinRTEs;	/* true if any RTEs are RTE_JOIN kind */
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 30388571119dd03ce7b88757593b7fd3178a1f22..e0250f5f396847b985635a0f2147bdc36917cb8e 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.79 2006/08/02 01:59:48 joe Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.80 2006/09/19 22:49:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,7 +56,7 @@ extern bool constraint_exclusion;
 
 extern double clamp_row_est(double nrows);
 extern double index_pages_fetched(double tuples_fetched, BlockNumber pages,
-								  BlockNumber other_pages);
+								  double index_pages, PlannerInfo *root);
 extern void cost_seqscan(Path *path, PlannerInfo *root, RelOptInfo *baserel);
 extern void cost_index(IndexPath *path, PlannerInfo *root, IndexOptInfo *index,
 		   List *indexQuals, RelOptInfo *outer_rel);
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index 322ae97741cf5fba468d2dd771aa2363fa5457b1..173c7f846beee6bc65c8faa675711f4cb2701f20 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/plancat.h,v 1.40 2006/07/31 20:09:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/plancat.h,v 1.41 2006/09/19 22:49:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,7 +17,8 @@
 #include "nodes/relation.h"
 
 
-extern void get_relation_info(PlannerInfo *root, Oid relationObjectId, RelOptInfo *rel);
+extern void get_relation_info(PlannerInfo *root, Oid relationObjectId,
+							  bool inhparent, RelOptInfo *rel);
 
 extern bool relation_excluded_by_constraints(RelOptInfo *rel,
 											 RangeTblEntry *rte);