Oid typoid;
Oid rangeArrayOid;
List *parameters = stmt->params;
-
- ListCell *lc;
List *rangeSubOpclassName = NIL;
List *rangeSubtypeDiffName = NIL;
List *rangeCollationName = NIL;
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,
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 */
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 */
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 */
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 */
pfree(rangeArrayName);
+ /* And create the constructor functions for this range type */
makeRangeConstructor(typeName, typeNamespace, typoid, rangeSubtype);
}
#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);
/* 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);
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);
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;
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;
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)
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 */
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 */
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 */
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 */
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 */
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 */
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 */
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);
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));
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 */
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;
*/
/*
- * 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,
(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;
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)
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;
}
range_gettypinfo(fcinfo, lower->rngtypid, &rngtypinfo);
- if (lower->rngtypid != upper->rngtypid)
- elog(ERROR, "range types do not match");
-
range = range_serialize(fcinfo, lower, upper, empty);
if (rngtypinfo.canonicalFn.fn_addr != NULL)
range_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)
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)
}
/*
- * 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,
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))
{