diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c index fd5d7810380..a2eb0562e0a 100644 --- a/src/backend/utils/adt/rangetypes.c +++ b/src/backend/utils/adt/rangetypes.c @@ -33,23 +33,6 @@ #include "utils/typcache.h" -/* flags */ -#define RANGE_EMPTY 0x01 -#define RANGE_LB_INC 0x02 -#define RANGE_LB_NULL 0x04 /* NOT CURRENTLY USED */ -#define RANGE_LB_INF 0x08 -#define RANGE_UB_INC 0x10 -#define RANGE_UB_NULL 0x20 /* NOT CURRENTLY USED */ -#define RANGE_UB_INF 0x40 - -#define RANGE_HAS_LBOUND(flags) (!((flags) & (RANGE_EMPTY | \ - RANGE_LB_NULL | \ - RANGE_LB_INF))) - -#define RANGE_HAS_UBOUND(flags) (!((flags) & (RANGE_EMPTY | \ - RANGE_UB_NULL | \ - RANGE_UB_INF))) - #define RANGE_EMPTY_LITERAL "empty" #define RANGE_DEFAULT_FLAGS "[)" @@ -62,8 +45,8 @@ static char *range_parse_bound(char *string, char *ptr, char **bound_str, bool *infinite); static char *range_deparse(char flags, char *lbound_str, char *ubound_str); static char *range_bound_escape(char *in_str); -static bool range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, - RangeType *r2); +static bool range_contains_internal(TypeCacheEntry *typcache, + RangeType *r1, RangeType *r2); static Size datum_compute_size(Size sz, Datum datum, bool typbyval, char typalign, int16 typlen, char typstorage); static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval, @@ -82,40 +65,26 @@ range_in(PG_FUNCTION_ARGS) char *input_str = PG_GETARG_CSTRING(0); Oid rngtypoid = PG_GETARG_OID(1); Oid typmod = PG_GETARG_INT32(2); - Datum range; + RangeType *range; + TypeCacheEntry *typcache; char flags; char *lbound_str; char *ubound_str; regproc subInput; FmgrInfo subInputFn; Oid ioParam; - RangeTypeInfo rngtypinfo; RangeBound lower; RangeBound upper; - if (rngtypoid == ANYRANGEOID) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("cannot accept a value of type anyrange"))); - - range_gettypinfo(fcinfo, rngtypoid, &rngtypinfo); + typcache = range_get_typcache(fcinfo, rngtypoid); /* parse */ range_parse(input_str, &flags, &lbound_str, &ubound_str); /* input */ - getTypeInputInfo(rngtypinfo.subtype, &subInput, &ioParam); + getTypeInputInfo(typcache->rngelemtype->type_id, &subInput, &ioParam); fmgr_info(subInput, &subInputFn); - lower.rngtypid = rngtypoid; - lower.infinite = (flags & RANGE_LB_INF) != 0; - lower.inclusive = (flags & RANGE_LB_INC) != 0; - lower.lower = true; - upper.rngtypid = rngtypoid; - upper.infinite = (flags & RANGE_UB_INF) != 0; - upper.inclusive = (flags & RANGE_UB_INC) != 0; - upper.lower = false; - if (RANGE_HAS_LBOUND(flags)) lower.val = InputFunctionCall(&subInputFn, lbound_str, ioParam, typmod); @@ -123,8 +92,15 @@ range_in(PG_FUNCTION_ARGS) upper.val = InputFunctionCall(&subInputFn, ubound_str, ioParam, typmod); + lower.infinite = (flags & RANGE_LB_INF) != 0; + lower.inclusive = (flags & RANGE_LB_INC) != 0; + lower.lower = true; + upper.infinite = (flags & RANGE_UB_INF) != 0; + upper.inclusive = (flags & RANGE_UB_INC) != 0; + upper.lower = false; + /* serialize and canonicalize */ - range = make_range(fcinfo, &lower, &upper, flags & RANGE_EMPTY); + range = make_range(typcache, &lower, &upper, flags & RANGE_EMPTY); PG_RETURN_RANGE(range); } @@ -133,6 +109,7 @@ Datum range_out(PG_FUNCTION_ARGS) { RangeType *range = PG_GETARG_RANGE(0); + TypeCacheEntry *typcache; char *output_str; regproc subOutput; FmgrInfo subOutputFn; @@ -141,14 +118,13 @@ range_out(PG_FUNCTION_ARGS) char *lbound_str = NULL; char *ubound_str = NULL; bool empty; - RangeTypeInfo rngtypinfo; RangeBound lower; RangeBound upper; - /* deserialize */ - range_deserialize(fcinfo, range, &lower, &upper, &empty); + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(range)); - range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo); + /* deserialize */ + range_deserialize(typcache, range, &lower, &upper, &empty); if (empty) flags |= RANGE_EMPTY; @@ -159,7 +135,7 @@ range_out(PG_FUNCTION_ARGS) flags |= upper.infinite ? RANGE_UB_INF : 0; /* output */ - getTypeOutputInfo(rngtypinfo.subtype, &subOutput, &isVarlena); + getTypeOutputInfo(typcache->rngelemtype->type_id, &subOutput, &isVarlena); fmgr_info(subOutput, &subOutputFn); if (RANGE_HAS_LBOUND(flags)) @@ -185,21 +161,21 @@ Datum range_recv(PG_FUNCTION_ARGS) { StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); - Oid rngtypid = PG_GETARG_OID(1); + Oid rngtypoid = PG_GETARG_OID(1); int32 typmod = PG_GETARG_INT32(2); - Datum range; + RangeType *range; + TypeCacheEntry *typcache; Oid subrecv; Oid ioparam; char flags; RangeBound lower; RangeBound upper; - RangeTypeInfo rngtypinfo; + + typcache = range_get_typcache(fcinfo, rngtypoid); flags = (unsigned char) pq_getmsgbyte(buf); - range_gettypinfo(fcinfo, rngtypid, &rngtypinfo); - - 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; + char flags = range_get_flags(r1); - range_deserialize(fcinfo, r1, &lower, &upper, &empty); - - 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; + char flags = range_get_flags(r1); - range_deserialize(fcinfo, r1, &lower, &upper, &empty); - - 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; + char flags = range_get_flags(r1); - range_deserialize(fcinfo, r1, &lower, &upper, &empty); - - 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; + char flags = range_get_flags(r1); - range_deserialize(fcinfo, r1, &lower, &upper, &empty); - - 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; - lower2.infinite = false; - lower2.lower = true; + /* Construct a singleton range representing just "val" */ lower2.val = val; + lower2.infinite = false; + lower2.inclusive = true; + lower2.lower = true; - upper2.rngtypid = lower1.rngtypid; - upper2.inclusive = true; - upper2.infinite = false; - upper2.lower = false; upper2.val = val; + upper2.infinite = false; + upper2.inclusive = true; + upper2.lower = false; - 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; - PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2)); + /* 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(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; - lower2.infinite = false; - lower2.lower = true; + /* Construct a singleton range representing just "val" */ lower2.val = val; + lower2.infinite = false; + lower2.inclusive = true; + lower2.lower = true; - upper2.rngtypid = lower1.rngtypid; - upper2.inclusive = true; - upper2.infinite = false; - upper2.lower = false; upper2.val = val; + upper2.infinite = false; + upper2.inclusive = true; + upper2.lower = false; - 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; - PG_RETURN_BOOL(range_contains_internal(fcinfo, r2, r1)); + /* 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(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"); - if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo))) - PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid)); + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1)); - if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0) + 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(typcache)); + + 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; + RangeType *range; - range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo); + range = range_serialize(typcache, lower, upper, empty); - range = range_serialize(fcinfo, lower, upper, empty); + if (OidIsValid(typcache->rng_canonical_finfo.fn_oid)) + range = DatumGetRangeType(FunctionCall1(&typcache->rng_canonical_finfo, + RangeTypeGetDatum(range))); - if (rngtypinfo.canonicalFn.fn_addr != NULL) - range = FunctionCall1(&rngtypinfo.canonicalFn, range); - - PG_RETURN_RANGE(range); + return range; } int -range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, RangeBound *b2) +range_cmp_bounds(TypeCacheEntry *typcache, RangeBound *b1, RangeBound *b2) { - int result; - RangeTypeInfo rngtypinfo; + int32 result; if (b1->infinite && b2->infinite) { @@ -1565,10 +1613,8 @@ range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, RangeBound *b2) else if (!b1->infinite && b2->infinite) return (b2->lower) ? 1 : -1; - range_gettypinfo(fcinfo, b1->rngtypid, &rngtypinfo); - - result = DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn, - rngtypinfo.collation, + result = DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo, + typcache->rng_collation, b1->val, b2->val)); if (result == 0) @@ -1583,132 +1629,24 @@ range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, RangeBound *b2) } RangeType * -make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid) +make_empty_range(TypeCacheEntry *typcache) { RangeBound lower; RangeBound upper; - memset(&lower, 0, sizeof(RangeBound)); - memset(&upper, 0, sizeof(RangeBound)); - - lower.rngtypid = rngtypid; + lower.val = (Datum) 0; + lower.infinite = false; + lower.inclusive = false; lower.lower = true; - upper.rngtypid = rngtypid; + + upper.val = (Datum) 0; + upper.infinite = false; + upper.inclusive = false; upper.lower = false; - return DatumGetRangeType(make_range(fcinfo, &lower, &upper, true)); + return make_range(typcache, &lower, &upper, true); } -/* - * Fills in rngtypinfo, from a cached copy if available. - */ -void -range_gettypinfo(FunctionCallInfo fcinfo, Oid rngtypid, - RangeTypeInfo *rngtypinfo) -{ - RangeTypeInfo *cached = (RangeTypeInfo *) fcinfo->flinfo->fn_extra; - - if (cached == NULL) - { - fcinfo->flinfo->fn_extra = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, - sizeof(RangeTypeInfo)); - cached = (RangeTypeInfo *) fcinfo->flinfo->fn_extra; - cached->rngtypid = ~rngtypid; - } - - if (cached->rngtypid != rngtypid) - { - Form_pg_range pg_range; - Form_pg_opclass pg_opclass; - Form_pg_type pg_type; - HeapTuple tup; - Oid subtypeOid; - Oid collationOid; - Oid canonicalOid; - Oid subdiffOid; - Oid opclassOid; - Oid cmpFnOid; - Oid opfamilyOid; - Oid opcintype; - int16 subtyplen; - char subtypalign; - char subtypstorage; - bool subtypbyval; - - /* get information from pg_range */ - tup = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rngtypid)); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "cache lookup failed for range type %u", rngtypid); - - pg_range = (Form_pg_range) GETSTRUCT(tup); - - subtypeOid = pg_range->rngsubtype; - collationOid = pg_range->rngcollation; - canonicalOid = pg_range->rngcanonical; - opclassOid = pg_range->rngsubopc; - subdiffOid = pg_range->rngsubdiff; - - ReleaseSysCache(tup); - - /* get information from pg_opclass */ - tup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassOid)); - if (!HeapTupleIsValid(tup)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("operator class with OID %u does not exist", - opclassOid))); - - pg_opclass = (Form_pg_opclass) GETSTRUCT(tup); - - opfamilyOid = pg_opclass->opcfamily; - opcintype = pg_opclass->opcintype; - - ReleaseSysCache(tup); - - cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype, - BTORDER_PROC); - if (!RegProcedureIsValid(cmpFnOid)) - elog(ERROR, "missing support function %d(%u,%u) in opfamily %u", - BTORDER_PROC, opcintype, opcintype, opfamilyOid); - - /* get information from pg_type */ - tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(subtypeOid)); - if (!HeapTupleIsValid(tup)) - elog(ERROR, "cache lookup failed for type %u", subtypeOid); - - pg_type = (Form_pg_type) GETSTRUCT(tup); - - subtyplen = pg_type->typlen; - subtypalign = pg_type->typalign; - subtypstorage = pg_type->typstorage; - subtypbyval = pg_type->typbyval; - - ReleaseSysCache(tup); - - /* set up the cache */ - - if (OidIsValid(canonicalOid)) - fmgr_info(canonicalOid, &cached->canonicalFn); - else - cached->canonicalFn.fn_addr = NULL; - - if (OidIsValid(subdiffOid)) - fmgr_info(subdiffOid, &cached->subdiffFn); - else - cached->subdiffFn.fn_addr = NULL; - - fmgr_info(cmpFnOid, &cached->cmpFn); - cached->subtype = subtypeOid; - cached->collation = collationOid; - cached->subtyplen = subtyplen; - cached->subtypalign = subtypalign; - cached->subtypstorage = subtypstorage; - cached->subtypbyval = subtypbyval; - cached->rngtypid = rngtypid; - } - - memcpy(rngtypinfo, cached, sizeof(RangeTypeInfo)); -} /* *---------------------------------------------------------- @@ -2016,7 +1954,7 @@ range_bound_escape(char *value) } static bool -range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2) +range_contains_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2) { RangeBound lower1; RangeBound upper1; @@ -2025,20 +1963,17 @@ range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2) RangeBound upper2; bool empty2; - range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1); - range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2); - - if (lower1.rngtypid != lower2.rngtypid) - elog(ERROR, "range types do not match"); + range_deserialize(typcache, r1, &lower1, &upper1, &empty1); + range_deserialize(typcache, r2, &lower2, &upper2, &empty2); if (empty2) return true; else if (empty1) return false; - if (range_cmp_bounds(fcinfo, &lower1, &lower2) > 0) + if (range_cmp_bounds(typcache, &lower1, &lower2) > 0) return false; - if (range_cmp_bounds(fcinfo, &upper1, &upper2) < 0) + if (range_cmp_bounds(typcache, &upper1, &upper2) < 0) return false; return true; diff --git a/src/backend/utils/adt/rangetypes_gist.c b/src/backend/utils/adt/rangetypes_gist.c index e4737b19025..3eb177a5ced 100644 --- a/src/backend/utils/adt/rangetypes_gist.c +++ b/src/backend/utils/adt/rangetypes_gist.c @@ -17,8 +17,6 @@ #include "access/gist.h" #include "access/skey.h" #include "utils/builtins.h" -#include "utils/fmgroids.h" -#include "utils/lsyscache.h" #include "utils/rangetypes.h" @@ -36,22 +34,24 @@ #define RANGESTRAT_OVERRIGHT 11 #define RANGESTRAT_ADJACENT 12 +#define RangeIsEmpty(r) (range_get_flags(r) & RANGE_EMPTY) + /* * Auxiliary structure for picksplit method. */ typedef struct { - int index; - RangeType *data; - FunctionCallInfo fcinfo; + int index; /* original index in entryvec->vector[] */ + RangeType *data; /* range value to sort */ + TypeCacheEntry *typcache; /* range type's info */ } PickSplitSortItem; -static RangeType *range_super_union(FunctionCallInfo fcinfo, RangeType * r1, +static RangeType *range_super_union(TypeCacheEntry *typcache, RangeType * r1, RangeType * r2); -static bool range_gist_consistent_int(FunctionCallInfo fcinfo, +static bool range_gist_consistent_int(FmgrInfo *flinfo, StrategyNumber strategy, RangeType * key, RangeType * query); -static bool range_gist_consistent_leaf(FunctionCallInfo fcinfo, +static bool range_gist_consistent_leaf(FmgrInfo *flinfo, StrategyNumber strategy, RangeType * key, RangeType * query); static int sort_item_cmp(const void *a, const void *b); @@ -66,15 +66,13 @@ range_gist_consistent(PG_FUNCTION_ARGS) /* Oid subtype = PG_GETARG_OID(3); */ bool *recheck = (bool *) PG_GETARG_POINTER(4); RangeType *key = DatumGetRangeType(entry->key); + TypeCacheEntry *typcache; RangeType *query; RangeBound lower; RangeBound upper; - bool empty; - Oid rngtypid; + /* All operators served by this function are exact */ *recheck = false; - range_deserialize(fcinfo, key, &lower, &upper, &empty); - rngtypid = lower.rngtypid; switch (strategy) { @@ -85,18 +83,19 @@ range_gist_consistent(PG_FUNCTION_ARGS) */ case RANGESTRAT_CONTAINS_ELEM: case RANGESTRAT_ELEM_CONTAINED_BY: - lower.rngtypid = rngtypid; - lower.inclusive = true; + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(key)); + lower.val = dquery; - lower.lower = true; lower.infinite = false; - upper.rngtypid = rngtypid; - upper.inclusive = true; + lower.inclusive = true; + lower.lower = true; + upper.val = dquery; - upper.lower = false; upper.infinite = false; - query = DatumGetRangeType(make_range(fcinfo, - &lower, &upper, false)); + upper.inclusive = true; + upper.lower = false; + + query = make_range(typcache, &lower, &upper, false); break; default: @@ -105,10 +104,10 @@ range_gist_consistent(PG_FUNCTION_ARGS) } if (GIST_LEAF(entry)) - PG_RETURN_BOOL(range_gist_consistent_leaf(fcinfo, strategy, + PG_RETURN_BOOL(range_gist_consistent_leaf(fcinfo->flinfo, strategy, key, query)); else - PG_RETURN_BOOL(range_gist_consistent_int(fcinfo, strategy, + PG_RETURN_BOOL(range_gist_consistent_int(fcinfo->flinfo, strategy, key, query)); } @@ -118,13 +117,16 @@ range_gist_union(PG_FUNCTION_ARGS) GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GISTENTRY *ent = entryvec->vector; RangeType *result_range; + TypeCacheEntry *typcache; int i; result_range = DatumGetRangeType(ent[0].key); + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(result_range)); + for (i = 1; i < entryvec->n; i++) { - result_range = range_super_union(fcinfo, result_range, + result_range = range_super_union(typcache, result_range, DatumGetRangeType(ent[i].key)); } @@ -155,6 +157,7 @@ range_gist_penalty(PG_FUNCTION_ARGS) float *penalty = (float *) PG_GETARG_POINTER(2); RangeType *orig = DatumGetRangeType(origentry->key); RangeType *new = DatumGetRangeType(newentry->key); + TypeCacheEntry *typcache; RangeType *s_union; FmgrInfo *subtype_diff; RangeBound lower1, @@ -163,34 +166,54 @@ range_gist_penalty(PG_FUNCTION_ARGS) upper2; bool empty1, empty2; - float lower_diff, + float8 lower_diff, upper_diff; - RangeTypeInfo rngtypinfo; - s_union = range_super_union(fcinfo, orig, new); + if (RangeTypeGetOid(orig) != RangeTypeGetOid(new)) + elog(ERROR, "range types do not match"); - range_deserialize(fcinfo, orig, &lower1, &upper1, &empty1); - range_deserialize(fcinfo, s_union, &lower2, &upper2, &empty2); + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(orig)); - range_gettypinfo(fcinfo, lower1.rngtypid, &rngtypinfo); - subtype_diff = &rngtypinfo.subdiffFn; + subtype_diff = &typcache->rng_subdiff_finfo; + s_union = range_super_union(typcache, orig, new); + + range_deserialize(typcache, orig, &lower1, &upper1, &empty1); + range_deserialize(typcache, s_union, &lower2, &upper2, &empty2); + + /* if orig isn't empty, s_union can't be either */ Assert(empty1 || !empty2); if (empty1 && empty2) - return 0; + { + *penalty = 0; + PG_RETURN_POINTER(penalty); + } else if (empty1 && !empty2) { if (lower2.infinite || upper2.infinite) + { /* from empty to infinite */ - return get_float8_infinity(); - else if (subtype_diff->fn_addr != NULL) + *penalty = get_float4_infinity(); + PG_RETURN_POINTER(penalty); + } + else if (OidIsValid(subtype_diff->fn_oid)) + { /* from empty to upper2-lower2 */ - return DatumGetFloat8(FunctionCall2(subtype_diff, - upper2.val, lower2.val)); + *penalty = DatumGetFloat8(FunctionCall2Coll(subtype_diff, + typcache->rng_collation, + upper2.val, + lower2.val)); + if (*penalty < 0) + *penalty = 0; /* subtype_diff is broken */ + PG_RETURN_POINTER(penalty); + } else + { /* wild guess */ - return 1.0; + *penalty = 1.0; + PG_RETURN_POINTER(penalty); + } } Assert(lower2.infinite || !lower1.infinite); @@ -199,15 +222,20 @@ range_gist_penalty(PG_FUNCTION_ARGS) lower_diff = get_float8_infinity(); else if (lower2.infinite && lower1.infinite) lower_diff = 0; - else if (subtype_diff->fn_addr != NULL) + else if (OidIsValid(subtype_diff->fn_oid)) { - lower_diff = DatumGetFloat8(FunctionCall2(subtype_diff, - lower1.val, lower2.val)); + lower_diff = DatumGetFloat8(FunctionCall2Coll(subtype_diff, + typcache->rng_collation, + lower1.val, + lower2.val)); if (lower_diff < 0) lower_diff = 0; /* subtype_diff is broken */ } - else /* only know whether there is a difference or not */ - lower_diff = (float) range_cmp_bounds(fcinfo, &lower1, &lower2); + else + { + /* only know whether there is a difference or not */ + lower_diff = (float) range_cmp_bounds(typcache, &lower1, &lower2); + } Assert(upper2.infinite || !upper1.infinite); @@ -215,15 +243,20 @@ range_gist_penalty(PG_FUNCTION_ARGS) upper_diff = get_float8_infinity(); else if (upper2.infinite && upper1.infinite) upper_diff = 0; - else if (subtype_diff->fn_addr != NULL) + else if (OidIsValid(subtype_diff->fn_oid)) { - upper_diff = DatumGetFloat8(FunctionCall2(subtype_diff, - upper2.val, upper1.val)); + upper_diff = DatumGetFloat8(FunctionCall2Coll(subtype_diff, + typcache->rng_collation, + upper2.val, + upper1.val)); if (upper_diff < 0) upper_diff = 0; /* subtype_diff is broken */ } - else /* only know whether there is a difference or not */ - upper_diff = (float) range_cmp_bounds(fcinfo, &upper2, &upper1); + else + { + /* only know whether there is a difference or not */ + upper_diff = (float) range_cmp_bounds(typcache, &upper2, &upper1); + } Assert(lower_diff >= 0 && upper_diff >= 0); @@ -243,6 +276,7 @@ range_gist_picksplit(PG_FUNCTION_ARGS) { GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0); GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1); + TypeCacheEntry *typcache; OffsetNumber i; RangeType *pred_left; RangeType *pred_right; @@ -253,23 +287,28 @@ range_gist_picksplit(PG_FUNCTION_ARGS) OffsetNumber *right; OffsetNumber maxoff; + /* use first item to look up range type's info */ + pred_left = DatumGetRangeType(entryvec->vector[FirstOffsetNumber].key); + typcache = range_get_typcache(fcinfo, RangeTypeGetOid(pred_left)); + + /* allocate result and work arrays */ maxoff = entryvec->n - 1; nbytes = (maxoff + 1) * sizeof(OffsetNumber); - sortItems = (PickSplitSortItem *) palloc( - maxoff * sizeof(PickSplitSortItem)); v->spl_left = (OffsetNumber *) palloc(nbytes); v->spl_right = (OffsetNumber *) palloc(nbytes); + sortItems = (PickSplitSortItem *) palloc(maxoff * sizeof(PickSplitSortItem)); /* - * Preparing auxiliary array and sorting. + * Prepare auxiliary array and sort the values. */ for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i)) { sortItems[i - 1].index = i; sortItems[i - 1].data = DatumGetRangeType(entryvec->vector[i].key); - sortItems[i - 1].fcinfo = fcinfo; + sortItems[i - 1].typcache = typcache; } qsort(sortItems, maxoff, sizeof(PickSplitSortItem), sort_item_cmp); + split_idx = maxoff / 2; left = v->spl_left; @@ -278,29 +317,27 @@ range_gist_picksplit(PG_FUNCTION_ARGS) v->spl_nright = 0; /* - * First half of segs goes to the left datum. + * First half of items goes to the left datum. */ - pred_left = DatumGetRangeType(sortItems[0].data); + pred_left = sortItems[0].data; *left++ = sortItems[0].index; v->spl_nleft++; for (i = 1; i < split_idx; i++) { - pred_left = range_super_union(fcinfo, pred_left, - DatumGetRangeType(sortItems[i].data)); + pred_left = range_super_union(typcache, pred_left, sortItems[i].data); *left++ = sortItems[i].index; v->spl_nleft++; } /* - * Second half of segs goes to the right datum. + * Second half of items goes to the right datum. */ - pred_right = DatumGetRangeType(sortItems[split_idx].data); + pred_right = sortItems[split_idx].data; *right++ = sortItems[split_idx].index; v->spl_nright++; for (i = split_idx + 1; i < maxoff; i++) { - pred_right = range_super_union(fcinfo, pred_right, - DatumGetRangeType(sortItems[i].data)); + pred_right = range_super_union(typcache, pred_right, sortItems[i].data); *right++ = sortItems[i].index; v->spl_nright++; } @@ -316,11 +353,16 @@ range_gist_picksplit(PG_FUNCTION_ARGS) Datum range_gist_same(PG_FUNCTION_ARGS) { - Datum r1 = PG_GETARG_DATUM(0); - Datum r2 = PG_GETARG_DATUM(1); + /* Datum r1 = PG_GETARG_DATUM(0); */ + /* Datum r2 = PG_GETARG_DATUM(1); */ bool *result = (bool *) PG_GETARG_POINTER(2); - *result = DatumGetBool(OidFunctionCall2(F_RANGE_EQ, r1, r2)); + /* + * We can safely call range_eq using our fcinfo directly; it won't notice + * the third argument. This allows it to use fn_extra for caching. + */ + *result = DatumGetBool(range_eq(fcinfo)); + PG_RETURN_POINTER(result); } @@ -330,9 +372,11 @@ range_gist_same(PG_FUNCTION_ARGS) *---------------------------------------------------------- */ -/* return the smallest range that contains r1 and r2 */ +/* + * Return the smallest range that contains r1 and r2 + */ static RangeType * -range_super_union(FunctionCallInfo fcinfo, RangeType * r1, RangeType * r2) +range_super_union(TypeCacheEntry *typcache, RangeType * r1, RangeType * r2) { RangeBound lower1, lower2; @@ -343,20 +387,20 @@ range_super_union(FunctionCallInfo fcinfo, RangeType * r1, RangeType * r2) RangeBound *result_lower; RangeBound *result_upper; - range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1); - range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2); + range_deserialize(typcache, r1, &lower1, &upper1, &empty1); + range_deserialize(typcache, r2, &lower2, &upper2, &empty2); if (empty1) return r2; if (empty2) return r1; - if (range_cmp_bounds(fcinfo, &lower1, &lower2) <= 0) + if (range_cmp_bounds(typcache, &lower1, &lower2) <= 0) result_lower = &lower1; else result_lower = &lower2; - if (range_cmp_bounds(fcinfo, &upper1, &upper2) >= 0) + if (range_cmp_bounds(typcache, &upper1, &upper2) >= 0) result_upper = &upper1; else result_upper = &upper2; @@ -367,161 +411,182 @@ range_super_union(FunctionCallInfo fcinfo, RangeType * r1, RangeType * r2) if (result_lower == &lower2 && result_upper == &upper2) return r2; - return DatumGetRangeType(make_range(fcinfo, result_lower, result_upper, - false)); + return make_range(typcache, result_lower, result_upper, false); } +/* + * trick function call: call the given function with given FmgrInfo + * + * To allow the various functions called here to cache lookups of range + * datatype information, we use a trick: we pass them the FmgrInfo struct + * for the GiST consistent function. This relies on the knowledge that + * none of them consult FmgrInfo for anything but fn_extra, and that they + * all use fn_extra the same way, i.e. as a pointer to the typcache entry + * for the range data type. Since the FmgrInfo is long-lived (it's actually + * part of the relcache entry for the index, typically) this essentially + * eliminates lookup overhead during operations on a GiST range index. + */ +static Datum +TrickFunctionCall2(PGFunction proc, FmgrInfo *flinfo, Datum arg1, Datum arg2) +{ + FunctionCallInfoData fcinfo; + Datum result; + + InitFunctionCallInfoData(fcinfo, flinfo, 2, InvalidOid, NULL, NULL); + + fcinfo.arg[0] = arg1; + fcinfo.arg[1] = arg2; + fcinfo.argnull[0] = false; + fcinfo.argnull[1] = false; + + result = (*proc) (&fcinfo); + + if (fcinfo.isnull) + elog(ERROR, "function %p returned NULL", proc); + + return result; +} + +/* + * GiST consistent test on an index internal page + */ static bool -range_gist_consistent_int(FunctionCallInfo fcinfo, StrategyNumber strategy, +range_gist_consistent_int(FmgrInfo *flinfo, StrategyNumber strategy, RangeType * key, RangeType * query) { - Oid proc; - RangeBound lower1, - lower2; - RangeBound upper1, - upper2; - bool empty1, - empty2; - bool retval; + PGFunction proc; bool negate = false; - - range_deserialize(fcinfo, key, &lower1, &upper1, &empty1); - range_deserialize(fcinfo, query, &lower2, &upper2, &empty2); + bool retval; switch (strategy) { case RANGESTRAT_EQ: - proc = F_RANGE_CONTAINS; + proc = range_contains; break; case RANGESTRAT_NE: return true; break; case RANGESTRAT_OVERLAPS: - proc = F_RANGE_OVERLAPS; + proc = range_overlaps; break; case RANGESTRAT_CONTAINS_ELEM: case RANGESTRAT_CONTAINS: - proc = F_RANGE_CONTAINS; + proc = range_contains; break; case RANGESTRAT_ELEM_CONTAINED_BY: case RANGESTRAT_CONTAINED_BY: return true; break; case RANGESTRAT_BEFORE: - if (empty1) + if (RangeIsEmpty(key)) return false; - proc = F_RANGE_OVERRIGHT; + proc = range_overright; negate = true; break; case RANGESTRAT_AFTER: - if (empty1) + if (RangeIsEmpty(key)) return false; - proc = F_RANGE_OVERLEFT; + proc = range_overleft; negate = true; break; case RANGESTRAT_OVERLEFT: - if (empty1) + if (RangeIsEmpty(key)) return false; - proc = F_RANGE_AFTER; + proc = range_after; negate = true; break; case RANGESTRAT_OVERRIGHT: - if (empty1) + if (RangeIsEmpty(key)) return false; - proc = F_RANGE_BEFORE; + proc = range_before; negate = true; break; case RANGESTRAT_ADJACENT: - if (empty1 || empty2) + if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; - if (DatumGetBool(OidFunctionCall2(F_RANGE_ADJACENT, - RangeTypeGetDatum(key), - RangeTypeGetDatum(query)))) + if (DatumGetBool(TrickFunctionCall2(range_adjacent, flinfo, + RangeTypeGetDatum(key), + RangeTypeGetDatum(query)))) return true; - proc = F_RANGE_OVERLAPS; + proc = range_overlaps; break; default: elog(ERROR, "unrecognized range strategy: %d", strategy); - proc = InvalidOid; + proc = NULL; /* keep compiler quiet */ break; } - retval = DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key), - RangeTypeGetDatum(query))); - + retval = DatumGetBool(TrickFunctionCall2(proc, flinfo, + RangeTypeGetDatum(key), + RangeTypeGetDatum(query))); if (negate) retval = !retval; - PG_RETURN_BOOL(retval); + return retval; } +/* + * GiST consistent test on an index leaf page + */ static bool -range_gist_consistent_leaf(FunctionCallInfo fcinfo, StrategyNumber strategy, +range_gist_consistent_leaf(FmgrInfo *flinfo, StrategyNumber strategy, RangeType * key, RangeType * query) { - Oid proc; - RangeBound lower1, - lower2; - RangeBound upper1, - upper2; - bool empty1, - empty2; - - range_deserialize(fcinfo, key, &lower1, &upper1, &empty1); - range_deserialize(fcinfo, query, &lower2, &upper2, &empty2); + PGFunction proc; switch (strategy) { case RANGESTRAT_EQ: - proc = F_RANGE_EQ; + proc = range_eq; break; case RANGESTRAT_NE: - proc = F_RANGE_NE; + proc = range_ne; break; case RANGESTRAT_OVERLAPS: - proc = F_RANGE_OVERLAPS; + proc = range_overlaps; break; case RANGESTRAT_CONTAINS_ELEM: case RANGESTRAT_CONTAINS: - proc = F_RANGE_CONTAINS; + proc = range_contains; break; case RANGESTRAT_ELEM_CONTAINED_BY: case RANGESTRAT_CONTAINED_BY: - proc = F_RANGE_CONTAINED_BY; + proc = range_contained_by; break; case RANGESTRAT_BEFORE: - if (empty1 || empty2) + if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; - proc = F_RANGE_BEFORE; + proc = range_before; break; case RANGESTRAT_AFTER: - if (empty1 || empty2) + if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; - proc = F_RANGE_AFTER; + proc = range_after; break; case RANGESTRAT_OVERLEFT: - if (empty1 || empty2) + if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; - proc = F_RANGE_OVERLEFT; + proc = range_overleft; break; case RANGESTRAT_OVERRIGHT: - if (empty1 || empty2) + if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; - proc = F_RANGE_OVERRIGHT; + proc = range_overright; break; case RANGESTRAT_ADJACENT: - if (empty1 || empty2) + if (RangeIsEmpty(key) || RangeIsEmpty(query)) return false; - proc = F_RANGE_ADJACENT; + proc = range_adjacent; break; default: elog(ERROR, "unrecognized range strategy: %d", strategy); - proc = InvalidOid; + proc = NULL; /* keep compiler quiet */ break; } - return DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key), - RangeTypeGetDatum(query))); + return DatumGetBool(TrickFunctionCall2(proc, flinfo, + RangeTypeGetDatum(key), + RangeTypeGetDatum(query))); } /* @@ -545,17 +610,17 @@ sort_item_cmp(const void *a, const void *b) PickSplitSortItem *i2 = (PickSplitSortItem *) b; RangeType *r1 = i1->data; RangeType *r2 = i2->data; + TypeCacheEntry *typcache = i1->typcache; RangeBound lower1, lower2; RangeBound upper1, upper2; bool empty1, empty2; - FunctionCallInfo fcinfo = i1->fcinfo; int cmp; - range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1); - range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2); + range_deserialize(typcache, r1, &lower1, &upper1, &empty1); + range_deserialize(typcache, r2, &lower2, &upper2, &empty2); if (empty1 || empty2) { @@ -580,13 +645,13 @@ sort_item_cmp(const void *a, const void *b) lower2.infinite || upper2.infinite) { if (lower1.infinite && lower2.infinite) - return range_cmp_bounds(fcinfo, &upper1, &upper2); + return range_cmp_bounds(typcache, &upper1, &upper2); else if (lower1.infinite) return -1; else if (lower2.infinite) return 1; else if (upper1.infinite && upper2.infinite) - return -1 * range_cmp_bounds(fcinfo, &lower1, &lower2); + return -1 * range_cmp_bounds(typcache, &lower1, &lower2); else if (upper1.infinite) return 1; else if (upper2.infinite) @@ -595,8 +660,8 @@ sort_item_cmp(const void *a, const void *b) Assert(false); } - if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0) + if ((cmp = range_cmp_bounds(typcache, &lower1, &lower2)) != 0) return cmp; - return range_cmp_bounds(fcinfo, &upper1, &upper2); + return range_cmp_bounds(typcache, &upper1, &upper2); } diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index 924ee573025..8e334740ed2 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -51,6 +51,7 @@ #include "catalog/indexing.h" #include "catalog/pg_enum.h" #include "catalog/pg_operator.h" +#include "catalog/pg_range.h" #include "catalog/pg_type.h" #include "commands/defrem.h" #include "utils/builtins.h" @@ -120,6 +121,7 @@ static int32 RecordCacheArrayLen = 0; /* allocated length of array */ static int32 NextRecordTypmod = 0; /* number of entries used */ static void load_typcache_tupdesc(TypeCacheEntry *typentry); +static void load_rangetype_info(TypeCacheEntry *typentry); static bool array_element_has_equality(TypeCacheEntry *typentry); static bool array_element_has_compare(TypeCacheEntry *typentry); static bool array_element_has_hashing(TypeCacheEntry *typentry); @@ -205,6 +207,7 @@ lookup_type_cache(Oid type_id, int flags) typentry->typlen = typtup->typlen; typentry->typbyval = typtup->typbyval; typentry->typalign = typtup->typalign; + typentry->typstorage = typtup->typstorage; typentry->typtype = typtup->typtype; typentry->typrelid = typtup->typrelid; @@ -448,6 +451,16 @@ lookup_type_cache(Oid type_id, int flags) load_typcache_tupdesc(typentry); } + /* + * If requested, get information about a range type + */ + if ((flags & TYPECACHE_RANGE_INFO) && + typentry->rngelemtype == NULL && + typentry->typtype == TYPTYPE_RANGE) + { + load_rangetype_info(typentry); + } + return typentry; } @@ -479,6 +492,62 @@ load_typcache_tupdesc(TypeCacheEntry *typentry) relation_close(rel, AccessShareLock); } +/* + * load_rangetype_info --- helper routine to set up range type information + */ +static void +load_rangetype_info(TypeCacheEntry *typentry) +{ + Form_pg_range pg_range; + HeapTuple tup; + Oid subtypeOid; + Oid opclassOid; + Oid canonicalOid; + Oid subdiffOid; + Oid opfamilyOid; + Oid opcintype; + Oid cmpFnOid; + + /* get information from pg_range */ + tup = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(typentry->type_id)); + /* should not fail, since we already checked typtype ... */ + if (!HeapTupleIsValid(tup)) + elog(ERROR, "cache lookup failed for range type %u", + typentry->type_id); + pg_range = (Form_pg_range) GETSTRUCT(tup); + + subtypeOid = pg_range->rngsubtype; + typentry->rng_collation = pg_range->rngcollation; + opclassOid = pg_range->rngsubopc; + canonicalOid = pg_range->rngcanonical; + subdiffOid = pg_range->rngsubdiff; + + ReleaseSysCache(tup); + + /* get opclass properties and look up the comparison function */ + opfamilyOid = get_opclass_family(opclassOid); + opcintype = get_opclass_input_type(opclassOid); + + cmpFnOid = get_opfamily_proc(opfamilyOid, opcintype, opcintype, + BTORDER_PROC); + if (!RegProcedureIsValid(cmpFnOid)) + elog(ERROR, "missing support function %d(%u,%u) in opfamily %u", + BTORDER_PROC, opcintype, opcintype, opfamilyOid); + + /* set up cached fmgrinfo structs */ + fmgr_info_cxt(cmpFnOid, &typentry->rng_cmp_proc_finfo, + CacheMemoryContext); + if (OidIsValid(canonicalOid)) + fmgr_info_cxt(canonicalOid, &typentry->rng_canonical_finfo, + CacheMemoryContext); + if (OidIsValid(subdiffOid)) + fmgr_info_cxt(subdiffOid, &typentry->rng_subdiff_finfo, + CacheMemoryContext); + + /* Lastly, set up link to the element type --- this marks data valid */ + typentry->rngelemtype = lookup_type_cache(subtypeOid, 0); +} + /* * array_element_has_equality and friends are helper routines to check diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h index e218a91d1a8..585d32134c0 100644 --- a/src/include/utils/rangetypes.h +++ b/src/include/utils/rangetypes.h @@ -14,37 +14,51 @@ #ifndef RANGETYPES_H #define RANGETYPES_H -#include "fmgr.h" +#include "utils/typcache.h" -/* All ranges are represented as varlena objects */ -typedef struct varlena RangeType; +/* + * Ranges are varlena objects, so must meet the varlena convention that + * the first int32 of the object contains the total object size in bytes. + * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though! + */ +typedef struct +{ + int32 vl_len_; /* varlena header (do not touch directly!) */ + Oid rangetypid; /* range type's own OID */ + /* Following the OID are zero to two bound values, then a flags byte */ +} RangeType; + +/* Use this macro in preference to fetching rangetypid field directly */ +#define RangeTypeGetOid(r) ((r)->rangetypid) + +/* A range's flags byte contains these bits: */ +#define RANGE_EMPTY 0x01 /* range is empty */ +#define RANGE_LB_INC 0x02 /* lower bound is inclusive (vs exclusive) */ +#define RANGE_LB_NULL 0x04 /* lower bound is null (NOT CURRENTLY USED) */ +#define RANGE_LB_INF 0x08 /* lower bound is +/- infinity */ +#define RANGE_UB_INC 0x10 /* upper bound is inclusive (vs exclusive) */ +#define RANGE_UB_NULL 0x20 /* upper bound is null (NOT CURRENTLY USED) */ +#define RANGE_UB_INF 0x40 /* upper bound is +/- infinity */ + +#define RANGE_HAS_LBOUND(flags) (!((flags) & (RANGE_EMPTY | \ + RANGE_LB_NULL | \ + RANGE_LB_INF))) + +#define RANGE_HAS_UBOUND(flags) (!((flags) & (RANGE_EMPTY | \ + RANGE_UB_NULL | \ + RANGE_UB_INF))) + /* Internal representation of either bound of a range (not what's on disk) */ typedef struct { Datum val; /* the bound value, if any */ - Oid rngtypid; /* OID of the range type itself */ bool infinite; /* bound is +/- infinity */ - bool lower; /* this is the lower (vs upper) bound */ bool inclusive; /* bound is inclusive (vs exclusive) */ + bool lower; /* this is the lower (vs upper) bound */ } RangeBound; -/* Standard runtime-cached data for a range type */ -typedef struct -{ - FmgrInfo canonicalFn; /* canonicalization function, if any */ - FmgrInfo cmpFn; /* element type's btree comparison function */ - FmgrInfo subdiffFn; /* element type difference function, if any */ - Oid rngtypid; /* OID of the range type itself */ - Oid subtype; /* OID of the element type */ - Oid collation; /* collation for comparisons, if any */ - int16 subtyplen; /* typlen of element type */ - char subtypalign; /* typalign of element type */ - char subtypstorage; /* typstorage of element type */ - bool subtypbyval; /* typbyval of element type */ -} RangeTypeInfo; - /* * fmgr macros for range type objects */ @@ -129,18 +143,19 @@ extern Datum tsrange_subdiff(PG_FUNCTION_ARGS); extern Datum tstzrange_subdiff(PG_FUNCTION_ARGS); /* assorted support functions */ -extern Datum range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, +extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo, + Oid rngtypid); +extern RangeType *range_serialize(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper, bool empty); -extern void range_deserialize(FunctionCallInfo fcinfo, RangeType *range, +extern void range_deserialize(TypeCacheEntry *typcache, RangeType *range, RangeBound *lower, RangeBound *upper, bool *empty); -extern Datum make_range(FunctionCallInfo fcinfo, RangeBound *lower, +extern char range_get_flags(RangeType *range); +extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower, RangeBound *upper, bool empty); -extern int range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, +extern int range_cmp_bounds(TypeCacheEntry *typcache, RangeBound *b1, RangeBound *b2); -extern RangeType *make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid); -extern void range_gettypinfo(FunctionCallInfo fcinfo, Oid rngtypid, - RangeTypeInfo *rngtypinfo); +extern RangeType *make_empty_range(TypeCacheEntry *typcache); /* GiST support (in rangetypes_gist.c) */ extern Datum range_gist_consistent(PG_FUNCTION_ARGS); diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h index eb93c1d3b54..823b6ae576a 100644 --- a/src/include/utils/typcache.h +++ b/src/include/utils/typcache.h @@ -32,6 +32,7 @@ typedef struct TypeCacheEntry int16 typlen; bool typbyval; char typalign; + char typstorage; char typtype; Oid typrelid; @@ -71,6 +72,18 @@ typedef struct TypeCacheEntry */ TupleDesc tupDesc; + /* + * Fields computed when TYPECACHE_RANGE_INFO is requested. Zeroes if + * not a range type or information hasn't yet been requested. Note that + * rng_cmp_proc_finfo could be different from the element type's default + * btree comparison function. + */ + struct TypeCacheEntry *rngelemtype; /* range's element type */ + Oid rng_collation; /* collation for comparisons, if any */ + FmgrInfo rng_cmp_proc_finfo; /* comparison function */ + FmgrInfo rng_canonical_finfo; /* canonicalization function, if any */ + FmgrInfo rng_subdiff_finfo; /* difference function, if any */ + /* Private data, for internal use of typcache.c only */ int flags; /* flags about what we've computed */ @@ -93,6 +106,7 @@ typedef struct TypeCacheEntry #define TYPECACHE_TUPDESC 0x0100 #define TYPECACHE_BTREE_OPFAMILY 0x0200 #define TYPECACHE_HASH_OPFAMILY 0x0400 +#define TYPECACHE_RANGE_INFO 0x0800 extern TypeCacheEntry *lookup_type_cache(Oid type_id, int flags);