Commit 766948be authored by Tom Lane's avatar Tom Lane

Still more review for range-types patch.

Per discussion, relax the range input/construction rules so that the
only hard error is lower bound > upper bound.  Cases where the lower
bound is <= upper bound, but the range nonetheless normalizes to empty,
are now permitted.

Fix core dump in range_adjacent when bounds are infinite.  Marginal
cleanup of regression test cases, some more code commenting.
parent 024ea25c
This diff is collapsed.
......@@ -35,8 +35,6 @@
#define RANGESTRAT_EQ 18
#define RANGESTRAT_NE 19
#define RangeIsEmpty(r) (range_get_flags(r) & RANGE_EMPTY)
/*
* Auxiliary structure for picksplit method.
*/
......@@ -58,6 +56,7 @@ static bool range_gist_consistent_leaf(FmgrInfo *flinfo,
static int sort_item_cmp(const void *a, const void *b);
/* GiST query consistency check */
Datum
range_gist_consistent(PG_FUNCTION_ARGS)
{
......@@ -69,8 +68,6 @@ range_gist_consistent(PG_FUNCTION_ARGS)
RangeType *key = DatumGetRangeType(entry->key);
TypeCacheEntry *typcache;
RangeType *query;
RangeBound lower;
RangeBound upper;
/* All operators served by this function are exact */
*recheck = false;
......@@ -78,25 +75,18 @@ range_gist_consistent(PG_FUNCTION_ARGS)
switch (strategy)
{
/*
* For contains and contained by operators, the other operand is a
* "point" of the subtype. Construct a singleton range containing
* just that value.
* For element contains and contained by operators, the other operand
* is a "point" of the subtype. Construct a singleton range
* containing just that value. (Since range_contains_elem and
* elem_contained_by_range would do that anyway, it's actually more
* efficient not less so to merge these cases into range containment
* at this step. But revisit this if we ever change the implementation
* of those functions.)
*/
case RANGESTRAT_CONTAINS_ELEM:
case RANGESTRAT_ELEM_CONTAINED_BY:
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(key));
lower.val = dquery;
lower.infinite = false;
lower.inclusive = true;
lower.lower = true;
upper.val = dquery;
upper.infinite = false;
upper.inclusive = true;
upper.lower = false;
query = make_range(typcache, &lower, &upper, false);
query = make_singleton_range(typcache, dquery);
break;
default:
......@@ -112,6 +102,7 @@ range_gist_consistent(PG_FUNCTION_ARGS)
key, query));
}
/* form union range */
Datum
range_gist_union(PG_FUNCTION_ARGS)
{
......@@ -134,6 +125,7 @@ range_gist_union(PG_FUNCTION_ARGS)
PG_RETURN_RANGE(result_range);
}
/* compress, decompress are no-ops */
Datum
range_gist_compress(PG_FUNCTION_ARGS)
{
......@@ -150,6 +142,7 @@ range_gist_decompress(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(entry);
}
/* page split penalty function */
Datum
range_gist_penalty(PG_FUNCTION_ARGS)
{
......@@ -177,6 +170,7 @@ range_gist_penalty(PG_FUNCTION_ARGS)
subtype_diff = &typcache->rng_subdiff_finfo;
/* we want to compare the size of "orig" to size of "orig union new" */
s_union = range_super_union(typcache, orig, new);
range_deserialize(typcache, orig, &lower1, &upper1, &empty1);
......@@ -268,9 +262,9 @@ range_gist_penalty(PG_FUNCTION_ARGS)
/*
* The GiST PickSplit method for ranges
*
* Algorithm based on sorting. Incoming array of periods is sorted using
* sort_item_cmp function. After that first half of periods goes to the left
* datum, and the second half of periods goes to the right datum.
* Algorithm based on sorting. Incoming array of ranges is sorted using
* sort_item_cmp function. After that first half of ranges goes to the left
* output, and the second half of ranges goes to the right output.
*/
Datum
range_gist_picksplit(PG_FUNCTION_ARGS)
......@@ -318,7 +312,7 @@ range_gist_picksplit(PG_FUNCTION_ARGS)
v->spl_nright = 0;
/*
* First half of items goes to the left datum.
* First half of items goes to the left output.
*/
pred_left = sortItems[0].data;
*left++ = sortItems[0].index;
......@@ -331,7 +325,7 @@ range_gist_picksplit(PG_FUNCTION_ARGS)
}
/*
* Second half of items goes to the right datum.
* Second half of items goes to the right output.
*/
pred_right = sortItems[split_idx].data;
*right++ = sortItems[split_idx].index;
......@@ -351,6 +345,7 @@ range_gist_picksplit(PG_FUNCTION_ARGS)
PG_RETURN_POINTER(v);
}
/* equality comparator for GiST */
Datum
range_gist_same(PG_FUNCTION_ARGS)
{
......@@ -375,6 +370,8 @@ range_gist_same(PG_FUNCTION_ARGS)
/*
* Return the smallest range that contains r1 and r2
*
* XXX would it be better to redefine range_union as working this way?
*/
static RangeType *
range_super_union(TypeCacheEntry *typcache, RangeType * r1, RangeType * r2)
......
......@@ -49,6 +49,8 @@ typedef struct
RANGE_UB_NULL | \
RANGE_UB_INF)))
#define RangeIsEmpty(r) (range_get_flags(r) & RANGE_EMPTY)
/* Internal representation of either bound of a range (not what's on disk) */
typedef struct
......@@ -153,7 +155,10 @@ extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
extern int range_cmp_bounds(TypeCacheEntry *typcache, RangeBound *b1,
RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, RangeBound *b1,
RangeBound *b2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
extern RangeType *make_singleton_range(TypeCacheEntry *typcache, Datum val);
/* GiST support (in rangetypes_gist.c) */
extern Datum range_gist_consistent(PG_FUNCTION_ARGS);
......
This diff is collapsed.
-- Tests for range data types.
create type textrange as range (subtype=text, collation="C");
--
-- test parser
-- test input parser
--
create type textrange as range (subtype=text, collation="C");
-- negative tests; should fail
select ''::textrange;
select '-[a,z)'::textrange;
......@@ -15,12 +16,7 @@ select '(),a)'::textrange;
select '(a,))'::textrange;
select '(],a)'::textrange;
select '(a,])'::textrange;
select '( , )'::textrange;
select '("","")'::textrange;
select '(",",",")'::textrange;
select '("\\","\\")'::textrange;
select '[a,a)'::textrange;
select '(a,a]'::textrange;
select '[z,a]'::textrange;
-- should succeed
select ' empty '::textrange;
......@@ -31,6 +27,7 @@ select '(a,)'::textrange;
select '[,z]'::textrange;
select '[a,]'::textrange;
select '(,)'::textrange;
select '[ , ]'::textrange;
select '["",""]'::textrange;
select '[",",","]'::textrange;
select '["\\","\\"]'::textrange;
......@@ -39,6 +36,11 @@ select '((,z)'::textrange;
select '([,z)'::textrange;
select '(!,()'::textrange;
select '(!,[)'::textrange;
select '[a,a]'::textrange;
-- these are allowed but normalize to empty:
select '[a,a)'::textrange;
select '(a,a]'::textrange;
select '(a,a)'::textrange;
--
-- create some test data and test the operators
......@@ -46,7 +48,6 @@ select '(!,[)'::textrange;
CREATE TABLE numrange_test (nr NUMRANGE);
create index numrange_test_btree on numrange_test(nr);
SET enable_seqscan = f;
INSERT INTO numrange_test VALUES('[,)');
INSERT INTO numrange_test VALUES('[3,]');
......@@ -55,22 +56,29 @@ INSERT INTO numrange_test VALUES(numrange(1.1, 2.2));
INSERT INTO numrange_test VALUES('empty');
INSERT INTO numrange_test VALUES(numrange(1.7));
SELECT isempty(nr) FROM numrange_test;
SELECT lower_inc(nr), lower(nr), upper(nr), upper_inc(nr) FROM numrange_test
WHERE NOT isempty(nr) AND NOT lower_inf(nr) AND NOT upper_inf(nr);
SELECT nr, isempty(nr), lower(nr), upper(nr) FROM numrange_test;
SELECT nr, lower_inc(nr), lower_inf(nr), upper_inc(nr), upper_inf(nr) FROM numrange_test;
SELECT * FROM numrange_test WHERE range_contains(nr, numrange(1.9,1.91));
SELECT * FROM numrange_test WHERE nr @> numrange(1.0,10000.1);
SELECT * FROM numrange_test WHERE range_contained_by(numrange(-1e7,-10000.1), nr);
SELECT * FROM numrange_test WHERE 1.9 <@ nr;
SELECT * FROM numrange_test WHERE nr = 'empty';
SELECT * FROM numrange_test WHERE range_eq(nr, '(1.1, 2.2)');
SELECT * FROM numrange_test WHERE nr = '[1.1, 2.2)';
select * from numrange_test where nr = 'empty';
select * from numrange_test where nr = '(1.1, 2.2)';
select * from numrange_test where nr = '[1.1, 2.2)';
select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]');
select * from numrange_test where nr < numrange(0.0, 1.0,'[]');
select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]');
select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]');
select * from numrange_test where nr > numrange(0.0, 1.0,'[]');
select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]');
select numrange(2.0, 1.0);
select numrange(2.0, 3.0) -|- numrange(3.0, 4.0);
select range_adjacent(numrange(2.0, 3.0), numrange(3.1, 4.0));
select range_adjacent(numrange(2.0, 3.0), numrange(3.1, null));
select numrange(2.0, 3.0, '[]') -|- numrange(3.0, 4.0, '()');
select numrange(1.0, 2.0) -|- numrange(2.0, 3.0,'[]');
select range_adjacent(numrange(2.0, 3.0, '(]'), numrange(1.0, 2.0, '(]'));
......@@ -102,15 +110,9 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
select * from numrange_test where nr < numrange(-1000.0, -1000.0,'[]');
select * from numrange_test where nr < numrange(0.0, 1.0,'[]');
select * from numrange_test where nr < numrange(1000.0, 1001.0,'[]');
select * from numrange_test where nr > numrange(-1001.0, -1000.0,'[]');
select * from numrange_test where nr > numrange(0.0, 1.0,'[]');
select * from numrange_test where nr > numrange(1000.0, 1000.0,'[]');
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
INSERT INTO numrange_test2 VALUES(numrange(1.1, 2.2));
......@@ -137,21 +139,24 @@ select * from numrange_test natural join numrange_test2 order by nr;
set enable_nestloop to default;
set enable_hashjoin to default;
set enable_mergejoin to default;
SET enable_seqscan TO DEFAULT;
DROP TABLE numrange_test;
DROP TABLE numrange_test2;
-- test canonical form for int4range
select int4range(1,10,'[]');
select int4range(1,10,'[)');
select int4range(1,10,'(]');
select int4range(1,10,'[]');
select int4range(1, 10, '[]');
select int4range(1, 10, '[)');
select int4range(1, 10, '(]');
select int4range(1, 10, '()');
select int4range(1, 2, '()');
-- test canonical form for daterange
select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
select daterange('2000-01-10'::date, '2000-01-20'::date,'[)');
select daterange('2000-01-10'::date, '2000-01-20'::date,'(]');
select daterange('2000-01-10'::date, '2000-01-20'::date,'[]');
select daterange('2000-01-10'::date, '2000-01-20'::date, '[]');
select daterange('2000-01-10'::date, '2000-01-20'::date, '[)');
select daterange('2000-01-10'::date, '2000-01-20'::date, '(]');
select daterange('2000-01-10'::date, '2000-01-20'::date, '()');
select daterange('2000-01-10'::date, '2000-01-11'::date, '()');
select daterange('2000-01-10'::date, '2000-01-11'::date, '(]');
-- test GiST index that's been built incrementally
create table test_range_gist(ir int4range);
......@@ -238,11 +243,11 @@ insert into test_range_excl
insert into test_range_excl
values(int4range(123), int4range(2), '[2010-01-02 11:00, 2010-01-02 12:00)');
insert into test_range_excl
values(int4range(123), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
values(int4range(123), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:00)');
insert into test_range_excl
values(int4range(124), int4range(3), '[2010-01-02 10:10, 2010-01-02 11:10)');
insert into test_range_excl
values(int4range(125), int4range(1), '[2010-01-02 10:10, 2010-01-02 11:10)');
values(int4range(125), int4range(1), '[2010-01-02 10:10, 2010-01-02 11:00)');
-- test bigint ranges
select int8range(10000000000::int8, 20000000000::int8,'(]');
......@@ -264,7 +269,7 @@ create type float8range as range (subtype=float8, subtype_diff=float4mi);
create type float8range as range (subtype=float8, subtype_diff=float8mi);
select '[123.001, 5.e9)'::float8range @> 888.882::float8;
create table float8range_test(f8r float8range, i int);
insert into float8range_test values(float8range(-100.00007, '1.111113e9'));
insert into float8range_test values(float8range(-100.00007, '1.111113e9'), 42);
select * from float8range_test;
drop table float8range_test;
......@@ -275,8 +280,8 @@ drop table float8range_test;
create domain mydomain as int4;
create type mydomainrange as range(subtype=mydomain);
select '[4,50)'::mydomainrange @> 7::mydomain;
drop type mydomainrange;
drop domain mydomain;
drop domain mydomain; -- fail
drop domain mydomain cascade;
--
-- Test domains over range types
......@@ -301,7 +306,7 @@ drop type textrange1;
drop type textrange2;
--
-- Test out polymorphic type system
-- Test polymorphic type system
--
create function anyarray_anyrange_func(a anyarray, r anyrange)
......@@ -325,6 +330,7 @@ create function bogus_func(int)
create function range_add_bounds(anyrange)
returns anyelement as 'select lower($1) + upper($1)' language sql;
select range_add_bounds(int4range(1, 17));
select range_add_bounds(numrange(1.0001, 123.123));
create function rangetypes_sql(q anyrange, b anyarray, out c anyelement)
......@@ -352,6 +358,7 @@ drop table i8r_array;
create type arrayrange as range (subtype=int4[]);
select arrayrange(ARRAY[1,2], ARRAY[2,1]);
select arrayrange(ARRAY[2,1], ARRAY[1,2]); -- fail
select array[1,1] <@ arrayrange(array[1,2], array[2,1]);
select array[1,3] <@ arrayrange(array[1,2], array[2,1]);
......@@ -361,14 +368,20 @@ select array[1,3] <@ arrayrange(array[1,2], array[2,1]);
--
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo' $$ language sql;
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select $1, $2 $$ language sql;
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
-- should fail
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
......@@ -378,5 +391,5 @@ create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
--should fail
create function table_succeed(i anyelement) returns table(i anyelement, r anyrange)
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
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