aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/commands/typecmds.c34
-rw-r--r--src/backend/utils/adt/rangetypes.c219
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_type.h18
-rw-r--r--src/test/regress/expected/rangetypes.out10
-rw-r--r--src/test/regress/expected/type_sanity.out32
-rw-r--r--src/test/regress/sql/rangetypes.sql6
-rw-r--r--src/test/regress/sql/type_sanity.sql24
8 files changed, 215 insertions, 130 deletions
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index c2f1160e1f5..a1628bc0985 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -1167,8 +1167,6 @@ DefineRange(CreateRangeStmt *stmt)
Oid typoid;
Oid rangeArrayOid;
List *parameters = stmt->params;
-
- ListCell *lc;
List *rangeSubOpclassName = NIL;
List *rangeSubtypeDiffName = NIL;
List *rangeCollationName = NIL;
@@ -1178,8 +1176,12 @@ DefineRange(CreateRangeStmt *stmt)
regproc rangeSubOpclass = InvalidOid;
regproc rangeCanonical = InvalidOid;
regproc rangeSubtypeDiff = InvalidOid;
-
+ int16 subtyplen;
+ bool subtypbyval;
+ char subtypalign;
+ char alignment;
AclResult aclresult;
+ ListCell *lc;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1314,14 +1316,21 @@ DefineRange(CreateRangeStmt *stmt)
else if (rangeCollationName != NIL)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
- errmsg("range collation provided but subtype does not support collation")));
+ errmsg("range collation specified but subtype does not support collation")));
rangeSubOpclass = findRangeSubOpclass(rangeSubOpclassName, rangeSubtype);
if (rangeSubtypeDiffName != NIL)
- rangeSubtypeDiff = findRangeSubtypeDiffFunction(
- rangeSubtypeDiffName, rangeSubtype);
+ rangeSubtypeDiff = findRangeSubtypeDiffFunction(rangeSubtypeDiffName,
+ rangeSubtype);
+ get_typlenbyvalalign(rangeSubtype,
+ &subtyplen, &subtypbyval, &subtypalign);
+
+ /* alignment must be 'i' or 'd' for ranges */
+ alignment = (subtypalign == 'd') ? 'd' : 'i';
+
+ /* Allocate OID for array type */
rangeArrayOid = AssignTypeArrayOid();
/* Create the pg_type entry */
@@ -1332,7 +1341,7 @@ DefineRange(CreateRangeStmt *stmt)
InvalidOid, /* relation oid (n/a here) */
0, /* relation kind (ditto) */
GetUserId(), /* owner's ID */
- -1, /* internal size */
+ -1, /* internal size (always varlena) */
TYPTYPE_RANGE, /* type-type (range type) */
TYPCATEGORY_RANGE, /* type-category (range type) */
false, /* range types are never preferred */
@@ -1343,16 +1352,16 @@ DefineRange(CreateRangeStmt *stmt)
F_RANGE_SEND, /* send procedure */
InvalidOid, /* typmodin procedure - none */
InvalidOid, /* typmodout procedure - none */
- rangeAnalyze, /* analyze procedure - default */
- InvalidOid, /* element type ID */
+ rangeAnalyze, /* analyze procedure */
+ InvalidOid, /* element type ID - none */
false, /* this is not an array type */
rangeArrayOid, /* array type we are about to create */
InvalidOid, /* base type ID (only for domains) */
NULL, /* never a default type value */
NULL, /* binary default isn't sent either */
false, /* never passed by value */
- 'i', /* int alignment */
- 'x', /* TOAST strategy always plain */
+ alignment, /* alignment */
+ 'x', /* TOAST strategy (always extended) */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
@@ -1392,7 +1401,7 @@ DefineRange(CreateRangeStmt *stmt)
NULL, /* never a default type value */
NULL, /* binary default isn't sent either */
false, /* never passed by value */
- 'i', /* align 'i' */
+ alignment, /* alignment - same as range's */
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
@@ -1401,6 +1410,7 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
+ /* And create the constructor functions for this range type */
makeRangeConstructor(typeName, typeNamespace, typoid, rangeSubtype);
}
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index de9b9a5efb4..fd5d7810380 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -33,29 +33,26 @@
#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_NULL 0x04 /* NOT CURRENTLY USED */
#define RANGE_LB_INF 0x08
#define RANGE_UB_INC 0x10
-#define RANGE_UB_NULL 0x20 /* NOT USED */
+#define RANGE_UB_NULL 0x20 /* NOT CURRENTLY USED */
#define RANGE_UB_INF 0x40
-#define RANGE_HAS_LBOUND(flags) (!(flags & (RANGE_EMPTY | \
- RANGE_LB_NULL | \
- RANGE_LB_INF)))
+#define RANGE_HAS_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_HAS_UBOUND(flags) (!((flags) & (RANGE_EMPTY | \
+ RANGE_UB_NULL | \
+ RANGE_UB_INF)))
#define RANGE_EMPTY_LITERAL "empty"
-#define RANGE_DEFAULT_FLAGS "[)"
+#define RANGE_DEFAULT_FLAGS "[)"
static char range_parse_flags(const char *flags_str);
@@ -151,18 +148,15 @@ range_out(PG_FUNCTION_ARGS)
/* 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;
+ 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);
@@ -280,10 +274,10 @@ range_send(PG_FUNCTION_ARGS)
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;
+ 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);
@@ -395,13 +389,13 @@ range_constructor2(PG_FUNCTION_ARGS)
lower.rngtypid = rngtypid;
lower.val = PG_ARGISNULL(0) ? (Datum) 0 : arg1;
- lower.inclusive = flags & RANGE_LB_INC;
+ lower.inclusive = (flags & RANGE_LB_INC) != 0;
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.inclusive = (flags & RANGE_UB_INC) != 0;
upper.infinite = PG_ARGISNULL(1);
upper.lower = false;
@@ -430,13 +424,13 @@ range_constructor3(PG_FUNCTION_ARGS)
lower.rngtypid = rngtypid;
lower.val = PG_ARGISNULL(0) ? (Datum) 0 : arg1;
- lower.inclusive = flags & RANGE_LB_INC;
+ lower.inclusive = (flags & RANGE_LB_INC) != 0;
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.inclusive = (flags & RANGE_UB_INC) != 0;
upper.infinite = PG_ARGISNULL(1);
upper.lower = false;
@@ -564,9 +558,7 @@ range_eq(PG_FUNCTION_ARGS)
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)
+ if (lower1.rngtypid != lower2.rngtypid)
elog(ERROR, "range types do not match");
if (empty1 && empty2)
@@ -686,9 +678,7 @@ range_before(PG_FUNCTION_ARGS)
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)
+ if (lower1.rngtypid != lower2.rngtypid)
elog(ERROR, "range types do not match");
/* An empty range is neither before nor after any other range */
@@ -713,9 +703,7 @@ range_after(PG_FUNCTION_ARGS)
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)
+ if (lower1.rngtypid != lower2.rngtypid)
elog(ERROR, "range types do not match");
/* An empty range is neither before nor after any other range */
@@ -741,9 +729,7 @@ range_adjacent(PG_FUNCTION_ARGS)
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)
+ if (lower1.rngtypid != lower2.rngtypid)
elog(ERROR, "range types do not match");
/* An empty range is not adjacent to any other range */
@@ -795,9 +781,7 @@ range_overlaps(PG_FUNCTION_ARGS)
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)
+ if (lower1.rngtypid != lower2.rngtypid)
elog(ERROR, "range types do not match");
/* An empty range does not overlap any other range */
@@ -830,9 +814,7 @@ range_overleft(PG_FUNCTION_ARGS)
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)
+ if (lower1.rngtypid != lower2.rngtypid)
elog(ERROR, "range types do not match");
/* An empty range is neither before nor after any other range */
@@ -860,9 +842,7 @@ range_overright(PG_FUNCTION_ARGS)
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)
+ if (lower1.rngtypid != lower2.rngtypid)
elog(ERROR, "range types do not match");
/* An empty range is neither before nor after any other range */
@@ -896,9 +876,7 @@ range_minus(PG_FUNCTION_ARGS)
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)
+ if (lower1.rngtypid != lower2.rngtypid)
elog(ERROR, "range types do not match");
/* if either is empty, r1 is the correct answer */
@@ -956,6 +934,9 @@ range_union(PG_FUNCTION_ARGS)
range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+ if (lower1.rngtypid != lower2.rngtypid)
+ elog(ERROR, "range types do not match");
+
/* if either is empty, the other is the correct answer */
if (empty1)
PG_RETURN_RANGE(r2);
@@ -998,6 +979,9 @@ range_intersect(PG_FUNCTION_ARGS)
range_deserialize(fcinfo, r1, &lower1, &upper1, &empty1);
range_deserialize(fcinfo, r2, &lower2, &upper2, &empty2);
+ if (lower1.rngtypid != lower2.rngtypid)
+ elog(ERROR, "range types do not match");
+
if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
PG_RETURN_RANGE(make_empty_range(fcinfo, lower1.rngtypid));
@@ -1032,9 +1016,7 @@ range_cmp(PG_FUNCTION_ARGS)
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)
+ if (lower1.rngtypid != lower2.rngtypid)
elog(ERROR, "range types do not match");
/* For b-tree use, empty ranges sort before all else */
@@ -1103,16 +1085,13 @@ hash_range(PG_FUNCTION_ARGS)
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;
+ 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;
@@ -1370,8 +1349,10 @@ tstzrange_subdiff(PG_FUNCTION_ARGS)
*/
/*
- * This serializes a range, but does not canonicalize it. This should
- * only be called by a canonicalization function.
+ * range_serialize: construct a range value from bounds and empty-flag
+ *
+ * This does not force canonicalization of the range value. In most cases,
+ * external callers should only be canonicalization functions.
*/
Datum
range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
@@ -1404,28 +1385,45 @@ range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
(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;
+ 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))
{
+ /*
+ * Make sure item to be inserted is not toasted. It is essential that
+ * we not insert an out-of-line toast value pointer into a range
+ * object, for the same reasons that arrays and records can't contain
+ * them. It would work to store a compressed-in-line value, but we
+ * prefer to decompress and then let compression be applied to the
+ * whole range object if necessary. But, unlike arrays, we do allow
+ * short-header varlena objects to stay as-is.
+ */
+ if (typlen == -1)
+ lower->val = PointerGetDatum(PG_DETOAST_DATUM_PACKED(lower->val));
+
msize = datum_compute_size(msize, lower->val, typbyval, typalign,
typlen, typstorage);
}
if (RANGE_HAS_UBOUND(flags))
{
+ /* Make sure item to be inserted is not toasted */
+ if (typlen == -1)
+ upper->val = PointerGetDatum(PG_DETOAST_DATUM_PACKED(upper->val));
+
msize = datum_compute_size(msize, upper->val, typbyval, typalign,
typlen, typstorage);
}
msize += sizeof(char);
+ /* Note: zero-fill is required here, just as in heap tuples */
ptr = palloc0(msize);
range = (Datum) ptr;
@@ -1455,6 +1453,15 @@ range_serialize(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
PG_RETURN_RANGE(range);
}
+/*
+ * range_deserialize: deconstruct a range value
+ *
+ * NB: the given range object must be fully detoasted; it cannot have a
+ * short varlena header.
+ *
+ * Note that if the element type is pass-by-reference, the datums in the
+ * RangeBound structs will be pointers into the given range object.
+ */
void
range_deserialize(FunctionCallInfo fcinfo, RangeType *range, RangeBound *lower,
RangeBound *upper, bool *empty)
@@ -1467,64 +1474,55 @@ range_deserialize(FunctionCallInfo fcinfo, RangeType *range, RangeBound *lower,
Oid rngtypid;
Datum lbound;
Datum ubound;
- Pointer flags_ptr;
RangeTypeInfo rngtypinfo;
- memset(lower, 0, sizeof(RangeBound));
- memset(upper, 0, sizeof(RangeBound));
+ /* fetch the flag byte from datum's last byte */
+ flags = *((char *) (ptr + VARSIZE(range) - VARHDRSZ - 1));
- /* 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));
+ /* fetch and advance over the range type OID */
+ rngtypid = *((Oid *) ptr);
ptr += sizeof(Oid);
- if (rngtypid == ANYRANGEOID)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("cannot output a value of type anyrange")));
-
+ /* fetch information about range type */
range_gettypinfo(fcinfo, rngtypid, &rngtypinfo);
-
typalign = rngtypinfo.subtypalign;
typlen = rngtypinfo.subtyplen;
typbyval = rngtypinfo.subtypbyval;
+ /* fetch lower bound, if any */
if (RANGE_HAS_LBOUND(flags))
{
- ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
+ /* att_align_pointer cannot be necessary here */
lbound = fetch_att(ptr, typbyval, typlen);
- ptr = (Pointer) att_addlength_datum(ptr, typlen, PointerGetDatum(ptr));
- if (typlen == -1)
- lbound = PointerGetDatum(PG_DETOAST_DATUM(lbound));
+ ptr = (Pointer) att_addlength_pointer(ptr, typlen, ptr);
}
else
lbound = (Datum) 0;
+ /* fetch upper bound, if any */
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));
+ /* no need for att_addlength_pointer */
}
else
ubound = (Datum) 0;
+ /* emit results */
+
*empty = flags & RANGE_EMPTY;
lower->rngtypid = rngtypid;
lower->val = lbound;
- lower->inclusive = flags & RANGE_LB_INC;
- lower->infinite = flags & RANGE_LB_INF;
+ lower->inclusive = (flags & RANGE_LB_INC) != 0;
+ lower->infinite = (flags & RANGE_LB_INF) != 0;
lower->lower = true;
upper->rngtypid = rngtypid;
upper->val = ubound;
- upper->inclusive = flags & RANGE_UB_INC;
- upper->infinite = flags & RANGE_UB_INF;
+ upper->inclusive = (flags & RANGE_UB_INC) != 0;
+ upper->infinite = (flags & RANGE_UB_INF) != 0;
upper->lower = false;
}
@@ -1541,9 +1539,6 @@ make_range(FunctionCallInfo fcinfo, RangeBound *lower, RangeBound *upper,
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)
@@ -2033,9 +2028,7 @@ range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
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)
+ if (lower1.rngtypid != lower2.rngtypid)
elog(ERROR, "range types do not match");
if (empty2)
@@ -2051,11 +2044,23 @@ range_contains_internal(FunctionCallInfo fcinfo, RangeType *r1, RangeType *r2)
return true;
}
+
/*
- * datum_compute_size() and datum_write() are modeled after
- * heap_compute_data_size() and heap_fill_tuple().
+ * datum_compute_size() and datum_write() are used to insert the bound
+ * values into a range object. They are modeled after heaptuple.c's
+ * heap_compute_data_size() and heap_fill_tuple(), but we need not handle
+ * null values here. TYPE_IS_PACKABLE must test the same conditions as
+ * heaptuple.c's ATT_IS_PACKABLE macro.
*/
+/* Does datatype allow packing into the 1-byte-header varlena format? */
+#define TYPE_IS_PACKABLE(typlen, typstorage) \
+ ((typlen) == -1 && (typstorage) != 'p')
+
+/*
+ * Increment data_length by the space needed by the datum, including any
+ * preceding alignment padding.
+ */
static Size
datum_compute_size(Size data_length, Datum val, bool typbyval, char typalign,
int16 typlen, char typstorage)
@@ -2079,9 +2084,8 @@ datum_compute_size(Size data_length, Datum val, bool typbyval, char typalign,
}
/*
- * 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.
+ * Write the given datum beginning at ptr (after advancing to correct
+ * alignment, if needed). Return the pointer incremented by space used.
*/
static Pointer
datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
@@ -2103,9 +2107,12 @@ datum_write(Pointer ptr, Datum datum, bool typbyval, char typalign,
if (VARATT_IS_EXTERNAL(val))
{
- /* no alignment, since it's short by definition */
- data_length = VARSIZE_EXTERNAL(val);
- memcpy(ptr, val, data_length);
+ /*
+ * Throw error, because we must never put a toast pointer inside a
+ * range object. Caller should have detoasted it.
+ */
+ elog(ERROR, "cannot store a toast pointer inside a range");
+ data_length = 0; /* keep compiler quiet */
}
else if (VARATT_IS_SHORT(val))
{
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index ece718d939c..6e2a060a12a 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201111061
+#define CATALOG_VERSION_NO 201111141
#endif
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index b24fbc97f4e..a062d1e2480 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -594,24 +594,24 @@ DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 a
/* range types */
DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
-DESCR("range of int4s");
+DESCR("range of integers");
#define INT4RANGEOID 3904
DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
DESCR("range of numerics");
DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
-DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
-DESCR("range of timestamps");
-DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
-DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - - d x f 0 -1 0 0 _null_ _null_ ));
+DESCR("range of timestamps without time zone");
+DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ ));
+DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - - d x f 0 -1 0 0 _null_ _null_ ));
DESCR("range of timestamps with time zone");
-DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ ));
DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
DESCR("range of dates");
DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
-DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - - i x f 0 -1 0 0 _null_ _null_ ));
-DESCR("range of int8s");
-DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - - i x f 0 -1 0 0 _null_ _null_ ));
+DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - - d x f 0 -1 0 0 _null_ _null_ ));
+DESCR("range of bigints");
+DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - - d x f 0 -1 0 0 _null_ _null_ ));
/*
* pseudo-types
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 9b879486170..b2258b9045c 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -822,7 +822,6 @@ select * from float8range_test;
(1 row)
drop table float8range_test;
-drop type float8range;
--
-- Test range types over domains
--
@@ -909,6 +908,15 @@ select ARRAY[numrange(1.1), numrange(12.3,155.5)];
{"[1.1,1.1]","[12.3,155.5)"}
(1 row)
+create table i8r_array (f1 int, f2 int8range[]);
+insert into i8r_array values (42, array[int8range(1,10), int8range(2,20)]);
+select * from i8r_array;
+ f1 | f2
+----+---------------------
+ 42 | {"[1,10)","[2,20)"}
+(1 row)
+
+drop table i8r_array;
--
-- Ranges of arrays
--
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 7ca7a95ec66..a159ac718a2 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -81,6 +81,28 @@ WHERE p1.typarray <> 0 AND
-----+----------+-----------+---------+--------
(0 rows)
+-- Look for range types that do not have a pg_range entry
+SELECT p1.oid, p1.typname
+FROM pg_type as p1
+WHERE p1.typtype = 'r' AND
+ NOT EXISTS(SELECT 1 FROM pg_range r WHERE rngtypid = p1.oid);
+ oid | typname
+-----+---------
+(0 rows)
+
+-- Look for range types whose typalign isn't sufficient
+SELECT p1.oid, p1.typname, p1.typalign, p2.typname, p2.typalign
+FROM pg_type as p1
+ LEFT JOIN pg_range as r ON rngtypid = p1.oid
+ LEFT JOIN pg_type as p2 ON rngsubtype = p2.oid
+WHERE p1.typtype = 'r' AND
+ (p1.typalign != (CASE WHEN p2.typalign = 'd' THEN 'd'::"char"
+ ELSE 'i'::"char" END)
+ OR p2.oid IS NULL);
+ oid | typname | typalign | typname | typalign
+-----+---------+----------+---------+----------
+(0 rows)
+
-- Text conversion routines must be provided.
SELECT p1.oid, p1.typname
FROM pg_type as p1
@@ -263,6 +285,16 @@ WHERE p1.typarray = p2.oid AND NOT (p1.typdelim = p2.typdelim);
-----+---------+-----+---------
(0 rows)
+-- Look for array types whose typalign isn't sufficient
+SELECT p1.oid, p1.typname, p1.typalign, p2.typname, p2.typalign
+FROM pg_type AS p1, pg_type AS p2
+WHERE p1.typarray = p2.oid AND
+ p2.typalign != (CASE WHEN p1.typalign = 'd' THEN 'd'::"char"
+ ELSE 'i'::"char" END);
+ oid | typname | typalign | typname | typalign
+-----+---------+----------+---------+----------
+(0 rows)
+
-- Check for bogus typanalyze routines
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 573e85ebb25..4b455f1d357 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -260,7 +260,6 @@ create table float8range_test(f8r float8range, i int);
insert into float8range_test values(float8range(-100.00007, '1.111113e9'));
select * from float8range_test;
drop table float8range_test;
-drop type float8range;
--
-- Test range types over domains
@@ -327,6 +326,11 @@ select range_add_bounds(numrange(1.0001, 123.123));
select ARRAY[numrange(1.1), numrange(12.3,155.5)];
+create table i8r_array (f1 int, f2 int8range[]);
+insert into i8r_array values (42, array[int8range(1,10), int8range(2,20)]);
+select * from i8r_array;
+drop table i8r_array;
+
--
-- Ranges of arrays
--
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 1638861bc1d..2ed03f39bc1 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -67,6 +67,22 @@ FROM pg_type p1 LEFT JOIN pg_type p2 ON (p1.typarray = p2.oid)
WHERE p1.typarray <> 0 AND
(p2.oid IS NULL OR p2.typelem <> p1.oid OR p2.typlen <> -1);
+-- Look for range types that do not have a pg_range entry
+SELECT p1.oid, p1.typname
+FROM pg_type as p1
+WHERE p1.typtype = 'r' AND
+ NOT EXISTS(SELECT 1 FROM pg_range r WHERE rngtypid = p1.oid);
+
+-- Look for range types whose typalign isn't sufficient
+SELECT p1.oid, p1.typname, p1.typalign, p2.typname, p2.typalign
+FROM pg_type as p1
+ LEFT JOIN pg_range as r ON rngtypid = p1.oid
+ LEFT JOIN pg_type as p2 ON rngsubtype = p2.oid
+WHERE p1.typtype = 'r' AND
+ (p1.typalign != (CASE WHEN p2.typalign = 'd' THEN 'd'::"char"
+ ELSE 'i'::"char" END)
+ OR p2.oid IS NULL);
+
-- Text conversion routines must be provided.
SELECT p1.oid, p1.typname
@@ -202,6 +218,14 @@ SELECT p1.oid, p1.typname, p2.oid, p2.typname
FROM pg_type AS p1, pg_type AS p2
WHERE p1.typarray = p2.oid AND NOT (p1.typdelim = p2.typdelim);
+-- Look for array types whose typalign isn't sufficient
+
+SELECT p1.oid, p1.typname, p1.typalign, p2.typname, p2.typalign
+FROM pg_type AS p1, pg_type AS p2
+WHERE p1.typarray = p2.oid AND
+ p2.typalign != (CASE WHEN p1.typalign = 'd' THEN 'd'::"char"
+ ELSE 'i'::"char" END);
+
-- Check for bogus typanalyze routines
SELECT p1.oid, p1.typname, p2.oid, p2.proname