Fix alignment and toasting bugs in range types.

A range type whose element type has 'd' alignment must have 'd' alignment
itself, else there is no guarantee that the element value can be used
in-place.  (Because range_deserialize uses att_align_pointer which forcibly
aligns the given pointer, violations of this rule did not lead to SIGBUS
but rather to garbage data being extracted, as in one of the added
regression test cases.)

Also, you can't put a toast pointer inside a range datum, since the
referenced value could disappear with the range datum still present.
For consistency with the handling of arrays and records, I also forced
decompression of in-line-compressed bound values.  It would work to store
them as-is, but our policy is to avoid situations that might result in
double compression.

Add assorted regression tests for this, and bump catversion because of
fixes to built-in pg_type entries.

Also some marginal cleanup of inconsistent/unnecessary error checks.
This commit is contained in:
Tom Lane 2011-11-14 21:42:04 -05:00
parent 4165d5b6d7
commit ad50934eaa
8 changed files with 215 additions and 130 deletions

View File

@ -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);
}

View File

@ -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))
{

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201111061
#define CATALOG_VERSION_NO 201111141
#endif

View File

@ -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

View File

@ -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
--

View File

@ -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

View File

@ -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
--

View File

@ -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