Commit ad50934e authored by Tom Lane's avatar Tom Lane

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.
parent 4165d5b6
......@@ -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);
}
......
This diff is collapsed.
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 201111061
#define CATALOG_VERSION_NO 201111141
#endif
......@@ -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
......
......@@ -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
--
......
......@@ -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
......
......@@ -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
--
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment