diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index e5567abf27927cfbe420fbb47fdfc2510861e190..72bc1ff17e3e38181acb27dac517c8e7309bee92 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.313 2005/04/08 00:59:57 neilc Exp $
+$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.314 2005/04/21 19:18:12 tgl Exp $
 -->
 
 <chapter Id="runtime">
@@ -1768,6 +1768,22 @@ archive_command = 'copy "%p" /mnt/server/archivedir/"%f"'  # Windows
       </para>
 
      <variablelist>
+     <varlistentry id="guc-enable-bitmapscan" xreflabel="enable_bitmapscan">
+      <term><varname>enable_bitmapscan</varname> (<type>boolean</type>)</term>
+      <indexterm>
+       <primary>bitmap scan</primary>
+      </indexterm>
+      <indexterm>
+       <primary><varname>enable_bitmapscan</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+        Enables or disables the query planner's use of bitmap-scan plan
+        types. The default is on.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-enable-hashagg" xreflabel="enable_hashagg">
       <term><varname>enable_hashagg</varname> (<type>boolean</type>)</term>
       <indexterm>
@@ -4094,7 +4110,7 @@ plruby.bar = true        # generates error, unknown class name
 
        <row>
         <entry>
-          <option>-fi</option>, <option>-fh</option>,
+          <option>-fb</option>, <option>-fh</option>, <option>-fi</option>,
           <option>-fm</option>, <option>-fn</option>,
           <option>-fs</option>, <option>-ft</option><footnote
           id="fn.runtime-config-short">
@@ -4111,8 +4127,9 @@ $ <userinput>postmaster -o '-S 1024 -s'</userinput>
           </footnote>
          </entry>
          <entry>
-          <literal>enable_indexscan = off</>,
+          <literal>enable_bitmapscan = off</>,
           <literal>enable_hashjoin = off</>,
+          <literal>enable_indexscan = off</>,
           <literal>enable_mergejoin = off</>,
           <literal>enable_nestloop = off</>,
           <literal>enable_seqscan = off</>,
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 1ea59314ea2710861e15b30455b7820071609b2a..c1c6ac09f489f21ea2a2cc7cd990836d630769b5 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.248 2005/04/21 02:28:01 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.249 2005/04/21 19:18:12 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -1041,6 +1041,28 @@ _outBitmapHeapPath(StringInfo str, BitmapHeapPath *node)
 	WRITE_FLOAT_FIELD(rows, "%.0f");
 }
 
+static void
+_outBitmapAndPath(StringInfo str, BitmapAndPath *node)
+{
+	WRITE_NODE_TYPE("BITMAPANDPATH");
+
+	_outPathInfo(str, (Path *) node);
+
+	WRITE_NODE_FIELD(bitmapquals);
+	WRITE_FLOAT_FIELD(bitmapselectivity, "%.4f");
+}
+
+static void
+_outBitmapOrPath(StringInfo str, BitmapOrPath *node)
+{
+	WRITE_NODE_TYPE("BITMAPORPATH");
+
+	_outPathInfo(str, (Path *) node);
+
+	WRITE_NODE_FIELD(bitmapquals);
+	WRITE_FLOAT_FIELD(bitmapselectivity, "%.4f");
+}
+
 static void
 _outTidPath(StringInfo str, TidPath *node)
 {
@@ -1853,6 +1875,12 @@ _outNode(StringInfo str, void *obj)
 			case T_BitmapHeapPath:
 				_outBitmapHeapPath(str, obj);
 				break;
+			case T_BitmapAndPath:
+				_outBitmapAndPath(str, obj);
+				break;
+			case T_BitmapOrPath:
+				_outBitmapOrPath(str, obj);
+				break;
 			case T_TidPath:
 				_outTidPath(str, obj);
 				break;
diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README
index 55ad1d74000b9fc76fef5f6414a31cbb198bbf4f..174ad4fd0a029dc4518fdb25a11f760a9b902d70 100644
--- a/src/backend/optimizer/README
+++ b/src/backend/optimizer/README
@@ -255,6 +255,7 @@ RelOptInfo      - a relation or joined relations
  Path           - every way to generate a RelOptInfo(sequential,index,joins)
   SeqScan       - a plain Path node with pathtype = T_SeqScan
   IndexPath     - index scans
+  BitmapHeapPath - top of a bitmapped index scan
   TidPath       - scan by CTID
   AppendPath    - append multiple subpaths together
   ResultPath    - a Result plan node (used for variable-free tlist or qual)
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index dc5091c69af305bc8dd8e3426fa23c31ca6d26c3..9ad3a73e942dfdc990f4bb32461f6a84183a2c07 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.126 2005/04/19 22:35:15 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.127 2005/04/21 19:18:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -901,6 +901,12 @@ print_path(Query *root, Path *path, int indent)
 		case T_BitmapHeapPath:
 			ptype = "BitmapHeapScan";
 			break;
+		case T_BitmapAndPath:
+			ptype = "BitmapAndPath";
+			break;
+		case T_BitmapOrPath:
+			ptype = "BitmapOrPath";
+			break;
 		case T_TidPath:
 			ptype = "TidScan";
 			break;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index a33ba0f796f47624c936babf8d4d8edeb90ca08e..2afaa2a655e16fc4b7004c53bb5d1687f0bf1d78 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -49,7 +49,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.143 2005/04/21 02:28:01 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.144 2005/04/21 19:18:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -94,6 +94,7 @@ Cost		disable_cost = 100000000.0;
 
 bool		enable_seqscan = true;
 bool		enable_indexscan = true;
+bool		enable_bitmapscan = true;
 bool		enable_tidscan = true;
 bool		enable_sort = true;
 bool		enable_hashagg = true;
@@ -103,7 +104,7 @@ bool		enable_hashjoin = true;
 
 
 static bool cost_qual_eval_walker(Node *node, QualCost *total);
-static Selectivity cost_bitmap_qual(Node *bitmapqual, Cost *totalCost);
+static void cost_bitmap_tree_node(Path *path, Cost *cost, Selectivity *selec);
 static Selectivity approx_selectivity(Query *root, List *quals,
 				   JoinType jointype);
 static Selectivity join_in_selectivity(JoinPath *path, Query *root);
@@ -292,7 +293,7 @@ cost_index(IndexPath *path, Query *root,
 					 PointerGetDatum(&indexCorrelation));
 
 	/*
-	 * Save amcostestimate's results for possible use by cost_bitmap_scan.
+	 * Save amcostestimate's results for possible use in bitmap scan planning.
 	 * We don't bother to save indexStartupCost or indexCorrelation, because
 	 * a bitmap scan doesn't care about either.
 	 */
@@ -414,19 +415,19 @@ cost_index(IndexPath *path, Query *root,
 }
 
 /*
- * cost_bitmap_scan
+ * cost_bitmap_heap_scan
  *	  Determines and returns the cost of scanning a relation using a bitmap
  *	  index-then-heap plan.
  *
  * 'root' is the query root
  * 'baserel' is the relation to be scanned
- * 'bitmapqual' is an AND/OR tree of IndexPaths for the component scans
+ * 'bitmapqual' is a tree of IndexPaths, BitmapAndPaths, and BitmapOrPaths
  * 'is_injoin' is T if we are considering using the scan as the inside
  *		of a nestloop join (hence, some of the quals are join clauses)
  */
 void
-cost_bitmap_scan(Path *path, Query *root, RelOptInfo *baserel,
-				 Node *bitmapqual, bool is_injoin)
+cost_bitmap_heap_scan(Path *path, Query *root, RelOptInfo *baserel,
+					  Path *bitmapqual, bool is_injoin)
 {
 	Cost		startup_cost = 0;
 	Cost		run_cost = 0;
@@ -443,15 +444,14 @@ cost_bitmap_scan(Path *path, Query *root, RelOptInfo *baserel,
 	Assert(baserel->relid > 0);
 	Assert(baserel->rtekind == RTE_RELATION);
 
-	if (!enable_indexscan)		/* XXX use a separate enable flag? */
+	if (!enable_bitmapscan)
 		startup_cost += disable_cost;
 
 	/*
-	 * Estimate total cost of obtaining the bitmap, as well as its total
+	 * Fetch total cost of obtaining the bitmap, as well as its total
 	 * selectivity.
 	 */
-	indexTotalCost = 0;
-	indexSelectivity = cost_bitmap_qual(bitmapqual, &indexTotalCost);
+	cost_bitmap_tree_node(bitmapqual, &indexTotalCost, &indexSelectivity);
 
 	startup_cost += indexTotalCost;
 
@@ -497,82 +497,120 @@ cost_bitmap_scan(Path *path, Query *root, RelOptInfo *baserel,
 }
 
 /*
- * cost_bitmap_qual
- *		Recursively examine the AND/OR/IndexPath tree for a bitmap scan
- *
- * Total execution costs are added to *totalCost (so caller must be sure
- * to initialize that to zero).  Estimated total selectivity of the bitmap
- * is returned as the function result.
+ * cost_bitmap_tree_node
+ *		Extract cost and selectivity from a bitmap tree node (index/and/or)
  */
-static Selectivity
-cost_bitmap_qual(Node *bitmapqual, Cost *totalCost)
+static void
+cost_bitmap_tree_node(Path *path, Cost *cost, Selectivity *selec)
 {
-	Selectivity	result;
-	Selectivity	subresult;
-	ListCell   *l;
-
-	if (and_clause(bitmapqual))
+	if (IsA(path, IndexPath))
 	{
-		/*
-		 * We estimate AND selectivity on the assumption that the inputs
-		 * are independent.  This is probably often wrong, but we don't
-		 * have the info to do better.
-		 *
-		 * The runtime cost of the BitmapAnd itself is estimated at 100x
-		 * cpu_operator_cost for each tbm_intersect needed.  Probably too
-		 * small, definitely too simplistic?
-		 *
-		 * This must agree with make_bitmap_and in createplan.c.
-		 */
-		result = 1.0;
-		foreach(l, ((BoolExpr *) bitmapqual)->args)
-		{
-			subresult = cost_bitmap_qual((Node *) lfirst(l), totalCost);
-			result *= subresult;
-			if (l != list_head(((BoolExpr *) bitmapqual)->args))
-				*totalCost += 100.0 * cpu_operator_cost;
-		}
+		*cost = ((IndexPath *) path)->indextotalcost;
+		*selec = ((IndexPath *) path)->indexselectivity;
 	}
-	else if (or_clause(bitmapqual))
+	else if (IsA(path, BitmapAndPath))
 	{
-		/*
-		 * We estimate OR selectivity on the assumption that the inputs
-		 * are non-overlapping, since that's often the case in "x IN (list)"
-		 * type situations.  Of course, we clamp to 1.0 at the end.
-		 *
-		 * The runtime cost of the BitmapOr itself is estimated at 100x
-		 * cpu_operator_cost for each tbm_union needed.  Probably too
-		 * small, definitely too simplistic?  We are aware that the tbm_unions
-		 * are optimized out when the inputs are BitmapIndexScans.
-		 *
-		 * This must agree with make_bitmap_or in createplan.c.
-		 */
-		result = 0.0;
-		foreach(l, ((BoolExpr *) bitmapqual)->args)
-		{
-			subresult = cost_bitmap_qual((Node *) lfirst(l), totalCost);
-			result += subresult;
-			if (l != list_head(((BoolExpr *) bitmapqual)->args) &&
-				!IsA((Node *) lfirst(l), IndexPath))
-				*totalCost += 100.0 * cpu_operator_cost;
-		}
-		result = Min(result, 1.0);
+		*cost = path->total_cost;
+		*selec = ((BitmapAndPath *) path)->bitmapselectivity;
 	}
-	else if (IsA(bitmapqual, IndexPath))
+	else if (IsA(path, BitmapOrPath))
 	{
-		IndexPath *ipath = (IndexPath *) bitmapqual;
-
-		/* this must agree with create_bitmap_subplan in createplan.c */
-		*totalCost += ipath->indextotalcost;
-		result = ipath->indexselectivity;
+		*cost = path->total_cost;
+		*selec = ((BitmapOrPath *) path)->bitmapselectivity;
 	}
 	else
+		elog(ERROR, "unrecognized node type: %d", nodeTag(path));
+}
+
+/*
+ * cost_bitmap_and_node
+ *		Estimate the cost of a BitmapAnd node
+ *
+ * Note that this considers only the costs of index scanning and bitmap
+ * creation, not the eventual heap access.  In that sense the object isn't
+ * truly a Path, but it has enough path-like properties (costs in particular)
+ * to warrant treating it as one.
+ */
+void
+cost_bitmap_and_node(BitmapAndPath *path, Query *root)
+{
+	Cost		totalCost;
+	Selectivity	selec;
+	ListCell   *l;
+
+	/*
+	 * We estimate AND selectivity on the assumption that the inputs
+	 * are independent.  This is probably often wrong, but we don't
+	 * have the info to do better.
+	 *
+	 * The runtime cost of the BitmapAnd itself is estimated at 100x
+	 * cpu_operator_cost for each tbm_intersect needed.  Probably too
+	 * small, definitely too simplistic?
+	 */
+	totalCost = 0.0;
+	selec = 1.0;
+	foreach(l, path->bitmapquals)
 	{
-		elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual));
-		result = 0.0;				/* keep compiler quiet */
+		Path   *subpath = (Path *) lfirst(l);
+		Cost	subCost;
+		Selectivity subselec;
+
+		cost_bitmap_tree_node(subpath, &subCost, &subselec);
+
+		selec *= subselec;
+
+		totalCost += subCost;
+		if (l != list_head(path->bitmapquals))
+			totalCost += 100.0 * cpu_operator_cost;
 	}
+	path->bitmapselectivity = selec;
+	path->path.startup_cost = totalCost;
+	path->path.total_cost = totalCost;
+}
+
+/*
+ * cost_bitmap_or_node
+ *		Estimate the cost of a BitmapOr node
+ *
+ * See comments for cost_bitmap_and_node.
+ */
+void
+cost_bitmap_or_node(BitmapOrPath *path, Query *root)
+{
+	Cost		totalCost;
+	Selectivity	selec;
+	ListCell   *l;
+
+	/*
+	 * We estimate OR selectivity on the assumption that the inputs
+	 * are non-overlapping, since that's often the case in "x IN (list)"
+	 * type situations.  Of course, we clamp to 1.0 at the end.
+	 *
+	 * The runtime cost of the BitmapOr itself is estimated at 100x
+	 * cpu_operator_cost for each tbm_union needed.  Probably too
+	 * small, definitely too simplistic?  We are aware that the tbm_unions
+	 * are optimized out when the inputs are BitmapIndexScans.
+	 */
+	totalCost = 0.0;
+	selec = 0.0;
+	foreach(l, path->bitmapquals)
+	{
+		Path   *subpath = (Path *) lfirst(l);
+		Cost	subCost;
+		Selectivity subselec;
 
-	return result;
+		cost_bitmap_tree_node(subpath, &subCost, &subselec);
+
+		selec += subselec;
+
+		totalCost += subCost;
+		if (l != list_head(path->bitmapquals) &&
+			!IsA(subpath, IndexPath))
+			totalCost += 100.0 * cpu_operator_cost;
+	}
+	path->bitmapselectivity = Min(selec, 1.0);
+	path->path.startup_cost = totalCost;
+	path->path.total_cost = totalCost;
 }
 
 /*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 0abb900beaaee6b7ae53c7d3b1142c66b1460329..faaf727da0d00bb2ea3d25df7d2280d0e5e05f39 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.181 2005/04/21 02:28:01 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.182 2005/04/21 19:18:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -51,9 +51,9 @@ static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
 static BitmapHeapScan *create_bitmap_scan_plan(Query *root,
 											   BitmapHeapPath *best_path,
 											   List *tlist, List *scan_clauses);
-static Plan *create_bitmap_subplan(Query *root, Node *bitmapqual);
-static List *create_bitmap_qual(Node *bitmapqual);
-static List *create_bitmap_indxqual(Node *bitmapqual);
+static Plan *create_bitmap_subplan(Query *root, Path *bitmapqual);
+static List *create_bitmap_qual(Path *bitmapqual);
+static List *create_bitmap_indxqual(Path *bitmapqual);
 static TidScan *create_tidscan_plan(Query *root, TidPath *best_path,
 					List *tlist, List *scan_clauses);
 static SubqueryScan *create_subqueryscan_plan(Query *root, Path *best_path,
@@ -928,37 +928,41 @@ create_bitmap_scan_plan(Query *root,
  * Given a bitmapqual tree, generate the Plan tree that implements it
  */
 static Plan *
-create_bitmap_subplan(Query *root, Node *bitmapqual)
+create_bitmap_subplan(Query *root, Path *bitmapqual)
 {
 	Plan	   *plan;
-	Plan	   *subplan;
 
-	if (bitmapqual == NULL)
-		return NULL;			/* probably can't happen */
-	if (IsA(bitmapqual, List))
+	if (IsA(bitmapqual, BitmapAndPath))
 	{
-		/* this case is to handle the List arguments of AND/OR */
+		BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
 		List	   *newlist = NIL;
 		ListCell   *l;
 
-		foreach(l, (List *) bitmapqual)
+		foreach(l, apath->bitmapquals)
 		{
-			subplan = create_bitmap_subplan(root, lfirst(l));
+			Plan   *subplan = create_bitmap_subplan(root, lfirst(l));
+
 			newlist = lappend(newlist, subplan);
 		}
-		plan = (Plan *) newlist;
-	}
-	else if (and_clause(bitmapqual))
-	{
-		subplan = create_bitmap_subplan(root,
-									(Node *) ((BoolExpr *) bitmapqual)->args);
-		plan = (Plan *) make_bitmap_and((List *) subplan);
+		plan = (Plan *) make_bitmap_and(newlist);
+		copy_path_costsize(plan, bitmapqual);
+		plan->plan_width = 0;	/* meaningless */
 	}
-	else if (or_clause(bitmapqual))
+	else if (IsA(bitmapqual, BitmapOrPath))
 	{
-		subplan = create_bitmap_subplan(root,
-									(Node *) ((BoolExpr *) bitmapqual)->args);
-		plan = (Plan *) make_bitmap_or((List *) subplan);
+		BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
+		List	   *newlist = NIL;
+		ListCell   *l;
+
+		foreach(l, opath->bitmapquals)
+		{
+			Plan   *subplan = create_bitmap_subplan(root, lfirst(l));
+
+			newlist = lappend(newlist, subplan);
+		}
+		plan = (Plan *) make_bitmap_or(newlist);
+		copy_path_costsize(plan, bitmapqual);
+		plan->plan_width = 0;	/* meaningless */
 	}
 	else if (IsA(bitmapqual, IndexPath))
 	{
@@ -976,7 +980,6 @@ create_bitmap_subplan(Query *root, Node *bitmapqual)
 									  linitial(iscan->indxqualorig),
 									  linitial(iscan->indxstrategy),
 									  linitial(iscan->indxsubtype));
-		/* this must agree with cost_bitmap_qual in costsize.c */
 		bscan->scan.plan.startup_cost = 0.0;
 		bscan->scan.plan.total_cost = ipath->indextotalcost;
 		bscan->scan.plan.plan_rows =
@@ -999,28 +1002,30 @@ create_bitmap_subplan(Query *root, Node *bitmapqual)
  * The result is expressed as an implicit-AND list.
  */
 static List *
-create_bitmap_qual(Node *bitmapqual)
+create_bitmap_qual(Path *bitmapqual)
 {
 	List	   *result;
 	List	   *sublist;
 
-	if (and_clause(bitmapqual))
+	if (IsA(bitmapqual, BitmapAndPath))
 	{
+		BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
 		ListCell   *l;
 
 		result = NIL;
-		foreach(l, ((BoolExpr *) bitmapqual)->args)
+		foreach(l, apath->bitmapquals)
 		{
 			sublist = create_bitmap_qual(lfirst(l));
 			result = list_concat(result, sublist);
 		}
 	}
-	else if (or_clause(bitmapqual))
+	else if (IsA(bitmapqual, BitmapOrPath))
 	{
+		BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
 		List	   *newlist = NIL;
 		ListCell   *l;
 
-		foreach(l, ((BoolExpr *) bitmapqual)->args)
+		foreach(l, opath->bitmapquals)
 		{
 			sublist = create_bitmap_qual(lfirst(l));
 			if (sublist == NIL)
@@ -1056,26 +1061,29 @@ create_bitmap_qual(Node *bitmapqual)
  * to enforce, which may be weaker than the original qual expressions.
  */
 static List *
-create_bitmap_indxqual(Node *bitmapqual)
+create_bitmap_indxqual(Path *bitmapqual)
 {
 	List	   *result;
 	List	   *sublist;
 	ListCell   *l;
 
-	if (and_clause(bitmapqual))
+	if (IsA(bitmapqual, BitmapAndPath))
 	{
+		BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
+
 		result = NIL;
-		foreach(l, ((BoolExpr *) bitmapqual)->args)
+		foreach(l, apath->bitmapquals)
 		{
 			sublist = create_bitmap_indxqual(lfirst(l));
 			result = list_concat(result, sublist);
 		}
 	}
-	else if (or_clause(bitmapqual))
+	else if (IsA(bitmapqual, BitmapOrPath))
 	{
+		BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
 		List	   *newlist = NIL;
 
-		foreach(l, ((BoolExpr *) bitmapqual)->args)
+		foreach(l, opath->bitmapquals)
 		{
 			sublist = create_bitmap_indxqual(lfirst(l));
 			if (sublist == NIL)
@@ -2067,34 +2075,8 @@ make_bitmap_and(List *bitmapplans)
 {
 	BitmapAnd  *node = makeNode(BitmapAnd);
 	Plan	   *plan = &node->plan;
-	ListCell   *subnode;
-
-	/*
-	 * Compute cost as sum of subplan costs, plus 100x cpu_operator_cost
-	 * (a pretty arbitrary amount, agreed) for each tbm_intersect needed.
-	 * This must agree with cost_bitmap_qual in costsize.c.
-	 */
-	plan->startup_cost = 0;
-	plan->total_cost = 0;
-	plan->plan_rows = 0;
-	plan->plan_width = 0;		/* meaningless */
-	foreach(subnode, bitmapplans)
-	{
-		Plan	   *subplan = (Plan *) lfirst(subnode);
-
-		if (subnode == list_head(bitmapplans))	/* first node? */
-		{
-			plan->startup_cost = subplan->startup_cost;
-			plan->plan_rows = subplan->plan_rows;
-		}
-		else
-		{
-			plan->total_cost += cpu_operator_cost * 100.0;
-			plan->plan_rows = Min(plan->plan_rows, subplan->plan_rows);
-		}
-		plan->total_cost += subplan->total_cost;
-	}
 
+	/* cost should be inserted by caller */
 	plan->targetlist = NIL;
 	plan->qual = NIL;
 	plan->lefttree = NULL;
@@ -2109,32 +2091,8 @@ make_bitmap_or(List *bitmapplans)
 {
 	BitmapOr   *node = makeNode(BitmapOr);
 	Plan	   *plan = &node->plan;
-	ListCell   *subnode;
-
-	/*
-	 * Compute cost as sum of subplan costs, plus 100x cpu_operator_cost
-	 * (a pretty arbitrary amount, agreed) for each tbm_union needed.
-	 * We assume that tbm_union can be optimized away for BitmapIndexScan
-	 * subplans.
-	 *
-	 * This must agree with cost_bitmap_qual in costsize.c.
-	 */
-	plan->startup_cost = 0;
-	plan->total_cost = 0;
-	plan->plan_rows = 0;
-	plan->plan_width = 0;		/* meaningless */
-	foreach(subnode, bitmapplans)
-	{
-		Plan	   *subplan = (Plan *) lfirst(subnode);
-
-		if (subnode == list_head(bitmapplans))	/* first node? */
-			plan->startup_cost = subplan->startup_cost;
-		else if (!IsA(subplan, BitmapIndexScan))
-			plan->total_cost += cpu_operator_cost * 100.0;
-		plan->total_cost += subplan->total_cost;
-		plan->plan_rows += subplan->plan_rows; /* ignore overlap */
-	}
 
+	/* cost should be inserted by caller */
 	plan->targetlist = NIL;
 	plan->qual = NIL;
 	plan->lefttree = NULL;
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 823486e2f3b83390b47e246a7bef733c37341bc9..d0c81073c3fcfc46c1323c65ead68572f40e93ff 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.117 2005/04/21 02:28:01 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.118 2005/04/21 19:18:12 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -475,12 +475,12 @@ create_index_path(Query *root,
  * create_bitmap_heap_path
  *	  Creates a path node for a bitmap scan.
  *
- * 'bitmapqual' is an AND/OR tree of IndexPath nodes.
+ * 'bitmapqual' is a tree of IndexPath, BitmapAndPath, and BitmapOrPath nodes.
  */
 BitmapHeapPath *
 create_bitmap_heap_path(Query *root,
 						RelOptInfo *rel,
-						Node *bitmapqual)
+						Path *bitmapqual)
 {
 	BitmapHeapPath *pathnode = makeNode(BitmapHeapPath);
 
@@ -499,7 +499,53 @@ create_bitmap_heap_path(Query *root,
 	 */
 	pathnode->rows = rel->rows;
 
-	cost_bitmap_scan(&pathnode->path, root, rel, bitmapqual, false);
+	cost_bitmap_heap_scan(&pathnode->path, root, rel, bitmapqual, false);
+
+	return pathnode;
+}
+
+/*
+ * create_bitmap_and_path
+ *	  Creates a path node representing a BitmapAnd.
+ */
+BitmapAndPath *
+create_bitmap_and_path(Query *root,
+					   RelOptInfo *rel,
+					   List *bitmapquals)
+{
+	BitmapAndPath *pathnode = makeNode(BitmapAndPath);
+
+	pathnode->path.pathtype = T_BitmapAnd;
+	pathnode->path.parent = rel;
+	pathnode->path.pathkeys = NIL;			/* always unordered */
+
+	pathnode->bitmapquals = bitmapquals;
+
+	/* this sets bitmapselectivity as well as the regular cost fields: */
+	cost_bitmap_and_node(pathnode, root);
+
+	return pathnode;
+}
+
+/*
+ * create_bitmap_or_path
+ *	  Creates a path node representing a BitmapOr.
+ */
+BitmapOrPath *
+create_bitmap_or_path(Query *root,
+					  RelOptInfo *rel,
+					  List *bitmapquals)
+{
+	BitmapOrPath *pathnode = makeNode(BitmapOrPath);
+
+	pathnode->path.pathtype = T_BitmapOr;
+	pathnode->path.parent = rel;
+	pathnode->path.pathkeys = NIL;			/* always unordered */
+
+	pathnode->bitmapquals = bitmapquals;
+
+	/* this sets bitmapselectivity as well as the regular cost fields: */
+	cost_bitmap_or_node(pathnode, root);
 
 	return pathnode;
 }
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 381d0e61102fd61cb0f194cfa75713075b00d5cb..2872d939ca8fe75d6a1ea6d5907f8e8d2d330e17 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.442 2005/02/22 04:37:17 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.443 2005/04/21 19:18:13 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -2371,6 +2371,9 @@ PostgresMain(int argc, char *argv[], const char *username)
 					case 'i':	/* indexscan */
 						tmp = "enable_indexscan";
 						break;
+					case 'b':	/* bitmapscan */
+						tmp = "enable_bitmapscan";
+						break;
 					case 't':	/* tidscan */
 						tmp = "enable_tidscan";
 						break;
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 711ecd077e2af052f965c3c0a94c0d2757e494cf..646f9c7a8516e2b201538211864403b49589a952 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.259 2005/04/13 18:54:56 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.260 2005/04/21 19:18:13 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -369,6 +369,14 @@ static struct config_bool ConfigureNamesBool[] =
 		&enable_indexscan,
 		true, NULL, NULL
 	},
+	{
+		{"enable_bitmapscan", PGC_USERSET, QUERY_TUNING_METHOD,
+			gettext_noop("Enables the planner's use of bitmap-scan plans."),
+			NULL
+		},
+		&enable_bitmapscan,
+		true, NULL, NULL
+	},
 	{
 		{"enable_tidscan", PGC_USERSET, QUERY_TUNING_METHOD,
 			gettext_noop("Enables the planner's use of TID scan plans."),
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 6b06afcb7fdb17187586c9a7fe9d61a6060a50ba..b175236883aaa3a2c01a80b8db0b524077269c65 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -137,6 +137,7 @@
 
 # - Planner Method Configuration -
 
+#enable_bitmapscan = true
 #enable_hashagg = true
 #enable_hashjoin = true
 #enable_indexscan = true
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index f8645f030c2c2c13c61262b69f0cabf816eb8bc9..86df578aae2939bced288088e7fdf6e556366571 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2005, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.124 2005/04/07 01:51:39 neilc Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.125 2005/04/21 19:18:13 tgl Exp $
  */
 
 /*----------------------------------------------------------------------
@@ -533,6 +533,7 @@ psql_completion(char *text, int start, int end)
 		"default_with_oids",
 		"dynamic_library_path",
 		"effective_cache_size",
+		"enable_bitmapscan",
 		"enable_hashagg",
 		"enable_hashjoin",
 		"enable_indexscan",
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 32df3bff0eb95b3eb9bf453f943946f1e114d100..d69734f95e94a8689d274c90026e8b5c2a11ad1d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.167 2005/04/19 22:35:17 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.168 2005/04/21 19:18:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -170,6 +170,8 @@ typedef enum NodeTag
 	T_Path,
 	T_IndexPath,
 	T_BitmapHeapPath,
+	T_BitmapAndPath,
+	T_BitmapOrPath,
 	T_NestPath,
 	T_MergePath,
 	T_HashPath,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 4ae0ae3a2c058c7362e42b31ce1106ebefe0ca23..53cd96b6b28387e91c381fd550528fe02e698b25 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.106 2005/04/21 02:28:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.107 2005/04/21 19:18:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -406,7 +406,7 @@ typedef struct IndexPath
  * out in physical heap order no matter what the underlying indexes did.
  *
  * The individual indexscans are represented by IndexPath nodes, and any
- * logic on top of them is represented by regular AND and OR expressions.
+ * logic on top of them is represented by BitmapAndPath and BitmapOrPath.
  * Notice that we can use the same IndexPath node both to represent a regular
  * IndexScan plan, and as the child of a BitmapHeapPath that represents
  * scanning the same index using a BitmapIndexScan.  The startup_cost and
@@ -420,11 +420,37 @@ typedef struct IndexPath
 typedef struct BitmapHeapPath
 {
 	Path		path;
-	Node	   *bitmapqual;		/* the IndexPath/AND/OR tree */
+	Path	   *bitmapqual;		/* IndexPath, BitmapAndPath, BitmapOrPath */
 	bool		isjoininner;	/* T if it's a nestloop inner scan */
 	double		rows;			/* estimated number of result tuples */
 } BitmapHeapPath;
 
+/*
+ * BitmapAndPath represents a BitmapAnd plan node; it can only appear as
+ * part of the substructure of a BitmapHeapPath.  The Path structure is
+ * a bit more heavyweight than we really need for this, but for simplicity
+ * we make it a derivative of Path anyway.
+ */
+typedef struct BitmapAndPath
+{
+	Path		path;
+	List	   *bitmapquals;		/* IndexPaths and BitmapOrPaths */
+	Selectivity bitmapselectivity;
+} BitmapAndPath;
+
+/*
+ * BitmapOrPath represents a BitmapOr plan node; it can only appear as
+ * part of the substructure of a BitmapHeapPath.  The Path structure is
+ * a bit more heavyweight than we really need for this, but for simplicity
+ * we make it a derivative of Path anyway.
+ */
+typedef struct BitmapOrPath
+{
+	Path		path;
+	List	   *bitmapquals;		/* IndexPaths and BitmapAndPaths */
+	Selectivity bitmapselectivity;
+} BitmapOrPath;
+
 /*
  * TidPath represents a scan by TID
  *
diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h
index 1f7ea96ee04c4d05707af11d3b50af0ee93e2026..b55c2366cf9a12ae91b1184d635f3ac1cba91225 100644
--- a/src/include/optimizer/cost.h
+++ b/src/include/optimizer/cost.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.65 2005/04/21 02:28:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.66 2005/04/21 19:18:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -42,6 +42,7 @@ extern double cpu_operator_cost;
 extern Cost disable_cost;
 extern bool enable_seqscan;
 extern bool enable_indexscan;
+extern bool enable_bitmapscan;
 extern bool enable_tidscan;
 extern bool enable_sort;
 extern bool enable_hashagg;
@@ -53,8 +54,10 @@ extern double clamp_row_est(double nrows);
 extern void cost_seqscan(Path *path, Query *root, RelOptInfo *baserel);
 extern void cost_index(IndexPath *path, Query *root, IndexOptInfo *index,
 		   List *indexQuals, bool is_injoin);
-extern void cost_bitmap_scan(Path *path, Query *root, RelOptInfo *baserel,
-							 Node *bitmapqual, bool is_injoin);
+extern void cost_bitmap_heap_scan(Path *path, Query *root, RelOptInfo *baserel,
+								  Path *bitmapqual, bool is_injoin);
+extern void cost_bitmap_and_node(BitmapAndPath *path, Query *root);
+extern void cost_bitmap_or_node(BitmapOrPath *path, Query *root);
 extern void cost_tidscan(Path *path, Query *root,
 			 RelOptInfo *baserel, List *tideval);
 extern void cost_subqueryscan(Path *path, RelOptInfo *baserel);
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 308c5daa6cd7d8171e0258f956667a3c7fe426db..7881ed175363ee9a4f937f9fbf3c827c7400f18c 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.58 2005/04/19 22:35:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.59 2005/04/21 19:18:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,7 +35,13 @@ extern IndexPath *create_index_path(Query *root,
 				  ScanDirection indexscandir);
 extern BitmapHeapPath *create_bitmap_heap_path(Query *root,
 											   RelOptInfo *rel,
-											   Node *bitmapqual);
+											   Path *bitmapqual);
+extern BitmapAndPath *create_bitmap_and_path(Query *root,
+											 RelOptInfo *rel,
+											 List *bitmapquals);
+extern BitmapOrPath *create_bitmap_or_path(Query *root,
+										   RelOptInfo *rel,
+										   List *bitmapquals);
 extern TidPath *create_tidscan_path(Query *root, RelOptInfo *rel,
 					List *tideval);
 extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out
index 6caa7a1c7b5392cdb5ae7f44b2ac9cd6e1557b4f..7b70766742aa6870e9274cd1f6e446cb97d476a1 100644
--- a/src/test/regress/expected/rangefuncs.out
+++ b/src/test/regress/expected/rangefuncs.out
@@ -1,15 +1,16 @@
 SELECT name, setting FROM pg_settings WHERE name LIKE 'enable%';
-       name       | setting 
-------------------+---------
- enable_hashagg   | on
- enable_hashjoin  | on
- enable_indexscan | on
- enable_mergejoin | on
- enable_nestloop  | on
- enable_seqscan   | on
- enable_sort      | on
- enable_tidscan   | on
-(8 rows)
+       name        | setting 
+-------------------+---------
+ enable_bitmapscan | on
+ enable_hashagg    | on
+ enable_hashjoin   | on
+ enable_indexscan  | on
+ enable_mergejoin  | on
+ enable_nestloop   | on
+ enable_seqscan    | on
+ enable_sort       | on
+ enable_tidscan    | on
+(9 rows)
 
 CREATE TABLE foo2(fooid int, f2 int);
 INSERT INTO foo2 VALUES(1, 11);