summaryrefslogtreecommitdiff
path: root/src/backend/utils
diff options
context:
space:
mode:
authorHeikki Linnakangas2011-11-03 11:16:28 +0000
committerHeikki Linnakangas2011-11-03 11:42:15 +0000
commit4429f6a9e3e12bb4af6e3677fbc78cd80f160252 (patch)
treea2e272129e5515f7ef2f4e09989bddf0fd8158ea /src/backend/utils
parent43342891861cc2d08dea2b1c8b190e15e5a36551 (diff)
Support range data types.
Selectivity estimation functions are missing for some range type operators, which is a TODO. Jeff Davis
Diffstat (limited to 'src/backend/utils')
-rw-r--r--src/backend/utils/adt/Makefile4
-rw-r--r--src/backend/utils/adt/date.c1
-rw-r--r--src/backend/utils/adt/pseudotypes.c24
-rw-r--r--src/backend/utils/adt/rangetypes.c2153
-rw-r--r--src/backend/utils/adt/rangetypes_gist.c587
-rw-r--r--src/backend/utils/cache/lsyscache.c30
-rw-r--r--src/backend/utils/cache/syscache.c12
-rw-r--r--src/backend/utils/fmgr/funcapi.c115
8 files changed, 2911 insertions, 15 deletions
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index ce28abd9fe1..5f968b011fc 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -20,8 +20,8 @@ OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
enum.o float.o format_type.o \
geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
misc.o nabstime.o name.o numeric.o numutils.o \
- oid.o oracle_compat.o pseudotypes.o rowtypes.o \
- regexp.o regproc.o ruleutils.o selfuncs.o \
+ oid.o oracle_compat.o pseudotypes.o rangetypes.o rangetypes_gist.o \
+ rowtypes.o regexp.o regproc.o ruleutils.o selfuncs.o \
tid.o timestamp.o varbit.o varchar.o varlena.o version.o xid.o \
network.o mac.o inet_cidr_ntop.o inet_net_pton.o \
ri_triggers.o pg_lzcompress.o pg_locale.o formatting.o \
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index b06faf0c720..271b57a8302 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -889,7 +889,6 @@ date_timestamp(PG_FUNCTION_ARGS)
PG_RETURN_TIMESTAMP(result);
}
-
/* timestamp_date()
* Convert timestamp to date data type.
*/
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index ddb1bd2b71c..3b78c36a7ec 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -25,6 +25,7 @@
#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
+#include "utils/rangetypes.h"
/*
@@ -187,6 +188,29 @@ anyenum_out(PG_FUNCTION_ARGS)
return enum_out(fcinfo);
}
+/*
+ * anyrange_in - input routine for pseudo-type ANYRANGE.
+ */
+Datum
+anyrange_in(PG_FUNCTION_ARGS)
+{
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot accept a value of type anyrange")));
+
+ PG_RETURN_VOID(); /* keep compiler quiet */
+}
+
+/*
+ * anyrange_out - output routine for pseudo-type ANYRANGE.
+ *
+ * We may as well allow this, since range_out will in fact work.
+ */
+Datum
+anyrange_out(PG_FUNCTION_ARGS)
+{
+ return range_out(fcinfo);
+}
/*
* void_in - input routine for pseudo-type VOID.
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
new file mode 100644
index 00000000000..6327d33f3ae
--- /dev/null
+++ b/src/backend/utils/adt/rangetypes.c
@@ -0,0 +1,2153 @@
+/*-------------------------------------------------------------------------
+ *
+ * rangetypes.c
+ * I/O functions, operators, and support functions for range types
+ *
+ * Copyright (c) 2006-2011, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/rangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/hash.h"
+#include "access/nbtree.h"
+#include "catalog/pg_opclass.h"
+#include "catalog/pg_range.h"
+#include "catalog/pg_type.h"
+#include "fmgr.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/fmgroids.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/syscache.h"
+#include "utils/timestamp.h"
+#include "utils/typcache.h"
+
+#define TYPE_IS_PACKABLE(typlen, typstorage) \
+ (typlen == -1 && typstorage != 'p')
+
+/* flags */
+#define RANGE_EMPTY 0x01
+#define RANGE_LB_INC 0x02
+#define RANGE_LB_NULL 0x04 /* NOT USED */
+#define RANGE_LB_INF 0x08
+#define RANGE_UB_INC 0x10
+#define RANGE_UB_NULL 0x20 /* NOT 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"
+
+static void range_parse(char *input_str, char *flags, char **lbound_str,
+ char **ubound_str);
+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 Size datum_compute_size(Size sz, Datum datum, bool typbyval,
+ char typalign, int16 typlen, char typstorage);
+static Pointer datum_write(Pointer ptr, Datum datum, bool typbyval,
+ char typalign, int16 typlen, char typstorage);
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+Datum
+range_in(PG_FUNCTION_ARGS)
+{
+ char *input_str = PG_GETARG_CSTRING(0);
+ Oid rngtypoid = PG_GETARG_OID(1);
+ Oid typmod = PG_GETARG_INT32(2);
+
+ char flags;
+ Datum range;
+ 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);
+
+ /* parse */
+ range_parse(input_str, &flags, &lbound_str, &ubound_str);
+
+ /* input */
+ getTypeInputInfo(rngtypinfo.subtype, &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);
+ if (RANGE_HAS_UBOUND(flags))
+ upper.val = InputFunctionCall(&subInputFn, ubound_str,
+ ioParam, typmod);
+
+ /* serialize and canonicalize */
+ range = make_range(fcinfo, &lower, &upper, flags & RANGE_EMPTY);
+
+ PG_RETURN_RANGE(range);
+}
+
+Datum
+range_out(PG_FUNCTION_ARGS)
+{
+ RangeType *range = PG_GETARG_RANGE(0);
+
+ regproc subOutput;
+ FmgrInfo subOutputFn;
+ bool isVarlena;
+
+ char flags = 0;
+ char *lbound_str = NULL;
+ char *ubound_str = NULL;
+ char *output_str;
+
+ bool empty;
+
+ RangeTypeInfo rngtypinfo;
+ RangeBound lower;
+ RangeBound upper;
+
+ /* deserialize */
+ range_deserialize(fcinfo, range, &lower, &upper, &empty);
+
+ if (lower.rngtypid != upper.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ range_gettypinfo(fcinfo, lower.rngtypid, &rngtypinfo);
+
+ if (empty)
+ flags |= RANGE_EMPTY;
+
+ flags |= (lower.inclusive) ? RANGE_LB_INC : 0;
+ flags |= (lower.infinite) ? RANGE_LB_INF : 0;
+ flags |= (upper.inclusive) ? RANGE_UB_INC : 0;
+ flags |= (upper.infinite) ? RANGE_UB_INF : 0;
+
+ /* output */
+ getTypeOutputInfo(rngtypinfo.subtype, &subOutput, &isVarlena);
+ fmgr_info(subOutput, &subOutputFn);
+
+ if (RANGE_HAS_LBOUND(flags))
+ lbound_str = OutputFunctionCall(&subOutputFn, lower.val);
+ if (RANGE_HAS_UBOUND(flags))
+ ubound_str = OutputFunctionCall(&subOutputFn, upper.val);
+
+ /* deparse */
+ output_str = range_deparse(flags, lbound_str, ubound_str);
+
+ PG_RETURN_CSTRING(output_str);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then 4 bytes are the
+ * range type Oid, then the lower bound (if present) then the upper bound (if
+ * present). Each bound is represented by a 4-byte length header and the binary
+ * representation of that bound (as returned by a call to the send function for
+ * the subtype).
+ */
+
+Datum
+range_recv(PG_FUNCTION_ARGS)
+{
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
+ Oid rngtypid = PG_GETARG_OID(1);
+ int32 typmod = PG_GETARG_INT32(2);
+ Oid subrecv;
+ Oid ioparam;
+ Datum range;
+ char flags;
+ RangeBound lower;
+ RangeBound upper;
+
+ RangeTypeInfo rngtypinfo;
+
+ flags = (unsigned char) pq_getmsgbyte(buf);
+
+ range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);
+
+ getTypeBinaryInputInfo(rngtypinfo.subtype, &subrecv, &ioparam);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ uint32 bound_len = pq_getmsgint(buf, 4);
+ const char *bound_data = pq_getmsgbytes(buf, bound_len);
+ StringInfoData bound_buf;
+
+ initStringInfo(&bound_buf);
+ appendBinaryStringInfo(&bound_buf, bound_data, bound_len);
+
+ lower.val = OidReceiveFunctionCall(subrecv,
+ &bound_buf,
+ ioparam,
+ typmod);
+ pfree(bound_buf.data);
+ }
+ else
+ lower.val = (Datum) 0;
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ uint32 bound_len = pq_getmsgint(buf, 4);
+ const char *bound_data = pq_getmsgbytes(buf, bound_len);
+ StringInfoData bound_buf;
+
+ initStringInfo(&bound_buf);
+ appendBinaryStringInfo(&bound_buf, bound_data, bound_len);
+
+ upper.val = OidReceiveFunctionCall(subrecv,
+ &bound_buf,
+ ioparam,
+ typmod);
+ pfree(bound_buf.data);
+ }
+ else
+ upper.val = (Datum) 0;
+
+ 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);
+
+ /*
+ * XXX if the subtype is pass-by-val, we should pfree the upper and
+ * lower bounds here.
+ */
+
+ PG_RETURN_RANGE(range);
+}
+
+Datum
+range_send(PG_FUNCTION_ARGS)
+{
+ RangeType *range = PG_GETARG_RANGE(0);
+ StringInfo buf = makeStringInfo();
+ char flags = 0;
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+ Oid subsend;
+ bool typIsVarlena;
+
+ RangeTypeInfo rngtypinfo;
+
+ pq_begintypsend(buf);
+
+ range_deserialize(fcinfo, range, &lower, &upper, &empty);
+
+ if (empty)
+ flags |= RANGE_EMPTY;
+
+ flags |= (lower.inclusive) ? RANGE_LB_INC : 0;
+ flags |= (lower.infinite) ? RANGE_LB_INF : 0;
+ 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))
+ {
+ Datum bound = PointerGetDatum(
+ OidSendFunctionCall(subsend, lower.val));
+ uint32 bound_len = VARSIZE(bound) - VARHDRSZ;
+ char *bound_data = VARDATA(bound);
+
+ pq_sendint(buf, bound_len, 4);
+ pq_sendbytes(buf, bound_data, bound_len);
+ }
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ Datum bound = PointerGetDatum(
+ OidSendFunctionCall(subsend, upper.val));
+ uint32 bound_len = VARSIZE(bound) - VARHDRSZ;
+ char *bound_data = VARDATA(bound);
+
+ pq_sendint(buf, bound_len, 4);
+ pq_sendbytes(buf, bound_data, bound_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+Datum
+range_constructor0(PG_FUNCTION_ARGS)
+{
+ Oid rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+
+ lower.rngtypid = rngtypid;
+ lower.val = (Datum) 0;
+ lower.inclusive = false;
+ lower.infinite = false;
+ lower.lower = true;
+
+ upper.rngtypid = rngtypid;
+ upper.val = (Datum) 0;
+ upper.inclusive = false;
+ upper.infinite = false;
+ upper.lower = false;
+
+ range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, true));
+
+ PG_RETURN_RANGE(range);
+}
+
+Datum
+range_constructor1(PG_FUNCTION_ARGS)
+{
+ Datum arg1 = PG_GETARG_DATUM(0);
+ Oid rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+
+ if (PG_ARGISNULL(0))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("argument must not be NULL")));
+
+ lower.rngtypid = rngtypid;
+ lower.val = arg1;
+ lower.inclusive = true;
+ lower.infinite = false;
+ lower.lower = true;
+
+ upper.rngtypid = rngtypid;
+ upper.val = arg1;
+ upper.inclusive = true;
+ upper.infinite = false;
+ upper.lower = false;
+
+ range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+}
+
+Datum
+range_constructor2(PG_FUNCTION_ARGS)
+{
+ Datum arg1 = PG_GETARG_DATUM(0);
+ Datum arg2 = PG_GETARG_DATUM(1);
+ Oid rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+ char flags;
+
+ flags = range_parse_flags(RANGE_DEFAULT_FLAGS);
+
+ lower.rngtypid = rngtypid;
+ lower.val = PG_ARGISNULL(0) ? (Datum)0 : arg1;
+ lower.inclusive = flags & RANGE_LB_INC;
+ lower.infinite = PG_ARGISNULL(0);
+ lower.lower = true;
+
+ upper.rngtypid = rngtypid;
+ upper.val = PG_ARGISNULL(1) ? (Datum)0 : arg2;
+ upper.inclusive = flags & RANGE_UB_INC;
+ upper.infinite = PG_ARGISNULL(1);
+ upper.lower = false;
+
+ range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+}
+
+Datum
+range_constructor3(PG_FUNCTION_ARGS)
+{
+ Datum arg1 = PG_GETARG_DATUM(0);
+ Datum arg2 = PG_GETARG_DATUM(1);
+ Oid rngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+ RangeType *range;
+ RangeBound lower;
+ RangeBound upper;
+ char flags;
+
+ if (PG_ARGISNULL(2))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("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;
+ lower.infinite = PG_ARGISNULL(0);
+ lower.lower = true;
+
+ upper.rngtypid = rngtypid;
+ upper.val = PG_ARGISNULL(1) ? (Datum)0 : arg2;
+ upper.inclusive = flags & RANGE_UB_INC;
+ upper.infinite = PG_ARGISNULL(1);
+ upper.lower = false;
+
+ range = DatumGetRangeType(make_range(fcinfo, &lower, &upper, false));
+
+ PG_RETURN_RANGE(range);
+}
+
+/* range -> subtype */
+Datum
+range_lower(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ if (empty)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("empty range has no lower bound")));
+ if (lower.infinite)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("range lower bound is infinite")));
+
+ PG_RETURN_DATUM(lower.val);
+}
+
+Datum
+range_upper(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ if (empty)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("empty range has no upper bound")));
+ if (upper.infinite)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("range upper bound is infinite")));
+
+ PG_RETURN_DATUM(upper.val);
+}
+
+
+/* range -> bool */
+Datum
+range_empty(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(empty);
+}
+
+Datum
+range_lower_inc(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(lower.inclusive);
+}
+
+Datum
+range_upper_inc(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(upper.inclusive);
+}
+
+Datum
+range_lower_inf(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(lower.infinite);
+}
+
+Datum
+range_upper_inf(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r1, &lower, &upper, &empty);
+
+ PG_RETURN_BOOL(upper.infinite);
+}
+
+
+/* range, range -> bool */
+Datum
+range_eq(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 && empty2)
+ PG_RETURN_BOOL(true);
+ if (empty1 != empty2)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) != 0)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) != 0)
+ PG_RETURN_BOOL(false);
+
+ PG_RETURN_BOOL(true);
+}
+
+Datum
+range_ne(PG_FUNCTION_ARGS)
+{
+ bool eq = DatumGetBool(range_eq(fcinfo));
+
+ PG_RETURN_BOOL(!eq);
+}
+
+Datum
+range_contains_elem(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2;
+ Datum val = PG_GETARG_DATUM(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+
+ lower2.rngtypid = lower1.rngtypid;
+ lower2.inclusive = true;
+ lower2.infinite = false;
+ lower2.lower = true;
+ lower2.val = val;
+
+ upper2.rngtypid = lower1.rngtypid;
+ upper2.inclusive = true;
+ upper2.infinite = false;
+ upper2.lower = false;
+ upper2.val = val;
+
+ r2 = DatumGetRangeType(make_range(fcinfo, &lower2, &upper2, false));
+
+ PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+}
+
+Datum
+range_contains(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+}
+
+Datum
+elem_contained_by_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(1);
+ RangeType *r2;
+ Datum val = PG_GETARG_DATUM(0);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+
+ lower2.rngtypid = lower1.rngtypid;
+ lower2.inclusive = true;
+ lower2.infinite = false;
+ lower2.lower = true;
+ lower2.val = val;
+
+ upper2.rngtypid = lower1.rngtypid;
+ upper2.inclusive = true;
+ upper2.infinite = false;
+ upper2.lower = false;
+ upper2.val = val;
+
+ r2 = DatumGetRangeType(make_range(fcinfo, &lower2, &upper2, false));
+
+ PG_RETURN_BOOL(range_contains_internal(fcinfo, r1, r2));
+}
+
+Datum
+range_contained_by(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ PG_RETURN_BOOL(range_contains_internal(fcinfo, r2, r1));
+}
+
+Datum
+range_before(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("input range is empty")));
+
+ if (range_cmp_bounds(fcinfo, &upper1, &lower2) < 0)
+ PG_RETURN_BOOL(true);
+ else
+ PG_RETURN_BOOL(false);
+}
+
+Datum
+range_after(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("input range is empty")));
+
+ if (range_cmp_bounds(fcinfo, &lower1, &upper2) > 0)
+ PG_RETURN_BOOL(true);
+ else
+ PG_RETURN_BOOL(false);
+}
+
+Datum range_adjacent(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeTypeInfo rngtypinfo;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("undefined for empty ranges")));
+
+ /*
+ * For two ranges to be adjacent, the lower boundary of one range
+ * has to match the upper boundary of the other. However, the
+ * inclusivity of those two boundaries must also be different.
+ *
+ * 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,
+ lower1.val, upper2.val)) == 0)
+ PG_RETURN_BOOL(true);
+ }
+
+ if (upper1.inclusive != lower2.inclusive)
+ {
+ if (DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
+ rngtypinfo.collation,
+ upper1.val, lower2.val)) == 0)
+ PG_RETURN_BOOL(true);
+ }
+
+ PG_RETURN_BOOL(false);
+}
+
+Datum
+range_overlaps(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0 &&
+ range_cmp_bounds(fcinfo, &lower1, &upper2) <= 0)
+ PG_RETURN_BOOL(true);
+
+ if (range_cmp_bounds(fcinfo, &lower2, &lower1) >= 0 &&
+ range_cmp_bounds(fcinfo, &lower2, &upper1) <= 0)
+ PG_RETURN_BOOL(true);
+
+ PG_RETURN_BOOL(false);
+}
+
+Datum
+range_overleft(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) <= 0)
+ PG_RETURN_BOOL(true);
+
+ PG_RETURN_BOOL(false);
+}
+
+Datum
+range_overright(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 || empty2)
+ PG_RETURN_BOOL(false);
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0)
+ PG_RETURN_BOOL(true);
+
+ PG_RETURN_BOOL(false);
+}
+
+
+/* range, range -> range */
+Datum
+range_minus(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ int cmp_l1l2, cmp_l1u2, cmp_u1l2, cmp_u1u2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ 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);
+
+ if (cmp_l1l2 < 0 && cmp_u1u2 > 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("result range is not contiguous")));
+
+ if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
+ PG_RETURN_RANGE(r1);
+
+ if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
+ PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));
+
+ 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));
+ }
+
+ 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));
+ }
+
+ elog(ERROR, "unexpected error in range_minus");
+ PG_RETURN_VOID();
+}
+
+Datum
+range_union(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ RangeBound *result_lower;
+ RangeBound *result_upper;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (empty1)
+ PG_RETURN_RANGE(r2);
+ if (empty2)
+ PG_RETURN_RANGE(r1);
+
+ if (!DatumGetBool(range_overlaps(fcinfo)) &&
+ !DatumGetBool(range_adjacent(fcinfo)))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("result range is not contiguous")));
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) < 0)
+ result_lower = &lower1;
+ else
+ result_lower = &lower2;
+
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) > 0)
+ result_upper = &upper1;
+ else
+ result_upper = &upper2;
+
+ PG_RETURN_RANGE(make_range(fcinfo, result_lower, result_upper, false));
+}
+
+Datum
+range_intersect(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ RangeBound *result_lower;
+ RangeBound *result_upper;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
+ PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) >= 0)
+ result_lower = &lower1;
+ else
+ result_lower = &lower2;
+
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) <= 0)
+ result_upper = &upper1;
+ else
+ result_upper = &upper2;
+
+ PG_RETURN_RANGE(make_range(fcinfo, result_lower, result_upper, false));
+}
+
+/* Btree support */
+
+Datum
+range_cmp(PG_FUNCTION_ARGS)
+{
+ RangeType *r1 = PG_GETARG_RANGE(0);
+ RangeType *r2 = PG_GETARG_RANGE(1);
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ int cmp;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty1 && empty2)
+ PG_RETURN_INT32(0);
+ else if (empty1)
+ PG_RETURN_INT32(-1);
+ else if (empty2)
+ PG_RETURN_INT32(1);
+
+ if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0)
+ PG_RETURN_INT32(cmp);
+
+ PG_RETURN_INT32(range_cmp_bounds(fcinfo, &upper1, &upper2));
+}
+
+Datum
+range_lt(PG_FUNCTION_ARGS)
+{
+ int cmp = range_cmp(fcinfo);
+ PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+range_le(PG_FUNCTION_ARGS)
+{
+ int cmp = range_cmp(fcinfo);
+ PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+range_ge(PG_FUNCTION_ARGS)
+{
+ int cmp = range_cmp(fcinfo);
+ PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+range_gt(PG_FUNCTION_ARGS)
+{
+ int cmp = range_cmp(fcinfo);
+ PG_RETURN_BOOL(cmp > 0);
+}
+
+/* Hash support */
+Datum
+hash_range(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE(0);
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+ char flags = 0;
+ uint32 lower_hash = 0;
+ uint32 upper_hash = 0;
+ uint32 result = 0;
+
+ RangeTypeInfo rngtypinfo;
+
+ TypeCacheEntry *typentry;
+ Oid subtype;
+ FunctionCallInfoData locfcinfo;
+
+
+ range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+ if (lower.rngtypid != upper.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty)
+ flags |= RANGE_EMPTY;
+
+ flags |= (lower.inclusive) ? RANGE_LB_INC : 0;
+ flags |= (lower.infinite) ? RANGE_LB_INF : 0;
+ 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.
+ */
+ typentry = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+ if (typentry == NULL || typentry->type_id != subtype)
+ {
+ typentry = lookup_type_cache(subtype, TYPECACHE_HASH_PROC_FINFO);
+ if (!OidIsValid(typentry->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;
+ }
+
+ /*
+ * Apply the hash function to each bound (the hash function shouldn't
+ * care about the collation).
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->hash_proc_finfo, 1,
+ InvalidOid, NULL, NULL);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ locfcinfo.arg[0] = lower.val;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.isnull = false;
+ lower_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
+ }
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ locfcinfo.arg[0] = upper.val;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.isnull = false;
+ upper_hash = DatumGetUInt32(FunctionCallInvoke(&locfcinfo));
+ }
+
+ result = hash_uint32((uint32) flags);
+ result ^= lower_hash;
+ result = (result << 1) | (result >> 31);
+ result ^= upper_hash;
+
+ PG_RETURN_INT32(result);
+}
+
+/*
+ *----------------------------------------------------------
+ * CANONICAL FUNCTIONS
+ *
+ * Functions for specific built-in range types.
+ *----------------------------------------------------------
+ */
+
+Datum
+int4range_canonical(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE(0);
+
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+ if (empty)
+ PG_RETURN_RANGE(r);
+
+ if (!lower.infinite && !lower.inclusive)
+ {
+ lower.val = DirectFunctionCall2(int4pl, lower.val, Int32GetDatum(1));
+ lower.inclusive = true;
+ }
+
+ if (!upper.infinite && upper.inclusive)
+ {
+ upper.val = DirectFunctionCall2(int4pl, upper.val, Int32GetDatum(1));
+ upper.inclusive = false;
+ }
+
+ PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+}
+
+Datum
+int8range_canonical(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE(0);
+
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+ if (empty)
+ PG_RETURN_RANGE(r);
+
+ if (!lower.infinite && !lower.inclusive)
+ {
+ lower.val = DirectFunctionCall2(int8pl, lower.val, Int64GetDatum(1));
+ lower.inclusive = true;
+ }
+
+ if (!upper.infinite && upper.inclusive)
+ {
+ upper.val = DirectFunctionCall2(int8pl, upper.val, Int64GetDatum(1));
+ upper.inclusive = false;
+ }
+
+ PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+}
+
+Datum
+daterange_canonical(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE(0);
+
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+
+ range_deserialize(fcinfo, r, &lower, &upper, &empty);
+
+ if (empty)
+ PG_RETURN_RANGE(r);
+
+ if (!lower.infinite && !lower.inclusive)
+ {
+ lower.val = DirectFunctionCall2(date_pli, lower.val, Int32GetDatum(1));
+ lower.inclusive = true;
+ }
+
+ if (!upper.infinite && upper.inclusive)
+ {
+ upper.val = DirectFunctionCall2(date_pli, upper.val, Int32GetDatum(1));
+ upper.inclusive = false;
+ }
+
+ PG_RETURN_RANGE(range_serialize(fcinfo, &lower, &upper, false));
+}
+
+/*
+ *----------------------------------------------------------
+ * SUBTYPE_DIFF FUNCTIONS
+ *
+ * Functions for specific built-in range types.
+ *----------------------------------------------------------
+ */
+
+Datum
+int4range_subdiff(PG_FUNCTION_ARGS)
+{
+ int32 v1 = PG_GETARG_INT32(0);
+ int32 v2 = PG_GETARG_INT32(1);
+
+ PG_RETURN_FLOAT8((float8)(v1-v2));
+}
+
+Datum
+int8range_subdiff(PG_FUNCTION_ARGS)
+{
+ int64 v1 = PG_GETARG_INT64(0);
+ int64 v2 = PG_GETARG_INT64(1);
+
+ PG_RETURN_FLOAT8((float8)(v1-v2));
+}
+
+Datum
+daterange_subdiff(PG_FUNCTION_ARGS)
+{
+ int32 v1 = PG_GETARG_INT32(0);
+ int32 v2 = PG_GETARG_INT32(1);
+
+ PG_RETURN_FLOAT8((float8)(v1-v2));
+}
+
+Datum
+numrange_subdiff(PG_FUNCTION_ARGS)
+{
+ Datum v1 = PG_GETARG_DATUM(0);
+ Datum v2 = PG_GETARG_DATUM(1);
+ Datum numresult;
+ float8 floatresult;
+
+ numresult = DirectFunctionCall2(numeric_sub, v1, v2);
+
+ floatresult = DatumGetFloat8(
+ DirectFunctionCall1(numeric_float8, numresult));
+
+ PG_RETURN_FLOAT8(floatresult);
+}
+
+Datum
+tsrange_subdiff(PG_FUNCTION_ARGS)
+{
+ Timestamp v1 = PG_GETARG_TIMESTAMP(0);
+ Timestamp v2 = PG_GETARG_TIMESTAMP(1);
+ float8 result;
+
+#ifdef HAVE_INT64_TIMESTAMP
+ result = ((float8)(v1-v2)) / USECS_PER_SEC;
+#else
+ result = timestamp;
+#endif
+
+ PG_RETURN_FLOAT8(result);
+}
+
+Datum
+tstzrange_subdiff(PG_FUNCTION_ARGS)
+{
+ Timestamp v1 = PG_GETARG_TIMESTAMP(0);
+ Timestamp v2 = PG_GETARG_TIMESTAMP(1);
+ float8 result;
+
+#ifdef HAVE_INT64_TIMESTAMP
+ result = ((float8)(v1-v2)) / USECS_PER_SEC;
+#else
+ result = timestamp;
+#endif
+
+ PG_RETURN_FLOAT8(result);
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ * These functions aren't in pg_proc, but are useful if
+ * defining new generic range functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * Serialized format is:
+ *
+ * 4 bytes: Range type Oid
+ * Lower boundary, if any, aligned according to subtype's typalign
+ * Upper boundary, if any, aligned according to subtype's typalign
+ * 1 byte for flags
+ *
+ * This representation is chosen to be compact when the boundary
+ * values need to be MAXALIGNed. A palloc chunk always starts out
+ * MAXALIGNed, and the first 4 bytes will be the length header (range
+ * types are always variable-length), then the next 4 bytes will be
+ * the range type Oid. That leaves the first boundary item MAXALIGNed
+ * without the need for padding.
+ *
+ * However, it requires a slightly odd deserialization strategy,
+ * because we have to read the flags byte before we know whether to
+ * read a boundary value.
+ */
+
+/*
+ * This serializes a range, but does not canonicalize it. This should
+ * only be called by a canonicalization function.
+ */
+Datum
+range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
+ bool empty)
+{
+ Datum range;
+ size_t msize;
+ Pointer ptr;
+ int16 typlen;
+ char typalign;
+ bool typbyval;
+ 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;
+
+ if (empty)
+ flags |= RANGE_EMPTY;
+ else if (range_cmp_bounds(fcinfo, lower, upper) > 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATA_EXCEPTION),
+ errmsg("range lower bound must be less than or equal to range upper bound")));
+
+ flags |= (lower->inclusive) ? RANGE_LB_INC : 0;
+ flags |= (lower->infinite) ? RANGE_LB_INF : 0;
+ flags |= (upper->inclusive) ? RANGE_UB_INC : 0;
+ flags |= (upper->infinite) ? RANGE_UB_INF : 0;
+
+ msize = VARHDRSZ;
+ msize += sizeof(Oid);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ msize = datum_compute_size(msize, lower->val, typbyval, typalign,
+ typlen, typstorage);
+ }
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ msize = datum_compute_size(msize, upper->val, typbyval, typalign,
+ typlen, typstorage);
+ }
+
+ msize += sizeof(char);
+
+ ptr = palloc0(msize);
+ range = (Datum) ptr;
+
+ ptr += VARHDRSZ;
+
+ memcpy(ptr, &lower->rngtypid, sizeof(Oid));
+ ptr += sizeof(Oid);
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ Assert(lower->lower);
+ ptr = datum_write(ptr, lower->val, typbyval, typalign, typlen,
+ typstorage);
+ }
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ Assert(!upper->lower);
+ ptr = datum_write(ptr, upper->val, typbyval, typalign, typlen,
+ typstorage);
+ }
+
+ memcpy(ptr, &flags, sizeof(char));
+ ptr += sizeof(char);
+
+ SET_VARSIZE(range, msize);
+ PG_RETURN_RANGE(range);
+}
+
+void
+range_deserialize(FunctionCallInfo fcinfo, RangeType *range, RangeBound *lower,
+ RangeBound *upper, bool *empty)
+{
+ Pointer ptr = VARDATA(range);
+ char typalign;
+ int16 typlen;
+ int16 typbyval;
+ char flags;
+ Oid rngtypid;
+ Datum lbound;
+ Datum ubound;
+ Pointer flags_ptr;
+
+ RangeTypeInfo rngtypinfo;
+
+ memset(lower, 0, sizeof(RangeBound));
+ memset(upper, 0, sizeof(RangeBound));
+
+ /* peek at last byte to read the flag byte */
+ flags_ptr = ptr + VARSIZE(range) - VARHDRSZ - 1;
+ memcpy(&flags, flags_ptr, sizeof(char));
+
+ memcpy(&rngtypid, ptr, sizeof(Oid));
+ ptr += sizeof(Oid);
+
+ if (rngtypid == ANYRANGEOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot output a value of type anyrange")));
+
+ range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);
+
+ typalign = rngtypinfo.subtypalign;
+ typlen = rngtypinfo.subtyplen;
+ typbyval = rngtypinfo.subtypbyval;
+
+ if (RANGE_HAS_LBOUND(flags))
+ {
+ ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
+ lbound = fetch_att(ptr, typbyval, typlen);
+ ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr));
+ if (typlen == -1)
+ lbound = PointerGetDatum(PG_DETOAST_DATUM(lbound));
+ }
+ else
+ lbound = (Datum) 0;
+
+ if (RANGE_HAS_UBOUND(flags))
+ {
+ ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
+ ubound = fetch_att(ptr, typbyval, typlen);
+ ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr));
+ if (typlen == -1)
+ ubound = PointerGetDatum(PG_DETOAST_DATUM(ubound));
+ }
+ else
+ ubound = (Datum) 0;
+
+ *empty = flags & RANGE_EMPTY;
+
+ lower->rngtypid = rngtypid;
+ lower->val = lbound;
+ lower->inclusive = flags & RANGE_LB_INC;
+ lower->infinite = flags & RANGE_LB_INF;
+ lower->lower = true;
+
+ upper->rngtypid = rngtypid;
+ upper->val = ubound;
+ upper->inclusive = flags & RANGE_UB_INC;
+ upper->infinite = flags & RANGE_UB_INF;
+ upper->lower = false;
+}
+
+/*
+ * This both serializes and caonicalizes (if applicable) the
+ * range. This should be used by most callers.
+ */
+Datum
+make_range(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
+ bool empty)
+{
+ Datum range;
+
+ RangeTypeInfo rngtypinfo;
+
+ range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo);
+
+ if (lower->rngtypid != upper->rngtypid)
+ elog(ERROR, "range types do not match");
+
+ range = range_serialize(fcinfo, lower, upper, empty);
+
+ if (rngtypinfo.canonicalFn.fn_addr != NULL)
+ range = FunctionCall1(&rngtypinfo.canonicalFn, range);
+
+ PG_RETURN_RANGE(range);
+}
+
+int
+range_cmp_bounds(FunctionCallInfo fcinfo, RangeBound *b1, RangeBound *b2)
+{
+ int result;
+
+ RangeTypeInfo rngtypinfo;
+
+ if (b1->infinite && b2->infinite)
+ {
+ if (b1->lower == b2->lower)
+ return 0;
+ else
+ return (b1->lower) ? -1 : 1;
+ }
+ else if (b1->infinite && !b2->infinite)
+ return (b1->lower) ? -1 : 1;
+ else if (!b1->infinite && b2->infinite)
+ return (b2->lower) ? 1 : -1;
+
+ range_gettypinfo(fcinfo, b1->rngtypid, &rngtypinfo);
+ result = DatumGetInt32(FunctionCall2Coll(&rngtypinfo.cmpFn,
+ rngtypinfo.collation,
+ b1->val, b2->val));
+
+ if (result == 0)
+ {
+ if (b1->inclusive && !b2->inclusive)
+ return (b2->lower) ? -1 : 1;
+ else if (!b1->inclusive && b2->inclusive)
+ return (b1->lower) ? 1 : -1;
+ }
+
+ return result;
+}
+
+RangeType *
+make_empty_range(FunctionCallInfo fcinfo, Oid rngtypid)
+{
+ RangeBound lower;
+ RangeBound upper;
+
+ memset(&lower, 0, sizeof(RangeBound));
+ memset(&upper, 0, sizeof(RangeBound));
+
+ lower.rngtypid = rngtypid;
+ lower.lower = true;
+ upper.rngtypid = rngtypid;
+ upper.lower = false;
+
+ return DatumGetRangeType(make_range(fcinfo, &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 (!OidIsValid(cmpFnOid))
+ elog(ERROR, "unable to find compare function for operator class %d",
+ opclassOid);
+
+ /* 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));
+}
+
+/*
+ * Given a string representing the flags for the range type, return the flags
+ * represented as a char.
+ *
+ * Exported so that it can be called by DefineRange to check the default flags.
+ */
+char
+range_parse_flags(char *flags_str)
+{
+ char flags = 0;
+
+ if (flags_str[0] == '\0' ||
+ flags_str[1] == '\0' ||
+ flags_str[2] != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid range bound flags"),
+ errhint("Valid values are '[]', '[)', '(]', and '()'.")));
+
+ switch (flags_str[0])
+ {
+ case '[':
+ flags |= RANGE_LB_INC;
+ break;
+ case '(':
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid range bound flags"),
+ errhint("Valid values are '[]', '[)', '(]', and '()'.")));
+ }
+
+ switch (flags_str[1])
+ {
+ case ']':
+ flags |= RANGE_UB_INC;
+ break;
+ case ')':
+ break;
+ default:
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("invalid range bound flags"),
+ errhint("Valid values are '[]', '[)', '(]', and '()'.")));
+ }
+
+ return flags;
+}
+
+/*
+ *----------------------------------------------------------
+ * STATIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Parse range input, modeled after record_in in rowtypes.c.
+ *
+ * <range> := EMPTY
+ * | <lb-inc> <string>, <string> <ub-inc>
+ * <lb-inc> := '[' | '('
+ * <ub-inc> := ']' | ')'
+ *
+ * Whitespace before or after <range> is ignored. Whitespace within a <string>
+ * is taken literally and becomes the input string for that bound.
+ *
+ * A <string> of length zero is taken as "infinite" (i.e. no bound); unless it
+ * is surrounded by double-quotes, in which case it is the literal empty
+ * string.
+ *
+ * Within a <string>, special characters (such as comma, parenthesis, or
+ * brackets) can be enclosed in double-quotes or escaped with backslash. Within
+ * double-quotes, a double-quote can be escaped with double-quote or backslash.
+ */
+static void
+range_parse(char *string, char *flags, char **lbound_str,
+ char **ubound_str)
+{
+ char *ptr = string;
+ bool infinite;
+
+ *flags = 0;
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace(*ptr))
+ ptr++;
+
+ /* check for empty range */
+ if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+ strlen(RANGE_EMPTY_LITERAL)) == 0)
+ {
+ *flags = RANGE_EMPTY;
+
+ ptr += strlen(RANGE_EMPTY_LITERAL);
+
+ /* the rest should be whitespace */
+ while (*ptr != '\0' && isspace(*ptr))
+ ptr++;
+
+ /* should have consumed everything */
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Unexpected end of input.")));
+
+ return;
+ }
+
+ if (*ptr == '[' || *ptr == '(')
+ {
+ if (*ptr == '[')
+ *flags |= RANGE_LB_INC;
+ ptr++;
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Missing left parenthesis or bracket.")));
+ }
+
+ ptr = range_parse_bound(string, ptr, lbound_str, &infinite);
+ if (infinite)
+ {
+ *flags |= RANGE_LB_INF;
+ *flags &= ~RANGE_LB_INC;
+ }
+
+ if (*ptr != ',')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Missing upper bound.")));
+ ptr++;
+
+ ptr = range_parse_bound(string, ptr, ubound_str, &infinite);
+
+ if (*ptr == ')' || *ptr == ']')
+ {
+ if (*ptr == ']')
+ *flags |= RANGE_UB_INC;
+ }
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Too many boundaries.")));
+ }
+
+ ptr++;
+
+ if (infinite)
+ {
+ *flags |= RANGE_UB_INF;
+ *flags &= ~RANGE_UB_INC;
+ }
+
+ /* consume whitespace */
+ while (*ptr != '\0' && isspace(*ptr))
+ ptr++;
+
+ if (*ptr != '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Junk after right parenthesis or bracket.")));
+
+ return;
+}
+
+static char *
+range_parse_bound(char *string, char *ptr, char **bound_str, bool *infinite)
+{
+ StringInfoData buf;
+
+ /* Check for null: completely empty input means null */
+ if (*ptr == ',' || *ptr == ')' || *ptr == ']')
+ {
+ *bound_str = NULL;
+ *infinite = true;
+ }
+ else
+ {
+ /* Extract string for this column */
+ bool inquote = false;
+
+ initStringInfo(&buf);
+ while (inquote || !(*ptr == ',' || *ptr == ')' || *ptr == ']'))
+ {
+ char ch = *ptr++;
+
+ if (ch == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Unexpected end of input.")));
+ if (ch == '\\')
+ {
+ if (*ptr == '\0')
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("malformed range literal: \"%s\"",
+ string),
+ errdetail("Unexpected end of input.")));
+ appendStringInfoChar(&buf, *ptr++);
+ }
+ else if (ch == '\"')
+ {
+ if (!inquote)
+ inquote = true;
+ else if (*ptr == '\"')
+ {
+ /* doubled quote within quote sequence */
+ appendStringInfoChar(&buf, *ptr++);
+ }
+ else
+ inquote = false;
+ }
+ else
+ appendStringInfoChar(&buf, ch);
+ }
+
+ *bound_str = buf.data;
+ *infinite = false;
+ }
+
+ return ptr;
+}
+
+static char *
+range_deparse(char flags, char *lbound_str, char *ubound_str)
+{
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+
+ if (flags & RANGE_EMPTY)
+ return pstrdup(RANGE_EMPTY_LITERAL);
+
+ appendStringInfoString(&buf, (flags & RANGE_LB_INC) ? "[" : "(");
+
+ if (RANGE_HAS_LBOUND(flags))
+ appendStringInfoString(&buf, range_bound_escape(lbound_str));
+
+ appendStringInfoString(&buf, ",");
+
+ if (RANGE_HAS_UBOUND(flags))
+ appendStringInfoString(&buf, range_bound_escape(ubound_str));
+
+ appendStringInfoString(&buf, (flags & RANGE_UB_INC) ? "]" : ")");
+
+ return buf.data;
+}
+
+static char *
+range_bound_escape(char *value)
+{
+ bool nq;
+ char *tmp;
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+
+ /* Detect whether we need double quotes for this value */
+ nq = (value[0] == '\0'); /* force quotes for empty string */
+ for (tmp = value; *tmp; tmp++)
+ {
+ char ch = *tmp;
+
+ if (ch == '"' || ch == '\\' ||
+ ch == '(' || ch == ')' ||
+ ch == '[' || ch == ']' ||
+ ch == ',' ||
+ isspace((unsigned char) ch))
+ {
+ nq = true;
+ break;
+ }
+ }
+
+ /* And emit the string */
+ if (nq)
+ appendStringInfoChar(&buf, '"');
+ for (tmp = value; *tmp; tmp++)
+ {
+ char ch = *tmp;
+
+ if (ch == '"' || ch == '\\')
+ appendStringInfoChar(&buf, ch);
+ appendStringInfoChar(&buf, ch);
+ }
+ if (nq)
+ appendStringInfoChar(&buf, '"');
+
+ return buf.data;
+}
+
+static bool
+range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
+{
+ RangeBound lower1;
+ RangeBound upper1;
+ bool empty1;
+ RangeBound lower2;
+ RangeBound upper2;
+ bool empty2;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (lower1.rngtypid != upper1.rngtypid ||
+ lower1.rngtypid != lower2.rngtypid ||
+ lower1.rngtypid != upper2.rngtypid)
+ elog(ERROR, "range types do not match");
+
+ if (empty2)
+ return true;
+ else if (empty1)
+ return false;
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) > 0)
+ return false;
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) < 0)
+ return false;
+
+ return true;
+}
+
+/*
+ * datum_compute_size() and datum_write() are modeled after
+ * heap_compute_data_size() and heap_fill_tuple().
+ */
+
+static Size
+datum_compute_size(Size data_length, Datum val, bool typbyval, char typalign,
+ int16 typlen, char typstorage)
+{
+ if (TYPE_IS_PACKABLE(typlen, typstorage) &&
+ VARATT_CAN_MAKE_SHORT(DatumGetPointer(val)))
+ {
+ /*
+ * we're anticipating converting to a short varlena header, so
+ * adjust length and don't count any alignment
+ */
+ data_length += VARATT_CONVERTED_SHORT_SIZE(DatumGetPointer(val));
+ }
+ else
+ {
+ data_length = att_align_datum(data_length, typalign, typlen, val);
+ data_length = att_addlength_datum(data_length, typlen, val);
+ }
+
+ return data_length;
+}
+
+/*
+ * Modified version of the code in heap_fill_tuple(). Writes the datum to ptr
+ * using the correct alignment, and also uses short varlena header if
+ * applicable.
+ */
+static Pointer
+datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
+ int16 typlen, char typstorage)
+{
+ Size data_length;
+
+ if (typbyval)
+ {
+ /* pass-by-value */
+ ptr = (char *) att_align_nominal(ptr, typalign);
+ store_att_byval(ptr, datum, typlen);
+ data_length = typlen;
+ }
+ else if (typlen == -1)
+ {
+ /* varlena */
+ Pointer val = DatumGetPointer(datum);
+
+ if (VARATT_IS_EXTERNAL(val))
+ {
+ /* no alignment, since it's short by definition */
+ data_length = VARSIZE_EXTERNAL(val);
+ memcpy(ptr, val, data_length);
+ }
+ else if (VARATT_IS_SHORT(val))
+ {
+ /* no alignment for short varlenas */
+ data_length = VARSIZE_SHORT(val);
+ memcpy(ptr, val, data_length);
+ }
+ else if (TYPE_IS_PACKABLE(typlen, typstorage) &&
+ VARATT_CAN_MAKE_SHORT(val))
+ {
+ /* convert to short varlena -- no alignment */
+ data_length = VARATT_CONVERTED_SHORT_SIZE(val);
+ SET_VARSIZE_SHORT(ptr, data_length);
+ memcpy(ptr + 1, VARDATA(val), data_length - 1);
+ }
+ else
+ {
+ /* full 4-byte header varlena */
+ ptr = (char *) att_align_nominal(ptr, typalign);
+ data_length = VARSIZE(val);
+ memcpy(ptr, val, data_length);
+ }
+ }
+ else if (typlen == -2)
+ {
+ /* cstring ... never needs alignment */
+ Assert(typalign == 'c');
+ data_length = strlen(DatumGetCString(datum)) + 1;
+ memcpy(ptr, DatumGetPointer(datum), data_length);
+ }
+ else
+ {
+ /* fixed-length pass-by-reference */
+ ptr = (char *) att_align_nominal(ptr, typalign);
+ Assert(typlen > 0);
+ data_length = typlen;
+ memcpy(ptr, DatumGetPointer(datum), data_length);
+ }
+
+ ptr += data_length;
+
+ return ptr;
+}
diff --git a/src/backend/utils/adt/rangetypes_gist.c b/src/backend/utils/adt/rangetypes_gist.c
new file mode 100644
index 00000000000..9dc7fd1b5a5
--- /dev/null
+++ b/src/backend/utils/adt/rangetypes_gist.c
@@ -0,0 +1,587 @@
+/*-------------------------------------------------------------------------
+ *
+ * rangetypes_gist.c
+ * GiST support for range types.
+ *
+ * Copyright (c) 2006-2011, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/rangetypes_gist.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/gist.h"
+#include "access/skey.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+
+#define RANGESTRAT_EQ 1
+#define RANGESTRAT_NE 2
+#define RANGESTRAT_OVERLAPS 3
+#define RANGESTRAT_CONTAINS_ELEM 4
+#define RANGESTRAT_ELEM_CONTAINED_BY 5
+#define RANGESTRAT_CONTAINS 6
+#define RANGESTRAT_CONTAINED_BY 7
+#define RANGESTRAT_BEFORE 8
+#define RANGESTRAT_AFTER 9
+#define RANGESTRAT_OVERLEFT 10
+#define RANGESTRAT_OVERRIGHT 11
+#define RANGESTRAT_ADJACENT 12
+
+static RangeType *range_super_union(FunctionCallInfo fcinfo, RangeType *r1,
+ RangeType *r2);
+static bool range_gist_consistent_int(FunctionCallInfo fcinfo,
+ StrategyNumber strategy, RangeType *key,
+ RangeType *query);
+static bool range_gist_consistent_leaf(FunctionCallInfo fcinfo,
+ StrategyNumber strategy, RangeType *key,
+ RangeType *query);
+static int sort_item_cmp(const void *a, const void *b);
+
+/*
+ * Auxiliary structure for picksplit method.
+ */
+typedef struct
+{
+ int index;
+ RangeType *data;
+ FunctionCallInfo fcinfo;
+} PickSplitSortItem;
+
+
+Datum
+range_gist_consistent(PG_FUNCTION_ARGS)
+{
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ Datum dquery = PG_GETARG_DATUM(1);
+ StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+ /* Oid subtype = PG_GETARG_OID(3); */
+ bool *recheck = (bool *) PG_GETARG_POINTER(4);
+ RangeType *key = DatumGetRangeType(entry->key);
+ RangeType *query;
+
+ RangeBound lower;
+ RangeBound upper;
+ bool empty;
+ Oid rngtypid;
+
+ *recheck = false;
+ range_deserialize(fcinfo, key, &lower, &upper, &empty);
+ rngtypid = lower.rngtypid;
+
+ switch (strategy)
+ {
+ RangeBound lower;
+ RangeBound upper;
+
+ /*
+ * For contains and contained by operators, the other operand is a
+ * "point" of the subtype. Construct a singleton range containing just
+ * that value.
+ */
+ case RANGESTRAT_CONTAINS_ELEM:
+ case RANGESTRAT_ELEM_CONTAINED_BY:
+ lower.rngtypid = rngtypid;
+ lower.inclusive = true;
+ lower.val = dquery;
+ lower.lower = true;
+ lower.infinite = false;
+ upper.rngtypid = rngtypid;
+ upper.inclusive = true;
+ upper.val = dquery;
+ upper.lower = false;
+ upper.infinite = false;
+ query = DatumGetRangeType(
+ make_range(fcinfo, &lower, &upper, false));
+ break;
+
+ default:
+ query = DatumGetRangeType(dquery);
+ break;
+ }
+
+ if (GIST_LEAF(entry))
+ PG_RETURN_BOOL(range_gist_consistent_leaf(
+ fcinfo, strategy, key, query));
+ else
+ PG_RETURN_BOOL(range_gist_consistent_int(
+ fcinfo, strategy, key, query));
+}
+
+Datum
+range_gist_union(PG_FUNCTION_ARGS)
+{
+ GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+ GISTENTRY *ent = entryvec->vector;
+ RangeType *result_range;
+ int i;
+
+ result_range = DatumGetRangeType(ent[0].key);
+
+ for (i = 1; i < entryvec->n; i++)
+ {
+ result_range = range_super_union(fcinfo, result_range,
+ DatumGetRangeType(ent[i].key));
+ }
+
+ PG_RETURN_RANGE(result_range);
+}
+
+Datum
+range_gist_compress(PG_FUNCTION_ARGS)
+{
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ PG_RETURN_POINTER(entry);
+}
+
+Datum
+range_gist_decompress(PG_FUNCTION_ARGS)
+{
+ GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ PG_RETURN_POINTER(entry);
+}
+
+Datum
+range_gist_penalty(PG_FUNCTION_ARGS)
+{
+ GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
+ float *penalty = (float *) PG_GETARG_POINTER(2);
+ RangeType *orig = DatumGetRangeType(origentry->key);
+ RangeType *new = DatumGetRangeType(newentry->key);
+ RangeType *s_union = range_super_union(fcinfo, orig, new);
+
+ FmgrInfo *subtype_diff;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ float lower_diff, upper_diff;
+
+ RangeTypeInfo rngtypinfo;
+
+ range_deserialize(fcinfo, orig, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, s_union, &lower2, &upper2, &empty2);
+
+ range_gettypinfo(fcinfo, lower1.rngtypid, &rngtypinfo);
+ subtype_diff = &rngtypinfo.subdiffFn;
+
+ Assert(empty1 || !empty2);
+
+ if (empty1 && empty2)
+ return 0;
+ else if (empty1 && !empty2)
+ {
+ if (lower2.infinite || upper2.infinite)
+ /* from empty to infinite */
+ return get_float8_infinity();
+ else if (subtype_diff->fn_addr != NULL)
+ /* from empty to upper2-lower2 */
+ return DatumGetFloat8(FunctionCall2(subtype_diff,
+ upper2.val, lower2.val));
+ else
+ /* wild guess */
+ return 1.0;
+ }
+
+ Assert(lower2.infinite || !lower1.infinite);
+
+ if (lower2.infinite && !lower1.infinite)
+ lower_diff = get_float8_infinity();
+ else if (lower2.infinite && lower1.infinite)
+ lower_diff = 0;
+ else if (subtype_diff->fn_addr != NULL)
+ {
+ lower_diff = DatumGetFloat8(FunctionCall2(subtype_diff,
+ 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);
+
+ Assert(upper2.infinite || !upper1.infinite);
+
+ if (upper2.infinite && !upper1.infinite)
+ upper_diff = get_float8_infinity();
+ else if (upper2.infinite && upper1.infinite)
+ upper_diff = 0;
+ else if (subtype_diff->fn_addr != NULL)
+ {
+ upper_diff = DatumGetFloat8(FunctionCall2(subtype_diff,
+ 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);
+
+ Assert(lower_diff >= 0 && upper_diff >= 0);
+
+ *penalty = (float) (lower_diff + upper_diff);
+ PG_RETURN_POINTER(penalty);
+}
+
+/*
+ * The GiST PickSplit method for ranges
+ *
+ * Algorithm based on sorting. Incoming array of periods is sorted using
+ * sort_item_cmp function. After that first half of periods goes to the left
+ * datum, and the second half of periods goes to the right datum.
+ */
+Datum
+range_gist_picksplit(PG_FUNCTION_ARGS)
+{
+ GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
+ GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
+ OffsetNumber i;
+ RangeType *pred_left;
+ RangeType *pred_right;
+ PickSplitSortItem *sortItems;
+ int nbytes;
+ OffsetNumber split_idx;
+ OffsetNumber *left;
+ OffsetNumber *right;
+ OffsetNumber maxoff;
+
+ 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);
+
+ /*
+ * Preparing auxiliary array and sorting.
+ */
+ 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;
+ }
+ qsort(sortItems, maxoff, sizeof(PickSplitSortItem), sort_item_cmp);
+ split_idx = maxoff / 2;
+
+ left = v->spl_left;
+ v->spl_nleft = 0;
+ right = v->spl_right;
+ v->spl_nright = 0;
+
+ /*
+ * First half of segs goes to the left datum.
+ */
+ pred_left = DatumGetRangeType(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));
+ *left++ = sortItems[i].index;
+ v->spl_nleft++;
+ }
+
+ /*
+ * Second half of segs goes to the right datum.
+ */
+ pred_right = DatumGetRangeType(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));
+ *right++ = sortItems[i].index;
+ v->spl_nright++;
+ }
+
+ *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */
+
+ v->spl_ldatum = RangeTypeGetDatum(pred_left);
+ v->spl_rdatum = RangeTypeGetDatum(pred_right);
+
+ PG_RETURN_POINTER(v);
+}
+
+Datum
+range_gist_same(PG_FUNCTION_ARGS)
+{
+ 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));
+ PG_RETURN_POINTER(result);
+}
+
+/*
+ *----------------------------------------------------------
+ * STATIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/* return the smallest range that contains r1 and r2 */
+static RangeType *
+range_super_union(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
+{
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+ RangeBound *result_lower;
+ RangeBound *result_upper;
+
+ range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+
+ if (empty1)
+ return r2;
+ if (empty2)
+ return r1;
+
+ if (range_cmp_bounds(fcinfo, &lower1, &lower2) <= 0)
+ result_lower = &lower1;
+ else
+ result_lower = &lower2;
+
+ if (range_cmp_bounds(fcinfo, &upper1, &upper2) >= 0)
+ result_upper = &upper1;
+ else
+ result_upper = &upper2;
+
+ /* optimization to avoid constructing a new range */
+ if (result_lower == &lower1 && result_upper == &upper1)
+ return r1;
+ if (result_lower == &lower2 && result_upper == &upper2)
+ return r2;
+
+ return DatumGetRangeType(
+ make_range(fcinfo, result_lower, result_upper, false));
+}
+
+static bool
+range_gist_consistent_int(FunctionCallInfo fcinfo, StrategyNumber strategy,
+ RangeType *key, RangeType *query)
+{
+ Oid proc = InvalidOid;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ bool retval;
+ bool negate = false;
+
+ range_deserialize(fcinfo, key, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, query, &lower2, &upper2, &empty2);
+
+ switch (strategy)
+ {
+ case RANGESTRAT_EQ:
+ proc = F_RANGE_CONTAINS;
+ break;
+ case RANGESTRAT_NE:
+ return true;
+ break;
+ case RANGESTRAT_OVERLAPS:
+ proc = F_RANGE_OVERLAPS;
+ break;
+ case RANGESTRAT_CONTAINS_ELEM:
+ case RANGESTRAT_CONTAINS:
+ proc = F_RANGE_CONTAINS;
+ break;
+ case RANGESTRAT_ELEM_CONTAINED_BY:
+ case RANGESTRAT_CONTAINED_BY:
+ return true;
+ break;
+ case RANGESTRAT_BEFORE:
+ if (empty1)
+ return false;
+ proc = F_RANGE_OVERRIGHT;
+ negate = true;
+ break;
+ case RANGESTRAT_AFTER:
+ if (empty1)
+ return false;
+ proc = F_RANGE_OVERLEFT;
+ negate = true;
+ break;
+ case RANGESTRAT_OVERLEFT:
+ if (empty1)
+ return false;
+ proc = F_RANGE_AFTER;
+ negate = true;
+ break;
+ case RANGESTRAT_OVERRIGHT:
+ if (empty1)
+ return false;
+ proc = F_RANGE_BEFORE;
+ negate = true;
+ break;
+ case RANGESTRAT_ADJACENT:
+ if (empty1 || empty2)
+ return false;
+ if (DatumGetBool(
+ OidFunctionCall2(F_RANGE_ADJACENT,
+ RangeTypeGetDatum(key),
+ RangeTypeGetDatum(query))))
+ return true;
+ proc = F_RANGE_OVERLAPS;
+ break;
+ }
+
+ retval = DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key),
+ RangeTypeGetDatum(query)));
+
+ if (negate)
+ retval = !retval;
+
+ PG_RETURN_BOOL(retval);
+}
+
+static bool
+range_gist_consistent_leaf(FunctionCallInfo fcinfo, StrategyNumber strategy,
+ RangeType *key, RangeType *query)
+{
+ Oid proc = InvalidOid;
+
+ RangeBound lower1, lower2;
+ RangeBound upper1, upper2;
+ bool empty1, empty2;
+
+ range_deserialize(fcinfo, key, &lower1, &upper1, &empty1);
+ range_deserialize(fcinfo, query, &lower2, &upper2, &empty2);
+
+ switch (strategy)
+ {
+ case RANGESTRAT_EQ:
+ proc = F_RANGE_EQ;
+ break;
+ case RANGESTRAT_NE:
+ proc = F_RANGE_NE;
+ break;
+ case RANGESTRAT_OVERLAPS:
+ proc = F_RANGE_OVERLAPS;
+ break;
+ case RANGESTRAT_CONTAINS_ELEM:
+ case RANGESTRAT_CONTAINS:
+ proc = F_RANGE_CONTAINS;
+ break;
+ case RANGESTRAT_ELEM_CONTAINED_BY:
+ case RANGESTRAT_CONTAINED_BY:
+ proc = F_RANGE_CONTAINED_BY;
+ break;
+ case RANGESTRAT_BEFORE:
+ if (empty1 || empty2)
+ return false;
+ proc = F_RANGE_BEFORE;
+ break;
+ case RANGESTRAT_AFTER:
+ if (empty1 || empty2)
+ return false;
+ proc = F_RANGE_AFTER;
+ break;
+ case RANGESTRAT_OVERLEFT:
+ if (empty1 || empty2)
+ return false;
+ proc = F_RANGE_OVERLEFT;
+ break;
+ case RANGESTRAT_OVERRIGHT:
+ if (empty1 || empty2)
+ return false;
+ proc = F_RANGE_OVERRIGHT;
+ break;
+ case RANGESTRAT_ADJACENT:
+ if (empty1 || empty2)
+ return false;
+ proc = F_RANGE_ADJACENT;
+ break;
+ }
+
+ return DatumGetBool(OidFunctionCall2(proc, RangeTypeGetDatum(key),
+ RangeTypeGetDatum(query)));
+}
+
+/*
+ * Compare function for PickSplitSortItem. This is actually the
+ * interesting part of the picksplit algorithm.
+ *
+ * We want to separate out empty ranges, bounded ranges, and unbounded
+ * ranges. We assume that "contains" and "overlaps" are the most
+ * important queries, so empty ranges will rarely match and unbounded
+ * ranges frequently will. Bounded ranges should be in the middle.
+ *
+ * Empty ranges we push all the way to the left, then bounded ranges
+ * (sorted on lower bound, then upper), then ranges with no lower
+ * bound, then ranges with no upper bound; and finally, ranges with no
+ * upper or lower bound all the way to the right.
+ */
+static int
+sort_item_cmp(const void *a, const void *b)
+{
+ PickSplitSortItem *i1 = (PickSplitSortItem *)a;
+ PickSplitSortItem *i2 = (PickSplitSortItem *)b;
+ RangeType *r1 = i1->data;
+ RangeType *r2 = i2->data;
+
+ 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);
+
+ if (empty1 || empty2)
+ {
+ if (empty1 && empty2)
+ return 0;
+ else if (empty1)
+ return -1;
+ else if (empty2)
+ return 1;
+ else
+ Assert(false);
+ }
+
+ /*
+ * If both lower or both upper bounds are infinite, we sort by
+ * ascending range size. That means that if both upper bounds are
+ * infinite, we sort by the lower bound _descending_. That creates
+ * a slightly odd total order, but keeps the pages with very
+ * unselective predicates grouped more closely together on the
+ * right.
+ */
+ if (lower1.infinite || upper1.infinite ||
+ lower2.infinite || upper2.infinite)
+ {
+ if (lower1.infinite && lower2.infinite)
+ return range_cmp_bounds(fcinfo, &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);
+ else if (upper1.infinite)
+ return 1;
+ else if (upper2.infinite)
+ return -1;
+ else
+ Assert(false);
+ }
+
+ if ((cmp = range_cmp_bounds(fcinfo, &lower1, &lower2)) != 0)
+ return cmp;
+
+ return range_cmp_bounds(fcinfo, &upper1, &upper2);
+}
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 326f1eee92d..1b4d26d6593 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -26,6 +26,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
@@ -2251,6 +2252,16 @@ type_is_enum(Oid typid)
}
/*
+ * type_is_range
+ * Returns true if the given type is an range type.
+ */
+bool
+type_is_range(Oid typid)
+{
+ return (get_typtype(typid) == TYPTYPE_RANGE);
+}
+
+/*
* get_type_category_preferred
*
* Given the type OID, fetch its category and preferred-type status.
@@ -2855,3 +2866,22 @@ get_namespace_name(Oid nspid)
else
return NULL;
}
+
+Oid
+get_range_subtype(Oid rangeOid)
+{
+ HeapTuple tp;
+
+ tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+ if (HeapTupleIsValid(tp))
+ {
+ Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+ Oid result;
+
+ result = rngtup->rngsubtype;
+ ReleaseSysCache(tp);
+ return result;
+ }
+ else
+ return InvalidOid;
+}
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 99e5f1d9fe6..71b09abb232 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -43,6 +43,7 @@
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
+#include "catalog/pg_range.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_tablespace.h"
@@ -554,6 +555,17 @@ static const struct cachedesc cacheinfo[] = {
},
2048
},
+ {RangeRelationId, /* RANGETYPE */
+ RangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngtypid,
+ 0,
+ 0,
+ 0
+ },
+ 1024
+ },
{RelationRelationId, /* RELNAMENSP */
ClassNameNspIndexId,
2,
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 0911c8083be..3cc2a7ee075 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -407,11 +407,13 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
int nargs = declared_args->dim1;
bool have_anyelement_result = false;
bool have_anyarray_result = false;
+ bool have_anyrange_result = false;
bool have_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
- Oid anycollation;
+ Oid anyrange_type = InvalidOid;
+ Oid anycollation = InvalidOid;
int i;
/* See if there are any polymorphic outputs; quick out if not */
@@ -433,11 +435,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_anyelement_result = true;
have_anyenum = true;
break;
+ case ANYRANGEOID:
+ have_anyrange_result = true;
+ break;
default:
break;
}
}
- if (!have_anyelement_result && !have_anyarray_result)
+ if (!have_anyelement_result && !have_anyarray_result &&
+ !have_anyrange_result)
return true;
/*
@@ -461,20 +467,47 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyarray_type))
anyarray_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYRANGEOID:
+ if (!OidIsValid(anyrange_type))
+ anyrange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
}
/* If nothing found, parser messed up */
- if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
+ if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
+ !OidIsValid(anyrange_type))
+ return false;
+
+ /*
+ * We can't deduce a range type from the subtype, because there may be
+ * multiple range types for a single subtype.
+ */
+ if (have_anyrange_result && !OidIsValid(anyrange_type))
return false;
/* If needed, deduce one polymorphic type from the other */
if (have_anyelement_result && !OidIsValid(anyelement_type))
- anyelement_type = resolve_generic_type(ANYELEMENTOID,
- anyarray_type,
- ANYARRAYOID);
+ {
+ if (OidIsValid(anyarray_type))
+ anyelement_type = resolve_generic_type(ANYELEMENTOID,
+ anyarray_type,
+ ANYARRAYOID);
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+ if (OidIsValid(anyelement_type) &&
+ anyelement_type != subtype)
+ return false;
+ else
+ anyelement_type = subtype;
+ }
+ }
+
if (have_anyarray_result && !OidIsValid(anyarray_type))
anyarray_type = resolve_generic_type(ANYARRAYOID,
anyelement_type,
@@ -492,7 +525,12 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
* Identify the collation to use for polymorphic OUT parameters. (It'll
* necessarily be the same for both anyelement and anyarray.)
*/
- anycollation = get_typcollation(OidIsValid(anyelement_type) ? anyelement_type : anyarray_type);
+
+ if (OidIsValid(anyelement_type))
+ anycollation = get_typcollation(anyelement_type);
+ else if (OidIsValid(anyarray_type))
+ anycollation = get_typcollation(anyarray_type);
+
if (OidIsValid(anycollation))
{
/*
@@ -529,6 +567,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
break;
+ case ANYRANGEOID:
+ TupleDescInitEntry(tupdesc, i + 1,
+ NameStr(tupdesc->attrs[i]->attname),
+ anyrange_type,
+ -1,
+ 0);
+ TupleDescInitEntryCollation(tupdesc, i + 1, anycollation);
+ break;
default:
break;
}
@@ -552,8 +598,10 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
{
bool have_anyelement_result = false;
bool have_anyarray_result = false;
+ bool have_anyrange_result = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
+ Oid anyrange_type = InvalidOid;
int inargno;
int i;
@@ -597,6 +645,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyarray_type;
}
break;
+ case ANYRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anyrange_result = true;
+ else
+ {
+ if (!OidIsValid(anyrange_type))
+ {
+ anyrange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anyrange_type))
+ return false;
+ }
+ argtypes[i] = anyrange_type;
+ }
+ break;
default:
break;
}
@@ -605,18 +668,42 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
}
/* Done? */
- if (!have_anyelement_result && !have_anyarray_result)
+ if (!have_anyelement_result && !have_anyarray_result &&
+ !have_anyrange_result)
return true;
+ /*
+ * We can't deduce a range type from the subtype, because there may be
+ * multiple range types for a single subtype.
+ */
+ if (have_anyrange_result && !OidIsValid(anyrange_type))
+ return false;
+
/* If no input polymorphics, parser messed up */
- if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type))
+ if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
+ !OidIsValid(anyrange_type))
return false;
/* If needed, deduce one polymorphic type from the other */
if (have_anyelement_result && !OidIsValid(anyelement_type))
- anyelement_type = resolve_generic_type(ANYELEMENTOID,
- anyarray_type,
- ANYARRAYOID);
+ {
+ if (OidIsValid(anyarray_type))
+ anyelement_type = resolve_generic_type(ANYELEMENTOID,
+ anyarray_type,
+ ANYARRAYOID);
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+ if (OidIsValid(anyelement_type) &&
+ anyelement_type != subtype)
+ return false;
+ else
+ anyelement_type = subtype;
+ }
+ }
+
if (have_anyarray_result && !OidIsValid(anyarray_type))
anyarray_type = resolve_generic_type(ANYARRAYOID,
anyelement_type,
@@ -637,6 +724,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYARRAYOID:
argtypes[i] = anyarray_type;
break;
+ case ANYRANGEOID:
+ argtypes[i] = anyrange_type;
+ break;
default:
break;
}
@@ -663,6 +753,7 @@ get_type_func_class(Oid typid)
case TYPTYPE_BASE:
case TYPTYPE_DOMAIN:
case TYPTYPE_ENUM:
+ case TYPTYPE_RANGE:
return TYPEFUNC_SCALAR;
case TYPTYPE_PSEUDO:
if (typid == RECORDOID)