diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 2272d7bf3fb625c48a557d944aeb242c23b2cc73..aea5e45b231eb1d7f86ecccefb9be8234cea5bae 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.58 2003/08/04 02:39:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/analyze.c,v 1.59 2003/08/17 19:58:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -390,7 +390,6 @@ examine_attribute(Relation onerel, int attnum)
 {
 	Form_pg_attribute attr = onerel->rd_att->attrs[attnum - 1];
 	Operator	func_operator;
-	Oid			oprrest;
 	HeapTuple	typtuple;
 	Oid			eqopr = InvalidOid;
 	Oid			eqfunc = InvalidOid;
@@ -409,12 +408,8 @@ examine_attribute(Relation onerel, int attnum)
 	func_operator = equality_oper(attr->atttypid, true);
 	if (func_operator != NULL)
 	{
-		oprrest = ((Form_pg_operator) GETSTRUCT(func_operator))->oprrest;
-		if (oprrest == F_EQSEL)
-		{
-			eqopr = oprid(func_operator);
-			eqfunc = oprfuncid(func_operator);
-		}
+		eqopr = oprid(func_operator);
+		eqfunc = oprfuncid(func_operator);
 		ReleaseSysCache(func_operator);
 	}
 	if (!OidIsValid(eqfunc))
@@ -447,9 +442,7 @@ examine_attribute(Relation onerel, int attnum)
 	func_operator = ordering_oper(attr->atttypid, true);
 	if (func_operator != NULL)
 	{
-		oprrest = ((Form_pg_operator) GETSTRUCT(func_operator))->oprrest;
-		if (oprrest == F_SCALARLTSEL)
-			ltopr = oprid(func_operator);
+		ltopr = oprid(func_operator);
 		ReleaseSysCache(func_operator);
 	}
 	stats->ltopr = ltopr;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 36baf4690e3ce8e8194d677b2a90440242a5592e..45f4c3b4f7d82db8bd35c813bf8dd6fd5e0cda3d 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.105 2003/08/04 02:39:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.106 2003/08/17 19:58:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -529,7 +529,8 @@ GetDefaultOpClass(Oid attrType, Oid accessMethodId)
 	 * than one exact match, then someone put bogus entries in pg_opclass.
 	 *
 	 * The initial search is done by namespace.c so that we only consider
-	 * opclasses visible in the current namespace search path.
+	 * opclasses visible in the current namespace search path.  (See also
+	 * typcache.c, which applies the same logic, but over all opclasses.)
 	 */
 	for (opclass = OpclassGetCandidates(accessMethodId);
 		 opclass != NULL;
diff --git a/src/backend/commands/opclasscmds.c b/src/backend/commands/opclasscmds.c
index 0677751ead4c2bb89f90e382e0cdda6011957c2c..a93af3e6046ed8c5bcabbf3177c141de2b1e30bd 100644
--- a/src/backend/commands/opclasscmds.c
+++ b/src/backend/commands/opclasscmds.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/opclasscmds.c,v 1.17 2003/08/04 02:39:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/opclasscmds.c,v 1.18 2003/08/17 19:58:04 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -261,7 +261,9 @@ DefineOpClass(CreateOpClassStmt *stmt)
 
 	/*
 	 * If we are creating a default opclass, check there isn't one
-	 * already. (XXX should we restrict this test to visible opclasses?)
+	 * already.  (Note we do not restrict this test to visible opclasses;
+	 * this ensures that typcache.c can find unique solutions to its
+	 * questions.)
 	 */
 	if (stmt->isDefault)
 	{
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index fa1c567df0d022812ba638446392f98e037197b5..55076ec48d34550b31e86b6ada8f98acf7f5cf4f 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.263 2003/08/08 21:41:43 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.264 2003/08/17 19:58:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1402,11 +1402,12 @@ _copyTypeName(TypeName *from)
 	return newnode;
 }
 
-static SortGroupBy *
-_copySortGroupBy(SortGroupBy *from)
+static SortBy *
+_copySortBy(SortBy *from)
 {
-	SortGroupBy *newnode = makeNode(SortGroupBy);
+	SortBy *newnode = makeNode(SortBy);
 
+	COPY_SCALAR_FIELD(sortby_kind);
 	COPY_NODE_FIELD(useOp);
 	COPY_NODE_FIELD(node);
 
@@ -2924,8 +2925,8 @@ copyObject(void *from)
 		case T_TypeCast:
 			retval = _copyTypeCast(from);
 			break;
-		case T_SortGroupBy:
-			retval = _copySortGroupBy(from);
+		case T_SortBy:
+			retval = _copySortBy(from);
 			break;
 		case T_RangeSubselect:
 			retval = _copyRangeSubselect(from);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 5ad5dab1d3ca653662515ee37005a3abda255945..e3a59f4739a674a4b23ee072dba33b85caed2283 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.207 2003/08/08 21:41:43 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.208 2003/08/17 19:58:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1480,8 +1480,9 @@ _equalTypeCast(TypeCast *a, TypeCast *b)
 }
 
 static bool
-_equalSortGroupBy(SortGroupBy *a, SortGroupBy *b)
+_equalSortBy(SortBy *a, SortBy *b)
 {
+	COMPARE_SCALAR_FIELD(sortby_kind);
 	COMPARE_NODE_FIELD(useOp);
 	COMPARE_NODE_FIELD(node);
 
@@ -2045,8 +2046,8 @@ equal(void *a, void *b)
 		case T_TypeCast:
 			retval = _equalTypeCast(a, b);
 			break;
-		case T_SortGroupBy:
-			retval = _equalSortGroupBy(a, b);
+		case T_SortBy:
+			retval = _equalSortBy(a, b);
 			break;
 		case T_RangeSubselect:
 			retval = _equalRangeSubselect(a, b);
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 2f7fd02fb4b4f2e23fbfa3406f578a7355cc1a7a..634cfb3a57c4f5709f4ed6bb220ce009b49a04cb 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.154 2003/08/11 20:46:46 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.155 2003/08/17 19:58:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -629,8 +629,9 @@ create_unique_plan(Query *root, UniquePath *best_path)
 
 			tle = get_tle_by_resno(my_tlist, groupColIdx[groupColPos]);
 			Assert(tle != NULL);
-			sortList = addTargetToSortList(NULL, tle, sortList,
-										   my_tlist, NIL, false);
+			sortList = addTargetToSortList(NULL, tle,
+										   sortList, my_tlist,
+										   SORTBY_ASC, NIL, false);
 		}
 		plan = (Plan *) make_sort_from_sortclauses(root, my_tlist,
 												   subplan, sortList);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 9eea1c8d20ea797ca9a0339c853d69da3e3b8fb0..c29d127acf474280c4979bd2191bb72d8611d52c 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.159 2003/08/04 02:40:01 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.160 2003/08/17 19:58:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1327,7 +1327,9 @@ hash_safe_grouping(Query *parse)
 		Operator	optup;
 		bool		oprcanhash;
 
-		optup = equality_oper(tle->resdom->restype, false);
+		optup = equality_oper(tle->resdom->restype, true);
+		if (!optup)
+			return false;
 		oprcanhash = ((Form_pg_operator) GETSTRUCT(optup))->oprcanhash;
 		ReleaseSysCache(optup);
 		if (!oprcanhash)
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index d3060b76c7a739159c0e642cd7014d95095f7de9..ef6f8a80c5cb0d98ba2898ef3dd1f7387d9f5a39 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.428 2003/08/04 02:40:01 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.429 2003/08/17 19:58:05 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -115,7 +115,7 @@ static void doNegateFloat(Value *v);
 
 	TypeName			*typnam;
 	DefElem				*defelt;
-	SortGroupBy			*sortgroupby;
+	SortBy				*sortby;
 	JoinExpr			*jexpr;
 	IndexElem			*ielem;
 	Alias				*alias;
@@ -189,7 +189,7 @@ static void doNegateFloat(Value *v);
 				database_name access_method_clause access_method attr_name
 				index_name name function_name file_name
 
-%type <list>	func_name handler_name qual_Op qual_all_Op OptUseOp
+%type <list>	func_name handler_name qual_Op qual_all_Op
 				opt_class opt_validator
 
 %type <range>	qualified_name OptConstrFromTable
@@ -278,7 +278,7 @@ static void doNegateFloat(Value *v);
 %type <value>	NumericOnly FloatOnly IntegerOnly
 %type <columnref>	columnref
 %type <alias>	alias_clause
-%type <sortgroupby>		sortby
+%type <sortby>	sortby
 %type <ielem>	index_elem
 %type <node>	table_ref
 %type <jexpr>	joined_table
@@ -4577,21 +4577,34 @@ sortby_list:
 			| sortby_list ',' sortby				{ $$ = lappend($1, $3); }
 		;
 
-sortby:		a_expr OptUseOp
+sortby:		a_expr USING qual_all_Op
 				{
-					$$ = makeNode(SortGroupBy);
+					$$ = makeNode(SortBy);
 					$$->node = $1;
-					$$->useOp = $2;
+					$$->sortby_kind = SORTBY_USING;
+					$$->useOp = $3;
+				}
+			| a_expr ASC
+				{
+					$$ = makeNode(SortBy);
+					$$->node = $1;
+					$$->sortby_kind = SORTBY_ASC;
+					$$->useOp = NIL;
+				}
+			| a_expr DESC
+				{
+					$$ = makeNode(SortBy);
+					$$->node = $1;
+					$$->sortby_kind = SORTBY_DESC;
+					$$->useOp = NIL;
+				}
+			| a_expr
+				{
+					$$ = makeNode(SortBy);
+					$$->node = $1;
+					$$->sortby_kind = SORTBY_ASC;	/* default */
+					$$->useOp = NIL;
 				}
-		;
-
-OptUseOp:	USING qual_all_Op						{ $$ = $2; }
-			| ASC
-							{ $$ = makeList1(makeString("<")); }
-			| DESC
-							{ $$ = makeList1(makeString(">")); }
-			| /*EMPTY*/
-							{ $$ = makeList1(makeString("<"));	/*default*/ }
 		;
 
 
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index ebc3ed23eecf5d8dcbe5223a9c7fa5b2cf0d1bbd..b31e70205dd890cf0bfea0d130b3b6aa2b728d53 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.121 2003/08/07 19:20:22 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.122 2003/08/17 19:58:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1295,7 +1295,7 @@ transformSortClause(ParseState *pstate,
 
 	foreach(olitem, orderlist)
 	{
-		SortGroupBy *sortby = lfirst(olitem);
+		SortBy	   *sortby = lfirst(olitem);
 		TargetEntry *tle;
 
 		tle = findTargetlistEntry(pstate, sortby->node,
@@ -1303,7 +1303,9 @@ transformSortClause(ParseState *pstate,
 
 		sortlist = addTargetToSortList(pstate, tle,
 									   sortlist, targetlist,
-									   sortby->useOp, resolveUnknown);
+									   sortby->sortby_kind,
+									   sortby->useOp,
+									   resolveUnknown);
 	}
 
 	return sortlist;
@@ -1409,7 +1411,7 @@ transformDistinctClause(ParseState *pstate, List *distinctlist,
 			{
 				*sortClause = addTargetToSortList(pstate, tle,
 												  *sortClause, targetlist,
-												  NIL, true);
+												  SORTBY_ASC, NIL, true);
 
 				/*
 				 * Probably, the tle should always have been added at the
@@ -1457,7 +1459,8 @@ addAllTargetsToSortList(ParseState *pstate, List *sortlist,
 		if (!tle->resdom->resjunk)
 			sortlist = addTargetToSortList(pstate, tle,
 										   sortlist, targetlist,
-										   NIL, resolveUnknown);
+										   SORTBY_ASC, NIL,
+										   resolveUnknown);
 	}
 	return sortlist;
 }
@@ -1478,7 +1481,8 @@ addAllTargetsToSortList(ParseState *pstate, List *sortlist,
 List *
 addTargetToSortList(ParseState *pstate, TargetEntry *tle,
 					List *sortlist, List *targetlist,
-					List *opname, bool resolveUnknown)
+					int sortby_kind, List *sortby_opname,
+					bool resolveUnknown)
 {
 	/* avoid making duplicate sortlist entries */
 	if (!targetIsInSortList(tle, sortlist))
@@ -1499,13 +1503,25 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
 
 		sortcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist);
 
-		if (opname)
-			sortcl->sortop = compatible_oper_opid(opname,
-												  restype,
-												  restype,
-												  false);
-		else
-			sortcl->sortop = ordering_oper_opid(restype);
+		switch (sortby_kind)
+		{
+			case SORTBY_ASC:
+				sortcl->sortop = ordering_oper_opid(restype);
+				break;
+			case SORTBY_DESC:
+				sortcl->sortop = reverse_ordering_oper_opid(restype);
+				break;
+			case SORTBY_USING:
+				Assert(sortby_opname != NIL);
+				sortcl->sortop = compatible_oper_opid(sortby_opname,
+													  restype,
+													  restype,
+													  false);
+				break;
+			default:
+				elog(ERROR, "unrecognized sortby_kind: %d", sortby_kind);
+				break;
+		}
 
 		sortlist = lappend(sortlist, sortcl);
 	}
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index e5d2ab2d05f54b0a41841d4e7e12c48ed62b066c..244cd769646200f102d8c982db8993533e0d3707 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.73 2003/08/04 02:40:02 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.74 2003/08/17 19:58:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,6 +26,7 @@
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
 
 
 static Oid binary_oper_exact(Oid arg1, Oid arg2,
@@ -135,52 +136,49 @@ LookupOperNameTypeNames(List *opername, TypeName *oprleft,
 Operator
 equality_oper(Oid argtype, bool noError)
 {
+	TypeCacheEntry *typentry;
+	Oid			oproid;
 	Operator	optup;
-	Oid			elem_type;
+
+	/*
+	 * Look for an "=" operator for the datatype.  We require it to be
+	 * an exact or binary-compatible match, since most callers are not
+	 * prepared to cope with adding any run-time type coercion steps.
+	 */
+	typentry = lookup_type_cache(argtype, TYPECACHE_EQ_OPR);
+	oproid = typentry->eq_opr;
 
 	/*
 	 * If the datatype is an array, then we can use array_eq ... but only
-	 * if there is a suitable equality operator for the element type. (We
-	 * must run this test first, since compatible_oper will find array_eq,
-	 * but would not notice the lack of an element operator.)
+	 * if there is a suitable equality operator for the element type.
+	 * (This check is not in the raw typcache.c code ... should it be?)
 	 */
-	elem_type = get_element_type(argtype);
-	if (OidIsValid(elem_type))
+	if (oproid == ARRAY_EQ_OP)
 	{
-		optup = equality_oper(elem_type, true);
-		if (optup != NULL)
+		Oid		elem_type = get_element_type(argtype);
+
+		if (OidIsValid(elem_type))
 		{
-			ReleaseSysCache(optup);
-			return SearchSysCache(OPEROID,
-								  ObjectIdGetDatum(ARRAY_EQ_OP),
-								  0, 0, 0);
+			optup = equality_oper(elem_type, true);
+			if (optup != NULL)
+				ReleaseSysCache(optup);
+			else
+				oproid = InvalidOid;	/* element type has no "=" */
 		}
+		else
+			oproid = InvalidOid;		/* bogus array type? */
 	}
-	else
-	{
-		/*
-		 * Look for an "=" operator for the datatype.  We require it to be
-		 * an exact or binary-compatible match, since most callers are not
-		 * prepared to cope with adding any run-time type coercion steps.
-		 */
-		optup = compatible_oper(makeList1(makeString("=")),
-								argtype, argtype, true);
-		if (optup != NULL)
-		{
-			/*
-			 * Only believe that it's equality if it's mergejoinable,
-			 * hashjoinable, or uses eqsel() as oprrest.
-			 */
-			Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(optup);
-
-			if (OidIsValid(pgopform->oprlsortop) ||
-				pgopform->oprcanhash ||
-				pgopform->oprrest == F_EQSEL)
-				return optup;
 
-			ReleaseSysCache(optup);
-		}
+	if (OidIsValid(oproid))
+	{
+		optup = SearchSysCache(OPEROID,
+							   ObjectIdGetDatum(oproid),
+							   0, 0, 0);
+		if (optup == NULL)		/* should not fail */
+			elog(ERROR, "cache lookup failed for operator %u", oproid);
+		return optup;
 	}
+
 	if (!noError)
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -197,53 +195,119 @@ equality_oper(Oid argtype, bool noError)
 Operator
 ordering_oper(Oid argtype, bool noError)
 {
+	TypeCacheEntry *typentry;
+	Oid			oproid;
 	Operator	optup;
-	Oid			elem_type;
+
+	/*
+	 * Look for a "<" operator for the datatype.  We require it to be
+	 * an exact or binary-compatible match, since most callers are not
+	 * prepared to cope with adding any run-time type coercion steps.
+	 *
+	 * Note: the search algorithm used by typcache.c ensures that if a "<"
+	 * operator is returned, it will be consistent with the "=" operator
+	 * returned by equality_oper.  This is critical for sorting and grouping
+	 * purposes.
+	 */
+	typentry = lookup_type_cache(argtype, TYPECACHE_LT_OPR);
+	oproid = typentry->lt_opr;
 
 	/*
 	 * If the datatype is an array, then we can use array_lt ... but only
-	 * if there is a suitable ordering operator for the element type. (We
-	 * must run this test first, since the code below would find array_lt
-	 * if there's an element = operator, but would not notice the lack of
-	 * an element < operator.)
+	 * if there is a suitable less-than operator for the element type.
+	 * (This check is not in the raw typcache.c code ... should it be?)
 	 */
-	elem_type = get_element_type(argtype);
-	if (OidIsValid(elem_type))
+	if (oproid == ARRAY_LT_OP)
 	{
-		optup = ordering_oper(elem_type, true);
-		if (optup != NULL)
+		Oid		elem_type = get_element_type(argtype);
+
+		if (OidIsValid(elem_type))
 		{
-			ReleaseSysCache(optup);
-			return SearchSysCache(OPEROID,
-								  ObjectIdGetDatum(ARRAY_LT_OP),
-								  0, 0, 0);
+			optup = ordering_oper(elem_type, true);
+			if (optup != NULL)
+				ReleaseSysCache(optup);
+			else
+				oproid = InvalidOid;	/* element type has no "<" */
 		}
+		else
+			oproid = InvalidOid;		/* bogus array type? */
 	}
-	else
+
+	if (OidIsValid(oproid))
 	{
-		/*
-		 * Find the type's equality operator, and use its lsortop (it
-		 * *must* be mergejoinable).  We use this definition because for
-		 * sorting and grouping purposes, it's important that the equality
-		 * and ordering operators are consistent.
-		 */
-		optup = equality_oper(argtype, noError);
-		if (optup != NULL)
-		{
-			Oid			lsortop;
+		optup = SearchSysCache(OPEROID,
+							   ObjectIdGetDatum(oproid),
+							   0, 0, 0);
+		if (optup == NULL)		/* should not fail */
+			elog(ERROR, "cache lookup failed for operator %u", oproid);
+		return optup;
+	}
 
-			lsortop = ((Form_pg_operator) GETSTRUCT(optup))->oprlsortop;
-			ReleaseSysCache(optup);
-			if (OidIsValid(lsortop))
-			{
-				optup = SearchSysCache(OPEROID,
-									   ObjectIdGetDatum(lsortop),
-									   0, 0, 0);
-				if (optup != NULL)
-					return optup;
-			}
+	if (!noError)
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+			errmsg("could not identify an ordering operator for type %s",
+				   format_type_be(argtype)),
+				 errhint("Use an explicit ordering operator or modify the query.")));
+	return NULL;
+}
+
+/*
+ * reverse_ordering_oper - identify DESC sort operator (">") for a datatype
+ *
+ * On failure, return NULL if noError, else report a standard error
+ */
+Operator
+reverse_ordering_oper(Oid argtype, bool noError)
+{
+	TypeCacheEntry *typentry;
+	Oid			oproid;
+	Operator	optup;
+
+	/*
+	 * Look for a ">" operator for the datatype.  We require it to be
+	 * an exact or binary-compatible match, since most callers are not
+	 * prepared to cope with adding any run-time type coercion steps.
+	 *
+	 * Note: the search algorithm used by typcache.c ensures that if a ">"
+	 * operator is returned, it will be consistent with the "=" operator
+	 * returned by equality_oper.  This is critical for sorting and grouping
+	 * purposes.
+	 */
+	typentry = lookup_type_cache(argtype, TYPECACHE_GT_OPR);
+	oproid = typentry->gt_opr;
+
+	/*
+	 * If the datatype is an array, then we can use array_gt ... but only
+	 * if there is a suitable greater-than operator for the element type.
+	 * (This check is not in the raw typcache.c code ... should it be?)
+	 */
+	if (oproid == ARRAY_GT_OP)
+	{
+		Oid		elem_type = get_element_type(argtype);
+
+		if (OidIsValid(elem_type))
+		{
+			optup = reverse_ordering_oper(elem_type, true);
+			if (optup != NULL)
+				ReleaseSysCache(optup);
+			else
+				oproid = InvalidOid;	/* element type has no ">" */
 		}
+		else
+			oproid = InvalidOid;		/* bogus array type? */
 	}
+
+	if (OidIsValid(oproid))
+	{
+		optup = SearchSysCache(OPEROID,
+							   ObjectIdGetDatum(oproid),
+							   0, 0, 0);
+		if (optup == NULL)		/* should not fail */
+			elog(ERROR, "cache lookup failed for operator %u", oproid);
+		return optup;
+	}
+
 	if (!noError)
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
@@ -286,16 +350,16 @@ ordering_oper_opid(Oid argtype)
 }
 
 /*
- * ordering_oper_funcid - convenience routine for oprfuncid(ordering_oper())
+ * reverse_ordering_oper_opid - convenience routine for oprid(reverse_ordering_oper())
  */
 Oid
-ordering_oper_funcid(Oid argtype)
+reverse_ordering_oper_opid(Oid argtype)
 {
 	Operator	optup;
 	Oid			result;
 
-	optup = ordering_oper(argtype, false);
-	result = oprfuncid(optup);
+	optup = reverse_ordering_oper(argtype, false);
+	result = oprid(optup);
 	ReleaseSysCache(optup);
 	return result;
 }
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 504e8f5565234c2540a67c68bd146ccf397d2a05..8d5a675006bbfc2fa60f42253e6984e74cd880e8 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.95 2003/08/14 14:19:07 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.96 2003/08/17 19:58:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -480,6 +480,23 @@ aclitem_eq(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(result);
 }
 
+/*
+ * aclitem hash function
+ *
+ * We make aclitems hashable not so much because anyone is likely to hash
+ * them, as because we want array equality to work on aclitem arrays, and
+ * with the typcache mechanism we must have a hash or btree opclass.
+ */
+Datum
+hash_aclitem(PG_FUNCTION_ARGS)
+{
+	AclItem    *a = PG_GETARG_ACLITEM_P(0);
+
+	/* not very bright, but avoids any issue of padding in struct */
+	PG_RETURN_UINT32((uint32) (a->ai_privs + a->ai_grantee + a->ai_grantor));
+}
+
+
 /*
  * acldefault()  --- create an ACL describing default access permissions
  *
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 37504718ad5e234f6b20a886836ec358238b13b4..46f7881d64801e689ea21429cc0f7a3062db181d 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.98 2003/08/15 00:22:26 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.99 2003/08/17 19:58:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@
 #include "utils/memutils.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
+#include "utils/typcache.h"
 
 
 /*----------
@@ -2341,6 +2342,9 @@ deconstruct_array(ArrayType *array,
  *		  compares two arrays for equality
  * result :
  *		  returns true if the arrays are equal, false otherwise.
+ *
+ * Note: we do not use array_cmp here, since equality may be meaningful in
+ * datatypes that don't have a total ordering (and hence no btree support).
  *-----------------------------------------------------------------------------
  */
 Datum
@@ -2357,13 +2361,12 @@ array_eq(PG_FUNCTION_ARGS)
 	int			nitems1 = ArrayGetNItems(ndims1, dims1);
 	int			nitems2 = ArrayGetNItems(ndims2, dims2);
 	Oid			element_type = ARR_ELEMTYPE(array1);
-	FmgrInfo   *ae_fmgr_info = fcinfo->flinfo;
 	bool		result = true;
+	TypeCacheEntry *typentry;
 	int			typlen;
 	bool		typbyval;
 	char		typalign;
 	int			i;
-	ArrayMetaState *my_extra;
 	FunctionCallInfoData locfcinfo;
 
 	if (element_type != ARR_ELEMTYPE(array2))
@@ -2379,38 +2382,31 @@ array_eq(PG_FUNCTION_ARGS)
 		/*
 		 * We arrange to look up the equality function only once per
 		 * series of calls, assuming the element type doesn't change
-		 * underneath us.
+		 * underneath us.  The typcache is used so that we have no
+		 * memory leakage when being used as an index support function.
 		 */
-		my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
-		if (my_extra == NULL)
+		typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+		if (typentry == NULL ||
+			typentry->type_id != element_type)
 		{
-			ae_fmgr_info->fn_extra = MemoryContextAlloc(ae_fmgr_info->fn_mcxt,
-												 sizeof(ArrayMetaState));
-			my_extra = (ArrayMetaState *) ae_fmgr_info->fn_extra;
-			my_extra->element_type = InvalidOid;
-		}
-
-		if (my_extra->element_type != element_type)
-		{
-			Oid			opfuncid = equality_oper_funcid(element_type);
-
-			get_typlenbyvalalign(element_type,
-								 &my_extra->typlen,
-								 &my_extra->typbyval,
-								 &my_extra->typalign);
-			fmgr_info_cxt(opfuncid, &my_extra->proc,
-						  ae_fmgr_info->fn_mcxt);
-			my_extra->element_type = element_type;
+			typentry = lookup_type_cache(element_type,
+										 TYPECACHE_EQ_OPR_FINFO);
+			if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+				ereport(ERROR,
+						(errcode(ERRCODE_UNDEFINED_FUNCTION),
+						 errmsg("could not identify an equality operator for type %s",
+								format_type_be(element_type))));
+			fcinfo->flinfo->fn_extra = (void *) typentry;
 		}
-		typlen = my_extra->typlen;
-		typbyval = my_extra->typbyval;
-		typalign = my_extra->typalign;
+		typlen = typentry->typlen;
+		typbyval = typentry->typbyval;
+		typalign = typentry->typalign;
 
 		/*
 		 * apply the operator to each pair of array elements.
 		 */
 		MemSet(&locfcinfo, 0, sizeof(locfcinfo));
-		locfcinfo.flinfo = &my_extra->proc;
+		locfcinfo.flinfo = &typentry->eq_opr_finfo;
 		locfcinfo.nargs = 2;
 
 		/* Loop over source data */
@@ -2519,23 +2515,14 @@ array_cmp(FunctionCallInfo fcinfo)
 	int			nitems1 = ArrayGetNItems(ndims1, dims1);
 	int			nitems2 = ArrayGetNItems(ndims2, dims2);
 	Oid			element_type = ARR_ELEMTYPE(array1);
-	FmgrInfo   *ac_fmgr_info = fcinfo->flinfo;
 	int			result = 0;
+	TypeCacheEntry *typentry;
 	int			typlen;
 	bool		typbyval;
 	char		typalign;
 	int			min_nitems;
 	int			i;
-	typedef struct
-	{
-		Oid			element_type;
-		int16		typlen;
-		bool		typbyval;
-		char		typalign;
-		FmgrInfo	eqproc;
-		FmgrInfo	ordproc;
-	} ac_extra;
-	ac_extra   *my_extra;
+	FunctionCallInfoData locfcinfo;
 
 	if (element_type != ARR_ELEMTYPE(array2))
 		ereport(ERROR,
@@ -2543,37 +2530,34 @@ array_cmp(FunctionCallInfo fcinfo)
 			errmsg("cannot compare arrays of different element types")));
 
 	/*
-	 * We arrange to look up the element type info and related functions
-	 * only once per series of calls, assuming the element type doesn't
-	 * change underneath us.
+	 * We arrange to look up the comparison function only once per series of
+	 * calls, assuming the element type doesn't change underneath us.
+	 * The typcache is used so that we have no memory leakage when being used
+	 * as an index support function.
 	 */
-	my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
-	if (my_extra == NULL)
+	typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+	if (typentry == NULL ||
+		typentry->type_id != element_type)
 	{
-		ac_fmgr_info->fn_extra = MemoryContextAlloc(ac_fmgr_info->fn_mcxt,
-													sizeof(ac_extra));
-		my_extra = (ac_extra *) ac_fmgr_info->fn_extra;
-		my_extra->element_type = InvalidOid;
+		typentry = lookup_type_cache(element_type,
+									 TYPECACHE_CMP_PROC_FINFO);
+		if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
+			ereport(ERROR,
+					(errcode(ERRCODE_UNDEFINED_FUNCTION),
+					 errmsg("could not identify a comparison function for type %s",
+							format_type_be(element_type))));
+		fcinfo->flinfo->fn_extra = (void *) typentry;
 	}
+	typlen = typentry->typlen;
+	typbyval = typentry->typbyval;
+	typalign = typentry->typalign;
 
-	if (my_extra->element_type != element_type)
-	{
-		Oid			eqfuncid = equality_oper_funcid(element_type);
-		Oid			ordfuncid = ordering_oper_funcid(element_type);
-
-		get_typlenbyvalalign(element_type,
-							 &my_extra->typlen,
-							 &my_extra->typbyval,
-							 &my_extra->typalign);
-		fmgr_info_cxt(eqfuncid, &my_extra->eqproc,
-					  ac_fmgr_info->fn_mcxt);
-		fmgr_info_cxt(ordfuncid, &my_extra->ordproc,
-					  ac_fmgr_info->fn_mcxt);
-		my_extra->element_type = element_type;
-	}
-	typlen = my_extra->typlen;
-	typbyval = my_extra->typbyval;
-	typalign = my_extra->typalign;
+	/*
+	 * apply the operator to each pair of array elements.
+	 */
+	MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+	locfcinfo.flinfo = &typentry->cmp_proc_finfo;
+	locfcinfo.nargs = 2;
 
 	/* Loop over source data */
 	min_nitems = Min(nitems1, nitems2);
@@ -2581,7 +2565,7 @@ array_cmp(FunctionCallInfo fcinfo)
 	{
 		Datum		elt1;
 		Datum		elt2;
-		Datum		opresult;
+		int32		cmpresult;
 
 		/* Get element pair */
 		elt1 = fetch_att(p1, typbyval, typlen);
@@ -2594,15 +2578,17 @@ array_cmp(FunctionCallInfo fcinfo)
 		p2 = (char *) att_align(p2, typalign);
 
 		/* Compare the pair of elements */
+		locfcinfo.arg[0] = elt1;
+		locfcinfo.arg[1] = elt2;
+		locfcinfo.argnull[0] = false;
+		locfcinfo.argnull[1] = false;
+		locfcinfo.isnull = false;
+		cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
 
-		/* are they equal */
-		opresult = FunctionCall2(&my_extra->eqproc, elt1, elt2);
-		if (DatumGetBool(opresult))
-			continue;
+		if (cmpresult == 0)
+			continue;			/* equal */
 
-		/* nope, see if arg1 is less than arg2 */
-		opresult = FunctionCall2(&my_extra->ordproc, elt1, elt2);
-		if (DatumGetBool(opresult))
+		if (cmpresult < 0)
 		{
 			/* arg1 is less than arg2 */
 			result = -1;
diff --git a/src/backend/utils/adt/cash.c b/src/backend/utils/adt/cash.c
index e33aad28d6400f1aef49526828eeb8ff9c821b19..c4fc14134f0ce71f09156d468e072d64892140f7 100644
--- a/src/backend/utils/adt/cash.c
+++ b/src/backend/utils/adt/cash.c
@@ -9,7 +9,7 @@
  * workings can be found in the book "Software Solutions in C" by
  * Dale Schumacher, Academic Press, ISBN: 0-12-632360-7.
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.59 2003/07/27 04:53:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/cash.c,v 1.60 2003/08/17 19:58:05 tgl Exp $
  */
 
 #include "postgres.h"
@@ -342,6 +342,9 @@ cash_send(PG_FUNCTION_ARGS)
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
+/*
+ * Comparison functions
+ */
 
 Datum
 cash_eq(PG_FUNCTION_ARGS)
@@ -397,6 +400,20 @@ cash_ge(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(c1 >= c2);
 }
 
+Datum
+cash_cmp(PG_FUNCTION_ARGS)
+{
+	Cash		c1 = PG_GETARG_CASH(0);
+	Cash		c2 = PG_GETARG_CASH(1);
+
+	if (c1 > c2)
+		PG_RETURN_INT32(1);
+	else if (c1 == c2)
+		PG_RETURN_INT32(0);
+	else
+		PG_RETURN_INT32(-1);
+}
+
 
 /* cash_pl()
  * Add two cash values.
diff --git a/src/backend/utils/adt/nabstime.c b/src/backend/utils/adt/nabstime.c
index a4f3b061e6fedaa99f860530224836e9d2ecf664..f694349db7abe9db81b493deb55cff32b592bf71 100644
--- a/src/backend/utils/adt/nabstime.c
+++ b/src/backend/utils/adt/nabstime.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.113 2003/08/04 02:40:05 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/nabstime.c,v 1.114 2003/08/17 19:58:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -505,11 +505,11 @@ abstime_finite(PG_FUNCTION_ARGS)
 static int
 abstime_cmp_internal(AbsoluteTime a, AbsoluteTime b)
 {
-/*
- * We consider all INVALIDs to be equal and larger than any non-INVALID.
- * This is somewhat arbitrary; the important thing is to have a
- * consistent sort order.
- */
+	/*
+	 * We consider all INVALIDs to be equal and larger than any non-INVALID.
+	 * This is somewhat arbitrary; the important thing is to have a
+	 * consistent sort order.
+	 */
 	if (a == INVALID_ABSTIME)
 	{
 		if (b == INVALID_ABSTIME)
@@ -904,7 +904,7 @@ tintervalout(PG_FUNCTION_ARGS)
 	char	   *i_str,
 			   *p;
 
-	i_str = (char *) palloc(T_INTERVAL_LEN);	/* ['...' '...'] */
+	i_str = (char *) palloc(T_INTERVAL_LEN);	/* ["..." "..."] */
 	strcpy(i_str, "[\"");
 	if (interval->status == T_INTERVAL_INVAL)
 		strcat(i_str, INVALID_INTERVAL_STR);
@@ -920,7 +920,7 @@ tintervalout(PG_FUNCTION_ARGS)
 		strcat(i_str, p);
 		pfree(p);
 	}
-	strcat(i_str, "\"]\0");
+	strcat(i_str, "\"]");
 	PG_RETURN_CSTRING(i_str);
 }
 
@@ -1190,22 +1190,42 @@ timenow(PG_FUNCTION_ARGS)
 }
 
 /*
- *		reltimeeq		- returns true iff arguments are equal
- *		reltimene		- returns true iff arguments are not equal
- *		reltimelt		- returns true iff t1 less than t2
- *		reltimegt		- returns true iff t1 greater than t2
- *		reltimele		- returns true iff t1 less than or equal to t2
- *		reltimege		- returns true iff t1 greater than or equal to t2
+ * reltime comparison routines
  */
+static int
+reltime_cmp_internal(RelativeTime a, RelativeTime b)
+{
+	/*
+	 * We consider all INVALIDs to be equal and larger than any non-INVALID.
+	 * This is somewhat arbitrary; the important thing is to have a
+	 * consistent sort order.
+	 */
+	if (a == INVALID_RELTIME)
+	{
+		if (b == INVALID_RELTIME)
+			return 0;			/* INVALID = INVALID */
+		else
+			return 1;			/* INVALID > non-INVALID */
+	}
+
+	if (b == INVALID_RELTIME)
+		return -1;				/* non-INVALID < INVALID */
+
+	if (a > b)
+		return 1;
+	else if (a == b)
+		return 0;
+	else
+		return -1;
+}
+
 Datum
 reltimeeq(PG_FUNCTION_ARGS)
 {
 	RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
 	RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
 
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		PG_RETURN_BOOL(false);
-	PG_RETURN_BOOL(t1 == t2);
+	PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) == 0);
 }
 
 Datum
@@ -1214,9 +1234,7 @@ reltimene(PG_FUNCTION_ARGS)
 	RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
 	RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
 
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		PG_RETURN_BOOL(false);
-	PG_RETURN_BOOL(t1 != t2);
+	PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) != 0);
 }
 
 Datum
@@ -1225,9 +1243,7 @@ reltimelt(PG_FUNCTION_ARGS)
 	RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
 	RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
 
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		PG_RETURN_BOOL(false);
-	PG_RETURN_BOOL(t1 < t2);
+	PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) < 0);
 }
 
 Datum
@@ -1236,9 +1252,7 @@ reltimegt(PG_FUNCTION_ARGS)
 	RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
 	RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
 
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		PG_RETURN_BOOL(false);
-	PG_RETURN_BOOL(t1 > t2);
+	PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) > 0);
 }
 
 Datum
@@ -1247,9 +1261,7 @@ reltimele(PG_FUNCTION_ARGS)
 	RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
 	RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
 
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		PG_RETURN_BOOL(false);
-	PG_RETURN_BOOL(t1 <= t2);
+	PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) <= 0);
 }
 
 Datum
@@ -1258,9 +1270,16 @@ reltimege(PG_FUNCTION_ARGS)
 	RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
 	RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
 
-	if (t1 == INVALID_RELTIME || t2 == INVALID_RELTIME)
-		PG_RETURN_BOOL(false);
-	PG_RETURN_BOOL(t1 >= t2);
+	PG_RETURN_BOOL(reltime_cmp_internal(t1, t2) >= 0);
+}
+
+Datum
+btreltimecmp(PG_FUNCTION_ARGS)
+{
+	RelativeTime t1 = PG_GETARG_RELATIVETIME(0);
+	RelativeTime t2 = PG_GETARG_RELATIVETIME(1);
+
+	PG_RETURN_INT32(reltime_cmp_internal(t1, t2));
 }
 
 
@@ -1287,59 +1306,71 @@ tintervalsame(PG_FUNCTION_ARGS)
 	PG_RETURN_BOOL(false);
 }
 
-
 /*
- *		tintervaleq		- returns true iff interval i1 is equal to interval i2
- *		Check length of intervals.
+ * tinterval comparison routines
+ *
+ * Note: comparison is based on the lengths of the intervals, not on
+ * endpoint value.  This is pretty bogus, but since it's only a legacy
+ * datatype I'm not going to propose changing it.
  */
-Datum
-tintervaleq(PG_FUNCTION_ARGS)
+static int
+tinterval_cmp_internal(TimeInterval a, TimeInterval b)
 {
-	TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
-	TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
+	bool		a_invalid;
+	bool		b_invalid;
+	AbsoluteTime a_len;
+	AbsoluteTime b_len;
 
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		PG_RETURN_BOOL(false);
+	/*
+	 * We consider all INVALIDs to be equal and larger than any non-INVALID.
+	 * This is somewhat arbitrary; the important thing is to have a
+	 * consistent sort order.
+	 */
+	a_invalid = ((a->status == T_INTERVAL_INVAL) ||
+				 (a->data[0] == INVALID_ABSTIME) ||
+				 (a->data[1] == INVALID_ABSTIME));
+	b_invalid = ((b->status == T_INTERVAL_INVAL) ||
+				 (b->data[0] == INVALID_ABSTIME) ||
+				 (b->data[1] == INVALID_ABSTIME));
+
+	if (a_invalid)
+	{
+		if (b_invalid)
+			return 0;			/* INVALID = INVALID */
+		else
+			return 1;			/* INVALID > non-INVALID */
+	}
 
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
+	if (b_invalid)
+		return -1;				/* non-INVALID < INVALID */
 
-	if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		PG_RETURN_BOOL(false);
+	a_len = a->data[1] - a->data[0];
+	b_len = b->data[1] - b->data[0];
 
-	PG_RETURN_BOOL((t11 - t10) == (t21 - t20));
+	if (a_len > b_len)
+		return 1;
+	else if (a_len == b_len)
+		return 0;
+	else
+		return -1;
 }
 
 Datum
-tintervalne(PG_FUNCTION_ARGS)
+tintervaleq(PG_FUNCTION_ARGS)
 {
 	TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
 	TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
 
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		PG_RETURN_BOOL(false);
-
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
+	PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) == 0);
+}
 
-	if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		PG_RETURN_BOOL(false);
+Datum
+tintervalne(PG_FUNCTION_ARGS)
+{
+	TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
+	TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
 
-	PG_RETURN_BOOL((t11 - t10) != (t21 - t20));
+	PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) != 0);
 }
 
 Datum
@@ -1347,24 +1378,8 @@ tintervallt(PG_FUNCTION_ARGS)
 {
 	TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
 	TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
-
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		PG_RETURN_BOOL(false);
 
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
-
-	if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		PG_RETURN_BOOL(false);
-
-	PG_RETURN_BOOL((t11 - t10) < (t21 - t20));
+	PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) < 0);
 }
 
 Datum
@@ -1372,24 +1387,8 @@ tintervalle(PG_FUNCTION_ARGS)
 {
 	TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
 	TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
-
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		PG_RETURN_BOOL(false);
 
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
-
-	if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		PG_RETURN_BOOL(false);
-
-	PG_RETURN_BOOL((t11 - t10) <= (t21 - t20));
+	PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) <= 0);
 }
 
 Datum
@@ -1397,24 +1396,8 @@ tintervalgt(PG_FUNCTION_ARGS)
 {
 	TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
 	TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
-
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		PG_RETURN_BOOL(false);
 
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
-
-	if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		PG_RETURN_BOOL(false);
-
-	PG_RETURN_BOOL((t11 - t10) > (t21 - t20));
+	PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) > 0);
 }
 
 Datum
@@ -1422,24 +1405,17 @@ tintervalge(PG_FUNCTION_ARGS)
 {
 	TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
 	TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
-	AbsoluteTime t10,
-				t11,
-				t20,
-				t21;
-
-	if (i1->status == T_INTERVAL_INVAL || i2->status == T_INTERVAL_INVAL)
-		PG_RETURN_BOOL(false);
 
-	t10 = i1->data[0];
-	t11 = i1->data[1];
-	t20 = i2->data[0];
-	t21 = i2->data[1];
+	PG_RETURN_BOOL(tinterval_cmp_internal(i1, i2) >= 0);
+}
 
-	if ((t10 == INVALID_ABSTIME) || (t11 == INVALID_ABSTIME)
-		|| (t20 == INVALID_ABSTIME) || (t21 == INVALID_ABSTIME))
-		PG_RETURN_BOOL(false);
+Datum
+bttintervalcmp(PG_FUNCTION_ARGS)
+{
+	TimeInterval i1 = PG_GETARG_TIMEINTERVAL(0);
+	TimeInterval i2 = PG_GETARG_TIMEINTERVAL(1);
 
-	PG_RETURN_BOOL((t11 - t10) >= (t21 - t20));
+	PG_RETURN_INT32(tinterval_cmp_internal(i1, i2));
 }
 
 
@@ -1652,7 +1628,7 @@ istinterval(char *i_string,
 			break;
 	}
 	p++;
-	/* skip leading blanks up to "'" */
+	/* skip leading blanks up to '"' */
 	while ((c = *p) != '\0')
 	{
 		if (IsSpace(c))
@@ -1680,10 +1656,10 @@ istinterval(char *i_string,
 	/* get the first date */
 	*i_start = DatumGetAbsoluteTime(DirectFunctionCall1(abstimein,
 													CStringGetDatum(p)));
-	/* rechange NULL at the end of the first date to a "'" */
+	/* rechange NULL at the end of the first date to a '"' */
 	*p1 = '"';
 	p = ++p1;
-	/* skip blanks up to "'", beginning of second date */
+	/* skip blanks up to '"', beginning of second date */
 	while ((c = *p) != '\0')
 	{
 		if (IsSpace(c))
@@ -1708,7 +1684,7 @@ istinterval(char *i_string,
 	/* get the second date */
 	*i_end = DatumGetAbsoluteTime(DirectFunctionCall1(abstimein,
 													CStringGetDatum(p)));
-	/* rechange NULL at the end of the first date to a ''' */
+	/* rechange NULL at the end of the first date to a '"' */
 	*p1 = '"';
 	p = ++p1;
 	/* skip blanks up to ']' */
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
index 7476204d89f6fa832bf878ab476b3b158f17bee1..55a6944971e2bc1d5b30705e91a91ee12f9e481e 100644
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
@@ -17,7 +17,7 @@
  *
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  *
- * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.54 2003/08/04 02:40:05 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/utils/adt/ri_triggers.c,v 1.55 2003/08/17 19:58:05 tgl Exp $
  *
  * ----------
  */
@@ -39,6 +39,7 @@
 #include "parser/parse_oper.h"
 #include "rewrite/rewriteHandler.h"
 #include "utils/lsyscache.h"
+#include "utils/typcache.h"
 #include "miscadmin.h"
 
 
@@ -48,7 +49,6 @@
  */
 
 #define RI_INIT_QUERYHASHSIZE			128
-#define RI_INIT_OPREQHASHSIZE			128
 
 #define RI_MATCH_TYPE_UNSPECIFIED		0
 #define RI_MATCH_TYPE_FULL				1
@@ -109,20 +109,11 @@ typedef struct RI_QueryHashEntry
 } RI_QueryHashEntry;
 
 
-typedef struct RI_OpreqHashEntry
-{
-	Oid			typeid;
-	FmgrInfo	oprfmgrinfo;
-} RI_OpreqHashEntry;
-
-
-
 /* ----------
  * Local data
  * ----------
  */
 static HTAB *ri_query_cache = (HTAB *) NULL;
-static HTAB *ri_opreq_cache = (HTAB *) NULL;
 
 
 /* ----------
@@ -3197,8 +3188,8 @@ ri_NullCheck(Relation rel, HeapTuple tup, RI_QueryKey *key, int pairidx)
 /* ----------
  * ri_InitHashTables -
  *
- *	Initialize our internal hash tables for prepared
- *	query plans and equal operators.
+ *	Initialize our internal hash table for prepared
+ *	query plans.
  * ----------
  */
 static void
@@ -3212,12 +3203,6 @@ ri_InitHashTables(void)
 	ctl.hash = tag_hash;
 	ri_query_cache = hash_create("RI query cache", RI_INIT_QUERYHASHSIZE,
 								 &ctl, HASH_ELEM | HASH_FUNCTION);
-
-	ctl.keysize = sizeof(Oid);
-	ctl.entrysize = sizeof(RI_OpreqHashEntry);
-	ctl.hash = tag_hash;
-	ri_opreq_cache = hash_create("RI OpReq cache", RI_INIT_OPREQHASHSIZE,
-								 &ctl, HASH_ELEM | HASH_FUNCTION);
 }
 
 
@@ -3438,57 +3423,22 @@ ri_OneKeyEqual(Relation rel, int column, HeapTuple oldtup, HeapTuple newtup,
 static bool
 ri_AttributesEqual(Oid typeid, Datum oldvalue, Datum newvalue)
 {
-	RI_OpreqHashEntry *entry;
-	bool		found;
+	TypeCacheEntry *typentry;
 
 	/*
-	 * On the first call initialize the hashtable
+	 * Find the data type in the typcache, and ask for eq_opr info.
 	 */
-	if (!ri_opreq_cache)
-		ri_InitHashTables();
+	typentry = lookup_type_cache(typeid, TYPECACHE_EQ_OPR_FINFO);
 
-	/*
-	 * Try to find the '=' operator for this type in our cache
-	 */
-	entry = (RI_OpreqHashEntry *) hash_search(ri_opreq_cache,
-											  (void *) &typeid,
-											  HASH_FIND, NULL);
-
-	/*
-	 * If not found, lookup the operator, then do the function manager
-	 * lookup, and remember that info.
-	 */
-	if (!entry)
-	{
-		Oid			opr_proc;
-		FmgrInfo	finfo;
-
-		opr_proc = equality_oper_funcid(typeid);
-
-		/*
-		 * Since fmgr_info could fail, call it *before* creating the
-		 * hashtable entry --- otherwise we could ereport leaving an
-		 * incomplete entry in the hashtable.  Also, because this will be
-		 * a permanent table entry, we must make sure any subsidiary
-		 * structures of the fmgr record are kept in TopMemoryContext.
-		 */
-		fmgr_info_cxt(opr_proc, &finfo, TopMemoryContext);
-
-		entry = (RI_OpreqHashEntry *) hash_search(ri_opreq_cache,
-												  (void *) &typeid,
-												  HASH_ENTER, &found);
-		if (entry == NULL)
-			ereport(ERROR,
-					(errcode(ERRCODE_OUT_OF_MEMORY),
-					 errmsg("out of memory")));
-
-		entry->typeid = typeid;
-		memcpy(&(entry->oprfmgrinfo), &finfo, sizeof(FmgrInfo));
-	}
+	if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+				 errmsg("could not identify an equality operator for type %s",
+						format_type_be(typeid))));
 
 	/*
 	 * Call the type specific '=' function
 	 */
-	return DatumGetBool(FunctionCall2(&(entry->oprfmgrinfo),
+	return DatumGetBool(FunctionCall2(&(typentry->eq_opr_finfo),
 									  oldvalue, newvalue));
 }
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 83989292d6ae21ad8bd1268eb1caa20e45a96288..5504251bc471d9157cf43e8c3b0d6f7dfd73079a 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.151 2003/08/11 23:04:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.152 2003/08/17 19:58:05 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -69,6 +69,7 @@
 #include "utils/array.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
+#include "utils/typcache.h"
 
 
 /* ----------
@@ -1815,21 +1816,24 @@ get_select_query_def(Query *query, deparse_context *context,
 			SortClause *srt = (SortClause *) lfirst(l);
 			Node	   *sortexpr;
 			Oid			sortcoltype;
-			char	   *opname;
+			TypeCacheEntry *typentry;
 
 			appendStringInfo(buf, sep);
 			sortexpr = get_rule_sortgroupclause(srt, query->targetList,
 												force_colno, context);
 			sortcoltype = exprType(sortexpr);
-			opname = generate_operator_name(srt->sortop,
-											sortcoltype, sortcoltype);
-			if (strcmp(opname, "<") != 0)
-			{
-				if (strcmp(opname, ">") == 0)
-					appendStringInfo(buf, " DESC");
-				else
-					appendStringInfo(buf, " USING %s", opname);
-			}
+			/* See whether operator is default < or > for datatype */
+			typentry = lookup_type_cache(sortcoltype,
+										 TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
+			if (srt->sortop == typentry->lt_opr)
+				/* ASC is default, so emit nothing */ ;
+			else if (srt->sortop == typentry->gt_opr)
+				appendStringInfo(buf, " DESC");
+			else
+				appendStringInfo(buf, " USING %s",
+								 generate_operator_name(srt->sortop,
+														sortcoltype,
+														sortcoltype));
 			sep = ", ";
 		}
 	}
@@ -4032,6 +4036,15 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
 	if (!HeapTupleIsValid(ht_opc))
 		elog(ERROR, "cache lookup failed for opclass %u", opclass);
 	opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
+
+	/* Special case for ARRAY_OPS: pretend it is default for any array type */
+	if (OidIsValid(actual_datatype))
+	{
+		if (opcrec->opcintype == ANYARRAYOID &&
+			OidIsValid(get_element_type(actual_datatype)))
+			actual_datatype = opcrec->opcintype;
+	}
+	
 	if (actual_datatype != opcrec->opcintype || !opcrec->opcdefault)
 	{
 		/* Okay, we need the opclass name.	Do we need to qualify it? */
diff --git a/src/backend/utils/cache/Makefile b/src/backend/utils/cache/Makefile
index b13ecc38ddcc18c2ea88a4480e8db72ac765a76d..6a1156aca5eb99fa8946da6835bf55ac9af331da 100644
--- a/src/backend/utils/cache/Makefile
+++ b/src/backend/utils/cache/Makefile
@@ -4,7 +4,7 @@
 #    Makefile for utils/cache
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/utils/cache/Makefile,v 1.17 2002/12/13 19:45:56 tgl Exp $
+#    $Header: /cvsroot/pgsql/src/backend/utils/cache/Makefile,v 1.18 2003/08/17 19:58:06 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -12,7 +12,7 @@ subdir = src/backend/utils/cache
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = catcache.o inval.o relcache.o syscache.o lsyscache.o
+OBJS = catcache.o inval.o relcache.o syscache.o lsyscache.o typcache.o
 
 all: SUBSYS.o
 
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 0faa097f349f65ed36a45d2e56814ab057552325..3864a2fa52b4f1bdc5bfeeff45cb02169bc9e746 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.106 2003/08/11 23:04:49 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.107 2003/08/17 19:58:06 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -122,7 +122,6 @@ get_op_hash_function(Oid opno)
 {
 	CatCList   *catlist;
 	int			i;
-	HeapTuple	tuple;
 	Oid			opclass = InvalidOid;
 
 	/*
@@ -137,10 +136,8 @@ get_op_hash_function(Oid opno)
 
 	for (i = 0; i < catlist->n_members; i++)
 	{
-		Form_pg_amop aform;
-
-		tuple = &catlist->members[i]->tuple;
-		aform = (Form_pg_amop) GETSTRUCT(tuple);
+		HeapTuple	tuple = &catlist->members[i]->tuple;
+		Form_pg_amop aform = (Form_pg_amop) GETSTRUCT(tuple);
 
 		if (aform->amopstrategy == HTEqualStrategyNumber &&
 			opclass_is_hash(aform->amopclaid))
@@ -155,20 +152,7 @@ get_op_hash_function(Oid opno)
 	if (OidIsValid(opclass))
 	{
 		/* Found a suitable opclass, get its hash support function */
-		tuple = SearchSysCache(AMPROCNUM,
-							   ObjectIdGetDatum(opclass),
-							   Int16GetDatum(HASHPROC),
-							   0, 0);
-		if (HeapTupleIsValid(tuple))
-		{
-			Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
-			RegProcedure result;
-
-			result = aform->amproc;
-			ReleaseSysCache(tuple);
-			Assert(RegProcedureIsValid(result));
-			return result;
-		}
+		return get_opclass_proc(opclass, HASHPROC);
 	}
 
 	/* Didn't find a match... */
@@ -176,6 +160,35 @@ get_op_hash_function(Oid opno)
 }
 
 
+/*				---------- AMPROC CACHES ----------						 */
+
+/*
+ * get_opclass_proc
+ *		Get the OID of the specified support function
+ *		for the specified opclass.
+ *
+ * Returns InvalidOid if there is no pg_amproc entry for the given keys.
+ */
+Oid
+get_opclass_proc(Oid opclass, int16 procnum)
+{
+	HeapTuple	tp;
+	Form_pg_amproc amproc_tup;
+	RegProcedure result;
+
+	tp = SearchSysCache(AMPROCNUM,
+						ObjectIdGetDatum(opclass),
+						Int16GetDatum(procnum),
+						0, 0);
+	if (!HeapTupleIsValid(tp))
+		return InvalidOid;
+	amproc_tup = (Form_pg_amproc) GETSTRUCT(tp);
+	result = amproc_tup->amproc;
+	ReleaseSysCache(tp);
+	return result;
+}
+
+
 /*				---------- ATTRIBUTE CACHES ----------					 */
 
 /*
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
new file mode 100644
index 0000000000000000000000000000000000000000..3ae5a1d17b11bdaea5e09893f769e4a24a1737f1
--- /dev/null
+++ b/src/backend/utils/cache/typcache.c
@@ -0,0 +1,292 @@
+/*-------------------------------------------------------------------------
+ *
+ * typcache.c
+ *	  POSTGRES type cache code
+ *
+ * The type cache exists to speed lookup of certain information about data
+ * types that is not directly available from a type's pg_type row.  In
+ * particular, we use a type's default btree opclass, or the default hash
+ * opclass if no btree opclass exists, to determine which operators should
+ * be used for grouping and sorting the type (GROUP BY, ORDER BY ASC/DESC).
+ *
+ * Several seemingly-odd choices have been made to support use of the type
+ * cache by the generic array comparison routines array_eq() and array_cmp().
+ * Because these routines are used as index support operations, they cannot
+ * leak memory.  To allow them to execute efficiently, all information that
+ * either of them would like to re-use across calls is made available in the
+ * type cache.
+ *
+ * Once created, a type cache entry lives as long as the backend does, so
+ * there is no need for a call to release a cache entry.  (For present uses,
+ * it would be okay to flush type cache entries at the ends of transactions,
+ * if we needed to reclaim space.)
+ *
+ * There is presently no provision for clearing out a cache entry if the
+ * stored data becomes obsolete.  (The code will work if a type acquires
+ * opclasses it didn't have before while a backend runs --- but not if the
+ * definition of an existing opclass is altered.)  However, the relcache
+ * doesn't cope with opclasses changing under it, either, so this seems
+ * a low-priority problem.
+ *
+ *
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/cache/typcache.c,v 1.1 2003/08/17 19:58:06 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "access/hash.h"
+#include "access/nbtree.h"
+#include "catalog/catname.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_am.h"
+#include "catalog/pg_opclass.h"
+#include "parser/parse_coerce.h"
+#include "utils/builtins.h"
+#include "utils/catcache.h"
+#include "utils/fmgroids.h"
+#include "utils/hsearch.h"
+#include "utils/lsyscache.h"
+#include "utils/typcache.h"
+
+
+static HTAB *TypeCacheHash = NULL;
+
+
+static Oid lookup_default_opclass(Oid type_id, Oid am_id);
+
+
+/*
+ * lookup_type_cache
+ *
+ * Fetch the type cache entry for the specified datatype, and make sure that
+ * all the fields requested by bits in 'flags' are valid.
+ *
+ * The result is never NULL --- we will elog() if the passed type OID is
+ * invalid.  Note however that we may fail to find one or more of the
+ * requested opclass-dependent fields; the caller needs to check whether
+ * the fields are InvalidOid or not.
+ */
+TypeCacheEntry *
+lookup_type_cache(Oid type_id, int flags)
+{
+	TypeCacheEntry *typentry;
+	bool		found;
+
+	if (TypeCacheHash == NULL)
+	{
+		/* First time through: initialize the hash table */
+		HASHCTL		ctl;
+
+		if (!CacheMemoryContext)
+			CreateCacheMemoryContext();
+
+		MemSet(&ctl, 0, sizeof(ctl));
+		ctl.keysize = sizeof(Oid);
+		ctl.entrysize = sizeof(TypeCacheEntry);
+		ctl.hash = tag_hash;
+		TypeCacheHash = hash_create("Type information cache", 64,
+									&ctl, HASH_ELEM | HASH_FUNCTION);
+	}
+
+	/* Try to look up an existing entry */
+	typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
+											  (void *) &type_id,
+											  HASH_FIND, NULL);
+	if (typentry == NULL)
+	{
+		/*
+		 * If we didn't find one, we want to make one.  But first get the
+		 * required info from the pg_type row, just to make sure we don't
+		 * make a cache entry for an invalid type OID.
+		 */
+		int16	typlen;
+		bool	typbyval;
+		char	typalign;
+
+		get_typlenbyvalalign(type_id, &typlen, &typbyval, &typalign);
+
+		typentry = (TypeCacheEntry *) hash_search(TypeCacheHash,
+												  (void *) &type_id,
+												  HASH_ENTER, &found);
+		if (typentry == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_OUT_OF_MEMORY),
+					 errmsg("out of memory")));
+		Assert(!found);			/* it wasn't there a moment ago */
+
+		MemSet(typentry, 0, sizeof(TypeCacheEntry));
+		typentry->type_id = type_id;
+		typentry->typlen = typlen;
+		typentry->typbyval = typbyval;
+		typentry->typalign = typalign;
+	}
+
+	/* If we haven't already found the opclass, try to do so */
+	if (flags != 0 && typentry->btree_opc == InvalidOid)
+	{
+		typentry->btree_opc = lookup_default_opclass(type_id,
+													 BTREE_AM_OID);
+		/* Only care about hash opclass if no btree opclass... */
+		if (typentry->btree_opc == InvalidOid)
+		{
+			if (typentry->hash_opc == InvalidOid)
+				typentry->hash_opc = lookup_default_opclass(type_id,
+															HASH_AM_OID);
+		}
+		else
+		{
+			/*
+			 * If we find a btree opclass where previously we only found
+			 * a hash opclass, forget the hash equality operator so we
+			 * can use the btree operator instead.
+			 */
+			typentry->eq_opr = InvalidOid;
+			typentry->eq_opr_finfo.fn_oid = InvalidOid;
+		}
+	}
+
+	/* Look for requested operators and functions */
+	if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
+		typentry->eq_opr == InvalidOid)
+	{
+		if (typentry->btree_opc != InvalidOid)
+			typentry->eq_opr = get_opclass_member(typentry->btree_opc,
+												  BTEqualStrategyNumber);
+		if (typentry->eq_opr == InvalidOid &&
+			typentry->hash_opc != InvalidOid)
+			typentry->eq_opr = get_opclass_member(typentry->hash_opc,
+												  HTEqualStrategyNumber);
+	}
+	if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid)
+	{
+		if (typentry->btree_opc != InvalidOid)
+			typentry->lt_opr = get_opclass_member(typentry->btree_opc,
+												  BTLessStrategyNumber);
+	}
+	if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid)
+	{
+		if (typentry->btree_opc != InvalidOid)
+			typentry->gt_opr = get_opclass_member(typentry->btree_opc,
+												  BTGreaterStrategyNumber);
+	}
+	if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
+		typentry->cmp_proc == InvalidOid)
+	{
+		if (typentry->btree_opc != InvalidOid)
+			typentry->cmp_proc = get_opclass_proc(typentry->btree_opc,
+												  BTORDER_PROC);
+	}
+
+	/*
+	 * Set up fmgr lookup info as requested
+	 *
+	 * Note: we tell fmgr the finfo structures live in CacheMemoryContext,
+	 * which is not quite right (they're really in DynaHashContext) but this
+	 * will do for our purposes.
+	 */
+	if ((flags & TYPECACHE_EQ_OPR_FINFO) &&
+		typentry->eq_opr_finfo.fn_oid == InvalidOid &&
+		typentry->eq_opr != InvalidOid)
+	{
+		Oid		eq_opr_func;
+
+		eq_opr_func = get_opcode(typentry->eq_opr);
+		if (eq_opr_func != InvalidOid)
+			fmgr_info_cxt(eq_opr_func, &typentry->eq_opr_finfo,
+						  CacheMemoryContext);
+	}
+	if ((flags & TYPECACHE_CMP_PROC_FINFO) &&
+		typentry->cmp_proc_finfo.fn_oid == InvalidOid &&
+		typentry->cmp_proc != InvalidOid)
+	{
+		fmgr_info_cxt(typentry->cmp_proc, &typentry->cmp_proc_finfo,
+					  CacheMemoryContext);
+	}
+
+	return typentry;
+}
+
+/*
+ * lookup_default_opclass
+ *
+ * Given the OIDs of a datatype and an access method, find the default
+ * operator class, if any.  Returns InvalidOid if there is none.
+ */
+static Oid
+lookup_default_opclass(Oid type_id, Oid am_id)
+{
+	int			nexact = 0;
+	int			ncompatible = 0;
+	Oid			exactOid = InvalidOid;
+	Oid			compatibleOid = InvalidOid;
+	Relation	rel;
+	ScanKeyData skey[1];
+	SysScanDesc scan;
+	HeapTuple	tup;
+
+	/* If it's a domain, look at the base type instead */
+	type_id = getBaseType(type_id);
+
+	/*
+	 * We scan through all the opclasses available for the access method,
+	 * looking for one that is marked default and matches the target type
+	 * (either exactly or binary-compatibly, but prefer an exact match).
+	 *
+	 * We could find more than one binary-compatible match, in which case we
+	 * require the user to specify which one he wants.	If we find more
+	 * than one exact match, then someone put bogus entries in pg_opclass.
+	 *
+	 * This is the same logic as GetDefaultOpClass() in indexcmds.c, except
+	 * that we consider all opclasses, regardless of the current search path.
+	 */
+	rel = heap_openr(OperatorClassRelationName, AccessShareLock);
+
+	ScanKeyEntryInitialize(&skey[0], 0x0,
+						   Anum_pg_opclass_opcamid, F_OIDEQ,
+						   ObjectIdGetDatum(am_id));
+
+	scan = systable_beginscan(rel, OpclassAmNameNspIndex, true,
+							  SnapshotNow, 1, skey);
+
+	while (HeapTupleIsValid(tup = systable_getnext(scan)))
+	{
+		Form_pg_opclass opclass = (Form_pg_opclass) GETSTRUCT(tup);
+
+		if (opclass->opcdefault)
+		{
+			if (opclass->opcintype == type_id)
+			{
+				nexact++;
+				exactOid = HeapTupleGetOid(tup);
+			}
+			else if (IsBinaryCoercible(type_id, opclass->opcintype))
+			{
+				ncompatible++;
+				compatibleOid = HeapTupleGetOid(tup);
+			}
+		}
+	}
+
+	systable_endscan(scan);
+
+	heap_close(rel, AccessShareLock);
+
+	if (nexact == 1)
+		return exactOid;
+	if (nexact != 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_DUPLICATE_OBJECT),
+				 errmsg("there are multiple default operator classes for data type %s",
+						format_type_be(type_id))));
+	if (ncompatible == 1)
+		return compatibleOid;
+
+	return InvalidOid;
+}
diff --git a/src/backend/utils/sort/tuplesort.c b/src/backend/utils/sort/tuplesort.c
index 255ec71ff16282ccc6e4eab571d1bc3900277979..983296086be64f03ccd3cd50ed35788ef9ac68f8 100644
--- a/src/backend/utils/sort/tuplesort.c
+++ b/src/backend/utils/sort/tuplesort.c
@@ -78,7 +78,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.36 2003/08/04 02:40:09 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/sort/tuplesort.c,v 1.37 2003/08/17 19:58:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,7 +88,6 @@
 #include "access/heapam.h"
 #include "access/nbtree.h"
 #include "catalog/pg_amop.h"
-#include "catalog/pg_amproc.h"
 #include "catalog/pg_operator.h"
 #include "miscadmin.h"
 #include "utils/catcache.h"
@@ -1754,26 +1753,17 @@ SelectSortFunction(Oid sortOperator,
 	if (OidIsValid(opclass))
 	{
 		/* Found a suitable opclass, get its comparator support function */
-		tuple = SearchSysCache(AMPROCNUM,
-							   ObjectIdGetDatum(opclass),
-							   Int16GetDatum(BTORDER_PROC),
-							   0, 0);
-		if (HeapTupleIsValid(tuple))
-		{
-			Form_pg_amproc aform = (Form_pg_amproc) GETSTRUCT(tuple);
-
-			*sortFunction = aform->amproc;
-			ReleaseSysCache(tuple);
-			Assert(RegProcedureIsValid(*sortFunction));
-			return;
-		}
+		*sortFunction = get_opclass_proc(opclass, BTORDER_PROC);
+		Assert(RegProcedureIsValid(*sortFunction));
+		return;
 	}
 
 	/*
 	 * Can't find a comparator, so use the operator as-is.  Decide whether
 	 * it is forward or reverse sort by looking at its name (grotty, but
 	 * this only matters for deciding which end NULLs should get sorted
-	 * to).
+	 * to).  XXX possibly better idea: see whether its selectivity function
+	 * is scalargtcmp?
 	 */
 	tuple = SearchSysCache(OPEROID,
 						   ObjectIdGetDatum(sortOperator),
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 48723471a26004ef6a0d6d49a2013d52f3949ed5..9d4f80b727bfe67f8887078e5c4a7e8619241dad 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.206 2003/08/04 02:40:10 momjian Exp $
+ * $Id: catversion.h,v 1.207 2003/08/17 19:58:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200307301
+#define CATALOG_VERSION_NO	200308171
 
 #endif
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index fcf162eb98fe505842829c8c963fbb9b04bace38..bff4b21700341afa95aedeb7b11872a72cc1493a 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -16,7 +16,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amop.h,v 1.54 2003/08/04 02:40:10 momjian Exp $
+ * $Id: pg_amop.h,v 1.55 2003/08/17 19:58:06 tgl Exp $
  *
  * NOTES
  *	 the genbki.sh script reads this file and generates .bki
@@ -418,6 +418,36 @@ DATA(insert (	2098 3 f 2334 ));
 DATA(insert (	2098 4 f 2335 ));
 DATA(insert (	2098 5 f 2336 ));
 
+/*
+ *	btree money_ops
+ */
+
+DATA(insert (	2099 1 f  902 ));
+DATA(insert (	2099 2 f  904 ));
+DATA(insert (	2099 3 f  900 ));
+DATA(insert (	2099 4 f  905 ));
+DATA(insert (	2099 5 f  903 ));
+
+/*
+ *	btree reltime_ops
+ */
+
+DATA(insert (	2233 1 f  568 ));
+DATA(insert (	2233 2 f  570 ));
+DATA(insert (	2233 3 f  566 ));
+DATA(insert (	2233 4 f  569 ));
+DATA(insert (	2233 5 f  571 ));
+
+/*
+ *	btree tinterval_ops
+ */
+
+DATA(insert (	2234 1 f  813 ));
+DATA(insert (	2234 2 f  815 ));
+DATA(insert (	2234 3 f  811 ));
+DATA(insert (	2234 4 f  814 ));
+DATA(insert (	2234 5 f  816 ));
+
 /*
  *	btree array_ops
  */
@@ -496,5 +526,7 @@ DATA(insert (	2230 1 f 2316 ));
 DATA(insert (	2231 1 f 2328 ));
 /* name_pattern_ops */
 DATA(insert (	2232 1 f 2334 ));
+/* aclitem_ops */
+DATA(insert (	2235 1 f  974 ));
 
 #endif   /* PG_AMOP_H */
diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h
index 24c1fa416fe789faddfdbf93a03bf8fec2c3af14..a2607db5eb8b9f77719789c246d7ced2a6e74ff5 100644
--- a/src/include/catalog/pg_amproc.h
+++ b/src/include/catalog/pg_amproc.h
@@ -14,7 +14,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_amproc.h,v 1.43 2003/08/04 02:40:11 momjian Exp $
+ * $Id: pg_amproc.h,v 1.44 2003/08/17 19:58:06 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -110,6 +110,9 @@ DATA(insert (	2095 1 2166 ));
 DATA(insert (	2096 1 2166 ));
 DATA(insert (	2097 1 2180 ));
 DATA(insert (	2098 1 2187 ));
+DATA(insert (	2099 1  377 ));
+DATA(insert (	2233 1  380 ));
+DATA(insert (	2234 1  381 ));
 
 
 /* hash */
@@ -145,5 +148,6 @@ DATA(insert (	2229 1	456 ));
 DATA(insert (	2230 1	456 ));
 DATA(insert (	2231 1	456 ));
 DATA(insert (	2232 1	455 ));
+DATA(insert (	2235 1	329 ));
 
 #endif   /* PG_AMPROC_H */
diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h
index bbd69dca5b6d1ad27115fa3e3c3174df5eb170c4..af277ad687aec114506435da31a52d136c08e267 100644
--- a/src/include/catalog/pg_opclass.h
+++ b/src/include/catalog/pg_opclass.h
@@ -26,7 +26,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_opclass.h,v 1.56 2003/08/04 02:40:12 momjian Exp $
+ * $Id: pg_opclass.h,v 1.57 2003/08/17 19:58:06 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -157,6 +157,7 @@ DATA(insert OID = 2097 (	403		bpchar_pattern_ops	PGNSP PGUID 1042 f 0 ));
 #define BPCHAR_PATTERN_BTREE_OPS_OID 2097
 DATA(insert OID = 2098 (	403		name_pattern_ops	PGNSP PGUID   19 f 0 ));
 #define NAME_PATTERN_BTREE_OPS_OID 2098
+DATA(insert OID = 2099 (	403		money_ops		PGNSP PGUID  790 t 0 ));
 DATA(insert OID = 2222 (	405		bool_ops		PGNSP PGUID   16 t 0 ));
 DATA(insert OID = 2223 (	405		bytea_ops		PGNSP PGUID   17 t 0 ));
 DATA(insert OID = 2224 (	405		int2vector_ops	PGNSP PGUID   22 t 0 ));
@@ -168,5 +169,8 @@ DATA(insert OID = 2229 (	405		text_pattern_ops	PGNSP PGUID   25 f 0 ));
 DATA(insert OID = 2230 (	405		varchar_pattern_ops PGNSP PGUID 1043 f 0 ));
 DATA(insert OID = 2231 (	405		bpchar_pattern_ops	PGNSP PGUID 1042 f 0 ));
 DATA(insert OID = 2232 (	405		name_pattern_ops	PGNSP PGUID   19 f 0 ));
+DATA(insert OID = 2233 (	403		reltime_ops		PGNSP PGUID  703 t 0 ));
+DATA(insert OID = 2234 (	403		tinterval_ops	PGNSP PGUID  704 t 0 ));
+DATA(insert OID = 2235 (	405		aclitem_ops		PGNSP PGUID 1033 t 0 ));
 
 #endif   /* PG_OPCLASS_H */
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index 176cd1eaaf6ff5054fff8d364e3921a0256d63e2..991e9429d37bc0fd710196f6b4e249a0b8953cfd 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_operator.h,v 1.120 2003/08/04 02:40:12 momjian Exp $
+ * $Id: pg_operator.h,v 1.121 2003/08/17 19:58:06 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -424,7 +424,7 @@ DATA(insert OID = 965 (  "^"	   PGNSP PGUID b f	701  701	701 0 0 0 0 0 0 dpow -
 DATA(insert OID = 966 (  "+"	   PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclinsert - - ));
 DATA(insert OID = 967 (  "-"	   PGNSP PGUID b f 1034 1033 1034 0 0 0 0 0 0 aclremove - - ));
 DATA(insert OID = 968 (  "~"	   PGNSP PGUID b f 1034 1033	 16 0 0 0 0 0 0 aclcontains - - ));
-DATA(insert OID = 974 (  "="	   PGNSP PGUID b f 1033 1033	 16 974 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));
+DATA(insert OID = 974 (  "="	   PGNSP PGUID b t 1033 1033	 16 974 0 0 0 0 0 aclitemeq eqsel eqjoinsel ));
 
 /* additional geometric operators - thomas 1997-07-09 */
 DATA(insert OID =  969 (  "@@"	   PGNSP PGUID l f	0  601	600    0  0 0 0 0 0 lseg_center - - ));
@@ -448,6 +448,7 @@ DATA(insert OID = 1071 (  "<>"	   PGNSP PGUID b f 2277 2277 16 1071 1070  0 0 0
 DATA(insert OID = 1072 (  "<"	   PGNSP PGUID b f 2277 2277 16 1073 1075  0 0 0 0 array_lt scalarltsel scalarltjoinsel ));
 #define ARRAY_LT_OP 1072
 DATA(insert OID = 1073 (  ">"	   PGNSP PGUID b f 2277 2277 16 1072 1074  0 0 0 0 array_gt scalargtsel scalargtjoinsel ));
+#define ARRAY_GT_OP 1073
 DATA(insert OID = 1074 (  "<="	   PGNSP PGUID b f 2277 2277 16 1075 1073  0 0 0 0 array_le scalarltsel scalarltjoinsel ));
 DATA(insert OID = 1075 (  ">="	   PGNSP PGUID b f 2277 2277 16 1074 1072  0 0 0 0 array_ge scalargtsel scalargtjoinsel ));
 
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 1fe3812d709ae337dcc196b4c68ba8885041da31..59b957ddd0d8c0acc4edefdae0a6d594d53b115a 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pg_proc.h,v 1.312 2003/08/04 02:40:12 momjian Exp $
+ * $Id: pg_proc.h,v 1.313 2003/08/17 19:58:06 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -758,6 +758,12 @@ DATA(insert OID = 359 (  btnamecmp		   PGNSP PGUID 12 f f t f i 2 23 "19 19"	btn
 DESCR("btree less-equal-greater");
 DATA(insert OID = 360 (  bttextcmp		   PGNSP PGUID 12 f f t f i 2 23 "25 25"	bttextcmp - _null_ ));
 DESCR("btree less-equal-greater");
+DATA(insert OID = 377 (  cash_cmp		   PGNSP PGUID 12 f f t f i 2 23 "790 790"	cash_cmp - _null_ ));
+DESCR("btree less-equal-greater");
+DATA(insert OID = 380 (  btreltimecmp	   PGNSP PGUID 12 f f t f i 2 23 "703 703"	btreltimecmp - _null_ ));
+DESCR("btree less-equal-greater");
+DATA(insert OID = 381 (  bttintervalcmp	   PGNSP PGUID 12 f f t f i 2 23 "704 704"	bttintervalcmp - _null_ ));
+DESCR("btree less-equal-greater");
 DATA(insert OID = 382 (  btarraycmp		   PGNSP PGUID 12 f f t f i 2 23 "2277 2277"	btarraycmp - _null_ ));
 DESCR("btree less-equal-greater");
 
@@ -844,6 +850,8 @@ DATA(insert OID = 456 (  hashvarlena	   PGNSP PGUID 12 f f t f i 1 23 "2281" has
 DESCR("hash any varlena type");
 DATA(insert OID = 457 (  hashoidvector	   PGNSP PGUID 12 f f t f i 1 23 "30"  hashoidvector - _null_ ));
 DESCR("hash");
+DATA(insert OID = 329 (  hash_aclitem	   PGNSP PGUID 12 f f t f i 1 23 "1033"  hash_aclitem - _null_ ));
+DESCR("hash");
 DATA(insert OID = 398 (  hashint2vector    PGNSP PGUID 12 f f t f i 1 23 "22"  hashint2vector - _null_ ));
 DESCR("hash");
 DATA(insert OID = 399 (  hashmacaddr	   PGNSP PGUID 12 f f t f i 1 23 "829"	hashmacaddr - _null_ ));
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 9c80a9ebc74fea1eef9c3c8e8dd5aeaea49ef3c3..6e3254dc67a9749a360a173184233c74783b1114 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.146 2003/08/04 02:40:13 momjian Exp $
+ * $Id: nodes.h,v 1.147 2003/08/17 19:58:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -265,7 +265,7 @@ typedef enum NodeTag
 	T_ExprFieldSelect,
 	T_ResTarget,
 	T_TypeCast,
-	T_SortGroupBy,
+	T_SortBy,
 	T_RangeSubselect,
 	T_RangeFunction,
 	T_TypeName,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 497ba3b6247294eed7a49c8f8d6b56a79851e60d..cbaaee49cf951520bb1fc3c67f29b258d435efd8 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.246 2003/08/08 21:42:48 momjian Exp $
+ * $Id: parsenodes.h,v 1.247 2003/08/17 19:58:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -279,14 +279,19 @@ typedef struct ResTarget
 } ResTarget;
 
 /*
- * SortGroupBy - for ORDER BY clause
+ * SortBy - for ORDER BY clause
  */
-typedef struct SortGroupBy
+#define SORTBY_ASC		1
+#define SORTBY_DESC		2
+#define SORTBY_USING	3
+
+typedef struct SortBy
 {
 	NodeTag		type;
-	List	   *useOp;			/* operator to use */
-	Node	   *node;			/* Expression  */
-} SortGroupBy;
+	int			sortby_kind;	/* see codes above */
+	List	   *useOp;			/* name of op to use, if SORTBY_USING */
+	Node	   *node;			/* expression to sort on */
+} SortBy;
 
 /*
  * RangeSubselect - subquery appearing in a FROM clause
@@ -614,7 +619,7 @@ typedef struct SelectStmt
 	 * These fields are used in both "leaf" SelectStmts and upper-level
 	 * SelectStmts.
 	 */
-	List	   *sortClause;		/* sort clause (a list of SortGroupBy's) */
+	List	   *sortClause;		/* sort clause (a list of SortBy's) */
 	Node	   *limitOffset;	/* # of result tuples to skip */
 	Node	   *limitCount;		/* # of result tuples to return */
 	List	   *forUpdate;		/* FOR UPDATE clause */
diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h
index 670b72cfbac0e0bb108d075cd7f758b13e349886..8439c19804ceeedb5cc3993ed8e68383c287f375 100644
--- a/src/include/parser/parse_clause.h
+++ b/src/include/parser/parse_clause.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_clause.h,v 1.36 2003/08/07 19:20:23 tgl Exp $
+ * $Id: parse_clause.h,v 1.37 2003/08/17 19:58:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -37,7 +37,8 @@ extern List *addAllTargetsToSortList(ParseState *pstate,
 						bool resolveUnknown);
 extern List *addTargetToSortList(ParseState *pstate, TargetEntry *tle,
 					List *sortlist, List *targetlist,
-					List *opname, bool resolveUnknown);
+					int sortby_kind, List *sortby_opname,
+					bool resolveUnknown);
 extern Index assignSortGroupRef(TargetEntry *tle, List *tlist);
 extern bool targetIsInSortList(TargetEntry *tle, List *sortList);
 
diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h
index 1167997706a9b0c906ac22491970b746a85b0d40..130f125eff1282d3a505ecbfd4b025ea5b65da9a 100644
--- a/src/include/parser/parse_oper.h
+++ b/src/include/parser/parse_oper.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_oper.h,v 1.32 2003/08/04 02:40:14 momjian Exp $
+ * $Id: parse_oper.h,v 1.33 2003/08/17 19:58:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,15 +38,16 @@ extern Operator compatible_oper(List *op, Oid arg1, Oid arg2, bool noError);
 
 /* currently no need for compatible_left_oper/compatible_right_oper */
 
-/* Routines for identifying "=" and "<" operators for a type */
+/* Routines for identifying "=", "<", ">" operators for a type */
 extern Operator equality_oper(Oid argtype, bool noError);
 extern Operator ordering_oper(Oid argtype, bool noError);
+extern Operator reverse_ordering_oper(Oid argtype, bool noError);
 
 /* Convenience routines for common calls on the above */
 extern Oid	compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError);
 extern Oid	equality_oper_funcid(Oid argtype);
 extern Oid	ordering_oper_opid(Oid argtype);
-extern Oid	ordering_oper_funcid(Oid argtype);
+extern Oid	reverse_ordering_oper_opid(Oid argtype);
 
 /* Extract operator OID or underlying-function OID from an Operator tuple */
 extern Oid	oprid(Operator op);
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 59b5d106fa6060c65dd061d446bcf9bcb66e4042..29a807744ca01d7bfcf7a92291e3a7966b46b10b 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: acl.h,v 1.61 2003/08/08 21:42:55 momjian Exp $
+ * $Id: acl.h,v 1.62 2003/08/17 19:58:06 tgl Exp $
  *
  * NOTES
  *	  For backward-compatibility purposes we have to allow there
@@ -209,6 +209,7 @@ extern Datum aclremove(PG_FUNCTION_ARGS);
 extern Datum aclcontains(PG_FUNCTION_ARGS);
 extern Datum makeaclitem(PG_FUNCTION_ARGS);
 extern Datum aclitem_eq(PG_FUNCTION_ARGS);
+extern Datum hash_aclitem(PG_FUNCTION_ARGS);
 
 /*
  * prototypes for functions in aclchk.c
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index a84f85122566be3aeb416bd8cbf5b9d4b034f57c..71615b6610f001f45b26a4245ec276cfa8de6b25 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: builtins.h,v 1.227 2003/08/08 21:42:55 momjian Exp $
+ * $Id: builtins.h,v 1.228 2003/08/17 19:58:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -222,6 +222,8 @@ extern Datum btfloat8cmp(PG_FUNCTION_ARGS);
 extern Datum btoidcmp(PG_FUNCTION_ARGS);
 extern Datum btoidvectorcmp(PG_FUNCTION_ARGS);
 extern Datum btabstimecmp(PG_FUNCTION_ARGS);
+extern Datum btreltimecmp(PG_FUNCTION_ARGS);
+extern Datum bttintervalcmp(PG_FUNCTION_ARGS);
 extern Datum btcharcmp(PG_FUNCTION_ARGS);
 extern Datum btnamecmp(PG_FUNCTION_ARGS);
 extern Datum bttextcmp(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/cash.h b/src/include/utils/cash.h
index 305304c20d2a17d12d82b82ddd50e8808186389e..b78da25edd165876277639b4f365c9d9f44c9630 100644
--- a/src/include/utils/cash.h
+++ b/src/include/utils/cash.h
@@ -23,6 +23,7 @@ extern Datum cash_lt(PG_FUNCTION_ARGS);
 extern Datum cash_le(PG_FUNCTION_ARGS);
 extern Datum cash_gt(PG_FUNCTION_ARGS);
 extern Datum cash_ge(PG_FUNCTION_ARGS);
+extern Datum cash_cmp(PG_FUNCTION_ARGS);
 
 extern Datum cash_pl(PG_FUNCTION_ARGS);
 extern Datum cash_mi(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 4c9c073adea28b1af537d04fce18c7ca907d6f75..927190e35d9bc7c5f49ac931f864e881c09a4caf 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: lsyscache.h,v 1.80 2003/08/11 23:04:50 tgl Exp $
+ * $Id: lsyscache.h,v 1.81 2003/08/17 19:58:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,7 @@ extern bool op_in_opclass(Oid opno, Oid opclass);
 extern bool op_requires_recheck(Oid opno, Oid opclass);
 extern Oid	get_opclass_member(Oid opclass, int16 strategy);
 extern Oid	get_op_hash_function(Oid opno);
+extern Oid	get_opclass_proc(Oid opclass, int16 procnum);
 extern char *get_attname(Oid relid, AttrNumber attnum);
 extern char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
 extern AttrNumber get_attnum(Oid relid, const char *attname);
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
new file mode 100644
index 0000000000000000000000000000000000000000..ae080d3feed62d199a832acb15f38399606156d5
--- /dev/null
+++ b/src/include/utils/typcache.h
@@ -0,0 +1,66 @@
+/*-------------------------------------------------------------------------
+ *
+ * typcache.h
+ *	  Type cache definitions.
+ *
+ * The type cache exists to speed lookup of certain information about data
+ * types that is not directly available from a type's pg_type row.
+ *
+ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: typcache.h,v 1.1 2003/08/17 19:58:06 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef TYPCACHE_H
+#define TYPCACHE_H
+
+#include "fmgr.h"
+
+
+typedef struct TypeCacheEntry
+{
+	/* typeId is the hash lookup key and MUST BE FIRST */
+	Oid			type_id;		/* OID of the data type */
+
+	/* some subsidiary information copied from the pg_type row */
+	int16		typlen;
+	bool		typbyval;
+	char		typalign;
+
+	/*
+	 * Information obtained from opclass entries
+	 *
+	 * These will be InvalidOid if no match could be found, or if the
+	 * information hasn't yet been requested.
+	 */
+	Oid			btree_opc;		/* OID of the default btree opclass */
+	Oid			hash_opc;		/* OID of the default hash opclass */
+	Oid			eq_opr;			/* OID of the equality operator */
+	Oid			lt_opr;			/* OID of the less-than operator */
+	Oid			gt_opr;			/* OID of the greater-than operator */
+	Oid			cmp_proc;		/* OID of the btree comparison function */
+
+	/*
+	 * Pre-set-up fmgr call info for the equality operator and the btree
+	 * comparison function.  These are kept in the type cache to avoid
+	 * problems with memory leaks in repeated calls to array_eq and array_cmp.
+	 * There is not currently a need to maintain call info for the lt_opr
+	 * or gt_opr.
+	 */
+	FmgrInfo	eq_opr_finfo;
+	FmgrInfo	cmp_proc_finfo;
+} TypeCacheEntry;
+
+/* Bit flags to indicate which fields a given caller needs to have set */
+#define TYPECACHE_EQ_OPR			0x0001
+#define TYPECACHE_LT_OPR			0x0002
+#define TYPECACHE_GT_OPR			0x0004
+#define TYPECACHE_CMP_PROC			0x0008
+#define TYPECACHE_EQ_OPR_FINFO		0x0010
+#define TYPECACHE_CMP_PROC_FINFO	0x0020
+
+extern TypeCacheEntry *lookup_type_cache(Oid type_id, int flags);
+
+#endif   /* TYPCACHE_H */
diff --git a/src/test/regress/expected/circle.out b/src/test/regress/expected/circle.out
index 37a3591a64bbf6ae00464362e17eb2b836422e03..d8713dc53d5323845cafc5cd1670b8171c36ce50 100644
--- a/src/test/regress/expected/circle.out
+++ b/src/test/regress/expected/circle.out
@@ -81,7 +81,7 @@ SELECT '' AS four, f1 FROM CIRCLE_TBL WHERE diameter(f1) >= 10;
 SELECT '' as five, c1.f1 AS one, c2.f1 AS two, (c1.f1 <-> c2.f1) AS distance
   FROM CIRCLE_TBL c1, CIRCLE_TBL c2
   WHERE (c1.f1 < c2.f1) AND ((c1.f1 <-> c2.f1) > 0)
-  ORDER BY distance, one, two;
+  ORDER BY distance, one USING < , two USING < ;
  five |      one       |      two       |     distance     
 ------+----------------+----------------+------------------
       | <(100,200),10> | <(100,1),115>  |               74
diff --git a/src/test/regress/expected/geometry.out b/src/test/regress/expected/geometry.out
index 69118725f4a857d55628a91a16eb77fdc2b183b3..0889ea25e640efa7afa7a61d75305bcbfc5de9ed 100644
--- a/src/test/regress/expected/geometry.out
+++ b/src/test/regress/expected/geometry.out
@@ -504,7 +504,7 @@ SELECT '' AS two, circle(f1)
 SELECT '' AS twentyfour, c1.f1 AS circle, p1.f1 AS point, (p1.f1 <-> c1.f1) AS distance
    FROM CIRCLE_TBL c1, POINT_TBL p1
    WHERE (p1.f1 <-> c1.f1) > 0
-   ORDER BY distance, circle, point using <<;
+   ORDER BY distance, circle using <, point using <<;
  twentyfour |     circle     |   point    |   distance    
 ------------+----------------+------------+---------------
             | <(1,2),3>      | (-3,4)     |   1.472135955
diff --git a/src/test/regress/expected/geometry_1.out b/src/test/regress/expected/geometry_1.out
index 76b621fede615b6ac817914cd152ac3d1ccdcfaa..5c205d8bcc7e03be5d607d3aa99fcade1349bbbf 100644
--- a/src/test/regress/expected/geometry_1.out
+++ b/src/test/regress/expected/geometry_1.out
@@ -504,7 +504,7 @@ SELECT '' AS two, circle(f1)
 SELECT '' AS twentyfour, c1.f1 AS circle, p1.f1 AS point, (p1.f1 <-> c1.f1) AS distance
    FROM CIRCLE_TBL c1, POINT_TBL p1
    WHERE (p1.f1 <-> c1.f1) > 0
-   ORDER BY distance, circle, point using <<;
+   ORDER BY distance, circle using <, point using <<;
  twentyfour |     circle     |   point    |   distance    
 ------------+----------------+------------+---------------
             | <(1,2),3>      | (-3,4)     |   1.472135955
diff --git a/src/test/regress/sql/circle.sql b/src/test/regress/sql/circle.sql
index 9b384554d75926c50c86dd2fb62a3bc11697671a..fe229b3b2c0cd3b33730a8893f37ccdf8eb0dfe8 100644
--- a/src/test/regress/sql/circle.sql
+++ b/src/test/regress/sql/circle.sql
@@ -42,5 +42,4 @@ SELECT '' AS four, f1 FROM CIRCLE_TBL WHERE diameter(f1) >= 10;
 SELECT '' as five, c1.f1 AS one, c2.f1 AS two, (c1.f1 <-> c2.f1) AS distance
   FROM CIRCLE_TBL c1, CIRCLE_TBL c2
   WHERE (c1.f1 < c2.f1) AND ((c1.f1 <-> c2.f1) > 0)
-  ORDER BY distance, one, two;
-
+  ORDER BY distance, one USING < , two USING < ;
diff --git a/src/test/regress/sql/geometry.sql b/src/test/regress/sql/geometry.sql
index fab791df0d533c6abc21ad1144be5981e4f608f6..4abb679148f288412c835f30adfa0147954b563d 100644
--- a/src/test/regress/sql/geometry.sql
+++ b/src/test/regress/sql/geometry.sql
@@ -152,5 +152,4 @@ SELECT '' AS two, circle(f1)
 SELECT '' AS twentyfour, c1.f1 AS circle, p1.f1 AS point, (p1.f1 <-> c1.f1) AS distance
    FROM CIRCLE_TBL c1, POINT_TBL p1
    WHERE (p1.f1 <-> c1.f1) > 0
-   ORDER BY distance, circle, point using <<;
-
+   ORDER BY distance, circle using <, point using <<;