Restructure function-internal caching in the range type code.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 15 Nov 2011 18:05:45 +0000 (13:05 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 15 Nov 2011 18:05:45 +0000 (13:05 -0500)
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
src/backend/utils/adt/rangetypes_gist.c
src/backend/utils/cache/typcache.c
src/include/utils/rangetypes.h
src/include/utils/typcache.h

index fd5d7810380b63e33cbb0169177a224cc357d9f0..a2eb0562e0a582ad75ce9eb2313a28bb4c55bc4a 100644 (file)
 #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;
index e4737b1902509f031565c6436c6aaa63c731b77f..3eb177a5ced1227841a94957d6050938608b1507 100644 (file)
@@ -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"
 
 
 #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);
 }
index 924ee5730252d320a96867a37bf5b5fdd9d9e99e..8e334740ed2b0f72ff88cc0ada5ea4e4ae2190ef 100644 (file)
@@ -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
index e218a91d1a87553fd270af40ee3dcf20c165506a..585d32134c066c75abc9a5d4a9700a104bdfab8b 100644 (file)
 #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);
index eb93c1d3b54fc3744070807ce20ee8962e390c78..823b6ae576ad9c92a3b4ffbf1da188091e4d9d37 100644 (file)
@@ -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);