]> granicus.if.org Git - postgresql/commitdiff
Fix alignment and toasting bugs in range types.
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 15 Nov 2011 02:42:04 +0000 (21:42 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 15 Nov 2011 02:42:04 +0000 (21:42 -0500)
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.

src/backend/commands/typecmds.c
src/backend/utils/adt/rangetypes.c
src/include/catalog/catversion.h
src/include/catalog/pg_type.h
src/test/regress/expected/rangetypes.out
src/test/regress/expected/type_sanity.out
src/test/regress/sql/rangetypes.sql
src/test/regress/sql/type_sanity.sql

index c2f1160e1f582c07bec9d3c41137b3b551590e7f..a1628bc098535d55f0bb7b054151abb5b514917a 100644 (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);
 }
 
index de9b9a5efb4759aba35349435181e4ad8674fda9..fd5d7810380b63e33cbb0169177a224cc357d9f0 100644 (file)
 #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))
                {
index ece718d939c30b99335321225a0b45e8a9084f10..6e2a060a12aae8ca458b0767f0d2a31d728eeec9 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201111061
+#define CATALOG_VERSION_NO     201111141
 
 #endif
index b24fbc97f4e71182a6c9d59bc41244d2cf0b310c..a062d1e24807913f84b8dfb1fa1551622649c5ce 100644 (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
index 9b8794861705ae351df3df5f46140730dd12d52b..b2258b9045c7294d2f40ba5d8925258ea0ff51d0 100644 (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
 --
index 7ca7a95ec66c32d782d7ddb7f4a9eabdd7a31b7e..a159ac718a2703ee53f92fb38f93df25e08f0ff7 100644 (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
index 573e85ebb25ca4c6454716240af8ed27264844eb..4b455f1d35769ea751ad33b5881e9042ee1adb00 100644 (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
 --
index 1638861bc1dd8e270739a7c1191dfa11d2059d9e..2ed03f39bc1b3abbec263e6dd29c1a63a0e7278e 100644 (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