#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    "[)"
                  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,
    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);
        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);
 }
 range_out(PG_FUNCTION_ARGS)
 {
    RangeType  *range = PG_GETARG_RANGE(0);
+   TypeCacheEntry *typcache;
    char       *output_str;
    regproc     subOutput;
    FmgrInfo    subOutputFn;
    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;
    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))
 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))
    {
 
    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);
 }
 {
    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;
    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))
 {
    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);
 }
    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);
 }
    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);
 }
    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);
 }
 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)
 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)
 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);
 }
 
 
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
    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);
 {
    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
 {
    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
 {
    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
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
    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
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
    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
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
-   RangeTypeInfo rngtypinfo;
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
    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);
     * 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);
    }
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
    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);
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
    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);
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
    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);
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
                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,
        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");
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
    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);
                (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
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
    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 */
 {
    RangeType  *r1 = PG_GETARG_RANGE(0);
    RangeType  *r2 = PG_GETARG_RANGE(1);
+   TypeCacheEntry *typcache;
    RangeBound  lower1,
                lower2;
    RangeBound  upper1,
                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);
    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
 hash_range(PG_FUNCTION_ARGS)
 {
    RangeType  *r = PG_GETARG_RANGE(0);
+   TypeCacheEntry *typcache;
    RangeBound  lower;
    RangeBound  upper;
    bool        empty;
    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;
    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))
 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);
        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);
        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);
        upper.inclusive = false;
    }
 
-   PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+   PG_RETURN_RANGE(range_serialize(typcache, &lower, &upper, false));
 }
 
 /*
  *----------------------------------------------------------
  */
 
+/*
+ * 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:
  *
  * 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")));
    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))
    {
        /*
                                   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))
    {
                          typstorage);
    }
 
-   memcpy(ptr, &flags, sizeof(char));
-   ptr += sizeof(char);
+   *((char *) ptr) = flags;
 
-   SET_VARSIZE(range, msize);
-   PG_RETURN_RANGE(range);
+   return range;
 }
 
 /*
  * 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))
 
    /* 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)
    {
    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)
 }
 
 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));
-}
 
 /*
  *----------------------------------------------------------
 }
 
 static bool
-range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
+range_contains_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
 {
    RangeBound  lower1;
    RangeBound  upper1;
    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;
 
 #include "access/gist.h"
 #include "access/skey.h"
 #include "utils/builtins.h"
-#include "utils/fmgroids.h"
-#include "utils/lsyscache.h"
 #include "utils/rangetypes.h"
 
 
 #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);
    /* 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)
    {
         */
        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:
    }
 
    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));
 }
 
    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));
    }
 
    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,
                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);
        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);
 
        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);
 
 {
    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;
    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;
    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++;
    }
 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);
 }
 
  *----------------------------------------------------------
  */
 
-/* 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;
    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;
    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)));
 }
 
 /*
    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)
    {
        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)
            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);
 }