diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index fd5d7810380b63e33cbb0169177a224cc357d9f0..a2eb0562e0a582ad75ce9eb2313a28bb4c55bc4a 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -33,23 +33,6 @@
 #include "utils/typcache.h"
 
 
-/* flags */
-#define RANGE_EMPTY		0x01
-#define RANGE_LB_INC	0x02
-#define RANGE_LB_NULL	0x04	/* NOT CURRENTLY USED */
-#define RANGE_LB_INF	0x08
-#define RANGE_UB_INC	0x10
-#define RANGE_UB_NULL	0x20	/* NOT CURRENTLY USED */
-#define RANGE_UB_INF	0x40
-
-#define RANGE_HAS_LBOUND(flags) (!((flags) & (RANGE_EMPTY | \
-											  RANGE_LB_NULL | \
-											  RANGE_LB_INF)))
-
-#define RANGE_HAS_UBOUND(flags) (!((flags) & (RANGE_EMPTY | \
-											  RANGE_UB_NULL | \
-											  RANGE_UB_INF)))
-
 #define RANGE_EMPTY_LITERAL "empty"
 
 #define RANGE_DEFAULT_FLAGS	"[)"
@@ -62,8 +45,8 @@ static char *range_parse_bound(char *string, char *ptr, char **bound_str,
 				  bool *infinite);
 static char *range_deparse(char flags, char *lbound_str, char *ubound_str);
 static char *range_bound_escape(char *in_str);
-static bool range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1,
-						RangeType *r2);
+static bool range_contains_internal(TypeCacheEntry *typcache,
+									RangeType *r1, RangeType *r2);
 static Size datum_compute_size(Size sz, Datum datum, bool typbyval,
 				   char typalign, int16 typlen, char typstorage);
 static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval,
@@ -82,40 +65,26 @@ range_in(PG_FUNCTION_ARGS)
 	char	   *input_str = PG_GETARG_CSTRING(0);
 	Oid			rngtypoid = PG_GETARG_OID(1);
 	Oid			typmod = PG_GETARG_INT32(2);
-	Datum		range;
+	RangeType  *range;
+	TypeCacheEntry *typcache;
 	char		flags;
 	char	   *lbound_str;
 	char	   *ubound_str;
 	regproc		subInput;
 	FmgrInfo	subInputFn;
 	Oid			ioParam;
-	RangeTypeInfo rngtypinfo;
 	RangeBound	lower;
 	RangeBound	upper;
 
-	if (rngtypoid == ANYRANGEOID)
-		ereport(ERROR,
-				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot accept a value of type anyrange")));
-
-	range_gettypinfo(fcinfo, rngtypoid, &rngtypinfo);
+	typcache = range_get_typcache(fcinfo, rngtypoid);
 
 	/* parse */
 	range_parse(input_str, &flags, &lbound_str, &ubound_str);
 
 	/* input */
-	getTypeInputInfo(rngtypinfo.subtype, &subInput, &ioParam);
+	getTypeInputInfo(typcache->rngelemtype->type_id, &subInput, &ioParam);
 	fmgr_info(subInput, &subInputFn);
 
-	lower.rngtypid = rngtypoid;
-	lower.infinite = (flags & RANGE_LB_INF) != 0;
-	lower.inclusive = (flags & RANGE_LB_INC) != 0;
-	lower.lower = true;
-	upper.rngtypid = rngtypoid;
-	upper.infinite = (flags & RANGE_UB_INF) != 0;
-	upper.inclusive = (flags & RANGE_UB_INC) != 0;
-	upper.lower = false;
-
 	if (RANGE_HAS_LBOUND(flags))
 		lower.val = InputFunctionCall(&subInputFn, lbound_str,
 									  ioParam, typmod);
@@ -123,8 +92,15 @@ range_in(PG_FUNCTION_ARGS)
 		upper.val = InputFunctionCall(&subInputFn, ubound_str,
 									  ioParam, typmod);
 
+	lower.infinite = (flags & RANGE_LB_INF) != 0;
+	lower.inclusive = (flags & RANGE_LB_INC) != 0;
+	lower.lower = true;
+	upper.infinite = (flags & RANGE_UB_INF) != 0;
+	upper.inclusive = (flags & RANGE_UB_INC) != 0;
+	upper.lower = false;
+
 	/* serialize and canonicalize */
-	range = make_range(fcinfo, &lower, &upper, flags & RANGE_EMPTY);
+	range = make_range(typcache, &lower, &upper, flags & RANGE_EMPTY);
 
 	PG_RETURN_RANGE(range);
 }
@@ -133,6 +109,7 @@ Datum
 range_out(PG_FUNCTION_ARGS)
 {
 	RangeType  *range = PG_GETARG_RANGE(0);
+	TypeCacheEntry *typcache;
 	char	   *output_str;
 	regproc		subOutput;
 	FmgrInfo	subOutputFn;
@@ -141,14 +118,13 @@ range_out(PG_FUNCTION_ARGS)
 	char	   *lbound_str = NULL;
 	char	   *ubound_str = NULL;
 	bool		empty;
-	RangeTypeInfo rngtypinfo;
 	RangeBound	lower;
 	RangeBound	upper;
 
-	/* deserialize */
-	range_deserialize(fcinfo, range, &lower, &upper, &empty);
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(range));
 
-	range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
+	/* deserialize */
+	range_deserialize(typcache, range, &lower, &upper, &empty);
 
 	if (empty)
 		flags |= RANGE_EMPTY;
@@ -159,7 +135,7 @@ range_out(PG_FUNCTION_ARGS)
 	flags |= upper.infinite ? RANGE_UB_INF : 0;
 
 	/* output */
-	getTypeOutputInfo(rngtypinfo.subtype, &subOutput, &isVarlena);
+	getTypeOutputInfo(typcache->rngelemtype->type_id, &subOutput, &isVarlena);
 	fmgr_info(subOutput, &subOutputFn);
 
 	if (RANGE_HAS_LBOUND(flags))
@@ -185,21 +161,21 @@ Datum
 range_recv(PG_FUNCTION_ARGS)
 {
 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
-	Oid			rngtypid = PG_GETARG_OID(1);
+	Oid			rngtypoid = PG_GETARG_OID(1);
 	int32		typmod = PG_GETARG_INT32(2);
-	Datum		range;
+	RangeType  *range;
+	TypeCacheEntry *typcache;
 	Oid			subrecv;
 	Oid			ioparam;
 	char		flags;
 	RangeBound	lower;
 	RangeBound	upper;
-	RangeTypeInfo rngtypinfo;
 
-	flags = (unsigned char) pq_getmsgbyte(buf);
+	typcache = range_get_typcache(fcinfo, rngtypoid);
 
-	range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);
+	flags = (unsigned char) pq_getmsgbyte(buf);
 
-	getTypeBinaryInputInfo(rngtypinfo.subtype, &subrecv, &ioparam);
+	getTypeBinaryInputInfo(typcache->rngelemtype->type_id, &subrecv, &ioparam);
 
 	if (RANGE_HAS_LBOUND(flags))
 	{
@@ -239,17 +215,15 @@ range_recv(PG_FUNCTION_ARGS)
 
 	pq_getmsgend(buf);
 
-	lower.rngtypid = rngtypid;
 	lower.infinite = (flags & RANGE_LB_INF) != 0;
 	lower.inclusive = (flags & RANGE_LB_INC) != 0;
 	lower.lower = true;
-	upper.rngtypid = rngtypid;
 	upper.infinite = (flags & RANGE_UB_INF) != 0;
 	upper.inclusive = (flags & RANGE_UB_INC) != 0;
 	upper.lower = false;
 
 	/* serialize and canonicalize */
-	range = make_range(fcinfo, &lower, &upper, flags & RANGE_EMPTY);
+	range = make_range(typcache, &lower, &upper, flags & RANGE_EMPTY);
 
 	PG_RETURN_RANGE(range);
 }
@@ -259,17 +233,22 @@ range_send(PG_FUNCTION_ARGS)
 {
 	RangeType  *range = PG_GETARG_RANGE(0);
 	StringInfo	buf = makeStringInfo();
+	TypeCacheEntry *typcache;
 	char		flags = 0;
 	RangeBound	lower;
 	RangeBound	upper;
 	bool		empty;
 	Oid			subsend;
 	bool		typIsVarlena;
-	RangeTypeInfo rngtypinfo;
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(range));
+
+	getTypeBinaryOutputInfo(typcache->rngelemtype->type_id,
+							&subsend, &typIsVarlena);
 
 	pq_begintypsend(buf);
 
-	range_deserialize(fcinfo, range, &lower, &upper, &empty);
+	range_deserialize(typcache, range, &lower, &upper, &empty);
 
 	if (empty)
 		flags |= RANGE_EMPTY;
@@ -279,11 +258,6 @@ range_send(PG_FUNCTION_ARGS)
 	flags |= upper.inclusive ? RANGE_UB_INC : 0;
 	flags |= upper.infinite ? RANGE_UB_INF : 0;
 
-	range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
-
-	getTypeBinaryOutputInfo(rngtypinfo.subtype,
-							&subsend, &typIsVarlena);
-
 	pq_sendbyte(buf, flags);
 
 	if (RANGE_HAS_LBOUND(flags))
@@ -323,22 +297,23 @@ range_constructor0(PG_FUNCTION_ARGS)
 {
 	Oid			rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
 	RangeType  *range;
+	TypeCacheEntry *typcache;
 	RangeBound	lower;
 	RangeBound	upper;
 
-	lower.rngtypid = rngtypid;
+	typcache = range_get_typcache(fcinfo, rngtypid);
+
 	lower.val = (Datum) 0;
-	lower.inclusive = false;
 	lower.infinite = false;
+	lower.inclusive = false;
 	lower.lower = true;
 
-	upper.rngtypid = rngtypid;
 	upper.val = (Datum) 0;
-	upper.inclusive = false;
 	upper.infinite = false;
+	upper.inclusive = false;
 	upper.lower = false;
 
-	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, true));
+	range = make_range(typcache, &lower, &upper, true);
 
 	PG_RETURN_RANGE(range);
 }
@@ -349,27 +324,28 @@ range_constructor1(PG_FUNCTION_ARGS)
 	Datum		arg1 = PG_GETARG_DATUM(0);
 	Oid			rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
 	RangeType  *range;
+	TypeCacheEntry *typcache;
 	RangeBound	lower;
 	RangeBound	upper;
 
+	typcache = range_get_typcache(fcinfo, rngtypid);
+
 	if (PG_ARGISNULL(0))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_EXCEPTION),
-				 errmsg("argument must not be NULL")));
+				 errmsg("range constructor argument must not be NULL")));
 
-	lower.rngtypid = rngtypid;
 	lower.val = arg1;
-	lower.inclusive = true;
 	lower.infinite = false;
+	lower.inclusive = true;
 	lower.lower = true;
 
-	upper.rngtypid = rngtypid;
 	upper.val = arg1;
-	upper.inclusive = true;
 	upper.infinite = false;
+	upper.inclusive = true;
 	upper.lower = false;
 
-	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+	range = make_range(typcache, &lower, &upper, false);
 
 	PG_RETURN_RANGE(range);
 }
@@ -381,25 +357,26 @@ range_constructor2(PG_FUNCTION_ARGS)
 	Datum		arg2 = PG_GETARG_DATUM(1);
 	Oid			rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
 	RangeType  *range;
+	TypeCacheEntry *typcache;
 	RangeBound	lower;
 	RangeBound	upper;
 	char		flags;
 
+	typcache = range_get_typcache(fcinfo, rngtypid);
+
 	flags = range_parse_flags(RANGE_DEFAULT_FLAGS);
 
-	lower.rngtypid = rngtypid;
 	lower.val = PG_ARGISNULL(0) ? (Datum) 0 : arg1;
-	lower.inclusive = (flags & RANGE_LB_INC) != 0;
 	lower.infinite = PG_ARGISNULL(0);
+	lower.inclusive = (flags & RANGE_LB_INC) != 0;
 	lower.lower = true;
 
-	upper.rngtypid = rngtypid;
 	upper.val = PG_ARGISNULL(1) ? (Datum) 0 : arg2;
-	upper.inclusive = (flags & RANGE_UB_INC) != 0;
 	upper.infinite = PG_ARGISNULL(1);
+	upper.inclusive = (flags & RANGE_UB_INC) != 0;
 	upper.lower = false;
 
-	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+	range = make_range(typcache, &lower, &upper, false);
 
 	PG_RETURN_RANGE(range);
 }
@@ -411,30 +388,31 @@ range_constructor3(PG_FUNCTION_ARGS)
 	Datum		arg2 = PG_GETARG_DATUM(1);
 	Oid			rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
 	RangeType  *range;
+	TypeCacheEntry *typcache;
 	RangeBound	lower;
 	RangeBound	upper;
 	char		flags;
 
+	typcache = range_get_typcache(fcinfo, rngtypid);
+
 	if (PG_ARGISNULL(2))
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_EXCEPTION),
-				 errmsg("flags argument must not be NULL")));
+				 errmsg("range constructor flags argument must not be NULL")));
 
 	flags = range_parse_flags(text_to_cstring(PG_GETARG_TEXT_P(2)));
 
-	lower.rngtypid = rngtypid;
 	lower.val = PG_ARGISNULL(0) ? (Datum) 0 : arg1;
-	lower.inclusive = (flags & RANGE_LB_INC) != 0;
 	lower.infinite = PG_ARGISNULL(0);
+	lower.inclusive = (flags & RANGE_LB_INC) != 0;
 	lower.lower = true;
 
-	upper.rngtypid = rngtypid;
 	upper.val = PG_ARGISNULL(1) ? (Datum) 0 : arg2;
-	upper.inclusive = (flags & RANGE_UB_INC) != 0;
 	upper.infinite = PG_ARGISNULL(1);
+	upper.inclusive = (flags & RANGE_UB_INC) != 0;
 	upper.lower = false;
 
-	range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+	range = make_range(typcache, &lower, &upper, false);
 
 	PG_RETURN_RANGE(range);
 }
@@ -444,11 +422,14 @@ Datum
 range_lower(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
+	TypeCacheEntry *typcache;
 	RangeBound	lower;
 	RangeBound	upper;
 	bool		empty;
 
-	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower, &upper, &empty);
 
 	/* Return NULL if there's no finite lower bound */
 	if (empty || lower.infinite)
@@ -461,11 +442,14 @@ Datum
 range_upper(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
+	TypeCacheEntry *typcache;
 	RangeBound	lower;
 	RangeBound	upper;
 	bool		empty;
 
-	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower, &upper, &empty);
 
 	/* Return NULL if there's no finite upper bound */
 	if (empty || upper.infinite)
@@ -480,65 +464,45 @@ Datum
 range_empty(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
-	RangeBound	lower;
-	RangeBound	upper;
-	bool		empty;
+	char		flags = range_get_flags(r1);
 
-	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
-
-	PG_RETURN_BOOL(empty);
+	PG_RETURN_BOOL(flags & RANGE_EMPTY);
 }
 
 Datum
 range_lower_inc(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
-	RangeBound	lower;
-	RangeBound	upper;
-	bool		empty;
-
-	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+	char		flags = range_get_flags(r1);
 
-	PG_RETURN_BOOL(lower.inclusive);
+	PG_RETURN_BOOL(flags & RANGE_LB_INC);
 }
 
 Datum
 range_upper_inc(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
-	RangeBound	lower;
-	RangeBound	upper;
-	bool		empty;
-
-	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+	char		flags = range_get_flags(r1);
 
-	PG_RETURN_BOOL(upper.inclusive);
+	PG_RETURN_BOOL(flags & RANGE_UB_INC);
 }
 
 Datum
 range_lower_inf(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
-	RangeBound	lower;
-	RangeBound	upper;
-	bool		empty;
-
-	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+	char		flags = range_get_flags(r1);
 
-	PG_RETURN_BOOL(lower.infinite);
+	PG_RETURN_BOOL(flags & RANGE_LB_INF);
 }
 
 Datum
 range_upper_inf(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
-	RangeBound	lower;
-	RangeBound	upper;
-	bool		empty;
-
-	range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+	char		flags = range_get_flags(r1);
 
-	PG_RETURN_BOOL(upper.infinite);
+	PG_RETURN_BOOL(flags & RANGE_UB_INF);
 }
 
 
@@ -548,6 +512,7 @@ range_eq(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -555,21 +520,24 @@ range_eq(PG_FUNCTION_ARGS)
 	bool		empty1,
 				empty2;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	if (empty1 && empty2)
 		PG_RETURN_BOOL(true);
 	if (empty1 != empty2)
 		PG_RETURN_BOOL(false);
 
-	if (range_cmp_bounds(fcinfo, &lower1, &lower2) != 0)
+	if (range_cmp_bounds(typcache, &lower1, &lower2) != 0)
 		PG_RETURN_BOOL(false);
 
-	if (range_cmp_bounds(fcinfo, &upper1, &upper2) != 0)
+	if (range_cmp_bounds(typcache, &upper1, &upper2) != 0)
 		PG_RETURN_BOOL(false);
 
 	PG_RETURN_BOOL(true);
@@ -588,30 +556,28 @@ range_contains_elem(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	Datum		val = PG_GETARG_DATUM(1);
+	TypeCacheEntry *typcache;
+	RangeBound	lower2;
+	RangeBound	upper2;
 	RangeType  *r2;
-	RangeBound	lower1,
-				lower2;
-	RangeBound	upper1,
-				upper2;
-	bool		empty1;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
 
-	lower2.rngtypid = lower1.rngtypid;
-	lower2.inclusive = true;
+	/* Construct a singleton range representing just "val" */
+	lower2.val = val;
 	lower2.infinite = false;
+	lower2.inclusive = true;
 	lower2.lower = true;
-	lower2.val = val;
 
-	upper2.rngtypid = lower1.rngtypid;
-	upper2.inclusive = true;
+	upper2.val = val;
 	upper2.infinite = false;
+	upper2.inclusive = true;
 	upper2.lower = false;
-	upper2.val = val;
 
-	r2 = DatumGetRangeType(make_range(fcinfo, &lower2, &upper2, false));
+	r2 = make_range(typcache, &lower2, &upper2, false);
 
-	PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+	/* And use range_contains */
+	PG_RETURN_BOOL(range_contains_internal(typcache, r1, r2));
 }
 
 Datum
@@ -619,39 +585,44 @@ range_contains(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
+
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+		elog(ERROR, "range types do not match");
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
 
-	PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+	PG_RETURN_BOOL(range_contains_internal(typcache, r1, r2));
 }
 
 Datum
 elem_contained_by_range(PG_FUNCTION_ARGS)
 {
-	RangeType  *r1 = PG_GETARG_RANGE(1);
 	Datum		val = PG_GETARG_DATUM(0);
+	RangeType  *r1 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
+	RangeBound	lower2;
+	RangeBound	upper2;
 	RangeType  *r2;
-	RangeBound	lower1,
-				lower2;
-	RangeBound	upper1,
-				upper2;
-	bool		empty1;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
 
-	lower2.rngtypid = lower1.rngtypid;
-	lower2.inclusive = true;
+	/* Construct a singleton range representing just "val" */
+	lower2.val = val;
 	lower2.infinite = false;
+	lower2.inclusive = true;
 	lower2.lower = true;
-	lower2.val = val;
 
-	upper2.rngtypid = lower1.rngtypid;
-	upper2.inclusive = true;
+	upper2.val = val;
 	upper2.infinite = false;
+	upper2.inclusive = true;
 	upper2.lower = false;
-	upper2.val = val;
 
-	r2 = DatumGetRangeType(make_range(fcinfo, &lower2, &upper2, false));
+	r2 = make_range(typcache, &lower2, &upper2, false);
 
-	PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+	/* And use range_contains */
+	PG_RETURN_BOOL(range_contains_internal(typcache, r1, r2));
 }
 
 Datum
@@ -659,8 +630,15 @@ range_contained_by(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
+
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+		elog(ERROR, "range types do not match");
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
 
-	PG_RETURN_BOOL(range_contains_internal(fcinfo, r2, r1));
+	PG_RETURN_BOOL(range_contains_internal(typcache, r2, r1));
 }
 
 Datum
@@ -668,6 +646,7 @@ range_before(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -675,17 +654,20 @@ range_before(PG_FUNCTION_ARGS)
 	bool		empty1,
 				empty2;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	/* An empty range is neither before nor after any other range */
 	if (empty1 || empty2)
 		PG_RETURN_BOOL(false);
 
-	PG_RETURN_BOOL(range_cmp_bounds(fcinfo, &upper1, &lower2) < 0);
+	PG_RETURN_BOOL(range_cmp_bounds(typcache, &upper1, &lower2) < 0);
 }
 
 Datum
@@ -693,6 +675,7 @@ range_after(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -700,17 +683,20 @@ range_after(PG_FUNCTION_ARGS)
 	bool		empty1,
 				empty2;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	/* An empty range is neither before nor after any other range */
 	if (empty1 || empty2)
 		PG_RETURN_BOOL(false);
 
-	PG_RETURN_BOOL(range_cmp_bounds(fcinfo, &lower1, &upper2) > 0);
+	PG_RETURN_BOOL(range_cmp_bounds(typcache, &lower1, &upper2) > 0);
 }
 
 Datum
@@ -718,7 +704,7 @@ range_adjacent(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
-	RangeTypeInfo rngtypinfo;
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -726,12 +712,15 @@ range_adjacent(PG_FUNCTION_ARGS)
 	bool		empty1,
 				empty2;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	/* An empty range is not adjacent to any other range */
 	if (empty1 || empty2)
 		PG_RETURN_BOOL(false);
@@ -744,21 +733,18 @@ range_adjacent(PG_FUNCTION_ARGS)
 	 * The semantics for range_cmp_bounds aren't quite what we need here, so
 	 * we do the comparison more directly.
 	 */
-
-	range_gettypinfo(fcinfo, lower1.rngtypid, &rngtypinfo);
-
 	if (lower1.inclusive != upper2.inclusive)
 	{
-		if (DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
-											rngtypinfo.collation,
+		if (DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
+											typcache->rng_collation,
 											lower1.val, upper2.val)) == 0)
 			PG_RETURN_BOOL(true);
 	}
 
 	if (upper1.inclusive != lower2.inclusive)
 	{
-		if (DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
-											rngtypinfo.collation,
+		if (DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
+											typcache->rng_collation,
 											upper1.val, lower2.val)) == 0)
 			PG_RETURN_BOOL(true);
 	}
@@ -771,6 +757,7 @@ range_overlaps(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -778,22 +765,25 @@ range_overlaps(PG_FUNCTION_ARGS)
 	bool		empty1,
 				empty2;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	/* An empty range does not overlap any other range */
 	if (empty1 || empty2)
 		PG_RETURN_BOOL(false);
 
-	if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0 &&
-		range_cmp_bounds(fcinfo, &lower1, &upper2) <= 0)
+	if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0 &&
+		range_cmp_bounds(typcache, &lower1, &upper2) <= 0)
 		PG_RETURN_BOOL(true);
 
-	if (range_cmp_bounds(fcinfo, &lower2, &lower1) >= 0 &&
-		range_cmp_bounds(fcinfo, &lower2, &upper1) <= 0)
+	if (range_cmp_bounds(typcache, &lower2, &lower1) >= 0 &&
+		range_cmp_bounds(typcache, &lower2, &upper1) <= 0)
 		PG_RETURN_BOOL(true);
 
 	PG_RETURN_BOOL(false);
@@ -804,6 +794,7 @@ range_overleft(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -811,17 +802,20 @@ range_overleft(PG_FUNCTION_ARGS)
 	bool		empty1,
 				empty2;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	/* An empty range is neither before nor after any other range */
 	if (empty1 || empty2)
 		PG_RETURN_BOOL(false);
 
-	if (range_cmp_bounds(fcinfo, &upper1, &upper2) <= 0)
+	if (range_cmp_bounds(typcache, &upper1, &upper2) <= 0)
 		PG_RETURN_BOOL(true);
 
 	PG_RETURN_BOOL(false);
@@ -832,6 +826,7 @@ range_overright(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -839,17 +834,20 @@ range_overright(PG_FUNCTION_ARGS)
 	bool		empty1,
 				empty2;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	/* An empty range is neither before nor after any other range */
 	if (empty1 || empty2)
 		PG_RETURN_BOOL(false);
 
-	if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0)
+	if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
 		PG_RETURN_BOOL(true);
 
 	PG_RETURN_BOOL(false);
@@ -862,6 +860,7 @@ range_minus(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -873,20 +872,23 @@ range_minus(PG_FUNCTION_ARGS)
 				cmp_u1l2,
 				cmp_u1u2;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	/* if either is empty, r1 is the correct answer */
 	if (empty1 || empty2)
 		PG_RETURN_RANGE(r1);
 
-	cmp_l1l2 = range_cmp_bounds(fcinfo, &lower1, &lower2);
-	cmp_l1u2 = range_cmp_bounds(fcinfo, &lower1, &upper2);
-	cmp_u1l2 = range_cmp_bounds(fcinfo, &upper1, &lower2);
-	cmp_u1u2 = range_cmp_bounds(fcinfo, &upper1, &upper2);
+	cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
+	cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
+	cmp_u1l2 = range_cmp_bounds(typcache, &upper1, &lower2);
+	cmp_u1u2 = range_cmp_bounds(typcache, &upper1, &upper2);
 
 	if (cmp_l1l2 < 0 && cmp_u1u2 > 0)
 		ereport(ERROR,
@@ -897,20 +899,20 @@ range_minus(PG_FUNCTION_ARGS)
 		PG_RETURN_RANGE(r1);
 
 	if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
-		PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));
+		PG_RETURN_RANGE(make_empty_range(typcache));
 
 	if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
 	{
 		lower2.inclusive = !lower2.inclusive;
 		lower2.lower = false;	/* it will become the upper bound */
-		PG_RETURN_RANGE(make_range(fcinfo, &lower1, &lower2, false));
+		PG_RETURN_RANGE(make_range(typcache, &lower1, &lower2, false));
 	}
 
 	if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
 	{
 		upper2.inclusive = !upper2.inclusive;
 		upper2.lower = true;	/* it will become the lower bound */
-		PG_RETURN_RANGE(make_range(fcinfo, &upper2, &upper1, false));
+		PG_RETURN_RANGE(make_range(typcache, &upper2, &upper1, false));
 	}
 
 	elog(ERROR, "unexpected case in range_minus");
@@ -922,6 +924,7 @@ range_union(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -931,12 +934,15 @@ range_union(PG_FUNCTION_ARGS)
 	RangeBound *result_lower;
 	RangeBound *result_upper;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	/* if either is empty, the other is the correct answer */
 	if (empty1)
 		PG_RETURN_RANGE(r2);
@@ -949,17 +955,17 @@ range_union(PG_FUNCTION_ARGS)
 				(errcode(ERRCODE_DATA_EXCEPTION),
 				 errmsg("result of range union would not be contiguous")));
 
-	if (range_cmp_bounds(fcinfo, &lower1, &lower2) < 0)
+	if (range_cmp_bounds(typcache, &lower1, &lower2) < 0)
 		result_lower = &lower1;
 	else
 		result_lower = &lower2;
 
-	if (range_cmp_bounds(fcinfo, &upper1, &upper2) > 0)
+	if (range_cmp_bounds(typcache, &upper1, &upper2) > 0)
 		result_upper = &upper1;
 	else
 		result_upper = &upper2;
 
-	PG_RETURN_RANGE(make_range(fcinfo, result_lower, result_upper, false));
+	PG_RETURN_RANGE(make_range(typcache, result_lower, result_upper, false));
 }
 
 Datum
@@ -967,6 +973,7 @@ range_intersect(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -976,26 +983,29 @@ range_intersect(PG_FUNCTION_ARGS)
 	RangeBound *result_lower;
 	RangeBound *result_upper;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
-		PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));
+		PG_RETURN_RANGE(make_empty_range(typcache));
 
-	if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0)
+	if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
 		result_lower = &lower1;
 	else
 		result_lower = &lower2;
 
-	if (range_cmp_bounds(fcinfo, &upper1, &upper2) <= 0)
+	if (range_cmp_bounds(typcache, &upper1, &upper2) <= 0)
 		result_upper = &upper1;
 	else
 		result_upper = &upper2;
 
-	PG_RETURN_RANGE(make_range(fcinfo, result_lower, result_upper, false));
+	PG_RETURN_RANGE(make_range(typcache, result_lower, result_upper, false));
 }
 
 /* Btree support */
@@ -1005,6 +1015,7 @@ range_cmp(PG_FUNCTION_ARGS)
 {
 	RangeType  *r1 = PG_GETARG_RANGE(0);
 	RangeType  *r2 = PG_GETARG_RANGE(1);
+	TypeCacheEntry *typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
@@ -1013,12 +1024,15 @@ range_cmp(PG_FUNCTION_ARGS)
 				empty2;
 	int			cmp;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
+	/* Different types should be prevented by ANYRANGE matching rules */
+	if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
 		elog(ERROR, "range types do not match");
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
 	/* For b-tree use, empty ranges sort before all else */
 	if (empty1 && empty2)
 		PG_RETURN_INT32(0);
@@ -1027,10 +1041,10 @@ range_cmp(PG_FUNCTION_ARGS)
 	else if (empty2)
 		PG_RETURN_INT32(1);
 
-	if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0)
+	if ((cmp = range_cmp_bounds(typcache, &lower1, &lower2)) != 0)
 		PG_RETURN_INT32(cmp);
 
-	PG_RETURN_INT32(range_cmp_bounds(fcinfo, &upper1, &upper2));
+	PG_RETURN_INT32(range_cmp_bounds(typcache, &upper1, &upper2));
 }
 
 Datum
@@ -1071,6 +1085,7 @@ Datum
 hash_range(PG_FUNCTION_ARGS)
 {
 	RangeType  *r = PG_GETARG_RANGE(0);
+	TypeCacheEntry *typcache;
 	RangeBound	lower;
 	RangeBound	upper;
 	bool		empty;
@@ -1078,12 +1093,12 @@ hash_range(PG_FUNCTION_ARGS)
 	uint32		lower_hash = 0;
 	uint32		upper_hash = 0;
 	uint32		result = 0;
-	RangeTypeInfo rngtypinfo;
-	TypeCacheEntry *typentry;
-	Oid			subtype;
+	TypeCacheEntry *subtypcache;
 	FunctionCallInfoData locfcinfo;
 
-	range_deserialize(fcinfo, r, &lower, &upper, &empty);
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+	range_deserialize(typcache, r, &lower, &upper, &empty);
 
 	if (empty)
 		flags |= RANGE_EMPTY;
@@ -1093,32 +1108,26 @@ hash_range(PG_FUNCTION_ARGS)
 	flags |= upper.inclusive ? RANGE_UB_INC : 0;
 	flags |= upper.infinite ? RANGE_UB_INF : 0;
 
-	range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
-	subtype = rngtypinfo.subtype;
-
 	/*
-	 * We arrange to look up the hash function only once per series of calls,
-	 * assuming the subtype doesn't change underneath us.  The typcache is
-	 * used so that we have no memory leakage when being used as an index
-	 * support function.
+	 * Look up the element type's hash function, if not done already.
 	 */
-	typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
-	if (typentry == NULL || typentry->type_id != subtype)
+	subtypcache = typcache->rngelemtype;
+	if (!OidIsValid(subtypcache->hash_proc_finfo.fn_oid))
 	{
-		typentry = lookup_type_cache(subtype, TYPECACHE_HASH_PROC_FINFO);
-		if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
+		subtypcache = lookup_type_cache(subtypcache->type_id,
+										TYPECACHE_HASH_PROC_FINFO);
+		if (!OidIsValid(subtypcache->hash_proc_finfo.fn_oid))
 			ereport(ERROR,
 					(errcode(ERRCODE_UNDEFINED_FUNCTION),
 					 errmsg("could not identify a hash function for type %s",
-							format_type_be(subtype))));
-		fcinfo->flinfo->fn_extra = (void *) typentry;
+							format_type_be(subtypcache->type_id))));
 	}
 
 	/*
 	 * Apply the hash function to each bound (the hash function shouldn't care
 	 * about the collation).
 	 */
-	InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
+	InitFunctionCallInfoData(locfcinfo, &subtypcache->hash_proc_finfo, 1,
 							 InvalidOid, NULL, NULL);
 
 	if (RANGE_HAS_LBOUND(flags))
@@ -1156,11 +1165,14 @@ Datum
 int4range_canonical(PG_FUNCTION_ARGS)
 {
 	RangeType  *r = PG_GETARG_RANGE(0);
+	TypeCacheEntry *typcache;
 	RangeBound	lower;
 	RangeBound	upper;
 	bool		empty;
 
-	range_deserialize(fcinfo, r, &lower, &upper, &empty);
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+	range_deserialize(typcache, r, &lower, &upper, &empty);
 
 	if (empty)
 		PG_RETURN_RANGE(r);
@@ -1177,18 +1189,21 @@ int4range_canonical(PG_FUNCTION_ARGS)
 		upper.inclusive = false;
 	}
 
-	PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+	PG_RETURN_RANGE(range_serialize(typcache, &lower, &upper, false));
 }
 
 Datum
 int8range_canonical(PG_FUNCTION_ARGS)
 {
 	RangeType  *r = PG_GETARG_RANGE(0);
+	TypeCacheEntry *typcache;
 	RangeBound	lower;
 	RangeBound	upper;
 	bool		empty;
 
-	range_deserialize(fcinfo, r, &lower, &upper, &empty);
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+	range_deserialize(typcache, r, &lower, &upper, &empty);
 
 	if (empty)
 		PG_RETURN_RANGE(r);
@@ -1205,18 +1220,21 @@ int8range_canonical(PG_FUNCTION_ARGS)
 		upper.inclusive = false;
 	}
 
-	PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+	PG_RETURN_RANGE(range_serialize(typcache, &lower, &upper, false));
 }
 
 Datum
 daterange_canonical(PG_FUNCTION_ARGS)
 {
 	RangeType  *r = PG_GETARG_RANGE(0);
+	TypeCacheEntry *typcache;
 	RangeBound	lower;
 	RangeBound	upper;
 	bool		empty;
 
-	range_deserialize(fcinfo, r, &lower, &upper, &empty);
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+	range_deserialize(typcache, r, &lower, &upper, &empty);
 
 	if (empty)
 		PG_RETURN_RANGE(r);
@@ -1233,7 +1251,7 @@ daterange_canonical(PG_FUNCTION_ARGS)
 		upper.inclusive = false;
 	}
 
-	PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+	PG_RETURN_RANGE(range_serialize(typcache, &lower, &upper, false));
 }
 
 /*
@@ -1328,6 +1346,32 @@ tstzrange_subdiff(PG_FUNCTION_ARGS)
  *----------------------------------------------------------
  */
 
+/*
+ * range_get_typcache: get cached information about a range type
+ *
+ * This is for use by range-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the range type.  Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+range_get_typcache(FunctionCallInfo fcinfo, Oid rngtypid)
+{
+	TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+	if (typcache == NULL ||
+		typcache->type_id != rngtypid)
+	{
+		typcache = lookup_type_cache(rngtypid, TYPECACHE_RANGE_INFO);
+		if (typcache->rngelemtype == NULL)
+			elog(ERROR, "type %u is not a range type", rngtypid);
+		fcinfo->flinfo->fn_extra = (void *) typcache;
+	}
+
+	return typcache;
+}
+
+
 /*
  * Serialized format is:
  *
@@ -1354,33 +1398,29 @@ tstzrange_subdiff(PG_FUNCTION_ARGS)
  * This does not force canonicalization of the range value.  In most cases,
  * external callers should only be canonicalization functions.
  */
-Datum
-range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
+RangeType *
+range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
 				bool empty)
 {
-	Datum		range;
-	size_t		msize;
+	RangeType  *range;
+	Size		msize;
 	Pointer		ptr;
 	int16		typlen;
-	char		typalign;
 	bool		typbyval;
+	char		typalign;
 	char		typstorage;
 	char		flags = 0;
-	RangeTypeInfo rngtypinfo;
-
-	if (lower->rngtypid != upper->rngtypid)
-		elog(ERROR, "range types do not match");
-
-	range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo);
 
-	typlen = rngtypinfo.subtyplen;
-	typalign = rngtypinfo.subtypalign;
-	typbyval = rngtypinfo.subtypbyval;
-	typstorage = rngtypinfo.subtypstorage;
+	/* fetch information about range's element type */
+	typlen = typcache->rngelemtype->typlen;
+	typbyval = typcache->rngelemtype->typbyval;
+	typalign = typcache->rngelemtype->typalign;
+	typstorage = typcache->rngelemtype->typstorage;
 
+	/* construct flags value */
 	if (empty)
 		flags |= RANGE_EMPTY;
-	else if (range_cmp_bounds(fcinfo, lower, upper) > 0)
+	else if (range_cmp_bounds(typcache, lower, upper) > 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_DATA_EXCEPTION),
 				 errmsg("range lower bound must be less than or equal to range upper bound")));
@@ -1390,9 +1430,11 @@ range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
 	flags |= upper->inclusive ? RANGE_UB_INC : 0;
 	flags |= upper->infinite ? RANGE_UB_INF : 0;
 
-	msize = VARHDRSZ;
-	msize += sizeof(Oid);
+	/* Count space for varlena header and range type's OID */
+	msize = sizeof(RangeType);
+	Assert(msize == MAXALIGN(msize));
 
+	/* Count space for bounds */
 	if (RANGE_HAS_LBOUND(flags))
 	{
 		/*
@@ -1421,16 +1463,17 @@ range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
 								   typlen, typstorage);
 	}
 
+	/* Add space for flag byte */
 	msize += sizeof(char);
 
 	/* Note: zero-fill is required here, just as in heap tuples */
-	ptr = palloc0(msize);
-	range = (Datum) ptr;
+	range = (RangeType *) palloc0(msize);
+	SET_VARSIZE(range, msize);
 
-	ptr += VARHDRSZ;
+	/* Now fill in the datum */
+	range->rangetypid = typcache->type_id;
 
-	memcpy(ptr, &lower->rngtypid, sizeof(Oid));
-	ptr += sizeof(Oid);
+	ptr = (char *) (range + 1);
 
 	if (RANGE_HAS_LBOUND(flags))
 	{
@@ -1446,11 +1489,9 @@ range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
 						  typstorage);
 	}
 
-	memcpy(ptr, &flags, sizeof(char));
-	ptr += sizeof(char);
+	*((char *) ptr) = flags;
 
-	SET_VARSIZE(range, msize);
-	PG_RETURN_RANGE(range);
+	return range;
 }
 
 /*
@@ -1463,31 +1504,30 @@ range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
  * RangeBound structs will be pointers into the given range object.
  */
 void
-range_deserialize(FunctionCallInfo fcinfo, RangeType *range, RangeBound *lower,
-				  RangeBound *upper, bool *empty)
+range_deserialize(TypeCacheEntry *typcache, RangeType *range,
+				  RangeBound *lower, RangeBound *upper, bool *empty)
 {
-	Pointer		ptr = VARDATA(range);
-	char		typalign;
-	int16		typlen;
-	int16		typbyval;
 	char		flags;
-	Oid			rngtypid;
+	int16		typlen;
+	bool		typbyval;
+	char		typalign;
+	Pointer		ptr;
 	Datum		lbound;
 	Datum		ubound;
-	RangeTypeInfo rngtypinfo;
+
+	/* assert caller passed the right typcache entry */
+	Assert(RangeTypeGetOid(range) == typcache->type_id);
 
 	/* fetch the flag byte from datum's last byte */
-	flags = *((char *) (ptr + VARSIZE(range) - VARHDRSZ - 1));
+	flags = *((char *) range + VARSIZE(range) - 1);
 
-	/* fetch and advance over the range type OID */
-	rngtypid = *((Oid *) ptr);
-	ptr += sizeof(Oid);
+	/* fetch information about range's element type */
+	typlen = typcache->rngelemtype->typlen;
+	typbyval = typcache->rngelemtype->typbyval;
+	typalign = typcache->rngelemtype->typalign;
 
-	/* fetch information about range type */
-	range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);
-	typalign = rngtypinfo.subtypalign;
-	typlen = rngtypinfo.subtyplen;
-	typbyval = rngtypinfo.subtypbyval;
+	/* initialize data pointer just after the range OID */
+	ptr = (Pointer) (range + 1);
 
 	/* fetch lower bound, if any */
 	if (RANGE_HAS_LBOUND(flags))
@@ -1511,47 +1551,55 @@ range_deserialize(FunctionCallInfo fcinfo, RangeType *range, RangeBound *lower,
 
 	/* emit results */
 
-	*empty = flags & RANGE_EMPTY;
+	*empty = (flags & RANGE_EMPTY) != 0;
 
-	lower->rngtypid = rngtypid;
 	lower->val = lbound;
-	lower->inclusive = (flags & RANGE_LB_INC) != 0;
 	lower->infinite = (flags & RANGE_LB_INF) != 0;
+	lower->inclusive = (flags & RANGE_LB_INC) != 0;
 	lower->lower = true;
 
-	upper->rngtypid = rngtypid;
 	upper->val = ubound;
-	upper->inclusive = (flags & RANGE_UB_INC) != 0;
 	upper->infinite = (flags & RANGE_UB_INF) != 0;
+	upper->inclusive = (flags & RANGE_UB_INC) != 0;
 	upper->lower = false;
 }
 
+/*
+ * range_get_flags: just get the flags from a RangeType value.
+ *
+ * This is frequently useful in places that only need the flags and not
+ * the full results of range_deserialize.
+ */
+char
+range_get_flags(RangeType *range)
+{
+	/* fetch the flag byte from datum's last byte */
+	return *((char *) range + VARSIZE(range) - 1);
+}
+
 /*
  * This both serializes and canonicalizes (if applicable) the range.
  * This should be used by most callers.
  */
-Datum
-make_range(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
+RangeType *
+make_range(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper,
 		   bool empty)
 {
-	Datum		range;
-	RangeTypeInfo rngtypinfo;
-
-	range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo);
+	RangeType  *range;
 
-	range = range_serialize(fcinfo, lower, upper, empty);
+	range = range_serialize(typcache, lower, upper, empty);
 
-	if (rngtypinfo.canonicalFn.fn_addr != NULL)
-		range = FunctionCall1(&rngtypinfo.canonicalFn, range);
+	if (OidIsValid(typcache->rng_canonical_finfo.fn_oid))
+		range = DatumGetRangeType(FunctionCall1(&typcache->rng_canonical_finfo,
+												RangeTypeGetDatum(range)));
 
-	PG_RETURN_RANGE(range);
+	return range;
 }
 
 int
-range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, RangeBound *b2)
+range_cmp_bounds(TypeCacheEntry *typcache, RangeBound *b1, RangeBound *b2)
 {
-	int			result;
-	RangeTypeInfo rngtypinfo;
+	int32		result;
 
 	if (b1->infinite && b2->infinite)
 	{
@@ -1565,10 +1613,8 @@ range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, RangeBound *b2)
 	else if (!b1->infinite && b2->infinite)
 		return (b2->lower) ? 1 : -1;
 
-	range_gettypinfo(fcinfo, b1->rngtypid, &rngtypinfo);
-
-	result = DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
-											 rngtypinfo.collation,
+	result = DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
+											 typcache->rng_collation,
 											 b1->val, b2->val));
 
 	if (result == 0)
@@ -1583,132 +1629,24 @@ range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, RangeBound *b2)
 }
 
 RangeType *
-make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid)
+make_empty_range(TypeCacheEntry *typcache)
 {
 	RangeBound	lower;
 	RangeBound	upper;
 
-	memset(&lower, 0, sizeof(RangeBound));
-	memset(&upper, 0, sizeof(RangeBound));
-
-	lower.rngtypid = rngtypid;
+	lower.val = (Datum) 0;
+	lower.infinite = false;
+	lower.inclusive = false;
 	lower.lower = true;
-	upper.rngtypid = rngtypid;
+
+	upper.val = (Datum) 0;
+	upper.infinite = false;
+	upper.inclusive = false;
 	upper.lower = false;
 
-	return DatumGetRangeType(make_range(fcinfo, &lower, &upper, true));
+	return make_range(typcache, &lower, &upper, true);
 }
 
-/*
- * Fills in rngtypinfo, from a cached copy if available.
- */
-void
-range_gettypinfo(FunctionCallInfo fcinfo, Oid rngtypid,
-				 RangeTypeInfo *rngtypinfo)
-{
-	RangeTypeInfo *cached = (RangeTypeInfo *) fcinfo->flinfo->fn_extra;
-
-	if (cached == NULL)
-	{
-		fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
-													  sizeof(RangeTypeInfo));
-		cached = (RangeTypeInfo *) fcinfo->flinfo->fn_extra;
-		cached->rngtypid = ~rngtypid;
-	}
-
-	if (cached->rngtypid != rngtypid)
-	{
-		Form_pg_range pg_range;
-		Form_pg_opclass pg_opclass;
-		Form_pg_type pg_type;
-		HeapTuple	tup;
-		Oid			subtypeOid;
-		Oid			collationOid;
-		Oid			canonicalOid;
-		Oid			subdiffOid;
-		Oid			opclassOid;
-		Oid			cmpFnOid;
-		Oid			opfamilyOid;
-		Oid			opcintype;
-		int16		subtyplen;
-		char		subtypalign;
-		char		subtypstorage;
-		bool		subtypbyval;
-
-		/* get information from pg_range */
-		tup = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rngtypid));
-		if (!HeapTupleIsValid(tup))
-			elog(ERROR, "cache lookup failed for range type %u", rngtypid);
-
-		pg_range = (Form_pg_range) GETSTRUCT(tup);
-
-		subtypeOid = pg_range->rngsubtype;
-		collationOid = pg_range->rngcollation;
-		canonicalOid = pg_range->rngcanonical;
-		opclassOid = pg_range->rngsubopc;
-		subdiffOid = pg_range->rngsubdiff;
-
-		ReleaseSysCache(tup);
-
-		/* get information from pg_opclass */
-		tup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassOid));
-		if (!HeapTupleIsValid(tup))
-			ereport(ERROR,
-					(errcode(ERRCODE_UNDEFINED_OBJECT),
-					 errmsg("operator class with OID %u does not exist",
-							opclassOid)));
-
-		pg_opclass = (Form_pg_opclass) GETSTRUCT(tup);
-
-		opfamilyOid = pg_opclass->opcfamily;
-		opcintype = pg_opclass->opcintype;
-
-		ReleaseSysCache(tup);
-
-		cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
-									 BTORDER_PROC);
-		if (!RegProcedureIsValid(cmpFnOid))
-			elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
-				 BTORDER_PROC, opcintype, opcintype, opfamilyOid);
-
-		/* get information from pg_type */
-		tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(subtypeOid));
-		if (!HeapTupleIsValid(tup))
-			elog(ERROR, "cache lookup failed for type %u", subtypeOid);
-
-		pg_type = (Form_pg_type) GETSTRUCT(tup);
-
-		subtyplen = pg_type->typlen;
-		subtypalign = pg_type->typalign;
-		subtypstorage = pg_type->typstorage;
-		subtypbyval = pg_type->typbyval;
-
-		ReleaseSysCache(tup);
-
-		/* set up the cache */
-
-		if (OidIsValid(canonicalOid))
-			fmgr_info(canonicalOid, &cached->canonicalFn);
-		else
-			cached->canonicalFn.fn_addr = NULL;
-
-		if (OidIsValid(subdiffOid))
-			fmgr_info(subdiffOid, &cached->subdiffFn);
-		else
-			cached->subdiffFn.fn_addr = NULL;
-
-		fmgr_info(cmpFnOid, &cached->cmpFn);
-		cached->subtype = subtypeOid;
-		cached->collation = collationOid;
-		cached->subtyplen = subtyplen;
-		cached->subtypalign = subtypalign;
-		cached->subtypstorage = subtypstorage;
-		cached->subtypbyval = subtypbyval;
-		cached->rngtypid = rngtypid;
-	}
-
-	memcpy(rngtypinfo, cached, sizeof(RangeTypeInfo));
-}
 
 /*
  *----------------------------------------------------------
@@ -2016,7 +1954,7 @@ range_bound_escape(char *value)
 }
 
 static bool
-range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
+range_contains_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
 {
 	RangeBound	lower1;
 	RangeBound	upper1;
@@ -2025,20 +1963,17 @@ range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
 	RangeBound	upper2;
 	bool		empty2;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
-
-	if (lower1.rngtypid != lower2.rngtypid)
-		elog(ERROR, "range types do not match");
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
 
 	if (empty2)
 		return true;
 	else if (empty1)
 		return false;
 
-	if (range_cmp_bounds(fcinfo, &lower1, &lower2) > 0)
+	if (range_cmp_bounds(typcache, &lower1, &lower2) > 0)
 		return false;
-	if (range_cmp_bounds(fcinfo, &upper1, &upper2) < 0)
+	if (range_cmp_bounds(typcache, &upper1, &upper2) < 0)
 		return false;
 
 	return true;
diff --git a/src/backend/utils/adt/rangetypes_gist.c b/src/backend/utils/adt/rangetypes_gist.c
index e4737b1902509f031565c6436c6aaa63c731b77f..3eb177a5ced1227841a94957d6050938608b1507 100644
--- a/src/backend/utils/adt/rangetypes_gist.c
+++ b/src/backend/utils/adt/rangetypes_gist.c
@@ -17,8 +17,6 @@
 #include "access/gist.h"
 #include "access/skey.h"
 #include "utils/builtins.h"
-#include "utils/fmgroids.h"
-#include "utils/lsyscache.h"
 #include "utils/rangetypes.h"
 
 
@@ -36,22 +34,24 @@
 #define RANGESTRAT_OVERRIGHT			11
 #define RANGESTRAT_ADJACENT				12
 
+#define RangeIsEmpty(r)  (range_get_flags(r) & RANGE_EMPTY)
+
 /*
  * Auxiliary structure for picksplit method.
  */
 typedef struct
 {
-	int			index;
-	RangeType  *data;
-	FunctionCallInfo fcinfo;
+	int			index;			/* original index in entryvec->vector[] */
+	RangeType  *data;			/* range value to sort */
+	TypeCacheEntry *typcache;	/* range type's info */
 }	PickSplitSortItem;
 
-static RangeType *range_super_union(FunctionCallInfo fcinfo, RangeType * r1,
+static RangeType *range_super_union(TypeCacheEntry *typcache, RangeType * r1,
 				  RangeType * r2);
-static bool range_gist_consistent_int(FunctionCallInfo fcinfo,
+static bool range_gist_consistent_int(FmgrInfo *flinfo,
 						  StrategyNumber strategy, RangeType * key,
 						  RangeType * query);
-static bool range_gist_consistent_leaf(FunctionCallInfo fcinfo,
+static bool range_gist_consistent_leaf(FmgrInfo *flinfo,
 						   StrategyNumber strategy, RangeType * key,
 						   RangeType * query);
 static int	sort_item_cmp(const void *a, const void *b);
@@ -66,15 +66,13 @@ range_gist_consistent(PG_FUNCTION_ARGS)
 	/* Oid subtype = PG_GETARG_OID(3); */
 	bool	   *recheck = (bool *) PG_GETARG_POINTER(4);
 	RangeType  *key = DatumGetRangeType(entry->key);
+	TypeCacheEntry *typcache;
 	RangeType  *query;
 	RangeBound	lower;
 	RangeBound	upper;
-	bool		empty;
-	Oid			rngtypid;
 
+	/* All operators served by this function are exact */
 	*recheck = false;
-	range_deserialize(fcinfo, key, &lower, &upper, &empty);
-	rngtypid = lower.rngtypid;
 
 	switch (strategy)
 	{
@@ -85,18 +83,19 @@ range_gist_consistent(PG_FUNCTION_ARGS)
 		 */
 		case RANGESTRAT_CONTAINS_ELEM:
 		case RANGESTRAT_ELEM_CONTAINED_BY:
-			lower.rngtypid = rngtypid;
-			lower.inclusive = true;
+			typcache = range_get_typcache(fcinfo, RangeTypeGetOid(key));
+
 			lower.val = dquery;
-			lower.lower = true;
 			lower.infinite = false;
-			upper.rngtypid = rngtypid;
-			upper.inclusive = true;
+			lower.inclusive = true;
+			lower.lower = true;
+
 			upper.val = dquery;
-			upper.lower = false;
 			upper.infinite = false;
-			query = DatumGetRangeType(make_range(fcinfo,
-												 &lower, &upper, false));
+			upper.inclusive = true;
+			upper.lower = false;
+
+			query = make_range(typcache, &lower, &upper, false);
 			break;
 
 		default:
@@ -105,10 +104,10 @@ range_gist_consistent(PG_FUNCTION_ARGS)
 	}
 
 	if (GIST_LEAF(entry))
-		PG_RETURN_BOOL(range_gist_consistent_leaf(fcinfo, strategy,
+		PG_RETURN_BOOL(range_gist_consistent_leaf(fcinfo->flinfo, strategy,
 												  key, query));
 	else
-		PG_RETURN_BOOL(range_gist_consistent_int(fcinfo, strategy,
+		PG_RETURN_BOOL(range_gist_consistent_int(fcinfo->flinfo, strategy,
 												 key, query));
 }
 
@@ -118,13 +117,16 @@ range_gist_union(PG_FUNCTION_ARGS)
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GISTENTRY  *ent = entryvec->vector;
 	RangeType  *result_range;
+	TypeCacheEntry *typcache;
 	int			i;
 
 	result_range = DatumGetRangeType(ent[0].key);
 
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(result_range));
+
 	for (i = 1; i < entryvec->n; i++)
 	{
-		result_range = range_super_union(fcinfo, result_range,
+		result_range = range_super_union(typcache, result_range,
 										 DatumGetRangeType(ent[i].key));
 	}
 
@@ -155,6 +157,7 @@ range_gist_penalty(PG_FUNCTION_ARGS)
 	float	   *penalty = (float *) PG_GETARG_POINTER(2);
 	RangeType  *orig = DatumGetRangeType(origentry->key);
 	RangeType  *new = DatumGetRangeType(newentry->key);
+	TypeCacheEntry *typcache;
 	RangeType  *s_union;
 	FmgrInfo   *subtype_diff;
 	RangeBound	lower1,
@@ -163,34 +166,54 @@ range_gist_penalty(PG_FUNCTION_ARGS)
 				upper2;
 	bool		empty1,
 				empty2;
-	float		lower_diff,
+	float8		lower_diff,
 				upper_diff;
-	RangeTypeInfo rngtypinfo;
 
-	s_union = range_super_union(fcinfo, orig, new);
+	if (RangeTypeGetOid(orig) != RangeTypeGetOid(new))
+		elog(ERROR, "range types do not match");
+
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(orig));
 
-	range_deserialize(fcinfo, orig, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, s_union, &lower2, &upper2, &empty2);
+	subtype_diff = &typcache->rng_subdiff_finfo;
 
-	range_gettypinfo(fcinfo, lower1.rngtypid, &rngtypinfo);
-	subtype_diff = &rngtypinfo.subdiffFn;
+	s_union = range_super_union(typcache, orig, new);
 
+	range_deserialize(typcache, orig, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, s_union, &lower2, &upper2, &empty2);
+
+	/* if orig isn't empty, s_union can't be either */
 	Assert(empty1 || !empty2);
 
 	if (empty1 && empty2)
-		return 0;
+	{
+		*penalty = 0;
+		PG_RETURN_POINTER(penalty);
+	}
 	else if (empty1 && !empty2)
 	{
 		if (lower2.infinite || upper2.infinite)
+		{
 			/* from empty to infinite */
-			return get_float8_infinity();
-		else if (subtype_diff->fn_addr != NULL)
+			*penalty = get_float4_infinity();
+			PG_RETURN_POINTER(penalty);
+		}
+		else if (OidIsValid(subtype_diff->fn_oid))
+		{
 			/* from empty to upper2-lower2 */
-			return DatumGetFloat8(FunctionCall2(subtype_diff,
-												upper2.val, lower2.val));
+			*penalty = DatumGetFloat8(FunctionCall2Coll(subtype_diff,
+														typcache->rng_collation,
+														upper2.val,
+														lower2.val));
+			if (*penalty < 0)
+				*penalty = 0;		/* subtype_diff is broken */
+			PG_RETURN_POINTER(penalty);
+		}
 		else
+		{
 			/* wild guess */
-			return 1.0;
+			*penalty = 1.0;
+			PG_RETURN_POINTER(penalty);
+		}
 	}
 
 	Assert(lower2.infinite || !lower1.infinite);
@@ -199,15 +222,20 @@ range_gist_penalty(PG_FUNCTION_ARGS)
 		lower_diff = get_float8_infinity();
 	else if (lower2.infinite && lower1.infinite)
 		lower_diff = 0;
-	else if (subtype_diff->fn_addr != NULL)
+	else if (OidIsValid(subtype_diff->fn_oid))
 	{
-		lower_diff = DatumGetFloat8(FunctionCall2(subtype_diff,
-												  lower1.val, lower2.val));
+		lower_diff = DatumGetFloat8(FunctionCall2Coll(subtype_diff,
+													  typcache->rng_collation,
+													  lower1.val,
+													  lower2.val));
 		if (lower_diff < 0)
 			lower_diff = 0;		/* subtype_diff is broken */
 	}
-	else	/* only know whether there is a difference or not */
-		lower_diff = (float) range_cmp_bounds(fcinfo, &lower1, &lower2);
+	else
+	{
+		/* only know whether there is a difference or not */
+		lower_diff = (float) range_cmp_bounds(typcache, &lower1, &lower2);
+	}
 
 	Assert(upper2.infinite || !upper1.infinite);
 
@@ -215,15 +243,20 @@ range_gist_penalty(PG_FUNCTION_ARGS)
 		upper_diff = get_float8_infinity();
 	else if (upper2.infinite && upper1.infinite)
 		upper_diff = 0;
-	else if (subtype_diff->fn_addr != NULL)
+	else if (OidIsValid(subtype_diff->fn_oid))
 	{
-		upper_diff = DatumGetFloat8(FunctionCall2(subtype_diff,
-												  upper2.val, upper1.val));
+		upper_diff = DatumGetFloat8(FunctionCall2Coll(subtype_diff,
+													  typcache->rng_collation,
+													  upper2.val,
+													  upper1.val));
 		if (upper_diff < 0)
 			upper_diff = 0;		/* subtype_diff is broken */
 	}
-	else	/* only know whether there is a difference or not */
-		upper_diff = (float) range_cmp_bounds(fcinfo, &upper2, &upper1);
+	else
+	{
+		/* only know whether there is a difference or not */
+		upper_diff = (float) range_cmp_bounds(typcache, &upper2, &upper1);
+	}
 
 	Assert(lower_diff >= 0 && upper_diff >= 0);
 
@@ -243,6 +276,7 @@ range_gist_picksplit(PG_FUNCTION_ARGS)
 {
 	GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
 	GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+	TypeCacheEntry *typcache;
 	OffsetNumber i;
 	RangeType  *pred_left;
 	RangeType  *pred_right;
@@ -253,23 +287,28 @@ range_gist_picksplit(PG_FUNCTION_ARGS)
 	OffsetNumber *right;
 	OffsetNumber maxoff;
 
+	/* use first item to look up range type's info */
+	pred_left = DatumGetRangeType(entryvec->vector[FirstOffsetNumber].key);
+	typcache = range_get_typcache(fcinfo, RangeTypeGetOid(pred_left));
+
+	/* allocate result and work arrays */
 	maxoff = entryvec->n - 1;
 	nbytes = (maxoff + 1) * sizeof(OffsetNumber);
-	sortItems = (PickSplitSortItem *) palloc(
-										 maxoff * sizeof(PickSplitSortItem));
 	v->spl_left = (OffsetNumber *) palloc(nbytes);
 	v->spl_right = (OffsetNumber *) palloc(nbytes);
+	sortItems = (PickSplitSortItem *) palloc(maxoff * sizeof(PickSplitSortItem));
 
 	/*
-	 * Preparing auxiliary array and sorting.
+	 * Prepare auxiliary array and sort the values.
 	 */
 	for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
 	{
 		sortItems[i - 1].index = i;
 		sortItems[i - 1].data = DatumGetRangeType(entryvec->vector[i].key);
-		sortItems[i - 1].fcinfo = fcinfo;
+		sortItems[i - 1].typcache = typcache;
 	}
 	qsort(sortItems, maxoff, sizeof(PickSplitSortItem), sort_item_cmp);
+
 	split_idx = maxoff / 2;
 
 	left = v->spl_left;
@@ -278,29 +317,27 @@ range_gist_picksplit(PG_FUNCTION_ARGS)
 	v->spl_nright = 0;
 
 	/*
-	 * First half of segs goes to the left datum.
+	 * First half of items goes to the left datum.
 	 */
-	pred_left = DatumGetRangeType(sortItems[0].data);
+	pred_left = sortItems[0].data;
 	*left++ = sortItems[0].index;
 	v->spl_nleft++;
 	for (i = 1; i < split_idx; i++)
 	{
-		pred_left = range_super_union(fcinfo, pred_left,
-									  DatumGetRangeType(sortItems[i].data));
+		pred_left = range_super_union(typcache, pred_left, sortItems[i].data);
 		*left++ = sortItems[i].index;
 		v->spl_nleft++;
 	}
 
 	/*
-	 * Second half of segs goes to the right datum.
+	 * Second half of items goes to the right datum.
 	 */
-	pred_right = DatumGetRangeType(sortItems[split_idx].data);
+	pred_right = sortItems[split_idx].data;
 	*right++ = sortItems[split_idx].index;
 	v->spl_nright++;
 	for (i = split_idx + 1; i < maxoff; i++)
 	{
-		pred_right = range_super_union(fcinfo, pred_right,
-									   DatumGetRangeType(sortItems[i].data));
+		pred_right = range_super_union(typcache, pred_right, sortItems[i].data);
 		*right++ = sortItems[i].index;
 		v->spl_nright++;
 	}
@@ -316,11 +353,16 @@ range_gist_picksplit(PG_FUNCTION_ARGS)
 Datum
 range_gist_same(PG_FUNCTION_ARGS)
 {
-	Datum		r1 = PG_GETARG_DATUM(0);
-	Datum		r2 = PG_GETARG_DATUM(1);
+	/* Datum r1 = PG_GETARG_DATUM(0); */
+	/* Datum r2 = PG_GETARG_DATUM(1); */
 	bool	   *result = (bool *) PG_GETARG_POINTER(2);
 
-	*result = DatumGetBool(OidFunctionCall2(F_RANGE_EQ, r1, r2));
+	/*
+	 * We can safely call range_eq using our fcinfo directly; it won't notice
+	 * the third argument.  This allows it to use fn_extra for caching.
+	 */
+	*result = DatumGetBool(range_eq(fcinfo));
+
 	PG_RETURN_POINTER(result);
 }
 
@@ -330,9 +372,11 @@ range_gist_same(PG_FUNCTION_ARGS)
  *----------------------------------------------------------
  */
 
-/* return the smallest range that contains r1 and r2 */
+/*
+ * Return the smallest range that contains r1 and r2
+ */
 static RangeType *
-range_super_union(FunctionCallInfo fcinfo, RangeType * r1, RangeType * r2)
+range_super_union(TypeCacheEntry *typcache, RangeType * r1, RangeType * r2)
 {
 	RangeBound	lower1,
 				lower2;
@@ -343,20 +387,20 @@ range_super_union(FunctionCallInfo fcinfo, RangeType * r1, RangeType * r2)
 	RangeBound *result_lower;
 	RangeBound *result_upper;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
 
 	if (empty1)
 		return r2;
 	if (empty2)
 		return r1;
 
-	if (range_cmp_bounds(fcinfo, &lower1, &lower2) <= 0)
+	if (range_cmp_bounds(typcache, &lower1, &lower2) <= 0)
 		result_lower = &lower1;
 	else
 		result_lower = &lower2;
 
-	if (range_cmp_bounds(fcinfo, &upper1, &upper2) >= 0)
+	if (range_cmp_bounds(typcache, &upper1, &upper2) >= 0)
 		result_upper = &upper1;
 	else
 		result_upper = &upper2;
@@ -367,161 +411,182 @@ range_super_union(FunctionCallInfo fcinfo, RangeType * r1, RangeType * r2)
 	if (result_lower == &lower2 && result_upper == &upper2)
 		return r2;
 
-	return DatumGetRangeType(make_range(fcinfo, result_lower, result_upper,
-										false));
+	return make_range(typcache, result_lower, result_upper, false);
+}
+
+/*
+ * trick function call: call the given function with given FmgrInfo
+ *
+ * To allow the various functions called here to cache lookups of range
+ * datatype information, we use a trick: we pass them the FmgrInfo struct
+ * for the GiST consistent function.  This relies on the knowledge that
+ * none of them consult FmgrInfo for anything but fn_extra, and that they
+ * all use fn_extra the same way, i.e. as a pointer to the typcache entry
+ * for the range data type.  Since the FmgrInfo is long-lived (it's actually
+ * part of the relcache entry for the index, typically) this essentially
+ * eliminates lookup overhead during operations on a GiST range index.
+ */
+static Datum
+TrickFunctionCall2(PGFunction proc, FmgrInfo *flinfo, Datum arg1, Datum arg2)
+{
+	FunctionCallInfoData fcinfo;
+	Datum		result;
+
+	InitFunctionCallInfoData(fcinfo, flinfo, 2, InvalidOid, NULL, NULL);
+
+	fcinfo.arg[0] = arg1;
+	fcinfo.arg[1] = arg2;
+	fcinfo.argnull[0] = false;
+	fcinfo.argnull[1] = false;
+
+	result = (*proc) (&fcinfo);
+
+	if (fcinfo.isnull)
+		elog(ERROR, "function %p returned NULL", proc);
+
+	return result;
 }
 
+/*
+ * GiST consistent test on an index internal page
+ */
 static bool
-range_gist_consistent_int(FunctionCallInfo fcinfo, StrategyNumber strategy,
+range_gist_consistent_int(FmgrInfo *flinfo, StrategyNumber strategy,
 						  RangeType * key, RangeType * query)
 {
-	Oid			proc;
-	RangeBound	lower1,
-				lower2;
-	RangeBound	upper1,
-				upper2;
-	bool		empty1,
-				empty2;
-	bool		retval;
+	PGFunction	proc;
 	bool		negate = false;
-
-	range_deserialize(fcinfo, key, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, query, &lower2, &upper2, &empty2);
+	bool		retval;
 
 	switch (strategy)
 	{
 		case RANGESTRAT_EQ:
-			proc = F_RANGE_CONTAINS;
+			proc = range_contains;
 			break;
 		case RANGESTRAT_NE:
 			return true;
 			break;
 		case RANGESTRAT_OVERLAPS:
-			proc = F_RANGE_OVERLAPS;
+			proc = range_overlaps;
 			break;
 		case RANGESTRAT_CONTAINS_ELEM:
 		case RANGESTRAT_CONTAINS:
-			proc = F_RANGE_CONTAINS;
+			proc = range_contains;
 			break;
 		case RANGESTRAT_ELEM_CONTAINED_BY:
 		case RANGESTRAT_CONTAINED_BY:
 			return true;
 			break;
 		case RANGESTRAT_BEFORE:
-			if (empty1)
+			if (RangeIsEmpty(key))
 				return false;
-			proc = F_RANGE_OVERRIGHT;
+			proc = range_overright;
 			negate = true;
 			break;
 		case RANGESTRAT_AFTER:
-			if (empty1)
+			if (RangeIsEmpty(key))
 				return false;
-			proc = F_RANGE_OVERLEFT;
+			proc = range_overleft;
 			negate = true;
 			break;
 		case RANGESTRAT_OVERLEFT:
-			if (empty1)
+			if (RangeIsEmpty(key))
 				return false;
-			proc = F_RANGE_AFTER;
+			proc = range_after;
 			negate = true;
 			break;
 		case RANGESTRAT_OVERRIGHT:
-			if (empty1)
+			if (RangeIsEmpty(key))
 				return false;
-			proc = F_RANGE_BEFORE;
+			proc = range_before;
 			negate = true;
 			break;
 		case RANGESTRAT_ADJACENT:
-			if (empty1 || empty2)
+			if (RangeIsEmpty(key) || RangeIsEmpty(query))
 				return false;
-			if (DatumGetBool(OidFunctionCall2(F_RANGE_ADJACENT,
-											  RangeTypeGetDatum(key),
-											  RangeTypeGetDatum(query))))
+			if (DatumGetBool(TrickFunctionCall2(range_adjacent, flinfo,
+												RangeTypeGetDatum(key),
+												RangeTypeGetDatum(query))))
 				return true;
-			proc = F_RANGE_OVERLAPS;
+			proc = range_overlaps;
 			break;
 		default:
 			elog(ERROR, "unrecognized range strategy: %d", strategy);
-			proc = InvalidOid;
+			proc = NULL;		/* keep compiler quiet */
 			break;
 	}
 
-	retval = DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key),
-										   RangeTypeGetDatum(query)));
-
+	retval = DatumGetBool(TrickFunctionCall2(proc, flinfo,
+											 RangeTypeGetDatum(key),
+											 RangeTypeGetDatum(query)));
 	if (negate)
 		retval = !retval;
 
-	PG_RETURN_BOOL(retval);
+	return retval;
 }
 
+/*
+ * GiST consistent test on an index leaf page
+ */
 static bool
-range_gist_consistent_leaf(FunctionCallInfo fcinfo, StrategyNumber strategy,
+range_gist_consistent_leaf(FmgrInfo *flinfo, StrategyNumber strategy,
 						   RangeType * key, RangeType * query)
 {
-	Oid			proc;
-	RangeBound	lower1,
-				lower2;
-	RangeBound	upper1,
-				upper2;
-	bool		empty1,
-				empty2;
-
-	range_deserialize(fcinfo, key, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, query, &lower2, &upper2, &empty2);
+	PGFunction	proc;
 
 	switch (strategy)
 	{
 		case RANGESTRAT_EQ:
-			proc = F_RANGE_EQ;
+			proc = range_eq;
 			break;
 		case RANGESTRAT_NE:
-			proc = F_RANGE_NE;
+			proc = range_ne;
 			break;
 		case RANGESTRAT_OVERLAPS:
-			proc = F_RANGE_OVERLAPS;
+			proc = range_overlaps;
 			break;
 		case RANGESTRAT_CONTAINS_ELEM:
 		case RANGESTRAT_CONTAINS:
-			proc = F_RANGE_CONTAINS;
+			proc = range_contains;
 			break;
 		case RANGESTRAT_ELEM_CONTAINED_BY:
 		case RANGESTRAT_CONTAINED_BY:
-			proc = F_RANGE_CONTAINED_BY;
+			proc = range_contained_by;
 			break;
 		case RANGESTRAT_BEFORE:
-			if (empty1 || empty2)
+			if (RangeIsEmpty(key) || RangeIsEmpty(query))
 				return false;
-			proc = F_RANGE_BEFORE;
+			proc = range_before;
 			break;
 		case RANGESTRAT_AFTER:
-			if (empty1 || empty2)
+			if (RangeIsEmpty(key) || RangeIsEmpty(query))
 				return false;
-			proc = F_RANGE_AFTER;
+			proc = range_after;
 			break;
 		case RANGESTRAT_OVERLEFT:
-			if (empty1 || empty2)
+			if (RangeIsEmpty(key) || RangeIsEmpty(query))
 				return false;
-			proc = F_RANGE_OVERLEFT;
+			proc = range_overleft;
 			break;
 		case RANGESTRAT_OVERRIGHT:
-			if (empty1 || empty2)
+			if (RangeIsEmpty(key) || RangeIsEmpty(query))
 				return false;
-			proc = F_RANGE_OVERRIGHT;
+			proc = range_overright;
 			break;
 		case RANGESTRAT_ADJACENT:
-			if (empty1 || empty2)
+			if (RangeIsEmpty(key) || RangeIsEmpty(query))
 				return false;
-			proc = F_RANGE_ADJACENT;
+			proc = range_adjacent;
 			break;
 		default:
 			elog(ERROR, "unrecognized range strategy: %d", strategy);
-			proc = InvalidOid;
+			proc = NULL;		/* keep compiler quiet */
 			break;
 	}
 
-	return DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key),
-										 RangeTypeGetDatum(query)));
+	return DatumGetBool(TrickFunctionCall2(proc, flinfo,
+										   RangeTypeGetDatum(key),
+										   RangeTypeGetDatum(query)));
 }
 
 /*
@@ -545,17 +610,17 @@ sort_item_cmp(const void *a, const void *b)
 	PickSplitSortItem *i2 = (PickSplitSortItem *) b;
 	RangeType  *r1 = i1->data;
 	RangeType  *r2 = i2->data;
+	TypeCacheEntry *typcache = i1->typcache;
 	RangeBound	lower1,
 				lower2;
 	RangeBound	upper1,
 				upper2;
 	bool		empty1,
 				empty2;
-	FunctionCallInfo fcinfo = i1->fcinfo;
 	int			cmp;
 
-	range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
-	range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+	range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+	range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
 
 	if (empty1 || empty2)
 	{
@@ -580,13 +645,13 @@ sort_item_cmp(const void *a, const void *b)
 		lower2.infinite || upper2.infinite)
 	{
 		if (lower1.infinite && lower2.infinite)
-			return range_cmp_bounds(fcinfo, &upper1, &upper2);
+			return range_cmp_bounds(typcache, &upper1, &upper2);
 		else if (lower1.infinite)
 			return -1;
 		else if (lower2.infinite)
 			return 1;
 		else if (upper1.infinite && upper2.infinite)
-			return -1 * range_cmp_bounds(fcinfo, &lower1, &lower2);
+			return -1 * range_cmp_bounds(typcache, &lower1, &lower2);
 		else if (upper1.infinite)
 			return 1;
 		else if (upper2.infinite)
@@ -595,8 +660,8 @@ sort_item_cmp(const void *a, const void *b)
 			Assert(false);
 	}
 
-	if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0)
+	if ((cmp = range_cmp_bounds(typcache, &lower1, &lower2)) != 0)
 		return cmp;
 
-	return range_cmp_bounds(fcinfo, &upper1, &upper2);
+	return range_cmp_bounds(typcache, &upper1, &upper2);
 }
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 924ee5730252d320a96867a37bf5b5fdd9d9e99e..8e334740ed2b0f72ff88cc0ada5ea4e4ae2190ef 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -51,6 +51,7 @@
 #include "catalog/indexing.h"
 #include "catalog/pg_enum.h"
 #include "catalog/pg_operator.h"
+#include "catalog/pg_range.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "utils/builtins.h"
@@ -120,6 +121,7 @@ static int32 RecordCacheArrayLen = 0;	/* allocated length of array */
 static int32 NextRecordTypmod = 0;		/* number of entries used */
 
 static void load_typcache_tupdesc(TypeCacheEntry *typentry);
+static void load_rangetype_info(TypeCacheEntry *typentry);
 static bool array_element_has_equality(TypeCacheEntry *typentry);
 static bool array_element_has_compare(TypeCacheEntry *typentry);
 static bool array_element_has_hashing(TypeCacheEntry *typentry);
@@ -205,6 +207,7 @@ lookup_type_cache(Oid type_id, int flags)
 		typentry->typlen = typtup->typlen;
 		typentry->typbyval = typtup->typbyval;
 		typentry->typalign = typtup->typalign;
+		typentry->typstorage = typtup->typstorage;
 		typentry->typtype = typtup->typtype;
 		typentry->typrelid = typtup->typrelid;
 
@@ -448,6 +451,16 @@ lookup_type_cache(Oid type_id, int flags)
 		load_typcache_tupdesc(typentry);
 	}
 
+	/*
+	 * If requested, get information about a range type
+	 */
+	if ((flags & TYPECACHE_RANGE_INFO) &&
+		typentry->rngelemtype == NULL &&
+		typentry->typtype == TYPTYPE_RANGE)
+	{
+		load_rangetype_info(typentry);
+	}
+
 	return typentry;
 }
 
@@ -479,6 +492,62 @@ load_typcache_tupdesc(TypeCacheEntry *typentry)
 	relation_close(rel, AccessShareLock);
 }
 
+/*
+ * load_rangetype_info --- helper routine to set up range type information
+ */
+static void
+load_rangetype_info(TypeCacheEntry *typentry)
+{
+	Form_pg_range pg_range;
+	HeapTuple	tup;
+	Oid			subtypeOid;
+	Oid			opclassOid;
+	Oid			canonicalOid;
+	Oid			subdiffOid;
+	Oid			opfamilyOid;
+	Oid			opcintype;
+	Oid			cmpFnOid;
+
+	/* get information from pg_range */
+	tup = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(typentry->type_id));
+	/* should not fail, since we already checked typtype ... */
+	if (!HeapTupleIsValid(tup))
+		elog(ERROR, "cache lookup failed for range type %u",
+			 typentry->type_id);
+	pg_range = (Form_pg_range) GETSTRUCT(tup);
+
+	subtypeOid = pg_range->rngsubtype;
+	typentry->rng_collation = pg_range->rngcollation;
+	opclassOid = pg_range->rngsubopc;
+	canonicalOid = pg_range->rngcanonical;
+	subdiffOid = pg_range->rngsubdiff;
+
+	ReleaseSysCache(tup);
+
+	/* get opclass properties and look up the comparison function */
+	opfamilyOid = get_opclass_family(opclassOid);
+	opcintype = get_opclass_input_type(opclassOid);
+
+	cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype,
+								 BTORDER_PROC);
+	if (!RegProcedureIsValid(cmpFnOid))
+		elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
+			 BTORDER_PROC, opcintype, opcintype, opfamilyOid);
+
+	/* set up cached fmgrinfo structs */
+	fmgr_info_cxt(cmpFnOid, &typentry->rng_cmp_proc_finfo,
+				  CacheMemoryContext);
+	if (OidIsValid(canonicalOid))
+		fmgr_info_cxt(canonicalOid, &typentry->rng_canonical_finfo,
+					  CacheMemoryContext);
+	if (OidIsValid(subdiffOid))
+		fmgr_info_cxt(subdiffOid, &typentry->rng_subdiff_finfo,
+					  CacheMemoryContext);
+
+	/* Lastly, set up link to the element type --- this marks data valid */
+	typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
+}
+
 
 /*
  * array_element_has_equality and friends are helper routines to check
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index e218a91d1a87553fd270af40ee3dcf20c165506a..585d32134c066c75abc9a5d4a9700a104bdfab8b 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -14,37 +14,51 @@
 #ifndef RANGETYPES_H
 #define RANGETYPES_H
 
-#include "fmgr.h"
+#include "utils/typcache.h"
 
 
-/* All ranges are represented as varlena objects */
-typedef struct varlena RangeType;
+/*
+ * Ranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+	int32		vl_len_;		/* varlena header (do not touch directly!) */
+	Oid			rangetypid;		/* range type's own OID */
+	/* Following the OID are zero to two bound values, then a flags byte */
+} RangeType;
+
+/* Use this macro in preference to fetching rangetypid field directly */
+#define RangeTypeGetOid(r)  ((r)->rangetypid)
+
+/* A range's flags byte contains these bits: */
+#define RANGE_EMPTY		0x01	/* range is empty */
+#define RANGE_LB_INC	0x02	/* lower bound is inclusive (vs exclusive) */
+#define RANGE_LB_NULL	0x04	/* lower bound is null (NOT CURRENTLY USED) */
+#define RANGE_LB_INF	0x08	/* lower bound is +/- infinity */
+#define RANGE_UB_INC	0x10	/* upper bound is inclusive (vs exclusive) */
+#define RANGE_UB_NULL	0x20	/* upper bound is null (NOT CURRENTLY USED) */
+#define RANGE_UB_INF	0x40	/* upper bound is +/- infinity */
+
+#define RANGE_HAS_LBOUND(flags) (!((flags) & (RANGE_EMPTY | \
+											  RANGE_LB_NULL | \
+											  RANGE_LB_INF)))
+
+#define RANGE_HAS_UBOUND(flags) (!((flags) & (RANGE_EMPTY | \
+											  RANGE_UB_NULL | \
+											  RANGE_UB_INF)))
+
 
 /* Internal representation of either bound of a range (not what's on disk) */
 typedef struct
 {
 	Datum		val;			/* the bound value, if any */
-	Oid			rngtypid;		/* OID of the range type itself */
 	bool		infinite;		/* bound is +/- infinity */
-	bool		lower;			/* this is the lower (vs upper) bound */
 	bool		inclusive;		/* bound is inclusive (vs exclusive) */
+	bool		lower;			/* this is the lower (vs upper) bound */
 } RangeBound;
 
-/* Standard runtime-cached data for a range type */
-typedef struct
-{
-	FmgrInfo	canonicalFn;	/* canonicalization function, if any */
-	FmgrInfo	cmpFn;			/* element type's btree comparison function */
-	FmgrInfo	subdiffFn;		/* element type difference function, if any */
-	Oid			rngtypid;		/* OID of the range type itself */
-	Oid			subtype;		/* OID of the element type */
-	Oid			collation;		/* collation for comparisons, if any */
-	int16		subtyplen;		/* typlen of element type */
-	char		subtypalign;	/* typalign of element type */
-	char		subtypstorage;	/* typstorage of element type */
-	bool		subtypbyval;	/* typbyval of element type */
-} RangeTypeInfo;
-
 /*
  * fmgr macros for range type objects
  */
@@ -129,18 +143,19 @@ extern Datum tsrange_subdiff(PG_FUNCTION_ARGS);
 extern Datum tstzrange_subdiff(PG_FUNCTION_ARGS);
 
 /* assorted support functions */
-extern Datum range_serialize(FunctionCallInfo fcinfo, RangeBound *lower,
+extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
+										  Oid rngtypid);
+extern RangeType *range_serialize(TypeCacheEntry *typcache, RangeBound *lower,
 							 RangeBound *upper, bool empty);
-extern void range_deserialize(FunctionCallInfo fcinfo, RangeType *range,
+extern void range_deserialize(TypeCacheEntry *typcache, RangeType *range,
 							  RangeBound *lower, RangeBound *upper,
 							  bool *empty);
-extern Datum make_range(FunctionCallInfo fcinfo, RangeBound *lower,
+extern char range_get_flags(RangeType *range);
+extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
 						RangeBound *upper, bool empty);
-extern int range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1,
+extern int range_cmp_bounds(TypeCacheEntry *typcache, RangeBound *b1,
 							RangeBound *b2);
-extern RangeType *make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid);
-extern void range_gettypinfo(FunctionCallInfo fcinfo, Oid rngtypid,
-							 RangeTypeInfo *rngtypinfo);
+extern RangeType *make_empty_range(TypeCacheEntry *typcache);
 
 /* GiST support (in rangetypes_gist.c) */
 extern Datum range_gist_consistent(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index eb93c1d3b54fc3744070807ce20ee8962e390c78..823b6ae576ad9c92a3b4ffbf1da188091e4d9d37 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -32,6 +32,7 @@ typedef struct TypeCacheEntry
 	int16		typlen;
 	bool		typbyval;
 	char		typalign;
+	char		typstorage;
 	char		typtype;
 	Oid			typrelid;
 
@@ -71,6 +72,18 @@ typedef struct TypeCacheEntry
 	 */
 	TupleDesc	tupDesc;
 
+	/*
+	 * Fields computed when TYPECACHE_RANGE_INFO is requested.  Zeroes if
+	 * not a range type or information hasn't yet been requested.  Note that
+	 * rng_cmp_proc_finfo could be different from the element type's default
+	 * btree comparison function.
+	 */
+	struct TypeCacheEntry *rngelemtype;	/* range's element type */
+	Oid			rng_collation;			/* collation for comparisons, if any */
+	FmgrInfo	rng_cmp_proc_finfo;		/* comparison function */
+	FmgrInfo	rng_canonical_finfo;	/* canonicalization function, if any */
+	FmgrInfo	rng_subdiff_finfo;		/* difference function, if any */
+
 	/* Private data, for internal use of typcache.c only */
 	int			flags;			/* flags about what we've computed */
 
@@ -93,6 +106,7 @@ typedef struct TypeCacheEntry
 #define TYPECACHE_TUPDESC			0x0100
 #define TYPECACHE_BTREE_OPFAMILY	0x0200
 #define TYPECACHE_HASH_OPFAMILY		0x0400
+#define TYPECACHE_RANGE_INFO		0x0800
 
 extern TypeCacheEntry *lookup_type_cache(Oid type_id, int flags);