From 37ee4b75db8f979da6d67ba153d068b012394b46 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Tue, 15 Nov 2011 13:05:45 -0500
Subject: [PATCH] Restructure function-internal caching in the range type code.

Move the responsibility for caching specialized information about range
types into the type cache, so that the catalog lookups only have to occur
once per session.  Rearrange APIs a bit so that fn_extra caching is
actually effective in the GiST support code.  (Use of OidFunctionCallN is
bad enough for performance in itself, but it also prevents the function
from exploiting fn_extra caching.)

The range I/O functions are still not very bright about caching repeated
lookups, but that seems like material for a separate patch.

Also, avoid unnecessary use of memcpy to fetch/store the range type OID and
flags, and don't use the full range_deserialize machinery when all we need
to see is the flags value.

Also fix API error in range_gist_penalty --- it was failing to set *penalty
for any case involving an empty range.
---
 src/backend/utils/adt/rangetypes.c      | 809 +++++++++++-------------
 src/backend/utils/adt/rangetypes_gist.c | 341 ++++++----
 src/backend/utils/cache/typcache.c      |  69 ++
 src/include/utils/rangetypes.h          |  69 +-
 src/include/utils/typcache.h            |  14 +
 5 files changed, 700 insertions(+), 602 deletions(-)

diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index fd5d7810380..a2eb0562e0a 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 e4737b19025..3eb177a5ced 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 924ee573025..8e334740ed2 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 e218a91d1a8..585d32134c0 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 eb93c1d3b54..823b6ae576a 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);
 
-- 
GitLab