diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index cc466f5ae006ccae980b06423c06b9a16b8be7cc..1a78769e8f2d768b86ebd5fc825805249ef5ac45 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.227 2004/01/05 18:04:38 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.228 2004/01/05 23:39:53 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -968,8 +968,9 @@ _outIndexPath(StringInfo str, IndexPath *node)
 	_outPathInfo(str, (Path *) node);
 
 	WRITE_NODE_FIELD(indexinfo);
-	WRITE_NODE_FIELD(indexqual);
-	WRITE_NODE_FIELD(indexjoinclauses);
+	WRITE_NODE_FIELD(indexclauses);
+	WRITE_NODE_FIELD(indexquals);
+	WRITE_BOOL_FIELD(isjoininner);
 	WRITE_ENUM_FIELD(indexscandir, ScanDirection);
 	WRITE_FLOAT_FIELD(rows, "%.0f");
 }
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index fa7dcdc9740d8af1dfdaacda324468f9bebd19be..7353e73462b01bc013d5eb9c519a69b68cc447e7 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.120 2004/01/05 05:07:35 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.121 2004/01/05 23:39:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -238,6 +238,9 @@ cost_nonsequential_access(double relpages)
  * Any additional quals evaluated as qpquals may reduce the number of returned
  * tuples, but they won't reduce the number of tuples we have to fetch from
  * the table, so they don't reduce the scan cost.
+ *
+ * NOTE: as of 7.5, indexQuals is a list of RestrictInfo nodes, where formerly
+ * it was a list of bare clause expressions.
  */
 void
 cost_index(Path *path, Query *root,
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 83d129d0c9cff737b54f9960de1eea18916aa382..da11d7f86d06871a28a42dce85ad0c5c2c7ff7b7 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.154 2004/01/05 05:07:35 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.155 2004/01/05 23:39:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -76,7 +76,7 @@ static bool match_index_to_operand(Node *operand, int indexcol,
 					   RelOptInfo *rel, IndexOptInfo *index);
 static bool match_special_index_operator(Expr *clause, Oid opclass,
 							 bool indexkey_on_left);
-static List *expand_indexqual_condition(Expr *clause, Oid opclass);
+static List *expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass);
 static List *prefix_quals(Node *leftop, Oid opclass,
 			 Const *prefix, Pattern_Prefix_Status pstatus);
 static List *network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass,
@@ -1418,8 +1418,7 @@ make_innerjoin_index_path(Query *root,
 {
 	IndexPath  *pathnode = makeNode(IndexPath);
 	List	   *indexquals,
-			   *allclauses,
-			   *l;
+			   *allclauses;
 
 	/* XXX perhaps this code should be merged with create_index_path? */
 
@@ -1433,28 +1432,21 @@ make_innerjoin_index_path(Query *root,
 	 */
 	pathnode->path.pathkeys = NIL;
 
-	/* Convert RestrictInfo nodes to indexquals the executor can handle */
+	/* Convert clauses to indexquals the executor can handle */
 	indexquals = expand_indexqual_conditions(index, clausegroups);
 
-	/*
-	 * Also make a flattened list of the RestrictInfo nodes; createplan.c
-	 * will need this later.  We assume here that we can destructively
-	 * modify the passed-in clausegroups list structure.
-	 */
-	allclauses = NIL;
-	foreach(l, clausegroups)
-	{
-		/* nconc okay here since same clause couldn't be in two sublists */
-		allclauses = nconc(allclauses, (List *) lfirst(l));
-	}
+	/* Flatten the clausegroups list to produce indexclauses list */
+	allclauses = flatten_clausegroups_list(clausegroups);
 
 	/*
 	 * Note that we are making a pathnode for a single-scan indexscan;
-	 * therefore, indexinfo and indexqual should be single-element lists.
+	 * therefore, indexinfo etc should be single-element lists.
 	 */
 	pathnode->indexinfo = makeList1(index);
-	pathnode->indexqual = makeList1(indexquals);
-	pathnode->indexjoinclauses = makeList1(allclauses);
+	pathnode->indexclauses = makeList1(allclauses);
+	pathnode->indexquals = makeList1(indexquals);
+
+	pathnode->isjoininner = true;
 
 	/* We don't actually care what order the index scans in ... */
 	pathnode->indexscandir = NoMovementScanDirection;
@@ -1489,6 +1481,61 @@ make_innerjoin_index_path(Query *root,
 	return (Path *) pathnode;
 }
 
+/*
+ * flatten_clausegroups_list
+ *	  Given a list of lists of RestrictInfos, flatten it to a list
+ *	  of RestrictInfos.
+ *
+ * This is used to flatten out the result of group_clauses_by_indexkey()
+ * or one of its sibling routines, to produce an indexclauses list.
+ */
+List *
+flatten_clausegroups_list(List *clausegroups)
+{
+	List	   *allclauses = NIL;
+	List	   *l;
+
+	foreach(l, clausegroups)
+	{
+		allclauses = nconc(allclauses, listCopy((List *) lfirst(l)));
+	}
+	return allclauses;
+}
+
+/*
+ * make_expr_from_indexclauses()
+ *	  Given an indexclauses structure, produce an ordinary boolean expression.
+ *
+ * This consists of stripping out the RestrictInfo nodes and inserting
+ * explicit AND and OR nodes as needed.  There's not much to it, but
+ * the functionality is needed in a few places, so centralize the logic.
+ */
+Expr *
+make_expr_from_indexclauses(List *indexclauses)
+{
+	List	   *orclauses = NIL;
+	List	   *orlist;
+
+	/* There's no such thing as an indexpath with zero scans */
+	Assert(indexclauses != NIL);
+
+	foreach(orlist, indexclauses)
+	{
+		List   *andlist = (List *) lfirst(orlist);
+
+		/* Strip RestrictInfos */
+		andlist = get_actual_clauses(andlist);
+		/* Insert AND node if needed, and add to orclauses list */
+		orclauses = lappend(orclauses, make_ands_explicit(andlist));
+	}
+
+	if (length(orclauses) > 1)
+		return make_orclause(orclauses);
+	else
+		return (Expr *) lfirst(orclauses);
+}
+
+
 /****************************************************************************
  *				----  ROUTINES TO CHECK OPERANDS  ----
  ****************************************************************************/
@@ -1799,8 +1846,7 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
 			RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
 
 			FastConc(&resultquals,
-					 expand_indexqual_condition(rinfo->clause,
-												curClass));
+					 expand_indexqual_condition(rinfo, curClass));
 		}
 
 		clausegroups = lnext(clausegroups);
@@ -1816,10 +1862,13 @@ expand_indexqual_conditions(IndexOptInfo *index, List *clausegroups)
 
 /*
  * expand_indexqual_condition --- expand a single indexqual condition
+ *
+ * The input is a single RestrictInfo, the output a list of RestrictInfos
  */
 static List *
-expand_indexqual_condition(Expr *clause, Oid opclass)
+expand_indexqual_condition(RestrictInfo *rinfo, Oid opclass)
 {
+	Expr	   *clause = rinfo->clause;
 	/* we know these will succeed */
 	Node	   *leftop = get_leftop(clause);
 	Node	   *rightop = get_rightop(clause);
@@ -1883,7 +1932,7 @@ expand_indexqual_condition(Expr *clause, Oid opclass)
 			break;
 
 		default:
-			result = makeList1(clause);
+			result = makeList1(rinfo);
 			break;
 	}
 
@@ -1978,7 +2027,7 @@ prefix_quals(Node *leftop, Oid opclass,
 			elog(ERROR, "no = operator for opclass %u", opclass);
 		expr = make_opclause(oproid, BOOLOID, false,
 							 (Expr *) leftop, (Expr *) prefix_const);
-		result = makeList1(expr);
+		result = makeList1(make_restrictinfo(expr, true, true));
 		return result;
 	}
 
@@ -1993,7 +2042,7 @@ prefix_quals(Node *leftop, Oid opclass,
 		elog(ERROR, "no >= operator for opclass %u", opclass);
 	expr = make_opclause(oproid, BOOLOID, false,
 						 (Expr *) leftop, (Expr *) prefix_const);
-	result = makeList1(expr);
+	result = makeList1(make_restrictinfo(expr, true, true));
 
 	/*-------
 	 * If we can create a string larger than the prefix, we can say
@@ -2009,7 +2058,7 @@ prefix_quals(Node *leftop, Oid opclass,
 			elog(ERROR, "no < operator for opclass %u", opclass);
 		expr = make_opclause(oproid, BOOLOID, false,
 							 (Expr *) leftop, (Expr *) greaterstr);
-		result = lappend(result, expr);
+		result = lappend(result, make_restrictinfo(expr, true, true));
 	}
 
 	return result;
@@ -2080,7 +2129,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
 						 (Expr *) leftop,
 						 (Expr *) makeConst(datatype, -1, opr1right,
 											false, false));
-	result = makeList1(expr);
+	result = makeList1(make_restrictinfo(expr, true, true));
 
 	/* create clause "key <= network_scan_last( rightop )" */
 
@@ -2095,7 +2144,7 @@ network_prefix_quals(Node *leftop, Oid expr_op, Oid opclass, Datum rightop)
 						 (Expr *) leftop,
 						 (Expr *) makeConst(datatype, -1, opr2right,
 											false, false));
-	result = lappend(result, expr);
+	result = lappend(result, make_restrictinfo(expr, true, true));
 
 	return result;
 }
diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c
index 02e486ec35cf9a9fa5e0184b71502c5f6918e639..9722b69965dfb8e85119a3f254df10920b5fe84c 100644
--- a/src/backend/optimizer/path/orindxpath.c
+++ b/src/backend/optimizer/path/orindxpath.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.56 2004/01/05 05:07:35 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.57 2004/01/05 23:39:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,7 +28,8 @@ static bool best_or_subclause_index(Query *root,
 						RelOptInfo *rel,
 						Expr *subclause,
 						IndexOptInfo **retIndexInfo,
-						List **retIndexQual,
+						List **retIndexClauses,
+						List **retIndexQuals,
 						Cost *retStartupCost,
 						Cost *retTotalCost);
 
@@ -95,9 +96,7 @@ create_or_index_quals(Query *root, RelOptInfo *rel)
 {
 	IndexPath  *bestpath = NULL;
 	RestrictInfo *bestrinfo = NULL;
-	FastList	orclauses;
-	List	   *orclause;
-	Expr	   *indxqual_or_expr;
+	List	   *newrinfos;
 	RestrictInfo *or_rinfo;
 	Selectivity or_selec,
 				orig_selec;
@@ -145,20 +144,14 @@ create_or_index_quals(Query *root, RelOptInfo *rel)
 		return false;
 
 	/*
-	 * Build an expression representation of the indexqual, expanding
-	 * the implicit OR and AND semantics of the first- and
-	 * second-level lists.
+	 * Convert the indexclauses structure to a RestrictInfo tree,
+	 * and add it to the rel's restriction list.
 	 */
-	FastListInit(&orclauses);
-	foreach(orclause, bestpath->indexqual)
-		FastAppend(&orclauses, make_ands_explicit(lfirst(orclause)));
-	indxqual_or_expr = make_orclause(FastListValue(&orclauses));
-
-	/*
-	 * And add it to the rel's restriction list.
-	 */
-	or_rinfo = make_restrictinfo(indxqual_or_expr, true, true);
-	rel->baserestrictinfo = lappend(rel->baserestrictinfo, or_rinfo);
+	newrinfos = make_restrictinfo_from_indexclauses(bestpath->indexclauses,
+													true, true);
+	Assert(length(newrinfos) == 1);
+	or_rinfo = (RestrictInfo *) lfirst(newrinfos);
+	rel->baserestrictinfo = nconc(rel->baserestrictinfo, newrinfos);
 
 	/*
 	 * Adjust the original OR clause's cached selectivity to compensate
@@ -251,6 +244,7 @@ best_or_subclause_indexes(Query *root,
 						  List *subclauses)
 {
 	FastList	infos;
+	FastList	clauses;
 	FastList	quals;
 	Cost		path_startup_cost;
 	Cost		path_total_cost;
@@ -258,6 +252,7 @@ best_or_subclause_indexes(Query *root,
 	IndexPath  *pathnode;
 
 	FastListInit(&infos);
+	FastListInit(&clauses);
 	FastListInit(&quals);
 	path_startup_cost = 0;
 	path_total_cost = 0;
@@ -267,17 +262,20 @@ best_or_subclause_indexes(Query *root,
 	{
 		Expr	   *subclause = lfirst(slist);
 		IndexOptInfo *best_indexinfo;
-		List	   *best_indexqual;
+		List	   *best_indexclauses;
+		List	   *best_indexquals;
 		Cost		best_startup_cost;
 		Cost		best_total_cost;
 
 		if (!best_or_subclause_index(root, rel, subclause,
-									 &best_indexinfo, &best_indexqual,
+									 &best_indexinfo,
+									 &best_indexclauses, &best_indexquals,
 									 &best_startup_cost, &best_total_cost))
 			return NULL;		/* failed to match this subclause */
 
 		FastAppend(&infos, best_indexinfo);
-		FastAppend(&quals, best_indexqual);
+		FastAppend(&clauses, best_indexclauses);
+		FastAppend(&quals, best_indexquals);
 		/*
 		 * Path startup_cost is the startup cost for the first index scan only;
 		 * startup costs for later scans will be paid later on, so they just
@@ -306,10 +304,11 @@ best_or_subclause_indexes(Query *root,
 	pathnode->path.pathkeys = NIL;
 
 	pathnode->indexinfo = FastListValue(&infos);
-	pathnode->indexqual = FastListValue(&quals);
+	pathnode->indexclauses = FastListValue(&clauses);
+	pathnode->indexquals = FastListValue(&quals);
 
 	/* It's not an innerjoin path. */
-	pathnode->indexjoinclauses = NIL;
+	pathnode->isjoininner = false;
 
 	/* We don't actually care what order the index scans in. */
 	pathnode->indexscandir = NoMovementScanDirection;
@@ -336,7 +335,8 @@ best_or_subclause_indexes(Query *root,
  * 'subclause' is the OR subclause being considered
  *
  * '*retIndexInfo' gets the IndexOptInfo of the best index
- * '*retIndexQual' gets a list of the indexqual conditions for the best index
+ * '*retIndexClauses' gets a list of the index clauses for the best index
+ * '*retIndexQuals' gets a list of the expanded indexquals for the best index
  * '*retStartupCost' gets the startup cost of a scan with that index
  * '*retTotalCost' gets the total cost of a scan with that index
  */
@@ -345,7 +345,8 @@ best_or_subclause_index(Query *root,
 						RelOptInfo *rel,
 						Expr *subclause,
 						IndexOptInfo **retIndexInfo,	/* return value */
-						List **retIndexQual,	/* return value */
+						List **retIndexClauses,	/* return value */
+						List **retIndexQuals,	/* return value */
 						Cost *retStartupCost,	/* return value */
 						Cost *retTotalCost)		/* return value */
 {
@@ -355,7 +356,7 @@ best_or_subclause_index(Query *root,
 	foreach(ilist, rel->indexlist)
 	{
 		IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
-		List	   *qualrinfos;
+		List	   *indexclauses;
 		List	   *indexquals;
 		Path		subclause_path;
 
@@ -364,21 +365,22 @@ best_or_subclause_index(Query *root,
 			continue;
 
 		/* Collect index clauses usable with this index */
-		qualrinfos = group_clauses_by_indexkey_for_or(rel, index, subclause);
+		indexclauses = group_clauses_by_indexkey_for_or(rel, index, subclause);
 
 		/* Ignore index if it doesn't match the subclause at all */
-		if (qualrinfos == NIL)
+		if (indexclauses == NIL)
 			continue;
 
-		/* Convert RestrictInfo nodes to indexquals the executor can handle */
-		indexquals = expand_indexqual_conditions(index, qualrinfos);
+		/* Convert clauses to indexquals the executor can handle */
+		indexquals = expand_indexqual_conditions(index, indexclauses);
 
 		cost_index(&subclause_path, root, rel, index, indexquals, false);
 
 		if (!found || subclause_path.total_cost < *retTotalCost)
 		{
 			*retIndexInfo = index;
-			*retIndexQual = indexquals;
+			*retIndexClauses = flatten_clausegroups_list(indexclauses);
+			*retIndexQuals = indexquals;
 			*retStartupCost = subclause_path.startup_cost;
 			*retTotalCost = subclause_path.total_cost;
 			found = true;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index ace9029ce686c9c0f934371c685b59e2f8e0f858..591cf47f8c63723212c2d0268858b6521395be51 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.163 2004/01/05 18:04:38 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.164 2004/01/05 23:39:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,15 +44,15 @@ static Append *create_append_plan(Query *root, AppendPath *best_path);
 static Result *create_result_plan(Query *root, ResultPath *best_path);
 static Material *create_material_plan(Query *root, MaterialPath *best_path);
 static Plan *create_unique_plan(Query *root, UniquePath *best_path);
-static SeqScan *create_seqscan_plan(Path *best_path, List *tlist,
-					List *scan_clauses);
+static SeqScan *create_seqscan_plan(Query *root, Path *best_path,
+									List *tlist, List *scan_clauses);
 static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
 					  List *tlist, List *scan_clauses);
-static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
-					List *scan_clauses);
-static SubqueryScan *create_subqueryscan_plan(Path *best_path,
+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,
 						 List *tlist, List *scan_clauses);
-static FunctionScan *create_functionscan_plan(Path *best_path,
+static FunctionScan *create_functionscan_plan(Query *root, Path *best_path,
 						 List *tlist, List *scan_clauses);
 static NestLoop *create_nestloop_plan(Query *root, NestPath *best_path,
 					 Plan *outer_plan, Plan *inner_plan);
@@ -219,15 +219,13 @@ create_scan_plan(Query *root, Path *best_path)
 	 * Extract the relevant restriction clauses from the parent relation;
 	 * the executor must apply all these restrictions during the scan.
 	 */
-	scan_clauses = get_actual_clauses(rel->baserestrictinfo);
-
-	/* Sort clauses into best execution order */
-	scan_clauses = order_qual_clauses(root, scan_clauses);
+	scan_clauses = rel->baserestrictinfo;
 
 	switch (best_path->pathtype)
 	{
 		case T_SeqScan:
-			plan = (Scan *) create_seqscan_plan(best_path,
+			plan = (Scan *) create_seqscan_plan(root,
+												best_path,
 												tlist,
 												scan_clauses);
 			break;
@@ -240,19 +238,22 @@ create_scan_plan(Query *root, Path *best_path)
 			break;
 
 		case T_TidScan:
-			plan = (Scan *) create_tidscan_plan((TidPath *) best_path,
+			plan = (Scan *) create_tidscan_plan(root,
+												(TidPath *) best_path,
 												tlist,
 												scan_clauses);
 			break;
 
 		case T_SubqueryScan:
-			plan = (Scan *) create_subqueryscan_plan(best_path,
+			plan = (Scan *) create_subqueryscan_plan(root,
+													 best_path,
 													 tlist,
 													 scan_clauses);
 			break;
 
 		case T_FunctionScan:
-			plan = (Scan *) create_functionscan_plan(best_path,
+			plan = (Scan *) create_functionscan_plan(root,
+													 best_path,
 													 tlist,
 													 scan_clauses);
 			break;
@@ -667,7 +668,8 @@ create_unique_plan(Query *root, UniquePath *best_path)
  *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
 static SeqScan *
-create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
+create_seqscan_plan(Query *root, Path *best_path,
+					List *tlist, List *scan_clauses)
 {
 	SeqScan    *scan_plan;
 	Index		scan_relid = best_path->parent->relid;
@@ -676,6 +678,12 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
 	Assert(scan_relid > 0);
 	Assert(best_path->parent->rtekind == RTE_RELATION);
 
+	/* Reduce RestrictInfo list to bare expressions */
+	scan_clauses = get_actual_clauses(scan_clauses);
+
+	/* Sort clauses into best execution order */
+	scan_clauses = order_qual_clauses(root, scan_clauses);
+
 	scan_plan = make_seqscan(tlist,
 							 scan_clauses,
 							 scan_relid);
@@ -690,9 +698,9 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
  *	  Returns a indexscan plan for the base relation scanned by 'best_path'
  *	  with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  *
- * The indexqual of the path contains a sublist of implicitly-ANDed qual
- * conditions for each scan of the index(es); if there is more than one
- * scan then the retrieved tuple sets are ORed together.  The indexqual
+ * The indexquals list of the path contains a sublist of implicitly-ANDed
+ * qual conditions for each scan of the index(es); if there is more than one
+ * scan then the retrieved tuple sets are ORed together.  The indexquals
  * and indexinfo lists must have the same length, ie, the number of scans
  * that will occur.  Note it is possible for a qual condition sublist
  * to be empty --- then no index restrictions will be applied during that
@@ -704,16 +712,17 @@ create_indexscan_plan(Query *root,
 					  List *tlist,
 					  List *scan_clauses)
 {
-	List	   *indxqual = best_path->indexqual;
+	List	   *indxquals = best_path->indexquals;
 	Index		baserelid = best_path->path.parent->relid;
 	List	   *qpqual;
 	Expr	   *indxqual_or_expr = NULL;
-	List	   *fixed_indxqual;
-	List	   *recheck_indxqual;
+	List	   *stripped_indxquals;
+	List	   *fixed_indxquals;
+	List	   *recheck_indxquals;
 	List	   *indxstrategy;
 	List	   *indxsubtype;
 	FastList	indexids;
-	List	   *ixinfo;
+	List	   *i;
 	IndexScan  *scan_plan;
 
 	/* it should be a base rel... */
@@ -721,64 +730,94 @@ create_indexscan_plan(Query *root,
 	Assert(best_path->path.parent->rtekind == RTE_RELATION);
 
 	/*
-	 * Build list of index OIDs.
+	 * If this is a innerjoin scan, the indexclauses will contain join
+	 * clauses that are not present in scan_clauses (since the passed-in
+	 * value is just the rel's baserestrictinfo list).  We must add these
+	 * clauses to scan_clauses to ensure they get checked.  In most cases
+	 * we will remove the join clauses again below, but if a join clause
+	 * contains a lossy or special operator, we need to make sure it gets
+	 * into scan_clauses.
 	 */
+	if (best_path->isjoininner)
+	{
+		/*
+		 * We don't currently support OR indexscans in joins, so we only
+		 * need to worry about the plain AND case.  Also, pointer comparison
+		 * should be enough to determine RestrictInfo matches.
+		 */
+		Assert(length(best_path->indexclauses) == 1);
+		scan_clauses = set_ptrUnion(scan_clauses,
+									(List *) lfirst(best_path->indexclauses));
+	}
+
+	/* Reduce RestrictInfo list to bare expressions */
+	scan_clauses = get_actual_clauses(scan_clauses);
+
+	/* Sort clauses into best execution order */
+	scan_clauses = order_qual_clauses(root, scan_clauses);
+
+	/* Build list of index OIDs */
 	FastListInit(&indexids);
-	foreach(ixinfo, best_path->indexinfo)
+	foreach(i, best_path->indexinfo)
 	{
-		IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
+		IndexOptInfo *index = (IndexOptInfo *) lfirst(i);
 
 		FastAppendo(&indexids, index->indexoid);
 	}
 
+	/*
+	 * Build "stripped" indexquals structure (no RestrictInfos) to pass to
+	 * executor as indxqualorig
+	 */
+	stripped_indxquals = NIL;
+	foreach(i, indxquals)
+	{
+		List   *andlist = (List *) lfirst(i);
+
+		stripped_indxquals = lappend(stripped_indxquals,
+									 get_actual_clauses(andlist));
+	}
+
 	/*
 	 * The qpqual list must contain all restrictions not automatically
-	 * handled by the index.  Normally the predicates in the indxqual are
+	 * handled by the index.  Normally the predicates in the indexquals are
 	 * checked fully by the index, but if the index is "lossy" for a
 	 * particular operator (as signaled by the amopreqcheck flag in
 	 * pg_amop), then we need to double-check that predicate in qpqual,
 	 * because the index may return more tuples than match the predicate.
 	 *
 	 * Since the indexquals were generated from the restriction clauses given
-	 * by scan_clauses, there will normally be some duplications between
-	 * the lists.  We get rid of the duplicates, then add back if lossy.
+	 * by scan_clauses, there will normally be duplications between the lists.
+	 * We get rid of the duplicates, then add back if lossy.
 	 */
-	if (length(indxqual) > 1)
+	if (length(indxquals) > 1)
 	{
 		/*
 		 * Build an expression representation of the indexqual, expanding
 		 * the implicit OR and AND semantics of the first- and
 		 * second-level lists.
 		 */
-		FastList	orclauses;
-		List	   *orclause;
-
-		FastListInit(&orclauses);
-		foreach(orclause, indxqual)
-			FastAppend(&orclauses, make_ands_explicit(lfirst(orclause)));
-		indxqual_or_expr = make_orclause(FastListValue(&orclauses));
-
+		indxqual_or_expr = make_expr_from_indexclauses(indxquals);
 		qpqual = set_difference(scan_clauses, makeList1(indxqual_or_expr));
 	}
-	else if (indxqual != NIL)
+	else
 	{
 		/*
 		 * Here, we can simply treat the first sublist as an independent
 		 * set of qual expressions, since there is no top-level OR
 		 * behavior.
 		 */
-		qpqual = set_difference(scan_clauses, lfirst(indxqual));
+		Assert(stripped_indxquals != NIL);
+		qpqual = set_difference(scan_clauses, lfirst(stripped_indxquals));
 	}
-	else
-		qpqual = scan_clauses;
 
 	/*
 	 * The executor needs a copy with the indexkey on the left of each
 	 * clause and with index attr numbers substituted for table ones. This
 	 * pass also looks for "lossy" operators.
 	 */
-	fix_indxqual_references(indxqual, best_path,
-							&fixed_indxqual, &recheck_indxqual,
+	fix_indxqual_references(indxquals, best_path,
+							&fixed_indxquals, &recheck_indxquals,
 							&indxstrategy, &indxsubtype);
 
 	/*
@@ -786,10 +825,10 @@ create_indexscan_plan(Query *root,
 	 * appropriate qual clauses to the qpqual.	When there is just one
 	 * indexscan being performed (ie, we have simple AND semantics), we
 	 * can just add the lossy clauses themselves to qpqual.  If we have
-	 * OR-of-ANDs, we'd better add the entire original indexqual to make
+	 * OR-of-ANDs, we'd better add the entire original indexquals to make
 	 * sure that the semantics are correct.
 	 */
-	if (recheck_indxqual != NIL)
+	if (recheck_indxquals != NIL)
 	{
 		if (indxqual_or_expr)
 		{
@@ -799,8 +838,8 @@ create_indexscan_plan(Query *root,
 		else
 		{
 			/* Subroutine already copied quals, so just append to list */
-			Assert(length(recheck_indxqual) == 1);
-			qpqual = nconc(qpqual, (List *) lfirst(recheck_indxqual));
+			Assert(length(recheck_indxquals) == 1);
+			qpqual = nconc(qpqual, (List *) lfirst(recheck_indxquals));
 		}
 	}
 
@@ -809,8 +848,8 @@ create_indexscan_plan(Query *root,
 							   qpqual,
 							   baserelid,
 							   FastListValue(&indexids),
-							   fixed_indxqual,
-							   indxqual,
+							   fixed_indxquals,
+							   stripped_indxquals,
 							   indxstrategy,
 							   indxsubtype,
 							   best_path->indexscandir);
@@ -828,7 +867,8 @@ create_indexscan_plan(Query *root,
  *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
 static TidScan *
-create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
+create_tidscan_plan(Query *root, TidPath *best_path,
+					List *tlist, List *scan_clauses)
 {
 	TidScan    *scan_plan;
 	Index		scan_relid = best_path->path.parent->relid;
@@ -837,6 +877,12 @@ create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
 	Assert(scan_relid > 0);
 	Assert(best_path->path.parent->rtekind == RTE_RELATION);
 
+	/* Reduce RestrictInfo list to bare expressions */
+	scan_clauses = get_actual_clauses(scan_clauses);
+
+	/* Sort clauses into best execution order */
+	scan_clauses = order_qual_clauses(root, scan_clauses);
+
 	scan_plan = make_tidscan(tlist,
 							 scan_clauses,
 							 scan_relid,
@@ -853,7 +899,8 @@ create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
  *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
 static SubqueryScan *
-create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
+create_subqueryscan_plan(Query *root, Path *best_path,
+						 List *tlist, List *scan_clauses)
 {
 	SubqueryScan *scan_plan;
 	Index		scan_relid = best_path->parent->relid;
@@ -862,6 +909,12 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
 	Assert(scan_relid > 0);
 	Assert(best_path->parent->rtekind == RTE_SUBQUERY);
 
+	/* Reduce RestrictInfo list to bare expressions */
+	scan_clauses = get_actual_clauses(scan_clauses);
+
+	/* Sort clauses into best execution order */
+	scan_clauses = order_qual_clauses(root, scan_clauses);
+
 	scan_plan = make_subqueryscan(tlist,
 								  scan_clauses,
 								  scan_relid,
@@ -878,7 +931,8 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
  *	 with restriction clauses 'scan_clauses' and targetlist 'tlist'.
  */
 static FunctionScan *
-create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses)
+create_functionscan_plan(Query *root, Path *best_path,
+						 List *tlist, List *scan_clauses)
 {
 	FunctionScan *scan_plan;
 	Index		scan_relid = best_path->parent->relid;
@@ -887,6 +941,12 @@ create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses)
 	Assert(scan_relid > 0);
 	Assert(best_path->parent->rtekind == RTE_FUNCTION);
 
+	/* Reduce RestrictInfo list to bare expressions */
+	scan_clauses = get_actual_clauses(scan_clauses);
+
+	/* Sort clauses into best execution order */
+	scan_clauses = order_qual_clauses(root, scan_clauses);
+
 	scan_plan = make_functionscan(tlist, scan_clauses, scan_relid);
 
 	copy_path_costsize(&scan_plan->scan.plan, best_path);
@@ -928,20 +988,19 @@ create_nestloop_plan(Query *root,
 		 * have caught this case because the join clauses would never have
 		 * been put in the same joininfo list.
 		 *
-		 * This would be a waste of time if the indexpath was an ordinary
-		 * indexpath and not a special innerjoin path.	We will skip it in
-		 * that case since indexjoinclauses is NIL in an ordinary
-		 * indexpath.
+		 * We can skip this if the index path is an ordinary indexpath and
+		 * not a special innerjoin path.
 		 */
 		IndexPath  *innerpath = (IndexPath *) best_path->innerjoinpath;
-		List	   *indexjoinclauses = innerpath->indexjoinclauses;
+		List	   *indexclauses = innerpath->indexclauses;
 
-		if (length(indexjoinclauses) == 1)		/* single indexscan? */
+		if (innerpath->isjoininner &&
+			length(indexclauses) == 1)		/* single indexscan? */
 		{
 			joinrestrictclauses =
 				select_nonredundant_join_clauses(root,
 												 joinrestrictclauses,
-												 lfirst(indexjoinclauses),
+												 lfirst(indexclauses),
 												 best_path->jointype);
 		}
 	}
@@ -1138,7 +1197,8 @@ create_hashjoin_plan(Query *root,
  *	  Adjust indexqual clauses to the form the executor's indexqual
  *	  machinery needs, and check for recheckable (lossy) index conditions.
  *
- * We have four tasks here:
+ * We have five tasks here:
+ *	* Remove RestrictInfo nodes from the input clauses.
  *	* Index keys must be represented by Var nodes with varattno set to the
  *	  index's attribute number, not the attribute number in the original rel.
  *	* If the index key is on the right, commute the clause to put it on the
@@ -1154,14 +1214,15 @@ create_hashjoin_plan(Query *root,
  *
  * Both the input list and the output lists have the form of lists of sublists
  * of qual clauses --- the top-level list has one entry for each indexscan
- * to be performed.  The semantics are OR-of-ANDs.
+ * to be performed.  The semantics are OR-of-ANDs.  Note however that the
+ * input list contains RestrictInfos, while the output lists do not.
  *
  * fixed_indexquals receives a modified copy of the indexqual list --- the
  * original is not changed.  Note also that the copy shares no substructure
  * with the original; this is needed in case there is a subplan in it (we need
  * two separate copies of the subplan tree, or things will go awry).
  *
- * recheck_indexquals similarly receives a full copy of whichever clauses
+ * recheck_indexquals similarly receives a copy of whichever clauses
  * need rechecking.
  *
  * indxstrategy receives a list of integer sublists of strategy numbers.
@@ -1243,14 +1304,16 @@ fix_indxqual_sublist(List *indexqual,
 	*subtype = NIL;
 	foreach(i, indexqual)
 	{
-		OpExpr	   *clause = (OpExpr *) lfirst(i);
+		RestrictInfo *rinfo = (RestrictInfo *) lfirst(i);
+		OpExpr	   *clause;
 		OpExpr	   *newclause;
-		Relids		leftvarnos;
 		Oid			opclass;
 		int			stratno;
 		Oid			stratsubtype;
 		bool		recheck;
 
+		Assert(IsA(rinfo, RestrictInfo));
+		clause = (OpExpr *) rinfo->clause;
 		if (!IsA(clause, OpExpr) ||
 			length(clause->args) != 2)
 			elog(ERROR, "indexqual clause is not binary opclause");
@@ -1269,10 +1332,8 @@ fix_indxqual_sublist(List *indexqual,
 		 * the clause.	The indexkey should be the side that refers to
 		 * (only) the base relation.
 		 */
-		leftvarnos = pull_varnos((Node *) lfirst(newclause->args));
-		if (!bms_equal(leftvarnos, baserelids))
+		if (!bms_equal(rinfo->left_relids, baserelids))
 			CommuteClause(newclause);
-		bms_free(leftvarnos);
 
 		/*
 		 * Now, determine which index attribute this is, change the
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index a2a6b35cd322714d7da55d45b73458ec67396244..5fe12f2b6d5687588386cac2db59d46a4d912656 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.98 2004/01/05 18:04:39 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/pathnode.c,v 1.99 2004/01/05 23:39:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -354,18 +354,22 @@ create_index_path(Query *root,
 	pathnode->path.parent = rel;
 	pathnode->path.pathkeys = pathkeys;
 
-	/* Convert RestrictInfo nodes to indexquals the executor can handle */
+	/* Convert clauses to indexquals the executor can handle */
 	indexquals = expand_indexqual_conditions(index, restriction_clauses);
 
+	/* Flatten the clause-groups list to produce indexclauses list */
+	restriction_clauses = flatten_clausegroups_list(restriction_clauses);
+
 	/*
 	 * We are making a pathnode for a single-scan indexscan; therefore,
-	 * both indexinfo and indexqual should be single-element lists.
+	 * indexinfo etc should be single-element lists.
 	 */
 	pathnode->indexinfo = makeList1(index);
-	pathnode->indexqual = makeList1(indexquals);
+	pathnode->indexclauses = makeList1(restriction_clauses);
+	pathnode->indexquals = makeList1(indexquals);
 
 	/* It's not an innerjoin path. */
-	pathnode->indexjoinclauses = NIL;
+	pathnode->isjoininner = false;
 
 	pathnode->indexscandir = indexscandir;
 
diff --git a/src/backend/optimizer/util/restrictinfo.c b/src/backend/optimizer/util/restrictinfo.c
index 0ab4a75c5c1ddf2a497b8dd1ecc1a3819ff3d23c..e2cc53cc692350244cf8c0ea878028b63b8bffae 100644
--- a/src/backend/optimizer/util/restrictinfo.c
+++ b/src/backend/optimizer/util/restrictinfo.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.24 2004/01/05 05:07:36 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/util/restrictinfo.c,v 1.25 2004/01/05 23:39:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,12 @@
 #include "optimizer/var.h"
 
 
-static Expr *make_sub_restrictinfos(Expr *clause, bool is_pushed_down,
+static RestrictInfo *make_restrictinfo_internal(Expr *clause,
+												Expr *orclause,
+												bool is_pushed_down,
+												bool valid_everywhere);
+static Expr *make_sub_restrictinfos(Expr *clause,
+									bool is_pushed_down,
 									bool valid_everywhere);
 static bool join_clause_is_redundant(Query *root,
 						 RestrictInfo *rinfo,
@@ -42,10 +47,89 @@ static bool join_clause_is_redundant(Query *root,
  */
 RestrictInfo *
 make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere)
+{
+	Expr	   *orclause;
+
+	/*
+	 * If it's an OR clause, build a modified copy with RestrictInfos
+	 * inserted above each subclause of the top-level AND/OR structure.
+	 */
+	if (or_clause((Node *) clause))
+	{
+		orclause = make_sub_restrictinfos(clause,
+										  is_pushed_down,
+										  valid_everywhere);
+	}
+	else
+	{
+		/* Shouldn't be an AND clause, else flatten_andors messed up */
+		Assert(!and_clause((Node *) clause));
+
+		orclause = NULL;
+	}
+
+	return make_restrictinfo_internal(clause, orclause,
+									  is_pushed_down, valid_everywhere);
+}
+
+/*
+ * make_restrictinfo_from_indexclauses
+ *
+ * Given an indexclauses structure, convert to ordinary expression format
+ * and build RestrictInfo node(s).
+ *
+ * The result is a List since we might need to return multiple RestrictInfos.
+ *
+ * This could be done as make_restrictinfo(make_expr_from_indexclauses()),
+ * but if we did it that way then we would strip the original RestrictInfo
+ * nodes from the index clauses and be forced to build new ones.  It's better
+ * to have a specialized routine that allows sharing of RestrictInfos.
+ */
+List *
+make_restrictinfo_from_indexclauses(List *indexclauses,
+									bool is_pushed_down,
+									bool valid_everywhere)
+{
+	List	   *withris = NIL;
+	List	   *withoutris = NIL;
+	List	   *orlist;
+
+	/* Empty list probably can't happen, but here's what to do */
+	if (indexclauses == NIL)
+		return NIL;
+	/* If single indexscan, just return the ANDed clauses */
+	if (lnext(indexclauses) == NIL)
+		return (List *) lfirst(indexclauses);
+	/* Else we need an OR RestrictInfo structure */
+	foreach(orlist, indexclauses)
+	{
+		List   *andlist = (List *) lfirst(orlist);
+
+		/* Create AND subclause with RestrictInfos */
+		withris = lappend(withris, make_ands_explicit(andlist));
+		/* And one without */
+		andlist = get_actual_clauses(andlist);
+		withoutris = lappend(withoutris, make_ands_explicit(andlist));
+	}
+	return makeList1(make_restrictinfo_internal(make_orclause(withoutris),
+												make_orclause(withris),
+												is_pushed_down,
+												valid_everywhere));
+}
+
+/*
+ * make_restrictinfo_internal
+ *
+ * Common code for the above two entry points.
+ */
+static RestrictInfo *
+make_restrictinfo_internal(Expr *clause, Expr *orclause,
+						   bool is_pushed_down, bool valid_everywhere)
 {
 	RestrictInfo *restrictinfo = makeNode(RestrictInfo);
 
 	restrictinfo->clause = clause;
+	restrictinfo->orclause = orclause;
 	restrictinfo->is_pushed_down = is_pushed_down;
 	restrictinfo->valid_everywhere = valid_everywhere;
 	restrictinfo->can_join = false;		/* may get set below */
@@ -83,24 +167,6 @@ make_restrictinfo(Expr *clause, bool is_pushed_down, bool valid_everywhere)
 		restrictinfo->clause_relids = pull_varnos((Node *) clause);
 	}
 
-	/*
-	 * If it's an OR clause, set up a modified copy with RestrictInfos
-	 * inserted above each subclause of the top-level AND/OR structure.
-	 */
-	if (or_clause((Node *) clause))
-	{
-		restrictinfo->orclause = make_sub_restrictinfos(clause,
-														is_pushed_down,
-														valid_everywhere);
-	}
-	else
-	{
-		/* Shouldn't be an AND clause, else flatten_andors messed up */
-		Assert(!and_clause((Node *) clause));
-
-		restrictinfo->orclause = NULL;
-	}
-
 	/*
 	 * Fill in all the cacheable fields with "not yet set" markers.
 	 * None of these will be computed until/unless needed.  Note in
@@ -161,9 +227,10 @@ make_sub_restrictinfos(Expr *clause, bool is_pushed_down,
 		return make_andclause(andlist);
 	}
 	else
-		return (Expr *) make_restrictinfo(clause,
-										  is_pushed_down,
-										  valid_everywhere);
+		return (Expr *) make_restrictinfo_internal(clause,
+												   NULL,
+												   is_pushed_down,
+												   valid_everywhere);
 }
 
 /*
@@ -193,9 +260,11 @@ get_actual_clauses(List *restrictinfo_list)
 
 	foreach(temp, restrictinfo_list)
 	{
-		RestrictInfo *clause = (RestrictInfo *) lfirst(temp);
+		RestrictInfo *rinfo = (RestrictInfo *) lfirst(temp);
+
+		Assert(IsA(rinfo, RestrictInfo));
 
-		result = lappend(result, clause->clause);
+		result = lappend(result, rinfo->clause);
 	}
 	return result;
 }
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index 1c3bba0ca2d10b91d42be9f9dbdcc1095d10ba48..93e8040b9b512b763db5173b8309e43381c50a72 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.152 2003/12/29 22:22:45 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.153 2004/01/05 23:39:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -94,6 +94,7 @@
 #include "optimizer/paths.h"
 #include "optimizer/plancat.h"
 #include "optimizer/prep.h"
+#include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
 #include "parser/parse_expr.h"
@@ -3896,13 +3897,13 @@ genericcostestimate(Query *root, RelOptInfo *rel,
 	double		numIndexTuples;
 	double		numIndexPages;
 	QualCost	index_qual_cost;
-	List	   *selectivityQuals = indexQuals;
+	List	   *selectivityQuals;
 
 	/*
 	 * If the index is partial, AND the index predicate with the
 	 * explicitly given indexquals to produce a more accurate idea of the
-	 * index selectivity.  This may produce redundant clauses.  We can get
-	 * rid of exact duplicates by using set_union().  We expect that most
+	 * index selectivity.  This may produce redundant clauses.  We get rid
+	 * of exact duplicates in the code below.  We expect that most
 	 * cases of partial redundancy (such as "x < 4" from the qual and
 	 * "x < 5" from the predicate) will be recognized and handled correctly
 	 * by clauselist_selectivity().  This assumption is somewhat fragile,
@@ -3913,10 +3914,25 @@ genericcostestimate(Query *root, RelOptInfo *rel,
 	 * necessarily a bad thing.  But it'd be nice to do better someday.
 	 *
 	 * Note that index->indpred and indexQuals are both in implicit-AND form,
-	 * so ANDing them together just takes merging the lists.
+	 * so ANDing them together just takes merging the lists.  However,
+	 * eliminating duplicates is a bit trickier because indexQuals contains
+	 * RestrictInfo nodes and the indpred does not.  It is okay to pass a
+	 * mixed list to clauselist_selectivity, but we have to work a bit to
+	 * generate a list without logical duplicates.  (We could just set_union
+	 * indpred and strippedQuals, but then we'd not get caching of per-qual
+	 * selectivity estimates.)
 	 */
 	if (index->indpred != NIL)
-		selectivityQuals = set_union(index->indpred, indexQuals);
+	{
+		List   *strippedQuals;
+		List   *predExtraQuals;
+
+		strippedQuals = get_actual_clauses(indexQuals);
+		predExtraQuals = set_difference(index->indpred, strippedQuals);
+		selectivityQuals = nconc(predExtraQuals, indexQuals);
+	}
+	else
+		selectivityQuals = indexQuals;
 
 	/* Estimate the fraction of main-table tuples that will be visited */
 	*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 166f1242b0eca6125d258d54907601058449cf18..460679416f1e3c626ed45e776dfcf3b1f8edb036 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.92 2004/01/05 18:04:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.93 2004/01/05 23:39:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -344,23 +344,24 @@ typedef struct Path
  *
  * 'indexinfo' is a list of IndexOptInfo nodes, one per scan to be performed.
  *
- * 'indexqual' is a list of index qualifications, also one per scan.
- * Each entry in 'indexqual' is a sublist of qualification expressions with
- * implicit AND semantics across the sublist items.  Only expressions that
- * are usable as indexquals (as determined by indxpath.c) may appear here.
- * NOTE that the semantics of the top-level list in 'indexqual' is OR
+ * 'indexclauses' is a list of index qualifications, also one per scan.
+ * Each entry in 'indexclauses' is a sublist of qualification clauses to be
+ * used for that scan, with implicit AND semantics across the sublist items.
+ * NOTE that the semantics of the top-level list in 'indexclauses' is OR
  * combination, while the sublists are implicitly AND combinations!
- * Also note that indexquals lists do not contain RestrictInfo nodes,
- * just bare clause expressions.
- *
- * 'indexjoinclauses' is NIL for an ordinary indexpath (one that does not
- * use any join clauses in the index conditions).  For an innerjoin indexpath,
- * it has the same structure as 'indexqual', but references the RestrictInfo
- * nodes from which the indexqual was built, rather than the bare clause
- * expressions.  (Note: there isn't necessarily a one-to-one correspondence
- * between RestrictInfos and expressions, because of expansion of special
- * indexable operators.)  We need this so that we can eliminate redundant
- * join clauses when plans are built.
+ *
+ * 'indexquals' has the same structure as 'indexclauses', but it contains
+ * the actual indexqual conditions that can be used with the index(es).
+ * In simple cases this is identical to 'indexclauses', but when special
+ * indexable operators appear in 'indexclauses', they are replaced by the
+ * derived indexscannable conditions in 'indexquals'.
+ *
+ * Both 'indexclauses' and 'indexquals' are lists of sublists of RestrictInfo
+ * nodes.  (Before 7.5, we kept bare operator expressions in these lists, but
+ * storing RestrictInfos is more efficient since selectivities can be cached.)
+ *
+ * 'isjoininner' is TRUE if the path is a nestloop inner scan (that is,
+ * some of the index conditions are join rather than restriction clauses).
  *
  * 'indexscandir' is one of:
  *		ForwardScanDirection: forward scan of an ordered index
@@ -381,8 +382,9 @@ typedef struct IndexPath
 {
 	Path		path;
 	List	   *indexinfo;
-	List	   *indexqual;
-	List	   *indexjoinclauses;
+	List	   *indexclauses;
+	List	   *indexquals;
+	bool		isjoininner;
 	ScanDirection indexscandir;
 	double		rows;			/* estimated number of result tuples */
 } IndexPath;
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index e1a46bcb3f843ec01025a209fde6f4be861953de..40f7206fac380bed33e94114ded9ff30b0a5d91f 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.72 2004/01/05 05:07:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.73 2004/01/05 23:39:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,8 @@ extern List *group_clauses_by_indexkey_for_or(RelOptInfo *rel,
 extern List *expand_indexqual_conditions(IndexOptInfo *index,
 							List *clausegroups);
 extern void check_partial_indexes(Query *root, RelOptInfo *rel);
+extern List *flatten_clausegroups_list(List *clausegroups);
+extern Expr *make_expr_from_indexclauses(List *indexclauses);
 
 /*
  * orindxpath.c
diff --git a/src/include/optimizer/restrictinfo.h b/src/include/optimizer/restrictinfo.h
index 022bfa253a39e74db6309d7f691ecf43b013395e..dfb6e71e3195119a6ee576f9453a31794b21881e 100644
--- a/src/include/optimizer/restrictinfo.h
+++ b/src/include/optimizer/restrictinfo.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.22 2004/01/05 05:07:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/restrictinfo.h,v 1.23 2004/01/05 23:39:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,9 @@
 
 extern RestrictInfo *make_restrictinfo(Expr *clause, bool is_pushed_down,
 									   bool valid_everywhere);
+extern List *make_restrictinfo_from_indexclauses(List *indexclauses,
+												 bool is_pushed_down,
+												 bool valid_everywhere);
 extern bool restriction_is_or_clause(RestrictInfo *restrictinfo);
 extern List *get_actual_clauses(List *restrictinfo_list);
 extern void get_actual_join_clauses(List *restrictinfo_list,