From be03eb25f34c9c95c400504ef76c8abe0081d09f Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sun, 20 May 2001 20:28:20 +0000
Subject: [PATCH] Modify optimizer data structures so that IndexOptInfo lists
 built for create_index_paths are not immediately discarded, but are available
 for subsequent planner work.  This allows avoiding redundant syscache lookups
 in several places.  Change interface to operator selectivity estimation
 procedures to allow faster and more flexible estimation. Initdb forced due to
 change of pg_proc entries for selectivity functions!

---
 src/backend/catalog/pg_operator.c        |  39 +-
 src/backend/nodes/copyfuncs.c            |  47 +-
 src/backend/nodes/equalfuncs.c           |  10 +-
 src/backend/nodes/outfuncs.c             |  62 +-
 src/backend/nodes/readfuncs.c            |  90 +--
 src/backend/optimizer/path/allpaths.c    |  69 +-
 src/backend/optimizer/path/clausesel.c   | 123 +--
 src/backend/optimizer/path/costsize.c    |   4 +-
 src/backend/optimizer/path/indxpath.c    |  14 +-
 src/backend/optimizer/path/joinrels.c    |   8 +-
 src/backend/optimizer/path/orindxpath.c  |  40 +-
 src/backend/optimizer/plan/createplan.c  | 133 ++-
 src/backend/optimizer/plan/initsplan.c   |  26 +-
 src/backend/optimizer/plan/planmain.c    |   3 +-
 src/backend/optimizer/plan/planner.c     |   5 +-
 src/backend/optimizer/prep/prepunion.c   |  21 +-
 src/backend/optimizer/util/clauses.c     | 199 +----
 src/backend/optimizer/util/pathnode.c    |   6 +-
 src/backend/optimizer/util/plancat.c     | 151 ++--
 src/backend/optimizer/util/relnode.c     | 135 +++-
 src/backend/utils/adt/selfuncs.c         | 978 ++++++++++++++---------
 src/include/catalog/catversion.h         |   4 +-
 src/include/catalog/pg_proc.h            |  62 +-
 src/include/nodes/parsenodes.h           |   3 +-
 src/include/nodes/relation.h             |  47 +-
 src/include/optimizer/clauses.h          |  15 +-
 src/include/optimizer/pathnode.h         |   9 +-
 src/include/optimizer/paths.h            |   4 +-
 src/include/optimizer/plancat.h          |  22 +-
 src/include/optimizer/prep.h             |   6 +-
 src/test/regress/expected/opr_sanity.out |  18 +-
 src/test/regress/sql/opr_sanity.sql      |  18 +-
 32 files changed, 1150 insertions(+), 1221 deletions(-)

diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c
index 11494b0172a..db54ceede2f 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 ee5a803b802..a5a968515e6 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 284a534aa96..a89a8f7f335 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 2c0cfed7ee4..ebcacd49750 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 4c0c1b03ef5..ad832d7ca9e 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 243ff7c5a72..afb3259e736 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 2699b56cb37..78407fb833a 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 dddca240e95..b4379e4b39b 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 ca19465c897..a5f5bb151da 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 929a977112d..3bde257a37e 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 d4e467c3e04..25cbc3e4fa2 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 2d264c46881..81e7fec0427 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 c62fd5ecd7d..3b3c761bca6 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 b2b362e84a5..2f52e694d13 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 0aba4808c16..fbed3d6d092 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 ede4159d970..42cc47fa4ac 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 8bd6ef6f68b..e0cc97e3a1d 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 407c132b4f7..801b328d817 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 ee3523553e8..749390a4d2d 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 b4764ab6f8c..86d923116a4 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 d7633dc47dd..07c4da115f5 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 44ddb7611a5..e04f97e0ec8 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 7d7acf96f73..f905a063c63 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 63b1b1046a8..cfea5222965 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 c76d9b4af71..33927edc18d 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 a02ef9c77c3..79bb7a13593 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 0839feb4b2f..85ba6936f48 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 ac67f6a6551..39afe74d2ad 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 5d3062d981c..a103f51e3f1 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 b1ca99fed52..1888248ed53 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 33deafdd94a..cc71f44d1d7 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 a79d0eae754..1cb5cd18c92 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 ****************
 
-- 
GitLab