static char *range_deparse(char flags, const char *lbound_str,
const char *ubound_str);
static char *range_bound_escape(const char *value);
-static bool range_contains_internal(TypeCacheEntry *typcache,
- RangeType *r1, RangeType *r2);
-static bool range_contains_elem_internal(TypeCacheEntry *typcache,
- RangeType *r, Datum val);
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,
/* range, range -> bool functions */
-/* equality */
-Datum
-range_eq(PG_FUNCTION_ARGS)
+/* equality (internal version) */
+bool
+range_eq_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
{
- RangeType *r1 = PG_GETARG_RANGE(0);
- RangeType *r2 = PG_GETARG_RANGE(1);
- TypeCacheEntry *typcache;
RangeBound lower1,
lower2;
RangeBound upper1,
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);
+ return true;
if (empty1 != empty2)
- PG_RETURN_BOOL(false);
+ return false;
if (range_cmp_bounds(typcache, &lower1, &lower2) != 0)
- PG_RETURN_BOOL(false);
+ return false;
if (range_cmp_bounds(typcache, &upper1, &upper2) != 0)
- PG_RETURN_BOOL(false);
+ return false;
+
+ return true;
+}
- PG_RETURN_BOOL(true);
+/* equality */
+Datum
+range_eq(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+ TypeCacheEntry *typcache;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_BOOL(range_eq_internal(typcache, r1, r2));
+}
+
+/* inequality (internal version) */
+bool
+range_ne_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
+ return (!range_eq_internal(typcache, r1, r2));
}
/* inequality */
Datum
range_ne(PG_FUNCTION_ARGS)
{
- bool eq = DatumGetBool(range_eq(fcinfo));
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+ TypeCacheEntry *typcache;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
- PG_RETURN_BOOL(!eq);
+ PG_RETURN_BOOL(range_ne_internal(typcache, r1, r2));
}
/* contains? */
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(typcache, r1, r2));
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(typcache, r2, r1));
+ PG_RETURN_BOOL(range_contained_by_internal(typcache, r1, r2));
}
-/* strictly left of? */
-Datum
-range_before(PG_FUNCTION_ARGS)
+/* strictly left of? (internal version) */
+bool
+range_before_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
{
- RangeType *r1 = PG_GETARG_RANGE(0);
- RangeType *r2 = PG_GETARG_RANGE(1);
- TypeCacheEntry *typcache;
RangeBound lower1,
lower2;
RangeBound upper1,
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);
+ return false;
- PG_RETURN_BOOL(range_cmp_bounds(typcache, &upper1, &lower2) < 0);
+ return (range_cmp_bounds(typcache, &upper1, &lower2) < 0);
}
-/* strictly right of? */
+/* strictly left of? */
Datum
-range_after(PG_FUNCTION_ARGS)
+range_before(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE(0);
RangeType *r2 = PG_GETARG_RANGE(1);
TypeCacheEntry *typcache;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_BOOL(range_before_internal(typcache, r1, r2));
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
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);
+ return false;
- PG_RETURN_BOOL(range_cmp_bounds(typcache, &lower1, &upper2) > 0);
+ return (range_cmp_bounds(typcache, &lower1, &upper2) > 0);
}
-/* adjacent to (but not overlapping)? */
+/* strictly right of? */
Datum
-range_adjacent(PG_FUNCTION_ARGS)
+range_after(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE(0);
RangeType *r2 = PG_GETARG_RANGE(1);
TypeCacheEntry *typcache;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_BOOL(range_after_internal(typcache, r1, r2));
+}
+
+/* adjacent to (but not overlapping)? (internal version) */
+bool
+range_adjacent_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
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);
+ return false;
/*
* Given two ranges A..B and C..D, where B < C, the ranges are adjacent if
{
/* in a continuous subtype, there are assumed to be points between */
if (!OidIsValid(typcache->rng_canonical_finfo.fn_oid))
- PG_RETURN_BOOL(false);
+ return (false);
/* flip the inclusion flags */
upper1.inclusive = !upper1.inclusive;
lower2.inclusive = !lower2.inclusive;
upper1.lower = true;
lower2.lower = false;
r3 = make_range(typcache, &upper1, &lower2, false);
- PG_RETURN_BOOL(RangeIsEmpty(r3));
+ return RangeIsEmpty(r3);
}
if (cmp == 0)
{
- PG_RETURN_BOOL(upper1.inclusive != lower2.inclusive);
+ return (upper1.inclusive != lower2.inclusive);
}
cmp = range_cmp_bound_values(typcache, &upper2, &lower1);
{
/* in a continuous subtype, there are assumed to be points between */
if (!OidIsValid(typcache->rng_canonical_finfo.fn_oid))
- PG_RETURN_BOOL(false);
+ return (false);
/* flip the inclusion flags */
upper2.inclusive = !upper2.inclusive;
lower1.inclusive = !lower1.inclusive;
upper2.lower = true;
lower1.lower = false;
r3 = make_range(typcache, &upper2, &lower1, false);
- PG_RETURN_BOOL(RangeIsEmpty(r3));
+ return RangeIsEmpty(r3);
}
if (cmp == 0)
{
- PG_RETURN_BOOL(upper2.inclusive != lower1.inclusive);
+ return (upper2.inclusive != lower1.inclusive);
}
- PG_RETURN_BOOL(false);
+ return false;
}
-/* overlaps? */
+/* adjacent to (but not overlapping)? */
Datum
-range_overlaps(PG_FUNCTION_ARGS)
+range_adjacent(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE(0);
RangeType *r2 = PG_GETARG_RANGE(1);
TypeCacheEntry *typcache;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache, r1, r2));
+}
+
+/* overlaps? (internal version) */
+bool
+range_overlaps_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
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);
+ return false;
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0 &&
range_cmp_bounds(typcache, &lower1, &upper2) <= 0)
- PG_RETURN_BOOL(true);
+ return true;
if (range_cmp_bounds(typcache, &lower2, &lower1) >= 0 &&
range_cmp_bounds(typcache, &lower2, &upper1) <= 0)
- PG_RETURN_BOOL(true);
+ return true;
- PG_RETURN_BOOL(false);
+ return false;
}
-/* does not extend to right of? */
+/* overlaps? */
Datum
-range_overleft(PG_FUNCTION_ARGS)
+range_overlaps(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE(0);
RangeType *r2 = PG_GETARG_RANGE(1);
TypeCacheEntry *typcache;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_BOOL(range_overlaps_internal(typcache, r1, r2));
+}
+
+/* does not extend to right of? (internal version) */
+bool
+range_overleft_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
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);
+ return false;
if (range_cmp_bounds(typcache, &upper1, &upper2) <= 0)
- PG_RETURN_BOOL(true);
+ return true;
- PG_RETURN_BOOL(false);
+ return false;
}
-/* does not extend to left of? */
+/* does not extend to right of? */
Datum
-range_overright(PG_FUNCTION_ARGS)
+range_overleft(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE(0);
RangeType *r2 = PG_GETARG_RANGE(1);
TypeCacheEntry *typcache;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache, r1, r2));
+}
+
+/* does not extend to left of? (internal version) */
+bool
+range_overright_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
RangeBound lower1,
lower2;
RangeBound upper1,
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);
PG_RETURN_BOOL(false);
}
+/* does not extend to left of? */
+Datum
+range_overright(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+ TypeCacheEntry *typcache;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ PG_RETURN_BOOL(range_overright_internal(typcache, r1, r2));
+}
+
/* range, range -> range functions */
* Caller has already checked that they are the same range type, and looked up
* the necessary typcache entry.
*/
-static bool
+bool
range_contains_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
{
RangeBound lower1;
RangeBound upper2;
bool empty2;
+ /* Different types should be prevented by ANYRANGE matching rules */
+ if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+ elog(ERROR, "range types do not match");
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
return true;
}
+bool
+range_contained_by_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
+ return range_contains_internal(typcache, r2, r1);
+}
+
/*
* Test whether range r contains a specific element value.
*/
-static bool
+bool
range_contains_elem_internal(TypeCacheEntry *typcache, RangeType *r, Datum val)
{
RangeBound lower;
static RangeType *range_super_union(TypeCacheEntry *typcache, RangeType *r1,
RangeType *r2);
-static bool range_gist_consistent_int(FmgrInfo *flinfo,
+static bool range_gist_consistent_int(TypeCacheEntry *typcache,
StrategyNumber strategy, RangeType *key,
Datum query);
-static bool range_gist_consistent_leaf(FmgrInfo *flinfo,
+static bool range_gist_consistent_leaf(TypeCacheEntry *typcache,
StrategyNumber strategy, RangeType *key,
Datum query);
static void range_gist_fallback_split(TypeCacheEntry *typcache,
/* Oid subtype = PG_GETARG_OID(3); */
bool *recheck = (bool *) PG_GETARG_POINTER(4);
RangeType *key = DatumGetRangeType(entry->key);
+ TypeCacheEntry *typcache;
/* All operators served by this function are exact */
*recheck = false;
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(key));
+
if (GIST_LEAF(entry))
- PG_RETURN_BOOL(range_gist_consistent_leaf(fcinfo->flinfo, strategy,
+ PG_RETURN_BOOL(range_gist_consistent_leaf(typcache, strategy,
key, query));
else
- PG_RETURN_BOOL(range_gist_consistent_int(fcinfo->flinfo, strategy,
+ PG_RETURN_BOOL(range_gist_consistent_int(typcache, strategy,
key, query));
}
*result = false;
else
{
- /*
- * 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));
+ TypeCacheEntry *typcache;
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ *result = range_eq_internal(typcache, r1, r2);
}
PG_RETURN_POINTER(result);
return result;
}
-/*
- * 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(FmgrInfo *flinfo, StrategyNumber strategy,
+range_gist_consistent_int(TypeCacheEntry *typcache, StrategyNumber strategy,
RangeType *key, Datum query)
{
- PGFunction proc;
- bool negate = false;
- bool retval;
-
switch (strategy)
{
case RANGESTRAT_BEFORE:
if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeType(query)))
return false;
- proc = range_overright;
- negate = true;
- break;
+ return (!range_overright_internal(typcache, key,
+ DatumGetRangeType(query)));
case RANGESTRAT_OVERLEFT:
if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeType(query)))
return false;
- proc = range_after;
- negate = true;
- break;
+ return (!range_after_internal(typcache, key,
+ DatumGetRangeType(query)));
case RANGESTRAT_OVERLAPS:
- proc = range_overlaps;
- break;
+ return range_overlaps_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_OVERRIGHT:
if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeType(query)))
return false;
- proc = range_before;
- negate = true;
- break;
+ return (!range_before_internal(typcache, key,
+ DatumGetRangeType(query)));
case RANGESTRAT_AFTER:
if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeType(query)))
return false;
- proc = range_overleft;
- negate = true;
- break;
+ return (!range_overleft_internal(typcache, key,
+ DatumGetRangeType(query)));
case RANGESTRAT_ADJACENT:
if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeType(query)))
return false;
- if (DatumGetBool(TrickFunctionCall2(range_adjacent, flinfo,
- RangeTypeGetDatum(key),
- query)))
+ if (range_adjacent_internal(typcache, key,
+ DatumGetRangeType(query)))
return true;
- proc = range_overlaps;
- break;
+ return range_overlaps_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_CONTAINS:
- proc = range_contains;
- break;
+ return range_contains_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_CONTAINED_BY:
/*
*/
if (RangeIsOrContainsEmpty(key))
return true;
- proc = range_overlaps;
- break;
+ return range_overlaps_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_CONTAINS_ELEM:
- proc = range_contains_elem;
- break;
+ return range_contains_elem_internal(typcache, key, query);
case RANGESTRAT_EQ:
/*
*/
if (RangeIsEmpty(DatumGetRangeType(query)))
return RangeIsOrContainsEmpty(key);
- proc = range_contains;
- break;
+ return range_contains_internal(typcache, key,
+ DatumGetRangeType(query));
default:
elog(ERROR, "unrecognized range strategy: %d", strategy);
- proc = NULL; /* keep compiler quiet */
- break;
+ return false; /* keep compiler quiet */
}
-
- retval = DatumGetBool(TrickFunctionCall2(proc, flinfo,
- RangeTypeGetDatum(key),
- query));
- if (negate)
- retval = !retval;
-
- return retval;
}
/*
* GiST consistent test on an index leaf page
*/
static bool
-range_gist_consistent_leaf(FmgrInfo *flinfo, StrategyNumber strategy,
+range_gist_consistent_leaf(TypeCacheEntry *typcache, StrategyNumber strategy,
RangeType *key, Datum query)
{
- PGFunction proc;
-
switch (strategy)
{
case RANGESTRAT_BEFORE:
- proc = range_before;
- break;
+ return range_before_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_OVERLEFT:
- proc = range_overleft;
- break;
+ return range_overleft_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_OVERLAPS:
- proc = range_overlaps;
- break;
+ return range_overlaps_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_OVERRIGHT:
- proc = range_overright;
- break;
+ return range_overright_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_AFTER:
- proc = range_after;
- break;
+ return range_after_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_ADJACENT:
- proc = range_adjacent;
- break;
+ return range_adjacent_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_CONTAINS:
- proc = range_contains;
- break;
+ return range_contains_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_CONTAINED_BY:
- proc = range_contained_by;
- break;
+ return range_contained_by_internal(typcache, key,
+ DatumGetRangeType(query));
case RANGESTRAT_CONTAINS_ELEM:
- proc = range_contains_elem;
- break;
+ return range_contains_elem_internal(typcache, key, query);
case RANGESTRAT_EQ:
- proc = range_eq;
- break;
+ return range_eq_internal(typcache, key, DatumGetRangeType(query));
default:
elog(ERROR, "unrecognized range strategy: %d", strategy);
- proc = NULL; /* keep compiler quiet */
- break;
+ return false; /* keep compiler quiet */
}
-
- return DatumGetBool(TrickFunctionCall2(proc, flinfo,
- RangeTypeGetDatum(key),
- query));
}
/*