diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 11494b0172a6a5052f31304763aacd86510950a5..db54ceede2f5a0e136577d598aaedfd56fb1aa5c 100644
--- a/src/backend/catalog/pg_operator.c
+++ b/src/backend/catalog/pg_operator.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.57 2001/03/22 06:16:10 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.58 2001/05/20 20:28:17 tgl Exp $
  *
  * NOTES
  *	  these routines moved here from commands/define.c and somewhat cleaned up.
@@ -69,7 +69,7 @@ static void OperatorUpd(Oid baseId, Oid commId, Oid negId);
 /* ----------------------------------------------------------------
  *		OperatorGetWithOpenRelation
  *
- *		preforms a scan on pg_operator for an operator tuple
+ *		performs a scan on pg_operator for an operator tuple
  *		with given name and left/right type oids.
  * ----------------------------------------------------------------
  *	  pg_operator_desc	-- reldesc for pg_operator
@@ -570,26 +570,25 @@ OperatorDef(char *operatorName,
 	ReleaseSysCache(tup);
 
 	/*
-	 * find restriction
+	 * find restriction estimator
 	 */
 	if (restrictionName)
 	{							/* optional */
 		Oid			restOid;
 
 		MemSet(typeId, 0, FUNC_MAX_ARGS * sizeof(Oid));
-		typeId[0] = OIDOID;		/* operator OID */
-		typeId[1] = OIDOID;		/* relation OID */
-		typeId[2] = INT2OID;	/* attribute number */
-		typeId[3] = 0;			/* value - can be any type	*/
-		typeId[4] = INT4OID;	/* flags - left or right selectivity */
+		typeId[0] = 0;			/* Query (opaque type) */
+		typeId[1] = OIDOID;		/* operator OID */
+		typeId[2] = 0;			/* args list (opaque type) */
+		typeId[3] = INT4OID;	/* varRelid */
 
 		restOid = GetSysCacheOid(PROCNAME,
 								 PointerGetDatum(restrictionName),
-								 Int32GetDatum(5),
+								 Int32GetDatum(4),
 								 PointerGetDatum(typeId),
 								 0);
 		if (!OidIsValid(restOid))
-			func_error("OperatorDef", restrictionName, 5, typeId, NULL);
+			func_error("OperatorDef", restrictionName, 4, typeId, NULL);
 
 		values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(restOid);
 	}
@@ -597,26 +596,24 @@ OperatorDef(char *operatorName,
 		values[Anum_pg_operator_oprrest - 1] = ObjectIdGetDatum(InvalidOid);
 
 	/*
-	 * find join - only valid for binary operators
+	 * find join estimator
 	 */
 	if (joinName)
 	{							/* optional */
 		Oid			joinOid;
 
 		MemSet(typeId, 0, FUNC_MAX_ARGS * sizeof(Oid));
-		typeId[0] = OIDOID;		/* operator OID */
-		typeId[1] = OIDOID;		/* relation OID 1 */
-		typeId[2] = INT2OID;	/* attribute number 1 */
-		typeId[3] = OIDOID;		/* relation OID 2 */
-		typeId[4] = INT2OID;	/* attribute number 2 */
+		typeId[0] = 0;			/* Query (opaque type) */
+		typeId[1] = OIDOID;		/* operator OID */
+		typeId[2] = 0;			/* args list (opaque type) */
 
 		joinOid = GetSysCacheOid(PROCNAME,
 								 PointerGetDatum(joinName),
-								 Int32GetDatum(5),
+								 Int32GetDatum(3),
 								 PointerGetDatum(typeId),
 								 0);
 		if (!OidIsValid(joinOid))
-			func_error("OperatorDef", joinName, 5, typeId, NULL);
+			func_error("OperatorDef", joinName, 3, typeId, NULL);
 
 		values[Anum_pg_operator_oprjoin - 1] = ObjectIdGetDatum(joinOid);
 	}
@@ -1044,10 +1041,8 @@ OperatorCreate(char *operatorName,
 		/* If it's not a binary op, these things mustn't be set: */
 		if (commutatorName)
 			elog(ERROR, "OperatorCreate: only binary operators can have commutators");
-		if (negatorName)
-			elog(ERROR, "OperatorCreate: only binary operators can have negators");
-		if (restrictionName || joinName)
-			elog(ERROR, "OperatorCreate: only binary operators can have selectivity");
+		if (joinName)
+			elog(ERROR, "OperatorCreate: only binary operators can have join selectivity");
 		if (canHash)
 			elog(ERROR, "OperatorCreate: only binary operators can hash");
 		if (leftSortName || rightSortName)
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index ee5a803b8025ac9817834537bb5b4ccd10708527..a5a968515e68a0dda875910caa25d0e6a22a2e38 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.141 2001/05/07 00:43:18 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.142 2001/05/20 20:28:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1071,7 +1071,7 @@ _copyRelOptInfo(RelOptInfo *from)
 	newnode->pruneable = from->pruneable;
 
 	newnode->issubquery = from->issubquery;
-	newnode->indexed = from->indexed;
+	Node_Copy(from, newnode, indexlist);
 	newnode->pages = from->pages;
 	newnode->tuples = from->tuples;
 	Node_Copy(from, newnode, subplan);
@@ -1093,47 +1093,44 @@ static IndexOptInfo *
 _copyIndexOptInfo(IndexOptInfo *from)
 {
 	IndexOptInfo *newnode = makeNode(IndexOptInfo);
-	int			i,
-				len;
+	Size		len;
 
 	newnode->indexoid = from->indexoid;
 	newnode->pages = from->pages;
 	newnode->tuples = from->tuples;
 
+	newnode->ncolumns = from->ncolumns;
+	newnode->nkeys = from->nkeys;
+
 	if (from->classlist)
 	{
-		for (len = 0; from->classlist[len] != 0; len++)
-			;
-		newnode->classlist = (Oid *) palloc(sizeof(Oid) * (len + 1));
-		for (i = 0; i < len; i++)
-			newnode->classlist[i] = from->classlist[i];
-		newnode->classlist[len] = 0;
+		/* copy the trailing zero too */
+		len = (from->ncolumns + 1) * sizeof(Oid);
+		newnode->classlist = (Oid *) palloc(len);
+		memcpy(newnode->classlist, from->classlist, len);
 	}
 
 	if (from->indexkeys)
 	{
-		for (len = 0; from->indexkeys[len] != 0; len++)
-			;
-		newnode->indexkeys = (int *) palloc(sizeof(int) * (len + 1));
-		for (i = 0; i < len; i++)
-			newnode->indexkeys[i] = from->indexkeys[i];
-		newnode->indexkeys[len] = 0;
+		/* copy the trailing zero too */
+		len = (from->nkeys + 1) * sizeof(int);
+		newnode->indexkeys = (int *) palloc(len);
+		memcpy(newnode->indexkeys, from->indexkeys, len);
 	}
 
 	if (from->ordering)
 	{
-		for (len = 0; from->ordering[len] != 0; len++)
-			;
-		newnode->ordering = (Oid *) palloc(sizeof(Oid) * (len + 1));
-		for (i = 0; i < len; i++)
-			newnode->ordering[i] = from->ordering[i];
-		newnode->ordering[len] = 0;
+		/* copy the trailing zero too */
+		len = (from->ncolumns + 1) * sizeof(Oid);
+		newnode->ordering = (Oid *) palloc(len);
+		memcpy(newnode->ordering, from->ordering, len);
 	}
 
 	newnode->relam = from->relam;
 	newnode->amcostestimate = from->amcostestimate;
 	newnode->indproc = from->indproc;
 	Node_Copy(from, newnode, indpred);
+	newnode->unique = from->unique;
 	newnode->lossy = from->lossy;
 
 	return newnode;
@@ -1196,7 +1193,7 @@ _copyIndexPath(IndexPath *from)
 	/*
 	 * copy remainder of node
 	 */
-	newnode->indexid = listCopy(from->indexid);
+	Node_Copy(from, newnode, indexinfo);
 	Node_Copy(from, newnode, indexqual);
 	newnode->indexscandir = from->indexscandir;
 	newnode->joinrelids = listCopy(from->joinrelids);
@@ -1749,8 +1746,8 @@ _copyQuery(Query *from)
 
 	/*
 	 * We do not copy the planner internal fields: base_rel_list,
-	 * join_rel_list, equi_key_list, query_pathkeys. Not entirely clear if
-	 * this is right?
+	 * other_rel_list, join_rel_list, equi_key_list, query_pathkeys.
+	 * Not entirely clear if this is right?
 	 */
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 284a534aa966f03a5f69da55e5faa89a96925b1e..a89a8f7f335b9cdd77cf355afc6a0a85035ba4cb 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.89 2001/05/07 00:43:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.90 2001/05/20 20:28:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -405,7 +405,7 @@ _equalIndexPath(IndexPath *a, IndexPath *b)
 {
 	if (!_equalPath((Path *) a, (Path *) b))
 		return false;
-	if (!equali(a->indexid, b->indexid))
+	if (!equal(a->indexinfo, b->indexinfo))
 		return false;
 	if (!equal(a->indexqual, b->indexqual))
 		return false;
@@ -623,9 +623,9 @@ _equalQuery(Query *a, Query *b)
 
 	/*
 	 * We do not check the internal-to-the-planner fields: base_rel_list,
-	 * join_rel_list, equi_key_list, query_pathkeys. They might not be set
-	 * yet, and in any case they should be derivable from the other
-	 * fields.
+	 * other_rel_list, join_rel_list, equi_key_list, query_pathkeys.
+	 * They might not be set yet, and in any case they should be derivable
+	 * from the other fields.
 	 */
 	return true;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 2c0cfed7ee49c27f576fe5f5c9fed302e5cfbba8..ebcacd49750cc9a85246a087050dac32e61d2f0f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.140 2001/03/22 03:59:32 momjian Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.141 2001/05/20 20:28:18 tgl Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -952,56 +952,6 @@ _outJoinExpr(StringInfo str, JoinExpr *node)
 	_outNode(str, node->colvars);
 }
 
-/*
- *	Stuff from relation.h
- */
-
-static void
-_outRelOptInfo(StringInfo str, RelOptInfo *node)
-{
-	appendStringInfo(str, " RELOPTINFO :relids ");
-	_outIntList(str, node->relids);
-
-	appendStringInfo(str, " :rows %.0f :width %d :targetlist ",
-					 node->rows,
-					 node->width);
-	_outNode(str, node->targetlist);
-
-	appendStringInfo(str, " :pathlist ");
-	_outNode(str, node->pathlist);
-	appendStringInfo(str, " :cheapest_startup_path ");
-	_outNode(str, node->cheapest_startup_path);
-	appendStringInfo(str, " :cheapest_total_path ");
-	_outNode(str, node->cheapest_total_path);
-
-	appendStringInfo(str, " :pruneable %s :issubquery %s :indexed %s :pages %ld :tuples %.0f :subplan ",
-					 booltostr(node->pruneable),
-					 booltostr(node->issubquery),
-					 booltostr(node->indexed),
-					 node->pages,
-					 node->tuples);
-	_outNode(str, node->subplan);
-
-	appendStringInfo(str, " :baserestrictinfo ");
-	_outNode(str, node->baserestrictinfo);
-	appendStringInfo(str, " :baserestrictcost %.2f :outerjoinset ",
-					 node->baserestrictcost);
-	_outIntList(str, node->outerjoinset);
-	appendStringInfo(str, " :joininfo ");
-	_outNode(str, node->joininfo);
-	appendStringInfo(str, " :innerjoin ");
-	_outNode(str, node->innerjoin);
-}
-
-static void
-_outIndexOptInfo(StringInfo str, IndexOptInfo *node)
-{
-	appendStringInfo(str, " INDEXOPTINFO :indexoid %u :pages %ld :tuples %g ",
-					 node->indexoid,
-					 node->pages,
-					 node->tuples);
-}
-
 /*
  *	TargetEntry is a subclass of Node.
  */
@@ -1064,8 +1014,8 @@ _outIndexPath(StringInfo str, IndexPath *node)
 					 node->path.total_cost);
 	_outNode(str, node->path.pathkeys);
 
-	appendStringInfo(str, " :indexid ");
-	_outOidList(str, node->indexid);
+	appendStringInfo(str, " :indexinfo ");
+	_outNode(str, node->indexinfo);
 
 	appendStringInfo(str, " :indexqual ");
 	_outNode(str, node->indexqual);
@@ -1629,12 +1579,6 @@ _outNode(StringInfo str, void *obj)
 			case T_JoinExpr:
 				_outJoinExpr(str, obj);
 				break;
-			case T_RelOptInfo:
-				_outRelOptInfo(str, obj);
-				break;
-			case T_IndexOptInfo:
-				_outIndexOptInfo(str, obj);
-				break;
 			case T_TargetEntry:
 				_outTargetEntry(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 4c0c1b03ef544c60b9161208ceb950a83862419c..ad832d7ca9e82012235ad488d55d6478126a35b6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.108 2001/05/07 00:43:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.109 2001/05/20 20:28:18 tgl Exp $
  *
  * NOTES
  *	  Most of the read functions for plan nodes are tested. (In fact, they
@@ -1318,88 +1318,6 @@ _readJoinExpr(void)
 	return local_node;
 }
 
-/*
- *	Stuff from relation.h
- */
-
-/* ----------------
- *		_readRelOptInfo
- * ----------------
- */
-static RelOptInfo *
-_readRelOptInfo(void)
-{
-	RelOptInfo *local_node;
-	char	   *token;
-	int			length;
-
-	local_node = makeNode(RelOptInfo);
-
-	token = pg_strtok(&length); /* get :relids */
-	local_node->relids = toIntList(nodeRead(true));		/* now read it */
-
-	token = pg_strtok(&length); /* get :rows */
-	token = pg_strtok(&length); /* now read it */
-	local_node->rows = atof(token);
-
-	token = pg_strtok(&length); /* get :width */
-	token = pg_strtok(&length); /* now read it */
-	local_node->width = atoi(token);
-
-	token = pg_strtok(&length); /* get :targetlist */
-	local_node->targetlist = nodeRead(true);	/* now read it */
-
-	token = pg_strtok(&length); /* get :pathlist */
-	local_node->pathlist = nodeRead(true);		/* now read it */
-
-	token = pg_strtok(&length); /* get :cheapest_startup_path */
-	local_node->cheapest_startup_path = nodeRead(true); /* now read it */
-
-	token = pg_strtok(&length); /* get :cheapest_total_path */
-	local_node->cheapest_total_path = nodeRead(true);	/* now read it */
-
-	token = pg_strtok(&length); /* eat :pruneable */
-	token = pg_strtok(&length); /* get :pruneable */
-	local_node->pruneable = strtobool(token);
-
-	token = pg_strtok(&length); /* get :issubquery */
-	token = pg_strtok(&length); /* now read it */
-	local_node->issubquery = strtobool(token);
-
-	token = pg_strtok(&length); /* get :indexed */
-	token = pg_strtok(&length); /* now read it */
-	local_node->indexed = strtobool(token);
-
-	token = pg_strtok(&length); /* get :pages */
-	token = pg_strtok(&length); /* now read it */
-	local_node->pages = atol(token);
-
-	token = pg_strtok(&length); /* get :tuples */
-	token = pg_strtok(&length); /* now read it */
-	local_node->tuples = atof(token);
-
-	token = pg_strtok(&length); /* get :subplan */
-	local_node->subplan = nodeRead(true);		/* now read it */
-
-	token = pg_strtok(&length); /* get :baserestrictinfo */
-	local_node->baserestrictinfo = nodeRead(true);		/* now read it */
-
-	token = pg_strtok(&length); /* get :baserestrictcost */
-	token = pg_strtok(&length); /* now read it */
-	local_node->baserestrictcost = (Cost) atof(token);
-
-	token = pg_strtok(&length); /* get :outerjoinset */
-	local_node->outerjoinset = toIntList(nodeRead(true));		/* now read it */
-
-	token = pg_strtok(&length); /* get :joininfo */
-	local_node->joininfo = nodeRead(true);		/* now read it */
-
-	token = pg_strtok(&length); /* get :innerjoin */
-	local_node->innerjoin = nodeRead(true);		/* now read it */
-
-	return local_node;
-}
-
 /* ----------------
  *		_readTargetEntry
  * ----------------
@@ -1557,8 +1475,8 @@ _readIndexPath(void)
 	token = pg_strtok(&length); /* get :pathkeys */
 	local_node->path.pathkeys = nodeRead(true); /* now read it */
 
-	token = pg_strtok(&length); /* get :indexid */
-	local_node->indexid = toOidList(nodeRead(true));
+	token = pg_strtok(&length); /* get :indexinfo */
+	local_node->indexinfo = nodeRead(true);		/* now read it */
 
 	token = pg_strtok(&length); /* get :indexqual */
 	local_node->indexqual = nodeRead(true);		/* now read it */
@@ -2008,8 +1926,6 @@ parsePlanString(void)
 		return_value = _readOper();
 	else if (length == 5 && strncmp(token, "PARAM", length) == 0)
 		return_value = _readParam();
-	else if (length == 10 && strncmp(token, "RELOPTINFO", length) == 0)
-		return_value = _readRelOptInfo();
 	else if (length == 11 && strncmp(token, "TARGETENTRY", length) == 0)
 		return_value = _readTargetEntry();
 	else if (length == 3 && strncmp(token, "RTE", length) == 0)
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 243ff7c5a72abfb2c257bfd7f52ab616618b87d8..afb3259e7367154251891fdb906a28e557e5bdd3 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.73 2001/05/08 17:25:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.74 2001/05/20 20:28:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,8 +35,8 @@ static void set_base_rel_pathlists(Query *root);
 static void set_plain_rel_pathlist(Query *root, RelOptInfo *rel,
 					   RangeTblEntry *rte);
 static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
-						   RangeTblEntry *rte,
-						   List *inheritlist);
+									   Index rti, RangeTblEntry *rte,
+									   List *inheritlist);
 static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
 					  List *initial_rels);
 
@@ -69,7 +69,7 @@ make_one_rel(Query *root)
 	rel = make_fromexpr_rel(root, root->jointree);
 
 	/*
-	 * The result should join all the query's rels.
+	 * The result should join all the query's base rels.
 	 */
 	Assert(length(rel->relids) == length(root->base_rel_list));
 
@@ -190,10 +190,11 @@ set_base_rel_pathlists(Query *root)
 			/* Select cheapest path (pretty easy in this case...) */
 			set_cheapest(rel);
 		}
-		else if ((inheritlist = expand_inherted_rtentry(root, rti)) != NIL)
+		else if ((inheritlist = expand_inherted_rtentry(root, rti, true))
+				 != NIL)
 		{
 			/* Relation is root of an inheritance tree, process specially */
-			set_inherited_rel_pathlist(root, rel, rte, inheritlist);
+			set_inherited_rel_pathlist(root, rel, rti, rte, inheritlist);
 		}
 		else
 		{
@@ -210,8 +211,6 @@ set_base_rel_pathlists(Query *root)
 static void
 set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
 {
-	List	   *indices = find_secondary_indexes(rte->relid);
-
 	/* Mark rel with estimated output rows, width, etc */
 	set_baserel_size_estimates(root, rel);
 
@@ -230,13 +229,9 @@ set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
 	create_tidscan_paths(root, rel);
 
 	/* Consider index paths for both simple and OR index clauses */
-	create_index_paths(root, rel, indices);
+	create_index_paths(root, rel);
 
-	/*
-	 * Note: create_or_index_paths depends on create_index_paths to have
-	 * marked OR restriction clauses with relevant indices; this is why it
-	 * doesn't need to be given the list of indices.
-	 */
+	/* create_index_paths must be done before create_or_index_paths */
 	create_or_index_paths(root, rel, rel->baserestrictinfo);
 
 	/* Now find the cheapest of the paths for this rel */
@@ -248,14 +243,26 @@ set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
  *	  Build access paths for a inheritance tree rooted at rel
  *
  * inheritlist is a list of RT indexes of all tables in the inheritance tree,
- * including the parent itself.  Note we will not come here unless there's
- * at least one child in addition to the parent.
+ * including a duplicate of the parent itself.  Note we will not come here
+ * unless there's at least one child in addition to the parent.
+ *
+ * NOTE: the passed-in rel and RTE will henceforth represent the appended
+ * result of the whole inheritance tree.  The members of inheritlist represent
+ * the individual tables --- in particular, the inheritlist member that is a
+ * duplicate of the parent RTE represents the parent table alone.
+ * We will generate plans to scan the individual tables that refer to
+ * the inheritlist RTEs, whereas Vars elsewhere in the plan tree that
+ * refer to the original RTE are taken to refer to the append output.
+ * In particular, this means we have separate RelOptInfos for the parent
+ * table and for the append output, which is a good thing because they're
+ * not the same size.
  */
 static void
-set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
+set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
+						   Index rti, RangeTblEntry *rte,
 						   List *inheritlist)
 {
-	int			parentRTindex = lfirsti(rel->relids);
+	int			parentRTindex = rti;
 	Oid			parentOID = rte->relid;
 	List	   *subpaths = NIL;
 	List	   *il;
@@ -268,7 +275,15 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
 		elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
 
 	/*
-	 * Recompute size estimates for whole inheritance tree
+	 * The executor will check the parent table's access permissions when it
+	 * examines the parent's inheritlist entry.  There's no need to check
+	 * twice, so turn off access check bits in the original RTE.
+	 */
+	rte->checkForRead = false;
+	rte->checkForWrite = false;
+
+	/*
+	 * Initialize to compute size estimates for whole inheritance tree
 	 */
 	rel->rows = 0;
 	rel->width = 0;
@@ -289,21 +304,17 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
 
 		/*
 		 * Make a RelOptInfo for the child so we can do planning.  Do NOT
-		 * attach the RelOptInfo to the query's base_rel_list, however.
-		 *
-		 * NOTE: when childRTindex == parentRTindex, we create a second
-		 * RelOptInfo for the same relation.  This RelOptInfo will
-		 * represent the parent table alone, whereas the original
-		 * RelOptInfo represents the union of the inheritance tree
-		 * members.
+		 * attach the RelOptInfo to the query's base_rel_list, however,
+		 * since the child is not part of the main join tree.  Instead,
+		 * the child RelOptInfo is added to other_rel_list.
 		 */
-		childrel = make_base_rel(root, childRTindex);
+		childrel = build_other_rel(root, childRTindex);
 
 		/*
 		 * Copy the parent's targetlist and restriction quals to the
-		 * child, with attribute-number adjustment if needed.  We don't
+		 * child, with attribute-number adjustment as needed.  We don't
 		 * bother to copy the join quals, since we can't do any joining
-		 * here.
+		 * of the individual tables.
 		 */
 		childrel->targetlist = (List *)
 			adjust_inherited_attrs((Node *) rel->targetlist,
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 2699b56cb37849045cd8c0f542c3ecce5f3f3456..78407fb833a9a130bec20b0a097013012bb652e9 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -8,13 +8,15 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.43 2001/03/23 04:49:53 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.44 2001/05/20 20:28:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
 #include "catalog/pg_operator.h"
+#include "catalog/pg_type.h"
+#include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/plancat.h"
@@ -24,6 +26,12 @@
 #include "utils/lsyscache.h"
 
 
+/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */
+#define MAKEBOOLCONST(val,isnull) \
+	((Node *) makeConst(BOOLOID, 1, (Datum) (val), \
+						(isnull), true, false, false))
+
+
 /*
  * Data structure for accumulating info about possible range-query
  * clause pairs in clauselist_selectivity.
@@ -39,7 +47,7 @@ typedef struct RangeQueryClause
 } RangeQueryClause;
 
 static void addRangeClause(RangeQueryClause **rqlist, Node *clause,
-			   int flag, bool isLTsel, Selectivity s2);
+						   bool varonleft, bool isLTsel, Selectivity s2);
 static Selectivity clause_selectivity(Query *root,
 				   Node *clause,
 				   int varRelid);
@@ -131,35 +139,24 @@ clauselist_selectivity(Query *root,
 		 * match what clause_selectivity() would do in the cases it
 		 * handles.
 		 */
-		if (varRelid != 0 || NumRelids(clause) == 1)
+		if (is_opclause(clause) &&
+			(varRelid != 0 || NumRelids(clause) == 1))
 		{
-			int			relidx;
-			AttrNumber	attno;
-			Datum		constval;
-			int			flag;
-
-			get_relattval(clause, varRelid,
-						  &relidx, &attno, &constval, &flag);
-			if (relidx != 0)
+			Expr	   *expr = (Expr *) clause;
+
+			if (length(expr->args) == 2)
 			{
-				/* if get_relattval succeeded, it must be an opclause */
-				Var		   *other;
+				bool		varonleft = true;
 
-				other = (flag & SEL_RIGHT) ? get_rightop((Expr *) clause) :
-					get_leftop((Expr *) clause);
-				if (is_pseudo_constant_clause((Node *) other))
+				if (is_pseudo_constant_clause(lsecond(expr->args)) ||
+					(varonleft = false,
+					 is_pseudo_constant_clause(lfirst(expr->args))))
 				{
-					Oid			opno = ((Oper *) ((Expr *) clause)->oper)->opno;
+					Oid			opno = ((Oper *) expr->oper)->opno;
 					RegProcedure oprrest = get_oprrest(opno);
 
-					if (!oprrest)
-						s2 = (Selectivity) 0.5;
-					else
-						s2 = restriction_selectivity(oprrest, opno,
-													 getrelid(relidx,
-														   root->rtable),
-													 attno,
-													 constval, flag);
+					s2 = restriction_selectivity(root, opno,
+												 expr->args, varRelid);
 
 					/*
 					 * If we reach here, we have computed the same result
@@ -171,10 +168,12 @@ clauselist_selectivity(Query *root,
 					switch (oprrest)
 					{
 						case F_SCALARLTSEL:
-							addRangeClause(&rqlist, clause, flag, true, s2);
+							addRangeClause(&rqlist, clause,
+										   varonleft, true, s2);
 							break;
 						case F_SCALARGTSEL:
-							addRangeClause(&rqlist, clause, flag, false, s2);
+							addRangeClause(&rqlist, clause,
+										   varonleft, false, s2);
 							break;
 						default:
 							/* Just merge the selectivity in generically */
@@ -220,7 +219,7 @@ clauselist_selectivity(Query *root,
 					 * No data available --- use a default estimate that
 					 * is small, but not real small.
 					 */
-					s2 = 0.01;
+					s2 = 0.005;
 				}
 				else
 				{
@@ -259,14 +258,13 @@ clauselist_selectivity(Query *root,
  */
 static void
 addRangeClause(RangeQueryClause **rqlist, Node *clause,
-			   int flag, bool isLTsel, Selectivity s2)
+			   bool varonleft, bool isLTsel, Selectivity s2)
 {
 	RangeQueryClause *rqelem;
 	Node	   *var;
 	bool		is_lobound;
 
-	/* get_relattval sets flag&SEL_RIGHT if the var is on the LEFT. */
-	if (flag & SEL_RIGHT)
+	if (varonleft)
 	{
 		var = (Node *) get_leftop((Expr *) clause);
 		is_lobound = !isLTsel;	/* x < something is high bound */
@@ -405,12 +403,12 @@ clause_selectivity(Query *root,
 				 * is equivalent to the clause reln.attribute = 't', so we
 				 * compute the selectivity as if that is what we have.
 				 */
-				s1 = restriction_selectivity(F_EQSEL,
+				s1 = restriction_selectivity(root,
 											 BooleanEqualOperator,
-											 rte->relid,
-											 var->varattno,
-											 BoolGetDatum(true),
-											 SEL_CONSTANT | SEL_RIGHT);
+											 makeList2(var,
+													   MAKEBOOLCONST(true,
+																	 false)),
+											 varRelid);
 			}
 		}
 	}
@@ -486,57 +484,14 @@ clause_selectivity(Query *root,
 		if (is_join_clause)
 		{
 			/* Estimate selectivity for a join clause. */
-			RegProcedure oprjoin = get_oprjoin(opno);
-
-			/*
-			 * if the oprjoin procedure is missing for whatever reason,
-			 * use a selectivity of 0.5
-			 */
-			if (!oprjoin)
-				s1 = (Selectivity) 0.5;
-			else
-			{
-				int			relid1,
-							relid2;
-				AttrNumber	attno1,
-							attno2;
-				Oid			reloid1,
-							reloid2;
-
-				get_rels_atts(clause, &relid1, &attno1, &relid2, &attno2);
-				reloid1 = relid1 ? getrelid(relid1, root->rtable) : InvalidOid;
-				reloid2 = relid2 ? getrelid(relid2, root->rtable) : InvalidOid;
-				s1 = join_selectivity(oprjoin, opno,
-									  reloid1, attno1,
-									  reloid2, attno2);
-			}
+			s1 = join_selectivity(root, opno, 
+								  ((Expr *) clause)->args);
 		}
 		else
 		{
 			/* Estimate selectivity for a restriction clause. */
-			RegProcedure oprrest = get_oprrest(opno);
-
-			/*
-			 * if the oprrest procedure is missing for whatever reason,
-			 * use a selectivity of 0.5
-			 */
-			if (!oprrest)
-				s1 = (Selectivity) 0.5;
-			else
-			{
-				int			relidx;
-				AttrNumber	attno;
-				Datum		constval;
-				int			flag;
-				Oid			reloid;
-
-				get_relattval(clause, varRelid,
-							  &relidx, &attno, &constval, &flag);
-				reloid = relidx ? getrelid(relidx, root->rtable) : InvalidOid;
-				s1 = restriction_selectivity(oprrest, opno,
-											 reloid, attno,
-											 constval, flag);
-			}
+			s1 = restriction_selectivity(root, opno, 
+										 ((Expr *) clause)->args, varRelid);
 		}
 	}
 	else if (is_funcclause(clause))
@@ -555,7 +510,7 @@ clause_selectivity(Query *root,
 		/*
 		 * Just for the moment! FIX ME! - vadim 02/04/98
 		 */
-		s1 = 1.0;
+		s1 = (Selectivity) 0.5;
 	}
 	else if (IsA(clause, RelabelType))
 	{
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index dddca240e9586b4a044513123833a838e3838176..b4379e4b39bc5585cfacefdb89a8f91525acb054 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -42,7 +42,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.73 2001/05/09 23:13:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.74 2001/05/20 20:28:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -773,7 +773,7 @@ estimate_hash_bucketsize(Query *root, Var *var)
 	if (relid == InvalidOid)
 		return 0.1;
 
-	rel = get_base_rel(root, var->varno);
+	rel = find_base_rel(root, var->varno);
 
 	if (rel->tuples <= 0.0 || rel->rows <= 0.0)
 		return 0.1;				/* ensure we can divide below */
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index ca19465c8977abf1d7a513aa59f0c1a1b64cac03..a5f5bb151da0d5b86ba005d865fc2e73c91fabe7 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.104 2001/03/23 04:49:53 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.105 2001/05/20 20:28:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -127,18 +127,15 @@ static Const *string_to_const(const char *str, Oid datatype);
  * consideration in nested-loop joins.
  *
  * 'rel' is the relation for which we want to generate index paths
- * 'indices' is a list of available indexes for 'rel'
  */
 void
-create_index_paths(Query *root,
-				   RelOptInfo *rel,
-				   List *indices)
+create_index_paths(Query *root, RelOptInfo *rel)
 {
 	List	   *restrictinfo_list = rel->baserestrictinfo;
 	List	   *joininfo_list = rel->joininfo;
 	List	   *ilist;
 
-	foreach(ilist, indices)
+	foreach(ilist, rel->indexlist)
 	{
 		IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
 		List	   *restrictclauses;
@@ -1435,10 +1432,10 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
 
 		/*
 		 * Note that we are making a pathnode for a single-scan indexscan;
-		 * therefore, both indexid and indexqual should be single-element
+		 * therefore, both indexinfo and indexqual should be single-element
 		 * lists.
 		 */
-		pathnode->indexid = makeListi1(index->indexoid);
+		pathnode->indexinfo = makeList1(index);
 		pathnode->indexqual = makeList1(indexquals);
 
 		/* We don't actually care what order the index scans in ... */
@@ -2030,7 +2027,6 @@ find_operator(const char *opname, Oid datatype)
 static Datum
 string_to_datum(const char *str, Oid datatype)
 {
-
 	/*
 	 * We cheat a little by assuming that textin() will do for bpchar and
 	 * varchar constants too...
diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c
index 929a977112d13e515b81f12f5aee1d781b79038c..3bde257a37ee930f6822304a4c17c4ea926c7241 100644
--- a/src/backend/optimizer/path/joinrels.c
+++ b/src/backend/optimizer/path/joinrels.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.52 2001/03/22 03:59:35 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.53 2001/05/20 20:28:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -332,7 +332,7 @@ make_rels_by_clauseless_joins(Query *root,
 
 /*
  * make_jointree_rel
- *		Find or build a RelOptInfojoin rel representing a specific
+ *		Find or build a RelOptInfo join rel representing a specific
  *		jointree item.	For JoinExprs, we only consider the construction
  *		path that corresponds exactly to what the user wrote.
  */
@@ -343,7 +343,7 @@ make_jointree_rel(Query *root, Node *jtnode)
 	{
 		int			varno = ((RangeTblRef *) jtnode)->rtindex;
 
-		return get_base_rel(root, varno);
+		return build_base_rel(root, varno);
 	}
 	else if (IsA(jtnode, FromExpr))
 	{
@@ -402,7 +402,7 @@ make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2,
 	 * Find or build the join RelOptInfo, and compute the restrictlist
 	 * that goes with this particular joining.
 	 */
-	joinrel = get_join_rel(root, rel1, rel2, jointype, &restrictlist);
+	joinrel = build_join_rel(root, rel1, rel2, jointype, &restrictlist);
 
 	/*
 	 * Consider paths using each rel as both outer and inner.
diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c
index d4e467c3e04207c1daa84c60f6dfb59eeaaa125b..25cbc3e4fa206cae31741e5b6c05545fdb03f89e 100644
--- a/src/backend/optimizer/path/orindxpath.c
+++ b/src/backend/optimizer/path/orindxpath.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.42 2001/01/24 19:42:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.43 2001/05/20 20:28:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,8 +26,8 @@ static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
 						  IndexPath *pathnode);
 static void best_or_subclause_index(Query *root, RelOptInfo *rel,
 						Expr *subclause, List *indices,
+						IndexOptInfo **retIndexInfo,
 						List **retIndexQual,
-						Oid *retIndexid,
 						Cost *retStartupCost,
 						Cost *retTotalCost);
 
@@ -122,14 +122,14 @@ create_or_index_paths(Query *root,
  *	  of an 'or' clause and the cost of scanning a relation using these
  *	  indices.	The cost is the sum of the individual index costs, since
  *	  the executor will perform a scan for each subclause of the 'or'.
+ *	  Returns a list of IndexOptInfo nodes, one per scan.
  *
- * This routine also creates the indexqual and indexid lists that will
- * be needed by the executor.  The indexqual list has one entry for each
- * scan of the base rel, which is a sublist of indexqual conditions to
- * apply in that scan.	The implicit semantics are AND across each sublist
- * of quals, and OR across the toplevel list (note that the executor
- * takes care not to return any single tuple more than once).  The indexid
- * list gives the OID of the index to be used in each scan.
+ * This routine also creates the indexqual list that will be needed by
+ * the executor.  The indexqual list has one entry for each scan of the base
+ * rel, which is a sublist of indexqual conditions to apply in that scan.
+ * The implicit semantics are AND across each sublist of quals, and OR across
+ * the toplevel list (note that the executor takes care not to return any
+ * single tuple more than once).
  *
  * 'rel' is the node of the relation on which the indexes are defined
  * 'subclauses' are the subclauses of the 'or' clause
@@ -138,9 +138,9 @@ create_or_index_paths(Query *root,
  * 'pathnode' is the IndexPath node being built.
  *
  * Results are returned by setting these fields of the passed pathnode:
+ * 'indexinfo' gets a list of the index IndexOptInfo nodes, one per scan
  * 'indexqual' gets the constructed indexquals for the path (a list
  *		of sublists of clauses, one sublist per scan of the base rel)
- * 'indexid' gets a list of the index OIDs for each scan of the rel
  * 'startup_cost' and 'total_cost' get the complete path costs.
  *
  * 'startup_cost' is the startup cost for the first index scan only;
@@ -161,28 +161,28 @@ best_or_subclause_indices(Query *root,
 {
 	List	   *slist;
 
+	pathnode->indexinfo = NIL;
 	pathnode->indexqual = NIL;
-	pathnode->indexid = NIL;
 	pathnode->path.startup_cost = 0;
 	pathnode->path.total_cost = 0;
 
 	foreach(slist, subclauses)
 	{
 		Expr	   *subclause = lfirst(slist);
+		IndexOptInfo *best_indexinfo;
 		List	   *best_indexqual;
-		Oid			best_indexid;
 		Cost		best_startup_cost;
 		Cost		best_total_cost;
 
 		best_or_subclause_index(root, rel, subclause, lfirst(indices),
-								&best_indexqual, &best_indexid,
+								&best_indexinfo, &best_indexqual,
 								&best_startup_cost, &best_total_cost);
 
-		Assert(best_indexid != InvalidOid);
+		Assert(best_indexinfo != NULL);
 
+		pathnode->indexinfo = lappend(pathnode->indexinfo, best_indexinfo);
 		pathnode->indexqual = lappend(pathnode->indexqual, best_indexqual);
-		pathnode->indexid = lappendi(pathnode->indexid, best_indexid);
-		if (slist == subclauses)/* first scan? */
+		if (slist == subclauses) /* first scan? */
 			pathnode->path.startup_cost = best_startup_cost;
 		pathnode->path.total_cost += best_total_cost;
 
@@ -199,8 +199,8 @@ best_or_subclause_indices(Query *root,
  * 'rel' is the node of the relation on which the index is defined
  * 'subclause' is the OR subclause being considered
  * 'indices' is a list of IndexOptInfo nodes that match the subclause
+ * '*retIndexInfo' gets the IndexOptInfo of the best index
  * '*retIndexQual' gets a list of the indexqual conditions for the best index
- * '*retIndexid' gets the OID of 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
  */
@@ -209,8 +209,8 @@ best_or_subclause_index(Query *root,
 						RelOptInfo *rel,
 						Expr *subclause,
 						List *indices,
+						IndexOptInfo **retIndexInfo, /* return value */
 						List **retIndexQual,	/* return value */
-						Oid *retIndexid,		/* return value */
 						Cost *retStartupCost,	/* return value */
 						Cost *retTotalCost)		/* return value */
 {
@@ -218,8 +218,8 @@ best_or_subclause_index(Query *root,
 	List	   *ilist;
 
 	/* if we don't match anything, return zeros */
+	*retIndexInfo = NULL;
 	*retIndexQual = NIL;
-	*retIndexid = InvalidOid;
 	*retStartupCost = 0;
 	*retTotalCost = 0;
 
@@ -238,8 +238,8 @@ best_or_subclause_index(Query *root,
 
 		if (first_time || subclause_path.total_cost < *retTotalCost)
 		{
+			*retIndexInfo = index;
 			*retIndexQual = indexqual;
-			*retIndexid = index->indexoid;
 			*retStartupCost = subclause_path.startup_cost;
 			*retTotalCost = subclause_path.total_cost;
 			first_time = false;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 2d264c46881730ba4ace2ade745fe6942c9d49fb..81e7fec0427626377d3d6591c433abc45a55dbff 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.105 2001/05/07 00:43:20 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.106 2001/05/20 20:28:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,7 +18,6 @@
 
 #include <sys/types.h>
 
-#include "catalog/pg_index.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
@@ -27,6 +26,7 @@
 #include "optimizer/planmain.h"
 #include "optimizer/restrictinfo.h"
 #include "optimizer/tlist.h"
+#include "optimizer/var.h"
 #include "parser/parse_expr.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
@@ -56,11 +56,11 @@ static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist,
 					 Plan *outer_plan, List *outer_tlist,
 					 Plan *inner_plan, List *inner_tlist);
 static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
-static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
-					 Form_pg_index index);
+static List *fix_indxqual_sublist(List *indexqual, int baserelid,
+								  IndexOptInfo *index);
 static Node *fix_indxqual_operand(Node *node, int baserelid,
-					 Form_pg_index index,
-					 Oid *opclass);
+								  IndexOptInfo *index,
+								  Oid *opclass);
 static List *switch_outer(List *clauses);
 static void copy_path_costsize(Plan *dest, Path *src);
 static void copy_plan_costsize(Plan *dest, Plan *src);
@@ -365,7 +365,7 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
  * 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
- * and indexid lists must have the same length, ie, the number of scans
+ * 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
  * scan.
@@ -380,9 +380,10 @@ create_indexscan_plan(Query *root,
 	Index		baserelid;
 	List	   *qpqual;
 	List	   *fixed_indxqual;
-	List	   *ixid;
+	List	   *indexids;
+	List	   *ixinfo;
 	IndexScan  *scan_plan;
-	bool		lossy = false;
+	bool		lossy;
 
 	/* there should be exactly one base rel involved... */
 	Assert(length(best_path->path.parent->relids) == 1);
@@ -390,25 +391,18 @@ create_indexscan_plan(Query *root,
 
 	baserelid = lfirsti(best_path->path.parent->relids);
 
-	/* check to see if any of the indices are lossy */
-	foreach(ixid, best_path->indexid)
+	/*
+	 * Build list of index OIDs, and check to see if any of the indices
+	 * are lossy.
+	 */
+	indexids = NIL;
+	lossy = false;
+	foreach(ixinfo, best_path->indexinfo)
 	{
-		HeapTuple	indexTuple;
-		Form_pg_index index;
-
-		indexTuple = SearchSysCache(INDEXRELID,
-									ObjectIdGetDatum(lfirsti(ixid)),
-									0, 0, 0);
-		if (!HeapTupleIsValid(indexTuple))
-			elog(ERROR, "create_plan: index %u not found", lfirsti(ixid));
-		index = (Form_pg_index) GETSTRUCT(indexTuple);
-		if (index->indislossy)
-		{
-			lossy = true;
-			ReleaseSysCache(indexTuple);
-			break;
-		}
-		ReleaseSysCache(indexTuple);
+		IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
+
+		indexids = lappendi(indexids, index->indexoid);
+		lossy |= index->lossy;
 	}
 
 	/*
@@ -471,7 +465,7 @@ create_indexscan_plan(Query *root,
 	scan_plan = make_indexscan(tlist,
 							   qpqual,
 							   baserelid,
-							   best_path->indexid,
+							   indexids,
 							   fixed_indxqual,
 							   indxqual,
 							   best_path->indexscandir);
@@ -895,45 +889,19 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path)
 {
 	List	   *fixed_quals = NIL;
 	int			baserelid = lfirsti(index_path->path.parent->relids);
-	List	   *indexids = index_path->indexid;
+	List	   *ixinfo = index_path->indexinfo;
 	List	   *i;
 
 	foreach(i, indexquals)
 	{
 		List	   *indexqual = lfirst(i);
-		Oid			indexid = lfirsti(indexids);
-		HeapTuple	indexTuple;
-		Oid			relam;
-		Form_pg_index index;
-
-		/* Get the relam from the index's pg_class entry */
-		indexTuple = SearchSysCache(RELOID,
-									ObjectIdGetDatum(indexid),
-									0, 0, 0);
-		if (!HeapTupleIsValid(indexTuple))
-			elog(ERROR, "fix_indxqual_references: index %u not found in pg_class",
-				 indexid);
-		relam = ((Form_pg_class) GETSTRUCT(indexTuple))->relam;
-		ReleaseSysCache(indexTuple);
-
-		/* Need the index's pg_index entry for other stuff */
-		indexTuple = SearchSysCache(INDEXRELID,
-									ObjectIdGetDatum(indexid),
-									0, 0, 0);
-		if (!HeapTupleIsValid(indexTuple))
-			elog(ERROR, "fix_indxqual_references: index %u not found in pg_index",
-				 indexid);
-		index = (Form_pg_index) GETSTRUCT(indexTuple);
+		IndexOptInfo *index = (IndexOptInfo *) lfirst(ixinfo);
 
 		fixed_quals = lappend(fixed_quals,
 							  fix_indxqual_sublist(indexqual,
 												   baserelid,
-												   relam,
 												   index));
-
-		ReleaseSysCache(indexTuple);
-
-		indexids = lnext(indexids);
+		ixinfo = lnext(ixinfo);
 	}
 	return fixed_quals;
 }
@@ -946,8 +914,7 @@ fix_indxqual_references(List *indexquals, IndexPath *index_path)
  * of the clause.)	Also change the operator if necessary.
  */
 static List *
-fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
-					 Form_pg_index index)
+fix_indxqual_sublist(List *indexqual, int baserelid, IndexOptInfo *index)
 {
 	List	   *fixed_qual = NIL;
 	List	   *i;
@@ -955,26 +922,14 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
 	foreach(i, indexqual)
 	{
 		Expr	   *clause = (Expr *) lfirst(i);
-		int			relid;
-		AttrNumber	attno;
-		Datum		constval;
-		int			flag;
 		Expr	   *newclause;
+		List	   *leftvarnos;
 		Oid			opclass,
 					newopno;
 
-		if (!is_opclause((Node *) clause) ||
-			length(clause->args) != 2)
+		if (!is_opclause((Node *) clause) || length(clause->args) != 2)
 			elog(ERROR, "fix_indxqual_sublist: indexqual clause is not binary opclause");
 
-		/*
-		 * Which side is the indexkey on?
-		 *
-		 * get_relattval sets flag&SEL_RIGHT if the indexkey is on the LEFT.
-		 */
-		get_relattval((Node *) clause, baserelid,
-					  &relid, &attno, &constval, &flag);
-
 		/*
 		 * Make a copy that will become the fixed clause.
 		 *
@@ -984,9 +939,15 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
 		 */
 		newclause = (Expr *) copyObject((Node *) clause);
 
-		/* If the indexkey is on the right, commute the clause. */
-		if ((flag & SEL_RIGHT) == 0)
+		/*
+		 * Check to see if the indexkey is on the right; if so, commute
+		 * the clause.  The indexkey should be the side that refers to
+		 * (only) the base relation.
+		 */
+		leftvarnos = pull_varnos((Node *) lfirst(newclause->args));
+		if (length(leftvarnos) != 1 || lfirsti(leftvarnos) != baserelid)
 			CommuteClause(newclause);
+		freeList(leftvarnos);
 
 		/*
 		 * Now, determine which index attribute this is, change the
@@ -1002,7 +963,7 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
 		 * is merely binary-compatible with the index.	This shouldn't
 		 * fail, since indxpath.c found it before...
 		 */
-		newopno = indexable_operator(newclause, opclass, relam, true);
+		newopno = indexable_operator(newclause, opclass, index->relam, true);
 		if (newopno == InvalidOid)
 			elog(ERROR, "fix_indxqual_sublist: failed to find substitute op");
 		((Oper *) newclause->oper)->opno = newopno;
@@ -1013,7 +974,7 @@ fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
 }
 
 static Node *
-fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
+fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index,
 					 Oid *opclass)
 {
 
@@ -1033,27 +994,29 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
 	if (IsA(node, Var))
 	{
 		/* If it's a var, find which index key position it occupies */
+		Assert(index->indproc == InvalidOid);
+
 		if (((Var *) node)->varno == baserelid)
 		{
 			int			varatt = ((Var *) node)->varattno;
 			int			pos;
 
-			for (pos = 0; pos < INDEX_MAX_KEYS; pos++)
+			for (pos = 0; pos < index->nkeys; pos++)
 			{
-				if (index->indkey[pos] == varatt)
+				if (index->indexkeys[pos] == varatt)
 				{
 					Node	   *newnode = copyObject(node);
 
 					((Var *) newnode)->varattno = pos + 1;
 					/* return the correct opclass, too */
-					*opclass = index->indclass[pos];
+					*opclass = index->classlist[pos];
 					return newnode;
 				}
 			}
 		}
 
 		/*
-		 * Oops, this Var isn't the indexkey!
+		 * Oops, this Var isn't an indexkey!
 		 */
 		elog(ERROR, "fix_indxqual_operand: var is not index attribute");
 	}
@@ -1063,11 +1026,11 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
 	 * Since we currently only support single-column functional indexes,
 	 * the returned varattno must be 1.
 	 */
+	Assert(index->indproc != InvalidOid);
+	Assert(is_funcclause(node)); /* not a very thorough check, but easy */
 
-	Assert(is_funcclause(node));/* not a very thorough check, but easy */
-
-	/* indclass[0] is the only class of a functional index */
-	*opclass = index->indclass[0];
+	/* classlist[0] is the only class of a functional index */
+	*opclass = index->classlist[0];
 
 	return (Node *) makeVar(baserelid, 1, exprType(node), -1, 0);
 }
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c
index c62fd5ecd7da51e48acc7ec89d54141b40c0d74b..3b3c761bca670f7548ab67f1e2887bf30be4a986 100644
--- a/src/backend/optimizer/plan/initsplan.c
+++ b/src/backend/optimizer/plan/initsplan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.61 2001/05/14 20:25:00 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.62 2001/05/20 20:28:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -73,8 +73,8 @@ build_base_rel_tlists(Query *root, List *tlist)
 /*
  * add_vars_to_targetlist
  *	  For each variable appearing in the list, add it to the relation's
- *	  targetlist if not already present.  Rel nodes will also be created
- *	  if not already present.
+ *	  targetlist if not already present.  Corresponding base rel nodes
+ *	  will be created if not already present.
  */
 static void
 add_vars_to_targetlist(Query *root, List *vars)
@@ -84,7 +84,7 @@ add_vars_to_targetlist(Query *root, List *vars)
 	foreach(temp, vars)
 	{
 		Var		   *var = (Var *) lfirst(temp);
-		RelOptInfo *rel = get_base_rel(root, var->varno);
+		RelOptInfo *rel = build_base_rel(root, var->varno);
 
 		add_var_to_tlist(rel, var);
 	}
@@ -120,8 +120,8 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
 	{
 		int			varno = ((RangeTblRef *) jtnode)->rtindex;
 
-		/* This call to get_base_rel does the primary work... */
-		RelOptInfo *rel = get_base_rel(root, varno);
+		/* This call to build_base_rel does the primary work... */
+		RelOptInfo *rel = build_base_rel(root, varno);
 
 		result = makeList1(rel);
 	}
@@ -299,7 +299,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
 	foreach(relid, rels)
 	{
 		int			relno = lfirsti(relid);
-		RelOptInfo *rel = get_base_rel(root, relno);
+		RelOptInfo *rel = build_base_rel(root, relno);
 
 		/*
 		 * Since we do this bottom-up, any outer-rels previously marked
@@ -422,7 +422,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
 		can_be_equijoin = true;
 		foreach(relid, relids)
 		{
-			RelOptInfo *rel = get_base_rel(root, lfirsti(relid));
+			RelOptInfo *rel = build_base_rel(root, lfirsti(relid));
 
 			if (rel->outerjoinset &&
 				!is_subseti(rel->outerjoinset, relids))
@@ -454,12 +454,11 @@ distribute_qual_to_rels(Query *root, Node *clause,
 
 	if (length(relids) == 1)
 	{
-
 		/*
 		 * There is only one relation participating in 'clause', so
 		 * 'clause' is a restriction clause for that relation.
 		 */
-		RelOptInfo *rel = get_base_rel(root, lfirsti(relids));
+		RelOptInfo *rel = build_base_rel(root, lfirsti(relids));
 
 		rel->baserestrictinfo = lappend(rel->baserestrictinfo,
 										restrictinfo);
@@ -564,7 +563,7 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
 		 * Find or make the joininfo node for this combination of rels,
 		 * and add the restrictinfo node to it.
 		 */
-		joininfo = find_joininfo_node(get_base_rel(root, cur_relid),
+		joininfo = find_joininfo_node(build_base_rel(root, cur_relid),
 									  unjoined_relids);
 		joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
 											   restrictinfo);
@@ -609,8 +608,11 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
 	 * If both vars belong to same rel, we need to look at that rel's
 	 * baserestrictinfo list.  If different rels, each will have a
 	 * joininfo node for the other, and we can scan either list.
+	 *
+	 * All baserel entries should already exist at this point, so use
+	 * find_base_rel not build_base_rel.
 	 */
-	rel1 = get_base_rel(root, irel1);
+	rel1 = find_base_rel(root, irel1);
 	if (irel1 == irel2)
 		restrictlist = rel1->baserestrictinfo;
 	else
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index b2b362e84a5299b16a3792966b1cb6897e310ad5..2f52e694d1310a31eb417abb295da6c441980f3e 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.64 2001/03/22 03:59:37 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.65 2001/05/20 20:28:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -194,6 +194,7 @@ subplanner(Query *root,
 	 * construction.
 	 */
 	root->base_rel_list = NIL;
+	root->other_rel_list = NIL;
 	root->join_rel_list = NIL;
 	root->equi_key_list = NIL;
 
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 0aba4808c160f3bf5ba3a9cc3fd2c6cf26fa2fa3..fbed3d6d092e5e53cadb6e94430bbc268e3ebdbe 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.106 2001/05/07 00:43:21 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.107 2001/05/20 20:28:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -206,7 +206,8 @@ subquery_planner(Query *parse, double tuple_fraction)
 	 * grouping_planner.
 	 */
 	if (parse->resultRelation &&
-	(lst = expand_inherted_rtentry(parse, parse->resultRelation)) != NIL)
+		(lst = expand_inherted_rtentry(parse, parse->resultRelation, false))
+		!= NIL)
 		plan = inheritance_planner(parse, lst);
 	else
 		plan = grouping_planner(parse, tuple_fraction);
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index ede4159d9707629729b5dffbc32f241f48629e72..42cc47fa4ac3dc80a4a1b47059884fe03d4b77c2 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.63 2001/05/07 00:43:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.64 2001/05/20 20:28:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -515,6 +515,11 @@ find_all_inheritors(Oid parentrel)
  *		whole inheritance set (parent and children).
  *		If not, return NIL.
  *
+ * When dup_parent is false, the initially given RT index is part of the
+ * returned list (if any).  When dup_parent is true, the given RT index
+ * is *not* in the returned list; a duplicate RTE will be made for the
+ * parent table.
+ *
  * A childless table is never considered to be an inheritance set; therefore
  * the result will never be a one-element list.  It'll be either empty
  * or have two or more elements.
@@ -525,7 +530,7 @@ find_all_inheritors(Oid parentrel)
  * for the case of an inherited UPDATE/DELETE target relation.
  */
 List *
-expand_inherted_rtentry(Query *parse, Index rti)
+expand_inherted_rtentry(Query *parse, Index rti, bool dup_parent)
 {
 	RangeTblEntry *rte = rt_fetch(rti, parse->rtable);
 	Oid			parentOID = rte->relid;
@@ -544,7 +549,6 @@ expand_inherted_rtentry(Query *parse, Index rti)
 		return NIL;
 	/* Scan for all members of inheritance set */
 	inhOIDs = find_all_inheritors(parentOID);
-
 	/*
 	 * Check that there's at least one descendant, else treat as no-child
 	 * case.  This could happen despite above has_subclass() check, if
@@ -553,15 +557,19 @@ expand_inherted_rtentry(Query *parse, Index rti)
 	if (lnext(inhOIDs) == NIL)
 		return NIL;
 	/* OK, it's an inheritance set; expand it */
-	inhRTIs = makeListi1(rti);
+	if (dup_parent)
+		inhRTIs = NIL;
+	else
+		inhRTIs = makeListi1(rti); /* include original RTE in result */
+
 	foreach(l, inhOIDs)
 	{
 		Oid			childOID = (Oid) lfirsti(l);
 		RangeTblEntry *childrte;
 		Index		childRTindex;
 
-		/* parent will be in the list too, so ignore it */
-		if (childOID == parentOID)
+		/* parent will be in the list too; skip it if not dup requested */
+		if (childOID == parentOID && !dup_parent)
 			continue;
 
 		/*
@@ -578,6 +586,7 @@ expand_inherted_rtentry(Query *parse, Index rti)
 
 		inhRTIs = lappendi(inhRTIs, childRTindex);
 	}
+
 	return inhRTIs;
 }
 
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 8bd6ef6f68b23e650f24cf7262713c68314c183f..e0cc97e3a1dc3314ef104e2bc34b9bac9aadc97d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.84 2001/03/27 17:12:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.85 2001/05/20 20:28:19 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -46,7 +46,6 @@ static bool pull_subplans_walker(Node *node, List **listptr);
 static bool check_subplans_for_ungrouped_vars_walker(Node *node,
 										 Query *context);
 static bool contain_noncachable_functions_walker(Node *node, void *context);
-static int	is_single_func(Node *node);
 static Node *eval_const_expressions_mutator(Node *node, void *context);
 static Expr *simplify_op_or_func(Expr *expr, List *args);
 
@@ -797,202 +796,6 @@ NumRelids(Node *clause)
 	return result;
 }
 
-/*
- * get_relattval
- *		Extract information from a restriction or join clause for
- *		selectivity estimation.  The inputs are an expression
- *		and a relation number (which can be 0 if we don't care which
- *		relation is used; that'd normally be the case for restriction
- *		clauses, where the caller already knows that only one relation
- *		is referenced in the clause).  The routine checks that the
- *		expression is of the form (var op something) or (something op var)
- *		where the var is an attribute of the specified relation, or
- *		a function of a var of the specified relation.	If so, it
- *		returns the following info:
- *			the found relation number (same as targetrelid unless that is 0)
- *			the found var number (or InvalidAttrNumber if a function)
- *			if the "something" is a constant, the value of the constant
- *			flags indicating whether a constant was found, and on which side.
- *		Default values are returned if the expression is too complicated,
- *		specifically 0 for the relid and attno, 0 for the constant value.
- *
- *		Note that negative attno values are *not* invalid, but represent
- *		system attributes such as OID.	It's sufficient to check for relid=0
- *		to determine whether the routine succeeded.
- */
-void
-get_relattval(Node *clause,
-			  int targetrelid,
-			  int *relid,
-			  AttrNumber *attno,
-			  Datum *constval,
-			  int *flag)
-{
-	Var		   *left,
-			   *right,
-			   *other;
-	int			funcvarno;
-
-	/* Careful; the passed clause might not be a binary operator at all */
-
-	if (!is_opclause(clause))
-		goto default_results;
-
-	left = get_leftop((Expr *) clause);
-	right = get_rightop((Expr *) clause);
-
-	if (!right)
-		goto default_results;
-
-	/* Ignore any binary-compatible relabeling */
-
-	if (IsA(left, RelabelType))
-		left = (Var *) ((RelabelType *) left)->arg;
-	if (IsA(right, RelabelType))
-		right = (Var *) ((RelabelType *) right)->arg;
-
-	/* First look for the var or func */
-
-	if (IsA(left, Var) &&
-		(targetrelid == 0 || targetrelid == left->varno))
-	{
-		*relid = left->varno;
-		*attno = left->varattno;
-		*flag = SEL_RIGHT;
-	}
-	else if (IsA(right, Var) &&
-			 (targetrelid == 0 || targetrelid == right->varno))
-	{
-		*relid = right->varno;
-		*attno = right->varattno;
-		*flag = 0;
-	}
-	else if ((funcvarno = is_single_func((Node *) left)) != 0 &&
-			 (targetrelid == 0 || targetrelid == funcvarno))
-	{
-		*relid = funcvarno;
-		*attno = InvalidAttrNumber;
-		*flag = SEL_RIGHT;
-	}
-	else if ((funcvarno = is_single_func((Node *) right)) != 0 &&
-			 (targetrelid == 0 || targetrelid == funcvarno))
-	{
-		*relid = funcvarno;
-		*attno = InvalidAttrNumber;
-		*flag = 0;
-	}
-	else
-	{
-		/* Duh, it's too complicated for me... */
-default_results:
-		*relid = 0;
-		*attno = 0;
-		*constval = 0;
-		*flag = 0;
-		return;
-	}
-
-	/* OK, we identified the var or func; now look at the other side */
-
-	other = (*flag == 0) ? left : right;
-
-	if (IsA(other, Const) &&
-		!((Const *) other)->constisnull)
-	{
-		*constval = ((Const *) other)->constvalue;
-		*flag |= SEL_CONSTANT;
-	}
-	else
-		*constval = 0;
-}
-
-/*
- * is_single_func
- *	 If the given expression is a function of a single relation,
- *	 return the relation number; else return 0
- */
-static int
-is_single_func(Node *node)
-{
-	if (is_funcclause(node))
-	{
-		List	   *varnos = pull_varnos(node);
-
-		if (length(varnos) == 1)
-		{
-			int			funcvarno = lfirsti(varnos);
-
-			freeList(varnos);
-			return funcvarno;
-		}
-		freeList(varnos);
-	}
-	return 0;
-}
-
-/*
- * get_rels_atts
- *
- * Returns the info
- *				( relid1 attno1 relid2 attno2 )
- *		for a joinclause.
- *
- * If the clause is not of the form (var op var) or if any of the vars
- * refer to nested attributes, then zeroes are returned.
- */
-void
-get_rels_atts(Node *clause,
-			  int *relid1,
-			  AttrNumber *attno1,
-			  int *relid2,
-			  AttrNumber *attno2)
-{
-	/* set default values */
-	*relid1 = 0;
-	*attno1 = 0;
-	*relid2 = 0;
-	*attno2 = 0;
-
-	if (is_opclause(clause))
-	{
-		Var		   *left = get_leftop((Expr *) clause);
-		Var		   *right = get_rightop((Expr *) clause);
-
-		if (left && right)
-		{
-			int			funcvarno;
-
-			/* Ignore any binary-compatible relabeling */
-			if (IsA(left, RelabelType))
-				left = (Var *) ((RelabelType *) left)->arg;
-			if (IsA(right, RelabelType))
-				right = (Var *) ((RelabelType *) right)->arg;
-
-			if (IsA(left, Var))
-			{
-				*relid1 = left->varno;
-				*attno1 = left->varattno;
-			}
-			else if ((funcvarno = is_single_func((Node *) left)) != 0)
-			{
-				*relid1 = funcvarno;
-				*attno1 = InvalidAttrNumber;
-			}
-
-			if (IsA(right, Var))
-			{
-				*relid2 = right->varno;
-				*attno2 = right->varattno;
-			}
-			else if ((funcvarno = is_single_func((Node *) right)) != 0)
-			{
-				*relid2 = funcvarno;
-				*attno2 = InvalidAttrNumber;
-			}
-		}
-	}
-}
-
 /*--------------------
  * CommuteClause: commute a binary operator clause
  *
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 407c132b4f7a6388b093806fd3eb01286906e084..801b328d81726a0f3a2315c4d9b299d104ccf9a6 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.72 2001/05/07 00:43:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.73 2001/05/20 20:28:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -346,9 +346,9 @@ create_index_path(Query *root,
 
 	/*
 	 * We are making a pathnode for a single-scan indexscan; therefore,
-	 * both indexid and indexqual should be single-element lists.
+	 * both indexinfo and indexqual should be single-element lists.
 	 */
-	pathnode->indexid = makeListi1(index->indexoid);
+	pathnode->indexinfo = makeList1(index);
 	pathnode->indexqual = makeList1(indexquals);
 
 	pathnode->indexscandir = indexscandir;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index ee3523553e8693ac1b7762d01ebbabc3697a4d7a..749390a4d2ddbc433f65f40c84ca8ee4bed5510d 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.65 2001/05/07 00:43:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.66 2001/05/20 20:28:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,9 +23,12 @@
 #include "catalog/pg_amop.h"
 #include "catalog/pg_inherits.h"
 #include "catalog/pg_index.h"
+#include "optimizer/clauses.h"
 #include "optimizer/plancat.h"
+#include "parser/parsetree.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
 #include "utils/relcache.h"
 #include "utils/syscache.h"
 #include "catalog/catalog.h"
@@ -33,7 +36,7 @@
 
 
 /*
- * relation_info -
+ * get_relation_info -
  *	  Retrieves catalog information for a given relation.
  *	  Given the Oid of the relation, return the following info:
  *				whether the relation has secondary indices
@@ -41,8 +44,8 @@
  *				number of tuples
  */
 void
-relation_info(Oid relationObjectId,
-			  bool *hasindex, long *pages, double *tuples)
+get_relation_info(Oid relationObjectId,
+				  bool *hasindex, long *pages, double *tuples)
 {
 	HeapTuple	relationTuple;
 	Form_pg_class relation;
@@ -51,16 +54,19 @@ relation_info(Oid relationObjectId,
 								   ObjectIdGetDatum(relationObjectId),
 								   0, 0, 0);
 	if (!HeapTupleIsValid(relationTuple))
-		elog(ERROR, "relation_info: Relation %u not found",
+		elog(ERROR, "get_relation_info: Relation %u not found",
 			 relationObjectId);
 	relation = (Form_pg_class) GETSTRUCT(relationTuple);
 
-	if (IsIgnoringSystemIndexes() && IsSystemRelationName(NameStr(relation->relname)))
+	if (IsIgnoringSystemIndexes() &&
+		IsSystemRelationName(NameStr(relation->relname)))
 		*hasindex = false;
 	else
-		*hasindex = (relation->relhasindex) ? true : false;
+		*hasindex = relation->relhasindex;
+
 	*pages = relation->relpages;
 	*tuples = relation->reltuples;
+
 	ReleaseSysCache(relationTuple);
 }
 
@@ -110,8 +116,8 @@ find_secondary_indexes(Oid relationObjectId)
 		info = makeNode(IndexOptInfo);
 
 		/*
-		 * Need to make these arrays large enough to be sure there is a
-		 * terminating 0 at the end of each one.
+		 * Need to make these arrays large enough to be sure there is
+		 * room for a terminating 0 at the end of each one.
 		 */
 		info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
 		info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS + 1));
@@ -131,14 +137,26 @@ find_secondary_indexes(Oid relationObjectId)
 		}
 		else
 			info->indpred = NIL;
+		info->unique = index->indisunique;
 		info->lossy = index->indislossy;
 
 		for (i = 0; i < INDEX_MAX_KEYS; i++)
-			info->indexkeys[i] = index->indkey[i];
-		info->indexkeys[INDEX_MAX_KEYS] = 0;
-		for (i = 0; i < INDEX_MAX_KEYS; i++)
+		{
+			if (index->indclass[i] == (Oid) 0)
+				break;
 			info->classlist[i] = index->indclass[i];
-		info->classlist[INDEX_MAX_KEYS] = (Oid) 0;
+		}
+		info->classlist[i] = (Oid) 0;
+		info->ncolumns = i;
+
+		for (i = 0; i < INDEX_MAX_KEYS; i++)
+		{
+			if (index->indkey[i] == 0)
+				break;
+			info->indexkeys[i] = index->indkey[i];
+		}
+		info->indexkeys[i] = 0;
+		info->nkeys = i;
 
 		/* Extract info from the relation descriptor for the index */
 		indexRelation = index_open(index->indexrelid);
@@ -156,7 +174,7 @@ find_secondary_indexes(Oid relationObjectId)
 		MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1));
 		if (amorderstrategy != 0)
 		{
-			for (i = 0; i < INDEX_MAX_KEYS && index->indclass[i]; i++)
+			for (i = 0; i < info->ncolumns; i++)
 			{
 				HeapTuple	amopTuple;
 				Form_pg_amop amop;
@@ -193,30 +211,34 @@ find_secondary_indexes(Oid relationObjectId)
 /*
  * restriction_selectivity
  *
- * Returns the selectivity of a specified operator.
+ * Returns the selectivity of a specified restriction operator clause.
  * This code executes registered procedures stored in the
  * operator relation, by calling the function manager.
  *
- * XXX The assumption in the selectivity procedures is that if the
- *		relation OIDs or attribute numbers are 0, then the clause
- *		isn't of the form (op var const).
+ * varRelid is either 0 or a rangetable index.  See clause_selectivity()
+ * for details about its meaning.
  */
 Selectivity
-restriction_selectivity(Oid functionObjectId,
-						Oid operatorObjectId,
-						Oid relationObjectId,
-						AttrNumber attributeNumber,
-						Datum constValue,
-						int constFlag)
+restriction_selectivity(Query *root,
+						Oid operator,
+						List *args,
+						int varRelid)
 {
+	RegProcedure oprrest = get_oprrest(operator);
 	float8		result;
 
-	result = DatumGetFloat8(OidFunctionCall5(functionObjectId,
-									  ObjectIdGetDatum(operatorObjectId),
-									  ObjectIdGetDatum(relationObjectId),
-										  Int16GetDatum(attributeNumber),
-											 constValue,
-											 Int32GetDatum(constFlag)));
+	/*
+	 * if the oprrest procedure is missing for whatever reason,
+	 * use a selectivity of 0.5
+	 */
+	if (!oprrest)
+		return (Selectivity) 0.5;
+
+	result = DatumGetFloat8(OidFunctionCall4(oprrest,
+											 PointerGetDatum(root),
+											 ObjectIdGetDatum(operator),
+											 PointerGetDatum(args),
+											 Int32GetDatum(varRelid)));
 
 	if (result < 0.0 || result > 1.0)
 		elog(ERROR, "restriction_selectivity: bad value %f", result);
@@ -227,29 +249,29 @@ restriction_selectivity(Oid functionObjectId,
 /*
  * join_selectivity
  *
- * Returns the selectivity of an operator, given the join clause
- * information.
- *
- * XXX The assumption in the selectivity procedures is that if the
- *		relation OIDs or attribute numbers are 0, then the clause
- *		isn't of the form (op var var).
+ * Returns the selectivity of a specified join operator clause.
+ * This code executes registered procedures stored in the
+ * operator relation, by calling the function manager.
  */
 Selectivity
-join_selectivity(Oid functionObjectId,
-				 Oid operatorObjectId,
-				 Oid relationObjectId1,
-				 AttrNumber attributeNumber1,
-				 Oid relationObjectId2,
-				 AttrNumber attributeNumber2)
+join_selectivity(Query *root,
+				 Oid operator,
+				 List *args)
 {
+	RegProcedure oprjoin = get_oprjoin(operator);
 	float8		result;
 
-	result = DatumGetFloat8(OidFunctionCall5(functionObjectId,
-									  ObjectIdGetDatum(operatorObjectId),
-									 ObjectIdGetDatum(relationObjectId1),
-										 Int16GetDatum(attributeNumber1),
-									 ObjectIdGetDatum(relationObjectId2),
-									   Int16GetDatum(attributeNumber2)));
+	/*
+	 * if the oprjoin procedure is missing for whatever reason,
+	 * use a selectivity of 0.5
+	 */
+	if (!oprjoin)
+		return (Selectivity) 0.5;
+
+	result = DatumGetFloat8(OidFunctionCall3(oprjoin,
+											 PointerGetDatum(root),
+											 ObjectIdGetDatum(operator),
+											 PointerGetDatum(args)));
 
 	if (result < 0.0 || result > 1.0)
 		elog(ERROR, "join_selectivity: bad value %f", result);
@@ -330,3 +352,36 @@ has_subclass(Oid relationId)
 	ReleaseSysCache(tuple);
 	return result;
 }
+
+/*
+ * has_unique_index
+ *
+ * Detect whether there is a unique index on the specified attribute
+ * of the specified relation, thus allowing us to conclude that all
+ * the (non-null) values of the attribute are distinct.
+ */
+bool
+has_unique_index(RelOptInfo *rel, AttrNumber attno)
+{
+	List	   *ilist;
+
+	foreach(ilist, rel->indexlist)
+	{
+		IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
+
+		/*
+		 * Note: ignore functional, partial, or lossy indexes, since they
+		 * don't allow us to conclude that all attr values are distinct.
+		 * Also, a multicolumn unique index doesn't allow us to conclude
+		 * that just the specified attr is unique.
+		 */
+		if (index->unique &&
+			index->nkeys == 1 &&
+			index->indexkeys[0] == attno &&
+			index->indproc == InvalidOid &&
+			index->indpred == NIL &&
+			!index->lossy)
+			return true;
+	}
+	return false;
+}
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index b4764ab6f8cd08295a6913ccf680c9d7c5cb9a47..86d923116a414155a829b525ed4cc510271c1582 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.32 2001/02/16 00:03:08 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.33 2001/05/20 20:28:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@
 #include "parser/parsetree.h"
 
 
+static RelOptInfo *make_base_rel(Query *root, int relid);
 static List *new_join_tlist(List *tlist, int first_resdomno);
 static List *build_joinrel_restrictlist(RelOptInfo *joinrel,
 						   RelOptInfo *outer_rel,
@@ -36,28 +37,35 @@ static void subbuild_joinrel_joinlist(RelOptInfo *joinrel,
 
 
 /*
- * get_base_rel
+ * build_base_rel
  *	  Returns relation entry corresponding to 'relid', creating a new one
  *	  if necessary.  This is for base relations.
  */
 RelOptInfo *
-get_base_rel(Query *root, int relid)
+build_base_rel(Query *root, int relid)
 {
-	List	   *baserels;
+	List	   *rels;
 	RelOptInfo *rel;
 
-	foreach(baserels, root->base_rel_list)
+	/* Already made? */
+	foreach(rels, root->base_rel_list)
 	{
-		rel = (RelOptInfo *) lfirst(baserels);
+		rel = (RelOptInfo *) lfirst(rels);
 
-		/*
-		 * We know length(rel->relids) == 1 for all members of
-		 * base_rel_list
-		 */
+		/* length(rel->relids) == 1 for all members of base_rel_list */
 		if (lfirsti(rel->relids) == relid)
 			return rel;
 	}
 
+	/* It should not exist as an "other" rel */
+	foreach(rels, root->other_rel_list)
+	{
+		rel = (RelOptInfo *) lfirst(rels);
+
+		if (lfirsti(rel->relids) == relid)
+			elog(ERROR, "build_base_rel: rel already exists as 'other' rel");
+	}
+
 	/* No existing RelOptInfo for this base rel, so make a new one */
 	rel = make_base_rel(root, relid);
 
@@ -67,17 +75,53 @@ get_base_rel(Query *root, int relid)
 	return rel;
 }
 
+/*
+ * build_other_rel
+ *	  Returns relation entry corresponding to 'relid', creating a new one
+ *	  if necessary.  This is for 'other' relations, which are just like
+ *	  base relations except that they live in a different list.
+ */
+RelOptInfo *
+build_other_rel(Query *root, int relid)
+{
+	List	   *rels;
+	RelOptInfo *rel;
+
+	/* Already made? */
+	foreach(rels, root->other_rel_list)
+	{
+		rel = (RelOptInfo *) lfirst(rels);
+
+		/* length(rel->relids) == 1 for all members of other_rel_list */
+		if (lfirsti(rel->relids) == relid)
+			return rel;
+	}
+
+	/* It should not exist as a base rel */
+	foreach(rels, root->base_rel_list)
+	{
+		rel = (RelOptInfo *) lfirst(rels);
+
+		if (lfirsti(rel->relids) == relid)
+			elog(ERROR, "build_other_rel: rel already exists as base rel");
+	}
+
+	/* No existing RelOptInfo for this other rel, so make a new one */
+	rel = make_base_rel(root, relid);
+
+	/* and add it to the list */
+	root->other_rel_list = lcons(rel, root->other_rel_list);
+
+	return rel;
+}
+
 /*
  * make_base_rel
  *	  Construct a base-relation RelOptInfo for the specified rangetable index.
  *
- * This is split out of get_base_rel so that inheritance-tree processing can
- * construct baserel nodes for child tables.  We need a RelOptInfo so we can
- * plan a suitable access path for each child table, but we do NOT want to
- * enter the child nodes into base_rel_list.  In most contexts, get_base_rel
- * should be called instead.
+ * Common code for build_base_rel and build_other_rel.
  */
-RelOptInfo *
+static RelOptInfo *
 make_base_rel(Query *root, int relid)
 {
 	RelOptInfo *rel = makeNode(RelOptInfo);
@@ -92,7 +136,7 @@ make_base_rel(Query *root, int relid)
 	rel->cheapest_total_path = NULL;
 	rel->pruneable = true;
 	rel->issubquery = false;
-	rel->indexed = false;
+	rel->indexlist = NIL;
 	rel->pages = 0;
 	rel->tuples = 0;
 	rel->subplan = NULL;
@@ -108,8 +152,12 @@ make_base_rel(Query *root, int relid)
 	if (relationObjectId != InvalidOid)
 	{
 		/* Plain relation --- retrieve statistics from the system catalogs */
-		relation_info(relationObjectId,
-					  &rel->indexed, &rel->pages, &rel->tuples);
+		bool	indexed;
+
+		get_relation_info(relationObjectId,
+						  &indexed, &rel->pages, &rel->tuples);
+		if (indexed)
+			rel->indexlist = find_secondary_indexes(relationObjectId);
 	}
 	else
 	{
@@ -120,13 +168,46 @@ make_base_rel(Query *root, int relid)
 	return rel;
 }
 
+/*
+ * find_base_rel
+ *	  Find a base or other relation entry, which must already exist
+ *	  (since we'd have no idea which list to add it to).
+ */
+RelOptInfo *
+find_base_rel(Query *root, int relid)
+{
+	List	   *rels;
+	RelOptInfo *rel;
+
+	foreach(rels, root->base_rel_list)
+	{
+		rel = (RelOptInfo *) lfirst(rels);
+
+		/* length(rel->relids) == 1 for all members of base_rel_list */
+		if (lfirsti(rel->relids) == relid)
+			return rel;
+	}
+
+	foreach(rels, root->other_rel_list)
+	{
+		rel = (RelOptInfo *) lfirst(rels);
+
+		if (lfirsti(rel->relids) == relid)
+			return rel;
+	}
+
+	elog(ERROR, "find_base_rel: no relation entry for relid %d", relid);
+
+	return NULL;				/* keep compiler quiet */
+}
+
 /*
  * find_join_rel
  *	  Returns relation entry corresponding to 'relids' (a list of RT indexes),
  *	  or NULL if none exists.  This is for join relations.
  *
  * Note: there is probably no good reason for this to be called from
- * anywhere except get_join_rel, but keep it as a separate routine
+ * anywhere except build_join_rel, but keep it as a separate routine
  * just in case.
  */
 static RelOptInfo *
@@ -146,7 +227,7 @@ find_join_rel(Query *root, Relids relids)
 }
 
 /*
- * get_join_rel
+ * build_join_rel
  *	  Returns relation entry corresponding to the union of two given rels,
  *	  creating a new relation entry if none already exists.
  *
@@ -161,11 +242,11 @@ find_join_rel(Query *root, Relids relids)
  * duplicated calculation of the restrictlist...
  */
 RelOptInfo *
-get_join_rel(Query *root,
-			 RelOptInfo *outer_rel,
-			 RelOptInfo *inner_rel,
-			 JoinType jointype,
-			 List **restrictlist_ptr)
+build_join_rel(Query *root,
+			   RelOptInfo *outer_rel,
+			   RelOptInfo *inner_rel,
+			   JoinType jointype,
+			   List **restrictlist_ptr)
 {
 	List	   *joinrelids;
 	RelOptInfo *joinrel;
@@ -212,7 +293,7 @@ get_join_rel(Query *root,
 	joinrel->cheapest_total_path = NULL;
 	joinrel->pruneable = true;
 	joinrel->issubquery = false;
-	joinrel->indexed = false;
+	joinrel->indexlist = NIL;
 	joinrel->pages = 0;
 	joinrel->tuples = 0;
 	joinrel->subplan = NULL;
diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c
index d7633dc47dd6a21040762a19772c2e7a98ff6ca6..07c4da115f59c6eec63cee3874194fdb3401c337 100644
--- a/src/backend/utils/adt/selfuncs.c
+++ b/src/backend/utils/adt/selfuncs.c
@@ -15,11 +15,57 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.89 2001/05/09 23:13:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.90 2001/05/20 20:28:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
+/*----------
+ * Operator selectivity estimation functions are called to estimate the
+ * selectivity of WHERE clauses whose top-level operator is their operator.
+ * We divide the problem into two cases:
+ *		Restriction clause estimation: the clause involves vars of just
+ *			one relation.
+ *		Join clause estimation: the clause involves vars of multiple rels.
+ * Join selectivity estimation is far more difficult and usually less accurate
+ * than restriction estimation.
+ *
+ * When dealing with the inner scan of a nestloop join, we consider the
+ * join's joinclauses as restriction clauses for the inner relation, and
+ * treat vars of the outer relation as parameters (a/k/a constants of unknown
+ * values).  So, restriction estimators need to be able to accept an argument
+ * telling which relation is to be treated as the variable.
+ *
+ * The call convention for a restriction estimator (oprrest function) is
+ *
+ *		Selectivity oprrest (Query *root,
+ *							 Oid operator,
+ *							 List *args,
+ *							 int varRelid);
+ *
+ * root: general information about the query (rtable and RelOptInfo lists
+ * are particularly important for the estimator).
+ * operator: OID of the specific operator in question.
+ * args: argument list from the operator clause.
+ * varRelid: if not zero, the relid (rtable index) of the relation to
+ * be treated as the variable relation.  May be zero if the args list
+ * is known to contain vars of only one relation.
+ *
+ * This is represented at the SQL level (in pg_proc) as
+ *
+ *		float8 oprrest (opaque, oid, opaque, int4);
+ *
+ * The call convention for a join estimator (oprjoin function) is similar
+ * except that varRelid is not needed:
+ *
+ *		Selectivity oprjoin (Query *root,
+ *							 Oid operator,
+ *							 List *args);
+ *
+ *		float8 oprjoin (opaque, oid, opaque);
+ *----------
+ */
+
 #include "postgres.h"
 
 #include <ctype.h>
@@ -35,8 +81,11 @@
 #include "catalog/pg_statistic.h"
 #include "catalog/pg_type.h"
 #include "mb/pg_wchar.h"
+#include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
+#include "optimizer/pathnode.h"
+#include "optimizer/plancat.h"
 #include "parser/parse_func.h"
 #include "parser/parse_oper.h"
 #include "parser/parsetree.h"
@@ -46,17 +95,28 @@
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
-/* N is not a valid var/constant or relation id */
-#define NONVALUE(N)		((N) == 0)
+/*
+ * Note: the default selectivity estimates are not chosen entirely at random.
+ * We want them to be small enough to ensure that indexscans will be used if
+ * available, for typical table densities of ~100 tuples/page.  Thus, for
+ * example, 0.01 is not quite small enough, since that makes it appear that
+ * nearly all pages will be hit anyway.  Also, since we sometimes estimate
+ * eqsel as 1/num_distinct, we probably want DEFAULT_NUM_DISTINCT to equal
+ * 1/DEFAULT_EQ_SEL.
+ */
 
 /* default selectivity estimate for equalities such as "A = b" */
-#define DEFAULT_EQ_SEL	0.01
+#define DEFAULT_EQ_SEL	0.005
 
 /* default selectivity estimate for inequalities such as "A < b" */
 #define DEFAULT_INEQ_SEL  (1.0 / 3.0)
 
 /* default selectivity estimate for pattern-match operators such as LIKE */
-#define DEFAULT_MATCH_SEL	0.01
+#define DEFAULT_MATCH_SEL	0.005
+
+/* default number of distinct values in a table */
+#define DEFAULT_NUM_DISTINCT  200
+
 
 static bool convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
 				  Datum lobound, Datum hibound, Oid boundstypid,
@@ -72,19 +132,19 @@ static double convert_one_string_to_scalar(unsigned char *value,
 							 int rangelo, int rangehi);
 static unsigned char *convert_string_datum(Datum value, Oid typid);
 static double convert_timevalue_to_scalar(Datum value, Oid typid);
-static void getattproperties(Oid relid, AttrNumber attnum,
-							 Oid *typid, int32 *typmod);
-static double get_att_numdistinct(Oid relid, AttrNumber attnum, Oid typid,
+static double get_att_numdistinct(Query *root, Var *var,
 								  Form_pg_statistic stats);
-static Selectivity prefix_selectivity(char *prefix,
-				   Oid relid,
-				   AttrNumber attno,
-				   Oid datatype);
+static bool get_restriction_var(List *args, int varRelid,
+								Var **var, Node **other,
+								bool *varonleft);
+static void get_join_vars(List *args, Var **var1, Var **var2);
+static Selectivity prefix_selectivity(Query *root, Var *var, char *prefix);
 static Selectivity pattern_selectivity(char *patt, Pattern_Type ptype);
 static bool string_lessthan(const char *str1, const char *str2,
 				Oid datatype);
 static Oid	find_operator(const char *opname, Oid datatype);
 static Datum string_to_datum(const char *str, Oid datatype);
+static Const *string_to_const(const char *str, Oid datatype);
 
 
 /*
@@ -93,20 +153,19 @@ static Datum string_to_datum(const char *str, Oid datatype);
  * Note: this routine is also used to estimate selectivity for some
  * operators that are not "=" but have comparable selectivity behavior,
  * such as "~=" (geometric approximate-match).	Even for "=", we must
- * keep in mind that the left and right datatypes may differ, so the type
- * of the given constant "value" may be different from the type of the
- * attribute.
+ * keep in mind that the left and right datatypes may differ.
  */
 Datum
 eqsel(PG_FUNCTION_ARGS)
 {
-	Oid			opid = PG_GETARG_OID(0);
-	Oid			relid = PG_GETARG_OID(1);
-	AttrNumber	attno = PG_GETARG_INT16(2);
-	Datum		value = PG_GETARG_DATUM(3);
-	int32		flag = PG_GETARG_INT32(4);
-	Oid			typid;
-	int32		typmod;
+	Query	   *root = (Query *) PG_GETARG_POINTER(0);
+	Oid			operator = PG_GETARG_OID(1);
+	List	   *args = (List *) PG_GETARG_POINTER(2);
+	int			varRelid = PG_GETARG_INT32(3);
+	Var		   *var;
+	Node	   *other;
+	bool		varonleft;
+	Oid			relid;
 	HeapTuple	statsTuple;
 	Datum	   *values;
 	int			nvalues;
@@ -114,16 +173,29 @@ eqsel(PG_FUNCTION_ARGS)
 	int			nnumbers;
 	double		selec;
 
-	if (NONVALUE(relid) || NONVALUE(attno))
+	/*
+	 * If expression is not var = something or something = var for
+	 * a simple var of a real relation (no subqueries, for now),
+	 * then punt and return a default estimate.
+	 */
+	if (!get_restriction_var(args, varRelid,
+							 &var, &other, &varonleft))
+		PG_RETURN_FLOAT8(DEFAULT_EQ_SEL);
+	relid = getrelid(var->varno, root->rtable);
+	if (relid == InvalidOid)
 		PG_RETURN_FLOAT8(DEFAULT_EQ_SEL);
 
-	/* get info about the attribute */
-	getattproperties(relid, attno, &typid, &typmod);
+	/*
+	 * If the something is a NULL constant, assume operator is strict
+	 * and return zero, ie, operator will never return TRUE.
+	 */
+	if (IsA(other, Const) && ((Const *) other)->constisnull)
+		PG_RETURN_FLOAT8(0.0);
 
 	/* get stats for the attribute, if available */
 	statsTuple = SearchSysCache(STATRELATT,
 								ObjectIdGetDatum(relid),
-								Int16GetDatum(attno),
+								Int16GetDatum(var->varattno),
 								0, 0);
 	if (HeapTupleIsValid(statsTuple))
 	{
@@ -131,8 +203,10 @@ eqsel(PG_FUNCTION_ARGS)
 
 		stats = (Form_pg_statistic) GETSTRUCT(statsTuple);
 
-		if (flag & SEL_CONSTANT)
+		if (IsA(other, Const))
 		{
+			/* Var is being compared to a known non-null constant */
+			Datum	constval = ((Const *) other)->constvalue;
 			bool	match = false;
 			int		i;
 
@@ -143,25 +217,25 @@ eqsel(PG_FUNCTION_ARGS)
 			 * is an appropriate test.  If you don't like this, maybe you
 			 * shouldn't be using eqsel for your operator...)
 			 */
-			if (get_attstatsslot(statsTuple, typid, typmod,
+			if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod,
 								 STATISTIC_KIND_MCV, InvalidOid,
 								 &values, &nvalues,
 								 &numbers, &nnumbers))
 			{
 				FmgrInfo	eqproc;
 
-				fmgr_info(get_opcode(opid), &eqproc);
+				fmgr_info(get_opcode(operator), &eqproc);
 
 				for (i = 0; i < nvalues; i++)
 				{
 					/* be careful to apply operator right way 'round */
-					if (flag & SEL_RIGHT)
+					if (varonleft)
 						match = DatumGetBool(FunctionCall2(&eqproc,
 														   values[i],
-														   value));
+														   constval));
 					else
 						match = DatumGetBool(FunctionCall2(&eqproc,
-														   value,
+														   constval,
 														   values[i]));
 					if (match)
 						break;
@@ -203,8 +277,7 @@ eqsel(PG_FUNCTION_ARGS)
 				 * share this remaining fraction equally, so we
 				 * divide by the number of other distinct values.
 				 */
-				otherdistinct = get_att_numdistinct(relid, attno,
-													typid, stats)
+				otherdistinct = get_att_numdistinct(root, var, stats)
 					- nnumbers;
 				if (otherdistinct > 1)
 					selec /= otherdistinct;
@@ -217,7 +290,8 @@ eqsel(PG_FUNCTION_ARGS)
 					selec = numbers[nnumbers-1];
 			}
 
-			free_attstatsslot(typid, values, nvalues, numbers, nnumbers);
+			free_attstatsslot(var->vartype, values, nvalues,
+							  numbers, nnumbers);
 		}
 		else
 		{
@@ -234,21 +308,21 @@ eqsel(PG_FUNCTION_ARGS)
 			 * frequency in the table.  Is that a good idea?)
 			 */
 			selec = 1.0 - stats->stanullfrac;
-			ndistinct = get_att_numdistinct(relid, attno, typid, stats);
+			ndistinct = get_att_numdistinct(root, var, stats);
 			if (ndistinct > 1)
 				selec /= ndistinct;
 			/*
 			 * Cross-check: selectivity should never be
 			 * estimated as more than the most common value's.
 			 */
-			if (get_attstatsslot(statsTuple, typid, typmod,
+			if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod,
 								 STATISTIC_KIND_MCV, InvalidOid,
 								 NULL, NULL,
 								 &numbers, &nnumbers))
 			{
 				if (nnumbers > 0 && selec > numbers[0])
 					selec = numbers[0];
-				free_attstatsslot(typid, NULL, 0, numbers, nnumbers);
+				free_attstatsslot(var->vartype, NULL, 0, numbers, nnumbers);
 			}
 		}
 
@@ -262,7 +336,7 @@ eqsel(PG_FUNCTION_ARGS)
 		 * equally common.  (The guess is unlikely to be very good,
 		 * but we do know a few special cases.)
 		 */
-		selec = 1.0 / get_att_numdistinct(relid, attno, typid, NULL);
+		selec = 1.0 / get_att_numdistinct(root, var, NULL);
 	}
 
 	/* result should be in range, but make sure... */
@@ -284,27 +358,25 @@ eqsel(PG_FUNCTION_ARGS)
 Datum
 neqsel(PG_FUNCTION_ARGS)
 {
-	Oid			opid = PG_GETARG_OID(0);
-	Oid			relid = PG_GETARG_OID(1);
-	AttrNumber	attno = PG_GETARG_INT16(2);
-	Datum		value = PG_GETARG_DATUM(3);
-	int32		flag = PG_GETARG_INT32(4);
-	Oid			eqopid;
+	Query	   *root = (Query *) PG_GETARG_POINTER(0);
+	Oid			operator = PG_GETARG_OID(1);
+	List	   *args = (List *) PG_GETARG_POINTER(2);
+	int			varRelid = PG_GETARG_INT32(3);
+	Oid			eqop;
 	float8		result;
 
 	/*
 	 * We want 1 - eqsel() where the equality operator is the one
 	 * associated with this != operator, that is, its negator.
 	 */
-	eqopid = get_negator(opid);
-	if (eqopid)
+	eqop = get_negator(operator);
+	if (eqop)
 	{
-		result = DatumGetFloat8(DirectFunctionCall5(eqsel,
-												ObjectIdGetDatum(eqopid),
-												 ObjectIdGetDatum(relid),
-													Int16GetDatum(attno),
-													value,
-													Int32GetDatum(flag)));
+		result = DatumGetFloat8(DirectFunctionCall4(eqsel,
+											 PointerGetDatum(root),
+											 ObjectIdGetDatum(eqop),
+											 PointerGetDatum(args),
+											 Int32GetDatum(varRelid)));
 	}
 	else
 	{
@@ -316,28 +388,26 @@ neqsel(PG_FUNCTION_ARGS)
 }
 
 /*
- *		scalarltsel		- Selectivity of "<" (also "<=") for scalars.
+ *	scalarineqsel		- Selectivity of "<", "<=", ">", ">=" for scalars.
+ *
+ * This is the guts of both scalarltsel and scalargtsel.  The caller has
+ * commuted the clause, if necessary, so that we can treat the Var as
+ * being on the left.
  *
  * This routine works for any datatype (or pair of datatypes) known to
  * convert_to_scalar().  If it is applied to some other datatype,
  * it will return a default estimate.
  */
-Datum
-scalarltsel(PG_FUNCTION_ARGS)
+static double
+scalarineqsel(Query *root, Oid operator, bool isgt,
+			  Var *var, Node *other)
 {
-	Oid			opid = PG_GETARG_OID(0);
-	Oid			relid = PG_GETARG_OID(1);
-	AttrNumber	attno = PG_GETARG_INT16(2);
-	Datum		value = PG_GETARG_DATUM(3);
-	int32		flag = PG_GETARG_INT32(4);
-	bool		isgt;
-	HeapTuple	oprTuple;
+	Oid			relid;
+	Datum		constval;
+	Oid			consttype;
 	HeapTuple	statsTuple;
 	Form_pg_statistic stats;
-	Oid			contype;
 	FmgrInfo	opproc;
-	Oid			typid;
-	int32		typmod;
 	Datum	   *values;
 	int			nvalues;
 	float4	   *numbers;
@@ -348,62 +418,44 @@ scalarltsel(PG_FUNCTION_ARGS)
 	double		selec;
 	int			i;
 
-	if (NONVALUE(relid) || NONVALUE(attno))
-		PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
-
-	/* Can't do anything useful if no constant to compare against, either */
-	if (!(flag & SEL_CONSTANT))
-		PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
+	/*
+	 * If expression is not var op something or something op var for
+	 * a simple var of a real relation (no subqueries, for now),
+	 * then punt and return a default estimate.
+	 */
+	relid = getrelid(var->varno, root->rtable);
+	if (relid == InvalidOid)
+		return DEFAULT_INEQ_SEL;
 
 	/*
-	 * Force the constant to be on the right to simplify later logic.
-	 * This means that we may be dealing with either "<" or ">" cases.
+	 * Can't do anything useful if the something is not a constant, either.
 	 */
-	if (flag & SEL_RIGHT)
-	{
-		/* we have x < const */
-		isgt = false;
-	}
-	else
-	{
-		/* we have const < x, commute to make x > const */
-		opid = get_commutator(opid);
-		if (!opid)
-		{
-			/* Use default selectivity (should we raise an error instead?) */
-			PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
-		}
-		isgt = true;
-	}
+	if (! IsA(other, Const))
+		return DEFAULT_INEQ_SEL;
 
 	/*
-	 * The constant might not be the same datatype as the column;
-	 * look at the operator's input types to find out what it is.
-	 * Also set up to be able to call the operator's execution proc.
+	 * If the constant is NULL, assume operator is strict
+	 * and return zero, ie, operator will never return TRUE.
 	 */
-	oprTuple = SearchSysCache(OPEROID,
-							  ObjectIdGetDatum(opid),
-							  0, 0, 0);
-	if (!HeapTupleIsValid(oprTuple))
-		elog(ERROR, "scalarltsel: no tuple for operator %u", opid);
-	contype = ((Form_pg_operator) GETSTRUCT(oprTuple))->oprright;
-	fmgr_info(((Form_pg_operator) GETSTRUCT(oprTuple))->oprcode, &opproc);
-	ReleaseSysCache(oprTuple);
-
-	/* Now get info and stats about the attribute */
-	getattproperties(relid, attno, &typid, &typmod);
+	if (((Const *) other)->constisnull)
+		return 0.0;
+	constval = ((Const *) other)->constvalue;
+	consttype = ((Const *) other)->consttype;
 
+	/* get stats for the attribute */
 	statsTuple = SearchSysCache(STATRELATT,
 								ObjectIdGetDatum(relid),
-								Int16GetDatum(attno),
+								Int16GetDatum(var->varattno),
 								0, 0);
 	if (!HeapTupleIsValid(statsTuple))
 	{
 		/* no stats available, so default result */
-		PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
+		return DEFAULT_INEQ_SEL;
 	}
 	stats = (Form_pg_statistic) GETSTRUCT(statsTuple);
 
+	fmgr_info(get_opcode(operator), &opproc);
+
 	/*
 	 * If we have most-common-values info, add up the fractions of the
 	 * MCV entries that satisfy MCV OP CONST.  These fractions contribute
@@ -413,7 +465,7 @@ scalarltsel(PG_FUNCTION_ARGS)
 	mcv_selec = 0.0;
 	sumcommon = 0.0;
 
-	if (get_attstatsslot(statsTuple, typid, typmod,
+	if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod,
 						 STATISTIC_KIND_MCV, InvalidOid,
 						 &values, &nvalues,
 						 &numbers, &nnumbers))
@@ -422,11 +474,11 @@ scalarltsel(PG_FUNCTION_ARGS)
 		{
 			if (DatumGetBool(FunctionCall2(&opproc,
 										   values[i],
-										   value)))
+										   constval)))
 				mcv_selec += numbers[i];
 			sumcommon += numbers[i];
 		}
-		free_attstatsslot(typid, values, nvalues, numbers, nnumbers);
+		free_attstatsslot(var->vartype, values, nvalues, numbers, nnumbers);
 	}
 
 	/*
@@ -440,11 +492,11 @@ scalarltsel(PG_FUNCTION_ARGS)
 	 * have at hand!  (For example, we might have a '<=' operator rather
 	 * than the '<' operator that will appear in staop.)  For now, assume
 	 * that whatever appears in pg_statistic is sorted the same way our
-	 * operator sorts.
+	 * operator sorts, or the reverse way if isgt is TRUE.
 	 */
 	hist_selec = 0.0;
 
-	if (get_attstatsslot(statsTuple, typid, typmod,
+	if (get_attstatsslot(statsTuple, var->vartype, var->vartypmod,
 						 STATISTIC_KIND_HISTOGRAM, InvalidOid,
 						 &values, &nvalues,
 						 NULL, NULL))
@@ -456,7 +508,7 @@ scalarltsel(PG_FUNCTION_ARGS)
 
 			ltcmp = DatumGetBool(FunctionCall2(&opproc,
 											   values[0],
-											   value));
+											   constval));
 			if (isgt)
 				ltcmp = !ltcmp;
 			if (!ltcmp)
@@ -475,7 +527,7 @@ scalarltsel(PG_FUNCTION_ARGS)
 				{
 					ltcmp = DatumGetBool(FunctionCall2(&opproc,
 													   values[i],
-													   value));
+													   constval));
 					if (isgt)
 						ltcmp = !ltcmp;
 					if (!ltcmp)
@@ -500,8 +552,9 @@ scalarltsel(PG_FUNCTION_ARGS)
 					 * values to a uniform comparison scale, and do a linear
 					 * interpolation within this bin.
 					 */
-					if (convert_to_scalar(value, contype, &val,
-										  values[i-1], values[i], typid,
+					if (convert_to_scalar(constval, consttype, &val,
+										  values[i-1], values[i],
+										  var->vartype,
 										  &low, &high))
 					{
 						if (high <= low)
@@ -520,10 +573,10 @@ scalarltsel(PG_FUNCTION_ARGS)
 					{
 						/*
 						 * Ideally we'd produce an error here, on the grounds
-						 * that the given operator shouldn't have scalarltsel
+						 * that the given operator shouldn't have scalarXXsel
 						 * registered as its selectivity func unless we can
 						 * deal with its operand types.  But currently, all
-						 * manner of stuff is invoking scalarltsel, so give a
+						 * manner of stuff is invoking scalarXXsel, so give a
 						 * default estimate until that can be fixed.
 						 */
 						binfrac = 0.5;
@@ -549,13 +602,13 @@ scalarltsel(PG_FUNCTION_ARGS)
 			 * don't believe extremely small or large selectivity
 			 * estimates.
 			 */
-			if (hist_selec < 0.001)
-				hist_selec = 0.001;
-			else if (hist_selec > 0.999)
-				hist_selec = 0.999;
+			if (hist_selec < 0.0001)
+				hist_selec = 0.0001;
+			else if (hist_selec > 0.9999)
+				hist_selec = 0.9999;
 		}
 
-		free_attstatsslot(typid, values, nvalues, NULL, 0);
+		free_attstatsslot(var->vartype, values, nvalues, NULL, 0);
 	}
 
 	/*
@@ -586,141 +639,210 @@ scalarltsel(PG_FUNCTION_ARGS)
 	else if (selec > 1.0)
 		selec = 1.0;
 
+	return selec;
+}
+
+/*
+ *		scalarltsel		- Selectivity of "<" (also "<=") for scalars.
+ */
+Datum
+scalarltsel(PG_FUNCTION_ARGS)
+{
+	Query	   *root = (Query *) PG_GETARG_POINTER(0);
+	Oid			operator = PG_GETARG_OID(1);
+	List	   *args = (List *) PG_GETARG_POINTER(2);
+	int			varRelid = PG_GETARG_INT32(3);
+	Var		   *var;
+	Node	   *other;
+	bool		varonleft;
+	bool		isgt;
+	double		selec;
+
+	/*
+	 * If expression is not var op something or something op var for
+	 * a simple var of a real relation (no subqueries, for now),
+	 * then punt and return a default estimate.
+	 */
+	if (!get_restriction_var(args, varRelid,
+							 &var, &other, &varonleft))
+		PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
+
+	/*
+	 * Force the var to be on the left to simplify logic in scalarineqsel.
+	 */
+	if (varonleft)
+	{
+		/* we have var < other */
+		isgt = false;
+	}
+	else
+	{
+		/* we have other < var, commute to make var > other */
+		operator = get_commutator(operator);
+		if (!operator)
+		{
+			/* Use default selectivity (should we raise an error instead?) */
+			PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
+		}
+		isgt = true;
+	}
+
+	selec = scalarineqsel(root, operator, isgt, var, other);
+
 	PG_RETURN_FLOAT8((float8) selec);
 }
 
 /*
  *		scalargtsel		- Selectivity of ">" (also ">=") for integers.
- *
- * See above comments for scalarltsel.
  */
 Datum
 scalargtsel(PG_FUNCTION_ARGS)
 {
-	Oid			opid = PG_GETARG_OID(0);
-	Oid			relid = PG_GETARG_OID(1);
-	AttrNumber	attno = PG_GETARG_INT16(2);
-	Datum		value = PG_GETARG_DATUM(3);
-	int32		flag = PG_GETARG_INT32(4);
-	Oid			ltopid;
+	Query	   *root = (Query *) PG_GETARG_POINTER(0);
+	Oid			operator = PG_GETARG_OID(1);
+	List	   *args = (List *) PG_GETARG_POINTER(2);
+	int			varRelid = PG_GETARG_INT32(3);
+	Var		   *var;
+	Node	   *other;
+	bool		varonleft;
+	bool		isgt;
+	double		selec;
 
 	/*
-	 * Commute so that we have a "<" or "<=" operator, then apply
-	 * scalarltsel.
+	 * If expression is not var op something or something op var for
+	 * a simple var of a real relation (no subqueries, for now),
+	 * then punt and return a default estimate.
 	 */
-	ltopid = get_commutator(opid);
-	if (!ltopid)
-	{
-		/* Use default selectivity (should we raise an error instead?) */
+	if (!get_restriction_var(args, varRelid,
+							 &var, &other, &varonleft))
 		PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
+
+	/*
+	 * Force the var to be on the left to simplify logic in scalarineqsel.
+	 */
+	if (varonleft)
+	{
+		/* we have var > other */
+		isgt = true;
+	}
+	else
+	{
+		/* we have other > var, commute to make var < other */
+		operator = get_commutator(operator);
+		if (!operator)
+		{
+			/* Use default selectivity (should we raise an error instead?) */
+			PG_RETURN_FLOAT8(DEFAULT_INEQ_SEL);
+		}
+		isgt = false;
 	}
 
-	flag ^= SEL_RIGHT;
-	return DirectFunctionCall5(scalarltsel,
-							   ObjectIdGetDatum(ltopid),
-							   ObjectIdGetDatum(relid),
-							   Int16GetDatum(attno),
-							   value,
-							   Int32GetDatum(flag));
+	selec = scalarineqsel(root, operator, isgt, var, other);
+
+	PG_RETURN_FLOAT8((float8) selec);
 }
 
 /*
  * patternsel			- Generic code for pattern-match selectivity.
  */
-static Datum
+static double
 patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
 {
-	Oid			opid = PG_GETARG_OID(0);
-	Oid			relid = PG_GETARG_OID(1);
-	AttrNumber	attno = PG_GETARG_INT16(2);
-	Datum		value = PG_GETARG_DATUM(3);
-	int32		flag = PG_GETARG_INT32(4);
-	float8		result;
+	Query	   *root = (Query *) PG_GETARG_POINTER(0);
+#ifdef NOT_USED
+	Oid			operator = PG_GETARG_OID(1);
+#endif
+	List	   *args = (List *) PG_GETARG_POINTER(2);
+	int			varRelid = PG_GETARG_INT32(3);
+	Var		   *var;
+	Node	   *other;
+	bool		varonleft;
+	Oid			relid;
+	Datum		constval;
+	char	   *patt;
+	Pattern_Prefix_Status pstatus;
+	char	   *prefix;
+	char	   *rest;
+	double		result;
+
+	/*
+	 * If expression is not var op constant for
+	 * a simple var of a real relation (no subqueries, for now),
+	 * then punt and return a default estimate.
+	 */
+	if (!get_restriction_var(args, varRelid,
+							 &var, &other, &varonleft))
+		return DEFAULT_MATCH_SEL;
+	if (!varonleft || !IsA(other, Const))
+		return DEFAULT_MATCH_SEL;
+	relid = getrelid(var->varno, root->rtable);
+	if (relid == InvalidOid)
+		return DEFAULT_MATCH_SEL;
+
+	/*
+	 * If the constant is NULL, assume operator is strict
+	 * and return zero, ie, operator will never return TRUE.
+	 */
+	if (((Const *) other)->constisnull)
+		return 0.0;
+	constval = ((Const *) other)->constvalue;
+	/* the right-hand const is type text for all supported operators */
+	Assert(((Const *) other)->consttype == TEXTOID);
+	patt = DatumGetCString(DirectFunctionCall1(textout, constval));
 
-	/* Must have a constant for the pattern, or cannot learn anything */
-	if ((flag & (SEL_CONSTANT | SEL_RIGHT)) != (SEL_CONSTANT | SEL_RIGHT))
-		result = DEFAULT_MATCH_SEL;
+	/* divide pattern into fixed prefix and remainder */
+	pstatus = pattern_fixed_prefix(patt, ptype, &prefix, &rest);
+
+	if (pstatus == Pattern_Prefix_Exact)
+	{
+		/*
+		 * Pattern specifies an exact match, so pretend operator is '='
+		 */
+		Oid			eqopr = find_operator("=", var->vartype);
+		Const	   *eqcon;
+		List	   *eqargs;
+
+		if (eqopr == InvalidOid)
+			elog(ERROR, "patternsel: no = operator for type %u",
+				 var->vartype);
+		eqcon = string_to_const(prefix, var->vartype);
+		eqargs = makeList2(var, eqcon);
+		result = DatumGetFloat8(DirectFunctionCall4(eqsel,
+													PointerGetDatum(root),
+													ObjectIdGetDatum(eqopr),
+													PointerGetDatum(eqargs),
+													Int32GetDatum(varRelid)));
+	}
 	else
 	{
-		HeapTuple	oprTuple;
-		Oid			ltype,
-					rtype;
-		char	   *patt;
-		Pattern_Prefix_Status pstatus;
-		char	   *prefix;
-		char	   *rest;
-
 		/*
-		 * Get left and right datatypes of the operator so we know what
-		 * type the attribute is.
+		 * Not exact-match pattern.  We estimate selectivity of the
+		 * fixed prefix and remainder of pattern separately, then
+		 * combine the two.
 		 */
-		oprTuple = SearchSysCache(OPEROID,
-								  ObjectIdGetDatum(opid),
-								  0, 0, 0);
-		if (!HeapTupleIsValid(oprTuple))
-			elog(ERROR, "patternsel: no tuple for operator %u", opid);
-		ltype = ((Form_pg_operator) GETSTRUCT(oprTuple))->oprleft;
-		rtype = ((Form_pg_operator) GETSTRUCT(oprTuple))->oprright;
-		ReleaseSysCache(oprTuple);
-
-		/* the right-hand const is type text for all supported operators */
-		Assert(rtype == TEXTOID);
-		patt = DatumGetCString(DirectFunctionCall1(textout, value));
-
-		/* divide pattern into fixed prefix and remainder */
-		pstatus = pattern_fixed_prefix(patt, ptype, &prefix, &rest);
-
-		if (pstatus == Pattern_Prefix_Exact)
-		{
+		Selectivity prefixsel;
+		Selectivity restsel;
+		Selectivity selec;
 
-			/*
-			 * Pattern specifies an exact match, so pretend operator is
-			 * '='
-			 */
-			Oid			eqopr = find_operator("=", ltype);
-			Datum		eqcon;
-
-			if (eqopr == InvalidOid)
-				elog(ERROR, "patternsel: no = operator for type %u", ltype);
-			eqcon = string_to_datum(prefix, ltype);
-			result = DatumGetFloat8(DirectFunctionCall5(eqsel,
-												 ObjectIdGetDatum(eqopr),
-												 ObjectIdGetDatum(relid),
-													Int16GetDatum(attno),
-														eqcon,
-							   Int32GetDatum(SEL_CONSTANT | SEL_RIGHT)));
-			pfree(DatumGetPointer(eqcon));
-		}
+		if (pstatus == Pattern_Prefix_Partial)
+			prefixsel = prefix_selectivity(root, var, prefix);
 		else
-		{
+			prefixsel = 1.0;
+		restsel = pattern_selectivity(rest, ptype);
+		selec = prefixsel * restsel;
+		/* result should be in range, but make sure... */
+		if (selec < 0.0)
+			selec = 0.0;
+		else if (selec > 1.0)
+			selec = 1.0;
+		result = selec;
+	}
 
-			/*
-			 * Not exact-match pattern.  We estimate selectivity of the
-			 * fixed prefix and remainder of pattern separately, then
-			 * combine the two.
-			 */
-			Selectivity prefixsel;
-			Selectivity restsel;
-			Selectivity selec;
+	if (prefix)
+		pfree(prefix);
+	pfree(patt);
 
-			if (pstatus == Pattern_Prefix_Partial)
-				prefixsel = prefix_selectivity(prefix, relid, attno, ltype);
-			else
-				prefixsel = 1.0;
-			restsel = pattern_selectivity(rest, ptype);
-			selec = prefixsel * restsel;
-			/* result should be in range, but make sure... */
-			if (selec < 0.0)
-				selec = 0.0;
-			else if (selec > 1.0)
-				selec = 1.0;
-			result = (float8) selec;
-		}
-		if (prefix)
-			pfree(prefix);
-		pfree(patt);
-	}
-	PG_RETURN_FLOAT8(result);
+	return result;
 }
 
 /*
@@ -729,7 +851,7 @@ patternsel(PG_FUNCTION_ARGS, Pattern_Type ptype)
 Datum
 regexeqsel(PG_FUNCTION_ARGS)
 {
-	return patternsel(fcinfo, Pattern_Type_Regex);
+	PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Regex));
 }
 
 /*
@@ -738,7 +860,7 @@ regexeqsel(PG_FUNCTION_ARGS)
 Datum
 icregexeqsel(PG_FUNCTION_ARGS)
 {
-	return patternsel(fcinfo, Pattern_Type_Regex_IC);
+	PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Regex_IC));
 }
 
 /*
@@ -747,7 +869,7 @@ icregexeqsel(PG_FUNCTION_ARGS)
 Datum
 likesel(PG_FUNCTION_ARGS)
 {
-	return patternsel(fcinfo, Pattern_Type_Like);
+	PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Like));
 }
 
 /*
@@ -756,7 +878,7 @@ likesel(PG_FUNCTION_ARGS)
 Datum
 iclikesel(PG_FUNCTION_ARGS)
 {
-	return patternsel(fcinfo, Pattern_Type_Like_IC);
+	PG_RETURN_FLOAT8(patternsel(fcinfo, Pattern_Type_Like_IC));
 }
 
 /*
@@ -765,9 +887,9 @@ iclikesel(PG_FUNCTION_ARGS)
 Datum
 regexnesel(PG_FUNCTION_ARGS)
 {
-	float8		result;
+	double		result;
 
-	result = DatumGetFloat8(patternsel(fcinfo, Pattern_Type_Regex));
+	result = patternsel(fcinfo, Pattern_Type_Regex);
 	result = 1.0 - result;
 	PG_RETURN_FLOAT8(result);
 }
@@ -778,9 +900,9 @@ regexnesel(PG_FUNCTION_ARGS)
 Datum
 icregexnesel(PG_FUNCTION_ARGS)
 {
-	float8		result;
+	double		result;
 
-	result = DatumGetFloat8(patternsel(fcinfo, Pattern_Type_Regex_IC));
+	result = patternsel(fcinfo, Pattern_Type_Regex_IC);
 	result = 1.0 - result;
 	PG_RETURN_FLOAT8(result);
 }
@@ -791,9 +913,9 @@ icregexnesel(PG_FUNCTION_ARGS)
 Datum
 nlikesel(PG_FUNCTION_ARGS)
 {
-	float8		result;
+	double		result;
 
-	result = DatumGetFloat8(patternsel(fcinfo, Pattern_Type_Like));
+	result = patternsel(fcinfo, Pattern_Type_Like);
 	result = 1.0 - result;
 	PG_RETURN_FLOAT8(result);
 }
@@ -804,9 +926,9 @@ nlikesel(PG_FUNCTION_ARGS)
 Datum
 icnlikesel(PG_FUNCTION_ARGS)
 {
-	float8		result;
+	double		result;
 
-	result = DatumGetFloat8(patternsel(fcinfo, Pattern_Type_Like_IC));
+	result = patternsel(fcinfo, Pattern_Type_Like_IC);
 	result = 1.0 - result;
 	PG_RETURN_FLOAT8(result);
 }
@@ -817,25 +939,21 @@ icnlikesel(PG_FUNCTION_ARGS)
 Datum
 eqjoinsel(PG_FUNCTION_ARGS)
 {
+	Query	   *root = (Query *) PG_GETARG_POINTER(0);
 #ifdef NOT_USED					/* see neqjoinsel() before removing me! */
-	Oid			opid = PG_GETARG_OID(0);
+	Oid			operator = PG_GETARG_OID(1);
 #endif
-	Oid			relid1 = PG_GETARG_OID(1);
-	AttrNumber	attno1 = PG_GETARG_INT16(2);
-	Oid			relid2 = PG_GETARG_OID(3);
-	AttrNumber	attno2 = PG_GETARG_INT16(4);
-	bool		unknown1 = NONVALUE(relid1) || NONVALUE(attno1);
-	bool		unknown2 = NONVALUE(relid2) || NONVALUE(attno2);
+	List	   *args = (List *) PG_GETARG_POINTER(2);
+	Var		   *var1;
+	Var		   *var2;
 	double		selec;
 
-	if (unknown1 && unknown2)
+	get_join_vars(args, &var1, &var2);
+
+	if (var1 == NULL && var2 == NULL)
 		selec = DEFAULT_EQ_SEL;
 	else
 	{
-		Oid			typid1;
-		Oid			typid2;
-		int32		typmod1;
-		int32		typmod2;
 		HeapTuple	statsTuple1 = NULL;
 		HeapTuple	statsTuple2 = NULL;
 		Form_pg_statistic stats1 = NULL;
@@ -843,44 +961,52 @@ eqjoinsel(PG_FUNCTION_ARGS)
 		double		nd1,
 					nd2;
 
-		if (unknown1)
+		if (var1 == NULL)
 		{
-			nd1 = 100.0;
+			nd1 = DEFAULT_NUM_DISTINCT;
 		}
 		else
 		{
-			/* get info about the attribute */
-			getattproperties(relid1, attno1, &typid1, &typmod1);
-
 			/* get stats for the attribute, if available */
-			statsTuple1 = SearchSysCache(STATRELATT,
-										 ObjectIdGetDatum(relid1),
-										 Int16GetDatum(attno1),
-										 0, 0);
-			if (HeapTupleIsValid(statsTuple1))
-				stats1 = (Form_pg_statistic) GETSTRUCT(statsTuple1);
-
-			nd1 = get_att_numdistinct(relid1, attno1, typid1, stats1);
+			Oid		relid1 = getrelid(var1->varno, root->rtable);
+
+			if (relid1 == InvalidOid)
+				nd1 = DEFAULT_NUM_DISTINCT;
+			else
+			{
+				statsTuple1 = SearchSysCache(STATRELATT,
+											 ObjectIdGetDatum(relid1),
+											 Int16GetDatum(var1->varattno),
+											 0, 0);
+				if (HeapTupleIsValid(statsTuple1))
+					stats1 = (Form_pg_statistic) GETSTRUCT(statsTuple1);
+
+				nd1 = get_att_numdistinct(root, var1, stats1);
+			}
 		}
 
-		if (unknown2)
+		if (var2 == NULL)
 		{
-			nd2 = 100.0;
+			nd2 = DEFAULT_NUM_DISTINCT;
 		}
 		else
 		{
-			/* get info about the attribute */
-			getattproperties(relid2, attno2, &typid2, &typmod2);
-
 			/* get stats for the attribute, if available */
-			statsTuple2 = SearchSysCache(STATRELATT,
-										 ObjectIdGetDatum(relid2),
-										 Int16GetDatum(attno2),
-										 0, 0);
-			if (HeapTupleIsValid(statsTuple2))
-				stats2 = (Form_pg_statistic) GETSTRUCT(statsTuple2);
-
-			nd2 = get_att_numdistinct(relid2, attno2, typid2, stats2);
+			Oid		relid2 = getrelid(var2->varno, root->rtable);
+
+			if (relid2 == InvalidOid)
+				nd2 = DEFAULT_NUM_DISTINCT;
+			else
+			{
+				statsTuple2 = SearchSysCache(STATRELATT,
+											 ObjectIdGetDatum(relid2),
+											 Int16GetDatum(var2->varattno),
+											 0, 0);
+				if (HeapTupleIsValid(statsTuple2))
+					stats2 = (Form_pg_statistic) GETSTRUCT(statsTuple2);
+
+				nd2 = get_att_numdistinct(root, var2, stats2);
+			}
 		}
 
 		/*
@@ -903,7 +1029,6 @@ eqjoinsel(PG_FUNCTION_ARGS)
 			ReleaseSysCache(statsTuple1);
 		if (HeapTupleIsValid(statsTuple2))
 			ReleaseSysCache(statsTuple2);
-
 	}
 	PG_RETURN_FLOAT8((float8) selec);
 }
@@ -1062,27 +1187,26 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
 {
 	switch (valuetypid)
 	{
-
-			/*
-			 * Built-in numeric types
-			 */
-			case BOOLOID:
-			case INT2OID:
-			case INT4OID:
-			case INT8OID:
-			case FLOAT4OID:
-			case FLOAT8OID:
-			case NUMERICOID:
-			case OIDOID:
-			case REGPROCOID:
+		/*
+		 * Built-in numeric types
+		 */
+		case BOOLOID:
+		case INT2OID:
+		case INT4OID:
+		case INT8OID:
+		case FLOAT4OID:
+		case FLOAT8OID:
+		case NUMERICOID:
+		case OIDOID:
+		case REGPROCOID:
 			*scaledvalue = convert_numeric_to_scalar(value, valuetypid);
 			*scaledlobound = convert_numeric_to_scalar(lobound, boundstypid);
 			*scaledhibound = convert_numeric_to_scalar(hibound, boundstypid);
 			return true;
 
-			/*
-			 * Built-in string types
-			 */
+		/*
+		 * Built-in string types
+		 */
 		case CHAROID:
 		case BPCHAROID:
 		case VARCHAROID:
@@ -1102,9 +1226,9 @@ convert_to_scalar(Datum value, Oid valuetypid, double *scaledvalue,
 				return true;
 			}
 
-			/*
-			 * Built-in time types
-			 */
+		/*
+		 * Built-in time types
+		 */
 		case TIMESTAMPOID:
 		case ABSTIMEOID:
 		case DATEOID:
@@ -1376,7 +1500,7 @@ convert_timevalue_to_scalar(Datum value, Oid typid)
 {
 	switch (typid)
 	{
-			case TIMESTAMPOID:
+		case TIMESTAMPOID:
 			return DatumGetTimestamp(value);
 		case ABSTIMEOID:
 			return DatumGetTimestamp(DirectFunctionCall1(abstime_timestamp,
@@ -1419,51 +1543,17 @@ convert_timevalue_to_scalar(Datum value, Oid typid)
 }
 
 
-/*
- * getattproperties
- *	  Retrieve pg_attribute properties for an attribute,
- *	  including type OID and typmod.
- */
-static void
-getattproperties(Oid relid, AttrNumber attnum,
-				 Oid *typid, int32 *typmod)
-{
-	HeapTuple	atp;
-	Form_pg_attribute att_tup;
-
-	atp = SearchSysCache(ATTNUM,
-						 ObjectIdGetDatum(relid),
-						 Int16GetDatum(attnum),
-						 0, 0);
-	if (!HeapTupleIsValid(atp))
-		elog(ERROR, "getattproperties: no attribute tuple %u %d",
-			 relid, (int) attnum);
-	att_tup = (Form_pg_attribute) GETSTRUCT(atp);
-
-	*typid = att_tup->atttypid;
-	*typmod = att_tup->atttypmod;
-
-	ReleaseSysCache(atp);
-}
-
 /*
  * get_att_numdistinct
- *
  *	  Estimate the number of distinct values of an attribute.
  *
- * relid, attnum: identify the attribute to examine.
- * typid: type of attribute.
+ * var: identifies the attribute to examine.
  * stats: pg_statistic tuple for attribute, or NULL if not available.
- *
- * XXX possible future improvement: look to see if there is a unique
- * index on the attribute.  If so, we can estimate ndistinct = ntuples.
- * This should probably override any info from pg_statistic.
  */
 static double
-get_att_numdistinct(Oid relid, AttrNumber attnum, Oid typid,
-					Form_pg_statistic stats)
+get_att_numdistinct(Query *root, Var *var, Form_pg_statistic stats)
 {
-	HeapTuple	reltup;
+	RelOptInfo *rel;
 	double		ntuples;
 
 	/*
@@ -1471,42 +1561,42 @@ get_att_numdistinct(Oid relid, AttrNumber attnum, Oid typid,
 	 *
 	 * Are there any other cases we should wire in special estimates for?
 	 */
-	if (typid == BOOLOID)
+	if (var->vartype == BOOLOID)
 		return 2.0;
 
-	/*
-	 * If VACUUM ANALYZE determined a fixed estimate, use it.
-	 */
-	if (stats && stats->stadistinct > 0.0)
-		return stats->stadistinct;
-
 	/*
 	 * Otherwise we need to get the relation size.
 	 */
-	reltup = SearchSysCache(RELOID,
-							ObjectIdGetDatum(relid),
-							0, 0, 0);
-	if (!HeapTupleIsValid(reltup))
-		elog(ERROR, "get_att_numdistinct: no relation tuple %u", relid);
-
-	ntuples = ((Form_pg_class) GETSTRUCT(reltup))->reltuples;
-
-	ReleaseSysCache(reltup);
+	rel = find_base_rel(root, var->varno);
+	ntuples = rel->tuples;
 
 	if (ntuples <= 0.0)
-		return 100.0;			/* no data available; return a default */
+		return DEFAULT_NUM_DISTINCT; /* no data available; return a default */
 
 	/*
-	 * If VACUUM ANALYZE determined a scaled estimate, use it.
+	 * Look to see if there is a unique index on the attribute.
+	 * If so, we assume it's distinct, ignoring pg_statistic info
+	 * which could be out of date.
 	 */
-	if (stats && stats->stadistinct < 0.0)
-		return - stats->stadistinct * ntuples;
+	if (has_unique_index(rel, var->varattno))
+		return ntuples;
 
 	/*
-	 * VACUUM ANALYZE does not compute stats for system attributes,
+	 * If ANALYZE determined a fixed or scaled estimate, use it.
+	 */
+	if (stats)
+	{
+		if (stats->stadistinct > 0.0)
+			return stats->stadistinct;
+		if (stats->stadistinct < 0.0)
+			return - stats->stadistinct * ntuples;
+	}
+
+	/*
+	 * ANALYZE does not compute stats for system attributes,
 	 * but some of them can reasonably be assumed unique anyway.
 	 */
-	switch (attnum)
+	switch (var->varattno)
 	{
 		case ObjectIdAttributeNumber:
 		case SelfItemPointerAttributeNumber:
@@ -1516,12 +1606,116 @@ get_att_numdistinct(Oid relid, AttrNumber attnum, Oid typid,
 	}
 
 	/*
-	 * Estimate ndistinct = ntuples if the table is small, else 100.
+	 * Estimate ndistinct = ntuples if the table is small, else use default.
 	 */
-	if (ntuples < 100.0)
+	if (ntuples < DEFAULT_NUM_DISTINCT)
 		return ntuples;
 
-	return 100.0;
+	return DEFAULT_NUM_DISTINCT;
+}
+
+/*
+ * get_restriction_var
+ *		Examine the args of a restriction clause to see if it's of the
+ *		form (var op something) or (something op var).  If so, extract
+ *		and return the var and the other argument.
+ *
+ * Inputs:
+ *	args: clause argument list
+ *	varRelid: see specs for restriction selectivity functions
+ *
+ * Outputs: (these are set only if TRUE is returned)
+ *	*var: gets Var node
+ *	*other: gets other clause argument
+ *	*varonleft: set TRUE if var is on the left, FALSE if on the right
+ *
+ * Returns TRUE if a Var is identified, otherwise FALSE.
+ */
+static bool
+get_restriction_var(List *args,
+					int varRelid,
+					Var **var,
+					Node **other,
+					bool *varonleft)
+{
+	Node	   *left,
+			   *right;
+
+	if (length(args) != 2)
+		return false;
+
+	left = (Node *) lfirst(args);
+	right = (Node *) lsecond(args);
+
+	/* Ignore any binary-compatible relabeling */
+
+	if (IsA(left, RelabelType))
+		left = ((RelabelType *) left)->arg;
+	if (IsA(right, RelabelType))
+		right = ((RelabelType *) right)->arg;
+
+	/* Look for the var */
+
+	if (IsA(left, Var) &&
+		(varRelid == 0 || varRelid == ((Var *) left)->varno))
+	{
+		*var = (Var *) left;
+		*other = right;
+		*varonleft = true;
+	}
+	else if (IsA(right, Var) &&
+			 (varRelid == 0 || varRelid == ((Var *) right)->varno))
+	{
+		*var = (Var *) right;
+		*other = left;
+		*varonleft = false;
+	}
+	else
+	{
+		/* Duh, it's too complicated for me... */
+		return false;
+	}
+
+	return true;
+}
+
+/*
+ * get_join_vars
+ *
+ * Extract the two Vars from a join clause's argument list.  Returns
+ * NULL for arguments that are not simple vars.
+ */
+static void
+get_join_vars(List *args, Var **var1, Var **var2)
+{
+	Node	   *left,
+			   *right;
+
+	if (length(args) != 2)
+	{
+		*var1 = NULL;
+		*var2 = NULL;
+		return;
+	}
+
+	left = (Node *) lfirst(args);
+	right = (Node *) lsecond(args);
+
+	/* Ignore any binary-compatible relabeling */
+	if (IsA(left, RelabelType))
+		left = ((RelabelType *) left)->arg;
+	if (IsA(right, RelabelType))
+		right = ((RelabelType *) right)->arg;
+
+	if (IsA(left, Var))
+		*var1 = (Var *) left;
+	else
+		*var1 = NULL;
+
+	if (IsA(right, Var))
+		*var2 = (Var *) right;
+	else
+		*var2 = NULL;
 }
 
 /*-------------------------------------------------------------------------
@@ -1755,54 +1949,49 @@ pattern_fixed_prefix(char *patt, Pattern_Type ptype,
  * more useful to use the upper-bound code than not.
  */
 static Selectivity
-prefix_selectivity(char *prefix,
-				   Oid relid,
-				   AttrNumber attno,
-				   Oid datatype)
+prefix_selectivity(Query *root, Var *var, char *prefix)
 {
 	Selectivity prefixsel;
 	Oid			cmpopr;
-	Datum		prefixcon;
+	Const	   *prefixcon;
+	List	   *cmpargs;
 	char	   *greaterstr;
 
-	cmpopr = find_operator(">=", datatype);
+	cmpopr = find_operator(">=", var->vartype);
 	if (cmpopr == InvalidOid)
 		elog(ERROR, "prefix_selectivity: no >= operator for type %u",
-			 datatype);
-	prefixcon = string_to_datum(prefix, datatype);
+			 var->vartype);
+	prefixcon = string_to_const(prefix, var->vartype);
+	cmpargs = makeList2(var, prefixcon);
 	/* Assume scalargtsel is appropriate for all supported types */
-	prefixsel = DatumGetFloat8(DirectFunctionCall5(scalargtsel,
-												ObjectIdGetDatum(cmpopr),
-												 ObjectIdGetDatum(relid),
-												   Int16GetDatum(attno),
-												   prefixcon,
-							   Int32GetDatum(SEL_CONSTANT | SEL_RIGHT)));
-	pfree(DatumGetPointer(prefixcon));
+	prefixsel = DatumGetFloat8(DirectFunctionCall4(scalargtsel,
+												   PointerGetDatum(root),
+												   ObjectIdGetDatum(cmpopr),
+												   PointerGetDatum(cmpargs),
+												   Int32GetDatum(0)));
 
 	/*-------
 	 * If we can create a string larger than the prefix, say
 	 *	"x < greaterstr".
 	 *-------
 	 */
-	greaterstr = make_greater_string(prefix, datatype);
+	greaterstr = make_greater_string(prefix, var->vartype);
 	if (greaterstr)
 	{
 		Selectivity topsel;
 
-		cmpopr = find_operator("<", datatype);
+		cmpopr = find_operator("<", var->vartype);
 		if (cmpopr == InvalidOid)
 			elog(ERROR, "prefix_selectivity: no < operator for type %u",
-				 datatype);
-		prefixcon = string_to_datum(greaterstr, datatype);
+				 var->vartype);
+		prefixcon = string_to_const(greaterstr, var->vartype);
+		cmpargs = makeList2(var, prefixcon);
 		/* Assume scalarltsel is appropriate for all supported types */
-		topsel = DatumGetFloat8(DirectFunctionCall5(scalarltsel,
-												ObjectIdGetDatum(cmpopr),
-												 ObjectIdGetDatum(relid),
-													Int16GetDatum(attno),
-													prefixcon,
-							   Int32GetDatum(SEL_CONSTANT | SEL_RIGHT)));
-		pfree(DatumGetPointer(prefixcon));
-		pfree(greaterstr);
+		topsel = DatumGetFloat8(DirectFunctionCall4(scalarltsel,
+													PointerGetDatum(root),
+													ObjectIdGetDatum(cmpopr),
+													PointerGetDatum(cmpargs),
+													Int32GetDatum(0)));
 
 		/*
 		 * Merge the two selectivities in the same way as for a range
@@ -1828,7 +2017,7 @@ prefix_selectivity(char *prefix,
 				 * No data available --- use a default estimate that is
 				 * small, but not real small.
 				 */
-				prefixsel = 0.01;
+				prefixsel = 0.005;
 			}
 			else
 			{
@@ -2074,7 +2263,7 @@ locale_is_like_safe(void)
 		result = false;
 	return (bool) result;
 #else							/* not USE_LOCALE */
-				return true;	/* We must be in C locale, which is OK */
+	return true;				/* We must be in C locale, which is OK */
 #endif	 /* USE_LOCALE */
 }
 
@@ -2208,7 +2397,6 @@ find_operator(const char *opname, Oid datatype)
 static Datum
 string_to_datum(const char *str, Oid datatype)
 {
-
 	/*
 	 * We cheat a little by assuming that textin() will do for bpchar and
 	 * varchar constants too...
@@ -2219,6 +2407,18 @@ string_to_datum(const char *str, Oid datatype)
 		return DirectFunctionCall1(textin, CStringGetDatum(str));
 }
 
+/*
+ * Generate a Const node of the appropriate type from a C string.
+ */
+static Const *
+string_to_const(const char *str, Oid datatype)
+{
+	Datum		conval = string_to_datum(str, datatype);
+
+	return makeConst(datatype, ((datatype == NAMEOID) ? NAMEDATALEN : -1),
+					 conval, false, false, false, false);
+}
+
 /*-------------------------------------------------------------------------
  *
  * Index cost estimation functions
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 44ddb7611a5062ee38b9d8b24ae195d6bd08595d..e04f97e0ec8322bdd5b833995e95e9cabcb1a360 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.78 2001/05/15 03:49:35 momjian Exp $
+ * $Id: catversion.h,v 1.79 2001/05/20 20:28:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200105145
+#define CATALOG_VERSION_NO	200105191
 
 #endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 7d7acf96f73752d498fa018f8eec90b94aa24192..f905a063c634ae550c53cc996f54c0aa4bbdd179 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.185 2001/05/09 23:13:35 tgl Exp $
+ * $Id: pg_proc.h,v 1.186 2001/05/20 20:28:19 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -219,21 +219,21 @@ DESCR("btree cost estimator");
 
 DATA(insert OID = 100 (  int8fac		   PGUID 12 f t t t 1 f 20 "20" 100 0 0 100  int8fac - ));
 DESCR("factorial");
-DATA(insert OID = 101 (  eqsel			   PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  eqsel - ));
+DATA(insert OID = 101 (  eqsel			   PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  eqsel - ));
 DESCR("restriction selectivity of = and related operators");
-DATA(insert OID = 102 (  neqsel			   PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  neqsel - ));
+DATA(insert OID = 102 (  neqsel			   PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  neqsel - ));
 DESCR("restriction selectivity of <> and related operators");
-DATA(insert OID = 103 (  scalarltsel	   PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  scalarltsel - ));
+DATA(insert OID = 103 (  scalarltsel	   PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  scalarltsel - ));
 DESCR("restriction selectivity of < and related operators on scalar datatypes");
-DATA(insert OID = 104 (  scalargtsel	   PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  scalargtsel - ));
+DATA(insert OID = 104 (  scalargtsel	   PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  scalargtsel - ));
 DESCR("restriction selectivity of > and related operators on scalar datatypes");
-DATA(insert OID = 105 (  eqjoinsel		   PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	eqjoinsel - ));
+DATA(insert OID = 105 (  eqjoinsel		   PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	eqjoinsel - ));
 DESCR("join selectivity of = and related operators");
-DATA(insert OID = 106 (  neqjoinsel		   PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	neqjoinsel - ));
+DATA(insert OID = 106 (  neqjoinsel		   PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	neqjoinsel - ));
 DESCR("join selectivity of <> and related operators");
-DATA(insert OID = 107 (  scalarltjoinsel   PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	scalarltjoinsel - ));
+DATA(insert OID = 107 (  scalarltjoinsel   PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	scalarltjoinsel - ));
 DESCR("join selectivity of < and related operators on scalar datatypes");
-DATA(insert OID = 108 (  scalargtjoinsel   PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	scalargtjoinsel - ));
+DATA(insert OID = 108 (  scalargtjoinsel   PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	scalargtjoinsel - ));
 DESCR("join selectivity of > and related operators on scalar datatypes");
 
 DATA(insert OID = 112 (  text			   PGUID 12 f t t t 1 f  25 "23" 100 0 0 100	int4_text - ));
@@ -292,9 +292,9 @@ DATA(insert OID = 137 (  on_ppath		   PGUID 12 f t t t 2 f 16 "600 602" 100 0 0
 DESCR("contained in");
 DATA(insert OID = 138 (  box_center		   PGUID 12 f t t t 1 f 600 "603" 100 0 0 100  box_center - ));
 DESCR("center of");
-DATA(insert OID = 139 (  areasel		   PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  areasel - ));
+DATA(insert OID = 139 (  areasel		   PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  areasel - ));
 DESCR("restriction selectivity for area-comparison operators");
-DATA(insert OID = 140 (  areajoinsel	   PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	areajoinsel - ));
+DATA(insert OID = 140 (  areajoinsel	   PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	areajoinsel - ));
 DESCR("join selectivity for area-comparison operators");
 DATA(insert OID = 141 (  int4mul		   PGUID 12 f t t t 2 f 23 "23 23" 100 0 0 100	int4mul - ));
 DESCR("multiply");
@@ -1562,13 +1562,13 @@ DESCR("current transaction time");
 
 /* OIDS 1300 - 1399 */
 
-DATA(insert OID = 1300 (  positionsel		   PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  positionsel - ));
+DATA(insert OID = 1300 (  positionsel		   PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  positionsel - ));
 DESCR("restriction selectivity for position-comparison operators");
-DATA(insert OID = 1301 (  positionjoinsel	   PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	positionjoinsel - ));
+DATA(insert OID = 1301 (  positionjoinsel	   PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	positionjoinsel - ));
 DESCR("join selectivity for position-comparison operators");
-DATA(insert OID = 1302 (  contsel		   PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  contsel - ));
+DATA(insert OID = 1302 (  contsel		   PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  contsel - ));
 DESCR("restriction selectivity for containment comparison operators");
-DATA(insert OID = 1303 (  contjoinsel	   PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	contjoinsel - ));
+DATA(insert OID = 1303 (  contjoinsel	   PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	contjoinsel - ));
 DESCR("join selectivity for containment comparison operators");
 
 DATA(insert OID = 1304 ( overlaps			 PGUID 12 f t t f 4 f 16 "1184 1184 1184 1184" 100 0 0 100	overlaps_timestamp - ));
@@ -2465,37 +2465,37 @@ DATA(insert OID = 1799 (  oidout		   PGUID 12 f t t t 1 f 23 "0" 100 0 0 100	oid
 DESCR("(internal)");
 
 /* Selectivity estimators for LIKE and related operators */
-DATA(insert OID = 1814 ( iclikesel			PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  iclikesel - ));
+DATA(insert OID = 1814 ( iclikesel			PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  iclikesel - ));
 DESCR("restriction selectivity of ILIKE");
-DATA(insert OID = 1815 ( icnlikesel			PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  icnlikesel - ));
+DATA(insert OID = 1815 ( icnlikesel			PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  icnlikesel - ));
 DESCR("restriction selectivity of NOT ILIKE");
-DATA(insert OID = 1816 ( iclikejoinsel		PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	iclikejoinsel - ));
+DATA(insert OID = 1816 ( iclikejoinsel		PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	iclikejoinsel - ));
 DESCR("join selectivity of ILIKE");
-DATA(insert OID = 1817 ( icnlikejoinsel		PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	icnlikejoinsel - ));
+DATA(insert OID = 1817 ( icnlikejoinsel		PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	icnlikejoinsel - ));
 DESCR("join selectivity of NOT ILIKE");
-DATA(insert OID = 1818 ( regexeqsel			PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  regexeqsel - ));
+DATA(insert OID = 1818 ( regexeqsel			PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  regexeqsel - ));
 DESCR("restriction selectivity of regex match");
-DATA(insert OID = 1819 ( likesel			PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  likesel - ));
+DATA(insert OID = 1819 ( likesel			PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  likesel - ));
 DESCR("restriction selectivity of LIKE");
-DATA(insert OID = 1820 ( icregexeqsel		PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  icregexeqsel - ));
+DATA(insert OID = 1820 ( icregexeqsel		PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  icregexeqsel - ));
 DESCR("restriction selectivity of case-insensitive regex match");
-DATA(insert OID = 1821 ( regexnesel			PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  regexnesel - ));
+DATA(insert OID = 1821 ( regexnesel			PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  regexnesel - ));
 DESCR("restriction selectivity of regex non-match");
-DATA(insert OID = 1822 ( nlikesel			PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  nlikesel - ));
+DATA(insert OID = 1822 ( nlikesel			PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  nlikesel - ));
 DESCR("restriction selectivity of NOT LIKE");
-DATA(insert OID = 1823 ( icregexnesel		PGUID 12 f t f t 5 f 701 "26 26 21 0 23" 100 0 0 100  icregexnesel - ));
+DATA(insert OID = 1823 ( icregexnesel		PGUID 12 f t f t 4 f 701 "0 26 0 23" 100 0 0 100  icregexnesel - ));
 DESCR("restriction selectivity of case-insensitive regex non-match");
-DATA(insert OID = 1824 ( regexeqjoinsel		PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	regexeqjoinsel - ));
+DATA(insert OID = 1824 ( regexeqjoinsel		PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	regexeqjoinsel - ));
 DESCR("join selectivity of regex match");
-DATA(insert OID = 1825 ( likejoinsel		PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	likejoinsel - ));
+DATA(insert OID = 1825 ( likejoinsel		PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	likejoinsel - ));
 DESCR("join selectivity of LIKE");
-DATA(insert OID = 1826 ( icregexeqjoinsel	PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	icregexeqjoinsel - ));
+DATA(insert OID = 1826 ( icregexeqjoinsel	PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	icregexeqjoinsel - ));
 DESCR("join selectivity of case-insensitive regex match");
-DATA(insert OID = 1827 ( regexnejoinsel		PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	regexnejoinsel - ));
+DATA(insert OID = 1827 ( regexnejoinsel		PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	regexnejoinsel - ));
 DESCR("join selectivity of regex non-match");
-DATA(insert OID = 1828 ( nlikejoinsel		PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	nlikejoinsel - ));
+DATA(insert OID = 1828 ( nlikejoinsel		PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	nlikejoinsel - ));
 DESCR("join selectivity of NOT LIKE");
-DATA(insert OID = 1829 ( icregexnejoinsel	PGUID 12 f t f t 5 f 701 "26 26 21 26 21" 100 0 0 100	icregexnejoinsel - ));
+DATA(insert OID = 1829 ( icregexnejoinsel	PGUID 12 f t f t 3 f 701 "0 26 0" 100 0 0 100	icregexnejoinsel - ));
 DESCR("join selectivity of case-insensitive regex non-match");
 
 /* Aggregate-related functions */
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 63b1b1046a8e71675ed81102c38134886a45f0bc..cfea5222965869fc3b15e0a58940b902b17abc8d 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.127 2001/05/07 00:43:25 tgl Exp $
+ * $Id: parsenodes.h,v 1.128 2001/05/20 20:28:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -83,6 +83,7 @@ typedef struct Query
 
 	/* internal to planner */
 	List	   *base_rel_list;	/* list of base-relation RelOptInfos */
+	List	   *other_rel_list;	/* list of other 1-relation RelOptInfos */
 	List	   *join_rel_list;	/* list of join-relation RelOptInfos */
 	List	   *equi_key_list;	/* list of lists of equijoined
 								 * PathKeyItems */
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index c76d9b4af7136f23fdc022f53127925129760519..33927edc18d6e47b5fc38cc55e501fcf4b29efc6 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relation.h,v 1.55 2001/05/07 00:43:26 tgl Exp $
+ * $Id: relation.h,v 1.56 2001/05/20 20:28:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -72,8 +72,8 @@ typedef enum CostSelector
  *	 * If the relation is a base relation it will have these fields set:
  *
  *		issubquery - true if baserel is a subquery RTE rather than a table
- *		indexed - true if the relation has secondary indices (always false
- *				  if it's a subquery)
+ *		indexlist - list of IndexOptInfo nodes for relation's indexes
+ *					(always NIL if it's a subquery)
  *		pages - number of disk pages in relation (zero if a subquery)
  *		tuples - number of tuples in relation (not considering restrictions)
  *		subplan - plan for subquery (NULL if it's a plain table)
@@ -150,7 +150,7 @@ typedef struct RelOptInfo
 
 	/* information about a base rel (not set for join rels!) */
 	bool		issubquery;
-	bool		indexed;
+	List	   *indexlist;
 	long		pages;
 	double		tuples;
 	struct Plan *subplan;
@@ -178,20 +178,30 @@ typedef struct RelOptInfo
  *		and indexes, but that created confusion without actually doing anything
  *		useful.  So now we have a separate IndexOptInfo struct for indexes.
  *
- *		indexoid - OID of the index relation itself
- *		pages - number of disk pages in index
- *		tuples - number of index tuples in index
+ *		indexoid  - OID of the index relation itself
+ *		pages     - number of disk pages in index
+ *		tuples    - number of index tuples in index
+ *		ncolumns  - number of columns in index
+ *		nkeys     - number of keys used by index (input columns)
  *		classlist - List of PG_AMOPCLASS OIDs for the index
  *		indexkeys - List of base-relation attribute numbers that are index keys
- *		ordering - List of PG_OPERATOR OIDs which order the indexscan result
- *		relam	  - the OID of the pg_am of the index
+ *		ordering  - List of PG_OPERATOR OIDs which order the indexscan result
+ *		relam     - the OID of the pg_am of the index
  *		amcostestimate - OID of the relam's cost estimator
  *		indproc   - OID of the function if a functional index, else 0
  *		indpred   - index predicate if a partial index, else NULL
+ *		unique	  - true if index is unique
  *		lossy	  - true if index is lossy (may return non-matching tuples)
  *
- *		NB. the last element of the arrays classlist, indexkeys and ordering
- *			is always 0.
+ *		ncolumns and nkeys are the same except for a functional index,
+ *		wherein ncolumns is 1 (the single function output) while nkeys
+ *		is the number of table columns passed to the function. classlist[]
+ *		and ordering[] have ncolumns entries, while indexkeys[] has nkeys
+ *		entries.
+ * 
+ *		Note: for historical reasons, the arrays classlist, indexkeys and
+ *		ordering have an extra entry that is always zero.  Some code scans
+ *		until it sees a zero rather than looking at ncolumns or nkeys.
  */
 
 typedef struct IndexOptInfo
@@ -205,15 +215,18 @@ typedef struct IndexOptInfo
 	double		tuples;
 
 	/* index descriptor information */
-	Oid		   *classlist;		/* classes of AM operators */
-	int		   *indexkeys;		/* keys over which we're indexing */
-	Oid		   *ordering;		/* OIDs of sort operators for each key */
+	int			ncolumns;		/* number of columns in index */
+	int			nkeys;			/* number of keys used by index */
+	Oid		   *classlist;		/* AM operator classes for columns */
+	int		   *indexkeys;		/* column numbers of index's keys */
+	Oid		   *ordering;		/* OIDs of sort operators for each column */
 	Oid			relam;			/* OID of the access method (in pg_am) */
 
 	RegProcedure amcostestimate;/* OID of the access method's cost fcn */
 
 	Oid			indproc;		/* if a functional index */
 	List	   *indpred;		/* if a partial index */
+	bool		unique;			/* if a unique index */
 	bool		lossy;			/* if a lossy index */
 } IndexOptInfo;
 
@@ -275,7 +288,7 @@ typedef struct Path
  * tuples matched during any scan.	(The executor is smart enough not to return
  * the same tuple more than once, even if it is matched in multiple scans.)
  *
- * 'indexid' is a list of index relation OIDs, one per scan to be performed.
+ * '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
@@ -313,7 +326,7 @@ typedef struct Path
 typedef struct IndexPath
 {
 	Path		path;
-	List	   *indexid;
+	List	   *indexinfo;
 	List	   *indexqual;
 	ScanDirection indexscandir;
 	Relids		joinrelids;		/* other rels mentioned in indexqual */
@@ -533,7 +546,7 @@ typedef struct RestrictInfo
 typedef struct JoinInfo
 {
 	NodeTag		type;
-	Relids		unjoined_relids;/* some rels not yet part of my RelOptInfo */
+	Relids		unjoined_relids; /* some rels not yet part of my RelOptInfo */
 	List	   *jinfo_restrictinfo;		/* relevant RestrictInfos */
 } JoinInfo;
 
diff --git a/src/include/optimizer/clauses.h b/src/include/optimizer/clauses.h
index a02ef9c77c30d432e4688071b200c652a179ac26..79bb7a1359347cf357c000dc80cb643efb09228e 100644
--- a/src/include/optimizer/clauses.h
+++ b/src/include/optimizer/clauses.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: clauses.h,v 1.43 2001/03/22 04:00:53 momjian Exp $
+ * $Id: clauses.h,v 1.44 2001/05/20 20:28:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,14 +16,6 @@
 
 #include "nodes/relation.h"
 
-/*
- *	Flag bits returned by get_relattval().
- *	These are used in selectivity-estimation routines, too.
- */
-#define SEL_CONSTANT	1		/* operator's non-var arg is a constant */
-#define SEL_RIGHT		2		/* operator's non-var arg is on the right */
-
-
 extern Expr *make_clause(int type, Node *oper, List *args);
 
 extern bool is_opclause(Node *clause);
@@ -61,11 +53,6 @@ extern List *pull_constant_clauses(List *quals, List **constantQual);
 
 extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
 extern int	NumRelids(Node *clause);
-extern void get_relattval(Node *clause, int targetrelid,
-			  int *relid, AttrNumber *attno,
-			  Datum *constval, int *flag);
-extern void get_rels_atts(Node *clause, int *relid1,
-			  AttrNumber *attno1, int *relid2, AttrNumber *attno2);
 extern void CommuteClause(Expr *clause);
 
 extern Node *eval_const_expressions(Node *node);
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 0839feb4b2fe5c0d137a7705469acb3814779181..85ba6936f4888652bdfd5b4613b92979515f51a0 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pathnode.h,v 1.36 2001/05/07 00:43:26 tgl Exp $
+ * $Id: pathnode.h,v 1.37 2001/05/20 20:28:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,9 +64,10 @@ extern HashPath *create_hashjoin_path(RelOptInfo *joinrel,
 /*
  * prototypes for relnode.c
  */
-extern RelOptInfo *get_base_rel(Query *root, int relid);
-extern RelOptInfo *make_base_rel(Query *root, int relid);
-extern RelOptInfo *get_join_rel(Query *root,
+extern RelOptInfo *build_base_rel(Query *root, int relid);
+extern RelOptInfo *build_other_rel(Query *root, int relid);
+extern RelOptInfo *find_base_rel(Query *root, int relid);
+extern RelOptInfo *build_join_rel(Query *root,
 			 RelOptInfo *outer_rel,
 			 RelOptInfo *inner_rel,
 			 JoinType jointype,
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index ac67f6a6551cbadbb127dfe330dbc8d0ca80d05e..39afe74d2adc811be5da79f1ab3e99e7496b98ce 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: paths.h,v 1.52 2001/03/22 04:00:54 momjian Exp $
+ * $Id: paths.h,v 1.53 2001/05/20 20:28:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,7 +35,7 @@ extern RelOptInfo *make_fromexpr_rel(Query *root, FromExpr *from);
  * indxpath.c
  *	  routines to generate index paths
  */
-extern void create_index_paths(Query *root, RelOptInfo *rel, List *indices);
+extern void create_index_paths(Query *root, RelOptInfo *rel);
 extern Oid indexable_operator(Expr *clause, Oid opclass, Oid relam,
 				   bool indexkey_on_left);
 extern List *extract_or_indexqual_conditions(RelOptInfo *rel,
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index 5d3062d981ce782a97005d464d16dace736a1e35..a103f51e3f19a1d87f2793401cfcf2cc76635251 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plancat.h,v 1.22 2001/03/22 04:00:55 momjian Exp $
+ * $Id: plancat.h,v 1.23 2001/05/20 20:28:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,7 +17,7 @@
 #include "nodes/relation.h"
 
 
-extern void relation_info(Oid relationObjectId,
+extern void get_relation_info(Oid relationObjectId,
 			  bool *hasindex, long *pages, double *tuples);
 
 extern List *find_secondary_indexes(Oid relationObjectId);
@@ -26,15 +26,15 @@ extern List *find_inheritance_children(Oid inhparent);
 
 extern bool has_subclass(Oid relationId);
 
-extern Selectivity restriction_selectivity(Oid functionObjectId,
-						Oid operatorObjectId,
-						Oid relationObjectId,
-						AttrNumber attributeNumber,
-						Datum constValue,
-						int constFlag);
+extern bool has_unique_index(RelOptInfo *rel, AttrNumber attno);
 
-extern Selectivity join_selectivity(Oid functionObjectId, Oid operatorObjectId,
-				 Oid relationObjectId1, AttrNumber attributeNumber1,
-				 Oid relationObjectId2, AttrNumber attributeNumber2);
+extern Selectivity restriction_selectivity(Query *root,
+										   Oid operator,
+										   List *args,
+										   int varRelid);
+
+extern Selectivity join_selectivity(Query *root,
+									Oid operator,
+									List *args);
 
 #endif	 /* PLANCAT_H */
diff --git a/src/include/optimizer/prep.h b/src/include/optimizer/prep.h
index b1ca99fed520cbdab5c3f45a9fb0ac87dfb1569d..1888248ed531a0d9935652b0b84db7b96cb6199f 100644
--- a/src/include/optimizer/prep.h
+++ b/src/include/optimizer/prep.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: prep.h,v 1.27 2001/03/22 04:00:55 momjian Exp $
+ * $Id: prep.h,v 1.28 2001/05/20 20:28:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,7 +35,9 @@ extern List *preprocess_targetlist(List *tlist, int command_type,
 extern Plan *plan_set_operations(Query *parse);
 
 extern List *find_all_inheritors(Oid parentrel);
-extern List *expand_inherted_rtentry(Query *parse, Index rti);
+
+extern List *expand_inherted_rtentry(Query *parse, Index rti,
+									 bool dup_parent);
 
 extern Node *adjust_inherited_attrs(Node *node,
 					   Index old_rt_index, Oid old_relid,
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 33deafdd94a91c3bcc19e042934be5e303060396..cc71f44d1d798d084838ddb1004fc4255a898d26 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -418,16 +418,15 @@ WHERE p1.oprcode = p2.oid AND
 -- If oprrest is set, the operator must return boolean,
 -- and it must link to a proc with the right signature
 -- to be a restriction selectivity estimator.
--- The proc signature we want is: float8 proc(oid, oid, int2, <any>, int4)
+-- The proc signature we want is: float8 proc(opaque, oid, opaque, int4)
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprrest = p2.oid AND
     (p1.oprresult != 16 OR
      p2.prorettype != 701 OR p2.proretset OR
-     p2.pronargs != 5 OR
-     p2.proargtypes[0] != 26 OR p2.proargtypes[1] != 26 OR
-     p2.proargtypes[2] != 21 OR p2.proargtypes[3] != 0 OR
-     p2.proargtypes[4] != 23);
+     p2.pronargs != 4 OR
+     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR
+     p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 23);
  oid | oprname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
@@ -435,16 +434,15 @@ WHERE p1.oprrest = p2.oid AND
 -- If oprjoin is set, the operator must be a binary boolean op,
 -- and it must link to a proc with the right signature
 -- to be a join selectivity estimator.
--- The proc signature we want is: float8 proc(oid, oid, int2, oid, int2)
+-- The proc signature we want is: float8 proc(opaque, oid, opaque)
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprjoin = p2.oid AND
     (p1.oprkind != 'b' OR p1.oprresult != 16 OR
      p2.prorettype != 701 OR p2.proretset OR
-     p2.pronargs != 5 OR
-     p2.proargtypes[0] != 26 OR p2.proargtypes[1] != 26 OR
-     p2.proargtypes[2] != 21 OR p2.proargtypes[3] != 26 OR
-     p2.proargtypes[4] != 21);
+     p2.pronargs != 3 OR
+     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR
+     p2.proargtypes[2] != 0);
  oid | oprname | oid | proname 
 -----+---------+-----+---------
 (0 rows)
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index a79d0eae754e157f2db6a3146331fb290edfdead..1cb5cd18c92cbc2fb6a6dc6c95e38eb9d21a0b6a 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -349,32 +349,30 @@ WHERE p1.oprcode = p2.oid AND
 -- If oprrest is set, the operator must return boolean,
 -- and it must link to a proc with the right signature
 -- to be a restriction selectivity estimator.
--- The proc signature we want is: float8 proc(oid, oid, int2, <any>, int4)
+-- The proc signature we want is: float8 proc(opaque, oid, opaque, int4)
 
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprrest = p2.oid AND
     (p1.oprresult != 16 OR
      p2.prorettype != 701 OR p2.proretset OR
-     p2.pronargs != 5 OR
-     p2.proargtypes[0] != 26 OR p2.proargtypes[1] != 26 OR
-     p2.proargtypes[2] != 21 OR p2.proargtypes[3] != 0 OR
-     p2.proargtypes[4] != 23);
+     p2.pronargs != 4 OR
+     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR
+     p2.proargtypes[2] != 0 OR p2.proargtypes[3] != 23);
 
 -- If oprjoin is set, the operator must be a binary boolean op,
 -- and it must link to a proc with the right signature
 -- to be a join selectivity estimator.
--- The proc signature we want is: float8 proc(oid, oid, int2, oid, int2)
+-- The proc signature we want is: float8 proc(opaque, oid, opaque)
 
 SELECT p1.oid, p1.oprname, p2.oid, p2.proname
 FROM pg_operator AS p1, pg_proc AS p2
 WHERE p1.oprjoin = p2.oid AND
     (p1.oprkind != 'b' OR p1.oprresult != 16 OR
      p2.prorettype != 701 OR p2.proretset OR
-     p2.pronargs != 5 OR
-     p2.proargtypes[0] != 26 OR p2.proargtypes[1] != 26 OR
-     p2.proargtypes[2] != 21 OR p2.proargtypes[3] != 26 OR
-     p2.proargtypes[4] != 21);
+     p2.pronargs != 3 OR
+     p2.proargtypes[0] != 0 OR p2.proargtypes[1] != 26 OR
+     p2.proargtypes[2] != 0);
 
 -- **************** pg_aggregate ****************