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