From 4d7684cc754f312aee468abb83ca4f7da94b30a3 Mon Sep 17 00:00:00 2001 From: Alexander Korotkov Date: Tue, 29 Dec 2020 23:35:33 +0300 Subject: [PATCH] Implement operators for checking if the range contains a multirange We have operators for checking if the multirange contains a range but don't have the opposite. This commit improves completeness of the operator set by adding two new operators: @> (anyrange,anymultirange) and <@(anymultirange,anyrange). Catversion is bumped. --- doc/src/sgml/func.sgml | 14 + src/backend/utils/adt/multirangetypes.c | 60 +++++ .../utils/adt/multirangetypes_selfuncs.c | 6 +- src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_operator.dat | 12 + src/include/catalog/pg_proc.dat | 6 + src/include/utils/multirangetypes.h | 4 +- src/test/regress/expected/multirangetypes.out | 240 ++++++++++++++++++ src/test/regress/sql/multirangetypes.sql | 40 +++ 9 files changed, 381 insertions(+), 3 deletions(-) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 93d17e4b558..5021ac1ca96 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -18182,6 +18182,20 @@ SELECT NULLIF(value, '(none)') ... + + + anyrange @> anymultirange + boolean + + + Does the range contain the multirange? + + + '[2,4)'::int4range @> '{[2,3)}'::int4multirange + t + + + anymultirange <@ anymultirange diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c index 46f661fee49..4b86be583ef 100644 --- a/src/backend/utils/adt/multirangetypes.c +++ b/src/backend/utils/adt/multirangetypes.c @@ -1631,6 +1631,18 @@ multirange_contains_range(PG_FUNCTION_ARGS) PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r)); } +Datum +range_contains_multirange(PG_FUNCTION_ARGS) +{ + RangeType *r = PG_GETARG_RANGE_P(0); + MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1); + TypeCacheEntry *typcache; + + typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr)); + + PG_RETURN_BOOL(range_contains_multirange_internal(typcache, r, mr)); +} + /* contained by? */ Datum range_contained_by_multirange(PG_FUNCTION_ARGS) @@ -1644,6 +1656,18 @@ range_contained_by_multirange(PG_FUNCTION_ARGS) PG_RETURN_BOOL(multirange_contains_range_internal(typcache, mr, r)); } +Datum +multirange_contained_by_range(PG_FUNCTION_ARGS) +{ + MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0); + RangeType *r = PG_GETARG_RANGE_P(1); + TypeCacheEntry *typcache; + + typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr)); + + PG_RETURN_BOOL(range_contains_multirange_internal(typcache, r, mr)); +} + /* * Comparison function for checking if any range of multirange contains given * key range using binary search. @@ -1701,6 +1725,42 @@ multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, multirange_range_contains_bsearch_comparison); } +/* + * Test whether range r contains a multirange mr. + */ +bool +range_contains_multirange_internal(TypeCacheEntry *typcache, RangeType *r, + MultirangeType *mr) +{ + TypeCacheEntry *rangetyp; + RangeBound lower1, + upper1, + lower2, + upper2, + tmp; + bool empty; + + rangetyp = typcache->rngtype; + + /* + * Every range contains an infinite number of empty multiranges, even an + * empty one. + */ + if (MultirangeIsEmpty(mr)) + return true; + + if (RangeIsEmpty(r)) + return false; + + /* Range contains multirange iff it contains its union range. */ + range_deserialize(rangetyp, r, &lower1, &upper1, &empty); + Assert(!empty); + multirange_get_bounds(rangetyp, mr, 0, &lower2, &tmp); + multirange_get_bounds(rangetyp, mr, mr->rangeCount - 1, &tmp, &upper2); + + return range_bounds_contains(rangetyp, &lower1, &upper1, &lower2, &upper2); +} + /* multirange, multirange -> bool functions */ diff --git a/src/backend/utils/adt/multirangetypes_selfuncs.c b/src/backend/utils/adt/multirangetypes_selfuncs.c index 7259af0b853..bb016b6e987 100644 --- a/src/backend/utils/adt/multirangetypes_selfuncs.c +++ b/src/backend/utils/adt/multirangetypes_selfuncs.c @@ -86,6 +86,8 @@ default_multirange_selectivity(Oid operator) case OID_RANGE_OVERLAPS_MULTIRANGE_OP: return 0.01; + case OID_RANGE_CONTAINS_MULTIRANGE_OP: + case OID_RANGE_MULTIRANGE_CONTAINED_OP: case OID_MULTIRANGE_CONTAINS_RANGE_OP: case OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP: case OID_MULTIRANGE_RANGE_CONTAINED_OP: @@ -224,7 +226,8 @@ multirangesel(PG_FUNCTION_ARGS) 1, &constrange); } } - else if (operator == OID_MULTIRANGE_CONTAINS_RANGE_OP || + else if (operator == OID_RANGE_MULTIRANGE_CONTAINED_OP || + operator == OID_MULTIRANGE_CONTAINS_RANGE_OP || operator == OID_MULTIRANGE_OVERLAPS_RANGE_OP || operator == OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP || operator == OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP || @@ -248,6 +251,7 @@ multirangesel(PG_FUNCTION_ARGS) operator == OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP || operator == OID_RANGE_LEFT_MULTIRANGE_OP || operator == OID_RANGE_RIGHT_MULTIRANGE_OP || + operator == OID_RANGE_CONTAINS_MULTIRANGE_OP || operator == OID_MULTIRANGE_ELEM_CONTAINED_OP || operator == OID_MULTIRANGE_RANGE_CONTAINED_OP) { diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 40fd5d43475..4fd88a47730 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202012201 +#define CATALOG_VERSION_NO 202012291 #endif diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat index bd0c3d0f81a..9c6bf6c9d11 100644 --- a/src/include/catalog/pg_operator.dat +++ b/src/include/catalog/pg_operator.dat @@ -3360,6 +3360,18 @@ oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)', oprcode => 'multirange_contained_by_multirange', oprrest => 'multirangesel', oprjoin => 'contjoinsel' }, +{ oid => '4539', oid_symbol => 'OID_RANGE_CONTAINS_MULTIRANGE_OP', + descr => 'contains', + oprname => '@>', oprleft => 'anyrange', oprright => 'anymultirange', + oprresult => 'bool', oprcom => '<@(anymultirange,anyrange)', + oprcode => 'range_contains_multirange', oprrest => 'multirangesel', + oprjoin => 'contjoinsel' }, +{ oid => '4540', oid_symbol => 'OID_RANGE_MULTIRANGE_CONTAINED_OP', + descr => 'is contained by', + oprname => '<@', oprleft => 'anymultirange', oprright => 'anyrange', + oprresult => 'bool', oprcom => '@>(anyrange,anymultirange)', + oprcode => 'multirange_contained_by_range', oprrest => 'multirangesel', + oprjoin => 'contjoinsel' }, { oid => '2875', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP', descr => 'overlaps or is left of', oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange', diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 22970f46cd7..834ee86c791 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -10085,6 +10085,12 @@ { oid => '4253', proname => 'range_contained_by_multirange', prorettype => 'bool', proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' }, +{ oid => '4541', + proname => 'range_contains_multirange', prorettype => 'bool', + proargtypes => 'anyrange anymultirange', prosrc => 'range_contains_multirange' }, +{ oid => '4542', + proname => 'multirange_contained_by_range', prorettype => 'bool', + proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contained_by_range' }, { oid => '4254', proname => 'multirange_contained_by_multirange', prorettype => 'bool', proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' }, diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h index 4cf72415701..f2290aac274 100644 --- a/src/include/utils/multirangetypes.h +++ b/src/include/utils/multirangetypes.h @@ -64,6 +64,8 @@ extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, Multiran Datum elem); extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, RangeType *r); +extern bool range_contains_multirange_internal(TypeCacheEntry *typcache, RangeType *r, + MultirangeType *mr); extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2); @@ -77,7 +79,7 @@ extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType *mr); extern bool range_adjacent_multirange_internal(TypeCacheEntry *typcache, RangeType *r, - MultirangeType *mr); + MultirangeType *mr); extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2); diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out index 180aa1e8a53..aa7232efc57 100644 --- a/src/test/regress/expected/multirangetypes.out +++ b/src/test/regress/expected/multirangetypes.out @@ -979,6 +979,126 @@ select '{(10,20),(30,40),(50,60)}'::nummultirange @> '(52,56)'::numrange; t (1 row) +SELECT numrange(null,null) @> nummultirange(numrange(1,2)); + ?column? +---------- + t +(1 row) + +SELECT numrange(null,null) @> nummultirange(numrange(null,2)); + ?column? +---------- + t +(1 row) + +SELECT numrange(null,null) @> nummultirange(numrange(2,null)); + ?column? +---------- + t +(1 row) + +SELECT numrange(null,5) @> nummultirange(numrange(null,3)); + ?column? +---------- + t +(1 row) + +SELECT numrange(null,5) @> nummultirange(numrange(null,8)); + ?column? +---------- + f +(1 row) + +SELECT numrange(5,null) @> nummultirange(numrange(8,null)); + ?column? +---------- + t +(1 row) + +SELECT numrange(5,null) @> nummultirange(numrange(3,null)); + ?column? +---------- + f +(1 row) + +SELECT numrange(1,5) @> nummultirange(numrange(8,9)); + ?column? +---------- + f +(1 row) + +SELECT numrange(1,5) @> nummultirange(numrange(3,9)); + ?column? +---------- + f +(1 row) + +SELECT numrange(1,5) @> nummultirange(numrange(1,4)); + ?column? +---------- + t +(1 row) + +SELECT numrange(1,5) @> nummultirange(numrange(1,5)); + ?column? +---------- + t +(1 row) + +SELECT numrange(1,9) @> nummultirange(numrange(-4,-2), numrange(1,5)); + ?column? +---------- + f +(1 row) + +SELECT numrange(1,9) @> nummultirange(numrange(1,5), numrange(8,9)); + ?column? +---------- + t +(1 row) + +SELECT numrange(1,9) @> nummultirange(numrange(1,5), numrange(6,9)); + ?column? +---------- + t +(1 row) + +SELECT numrange(1,9) @> nummultirange(numrange(1,5), numrange(6,10)); + ?column? +---------- + f +(1 row) + +SELECT '{[1,9)}' @> '{[1,5)}'::nummultirange; + ?column? +---------- + t +(1 row) + +SELECT '{[1,9)}' @> '{[-4,-2), [1,5)}'::nummultirange; + ?column? +---------- + f +(1 row) + +SELECT '{[1,9)}' @> '{[1,5), [8,9)}'::nummultirange; + ?column? +---------- + t +(1 row) + +SELECT '{[1,9)}' @> '{[1,5), [6,9)}'::nummultirange; + ?column? +---------- + t +(1 row) + +SELECT '{[1,9)}' @> '{[1,5), [6,10)}'::nummultirange; + ?column? +---------- + f +(1 row) + -- is contained by SELECT nummultirange() <@ nummultirange(); ?column? @@ -1112,6 +1232,126 @@ SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange; t (1 row) +SELECT nummultirange(numrange(1,2)) <@ numrange(null,null); + ?column? +---------- + t +(1 row) + +SELECT nummultirange(numrange(null,2)) <@ numrange(null,null); + ?column? +---------- + t +(1 row) + +SELECT nummultirange(numrange(2,null)) <@ numrange(null,null); + ?column? +---------- + t +(1 row) + +SELECT nummultirange(numrange(null,3)) <@ numrange(null,5); + ?column? +---------- + t +(1 row) + +SELECT nummultirange(numrange(null,8)) <@ numrange(null,5); + ?column? +---------- + f +(1 row) + +SELECT nummultirange(numrange(8,null)) <@ numrange(5,null); + ?column? +---------- + t +(1 row) + +SELECT nummultirange(numrange(3,null)) <@ numrange(5,null); + ?column? +---------- + f +(1 row) + +SELECT nummultirange(numrange(8,9)) <@ numrange(1,5); + ?column? +---------- + f +(1 row) + +SELECT nummultirange(numrange(3,9)) <@ numrange(1,5); + ?column? +---------- + f +(1 row) + +SELECT nummultirange(numrange(1,4)) <@ numrange(1,5); + ?column? +---------- + t +(1 row) + +SELECT nummultirange(numrange(1,5)) <@ numrange(1,5); + ?column? +---------- + t +(1 row) + +SELECT nummultirange(numrange(-4,-2), numrange(1,5)) <@ numrange(1,9); + ?column? +---------- + f +(1 row) + +SELECT nummultirange(numrange(1,5), numrange(8,9)) <@ numrange(1,9); + ?column? +---------- + t +(1 row) + +SELECT nummultirange(numrange(1,5), numrange(6,9)) <@ numrange(1,9); + ?column? +---------- + t +(1 row) + +SELECT nummultirange(numrange(1,5), numrange(6,10)) <@ numrange(1,9); + ?column? +---------- + f +(1 row) + +SELECT '{[1,5)}'::nummultirange <@ '{[1,9)}'; + ?column? +---------- + t +(1 row) + +SELECT '{[-4,-2), [1,5)}'::nummultirange <@ '{[1,9)}'; + ?column? +---------- + f +(1 row) + +SELECT '{[1,5), [8,9)}'::nummultirange <@ '{[1,9)}'; + ?column? +---------- + t +(1 row) + +SELECT '{[1,5), [6,9)}'::nummultirange <@ '{[1,9)}'; + ?column? +---------- + t +(1 row) + +SELECT '{[1,5), [6,10)}'::nummultirange <@ '{[1,9)}'; + ?column? +---------- + f +(1 row) + -- overleft SELECT 'empty'::numrange &< nummultirange(); ?column? diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql index c9f84cf81d4..9d7af404c98 100644 --- a/src/test/regress/sql/multirangetypes.sql +++ b/src/test/regress/sql/multirangetypes.sql @@ -188,6 +188,26 @@ SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}'; SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}'; SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}'; select '{(10,20),(30,40),(50,60)}'::nummultirange @> '(52,56)'::numrange; +SELECT numrange(null,null) @> nummultirange(numrange(1,2)); +SELECT numrange(null,null) @> nummultirange(numrange(null,2)); +SELECT numrange(null,null) @> nummultirange(numrange(2,null)); +SELECT numrange(null,5) @> nummultirange(numrange(null,3)); +SELECT numrange(null,5) @> nummultirange(numrange(null,8)); +SELECT numrange(5,null) @> nummultirange(numrange(8,null)); +SELECT numrange(5,null) @> nummultirange(numrange(3,null)); +SELECT numrange(1,5) @> nummultirange(numrange(8,9)); +SELECT numrange(1,5) @> nummultirange(numrange(3,9)); +SELECT numrange(1,5) @> nummultirange(numrange(1,4)); +SELECT numrange(1,5) @> nummultirange(numrange(1,5)); +SELECT numrange(1,9) @> nummultirange(numrange(-4,-2), numrange(1,5)); +SELECT numrange(1,9) @> nummultirange(numrange(1,5), numrange(8,9)); +SELECT numrange(1,9) @> nummultirange(numrange(1,5), numrange(6,9)); +SELECT numrange(1,9) @> nummultirange(numrange(1,5), numrange(6,10)); +SELECT '{[1,9)}' @> '{[1,5)}'::nummultirange; +SELECT '{[1,9)}' @> '{[-4,-2), [1,5)}'::nummultirange; +SELECT '{[1,9)}' @> '{[1,5), [8,9)}'::nummultirange; +SELECT '{[1,9)}' @> '{[1,5), [6,9)}'::nummultirange; +SELECT '{[1,9)}' @> '{[1,5), [6,10)}'::nummultirange; -- is contained by SELECT nummultirange() <@ nummultirange(); @@ -212,6 +232,26 @@ SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange; SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange; SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange; SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange; +SELECT nummultirange(numrange(1,2)) <@ numrange(null,null); +SELECT nummultirange(numrange(null,2)) <@ numrange(null,null); +SELECT nummultirange(numrange(2,null)) <@ numrange(null,null); +SELECT nummultirange(numrange(null,3)) <@ numrange(null,5); +SELECT nummultirange(numrange(null,8)) <@ numrange(null,5); +SELECT nummultirange(numrange(8,null)) <@ numrange(5,null); +SELECT nummultirange(numrange(3,null)) <@ numrange(5,null); +SELECT nummultirange(numrange(8,9)) <@ numrange(1,5); +SELECT nummultirange(numrange(3,9)) <@ numrange(1,5); +SELECT nummultirange(numrange(1,4)) <@ numrange(1,5); +SELECT nummultirange(numrange(1,5)) <@ numrange(1,5); +SELECT nummultirange(numrange(-4,-2), numrange(1,5)) <@ numrange(1,9); +SELECT nummultirange(numrange(1,5), numrange(8,9)) <@ numrange(1,9); +SELECT nummultirange(numrange(1,5), numrange(6,9)) <@ numrange(1,9); +SELECT nummultirange(numrange(1,5), numrange(6,10)) <@ numrange(1,9); +SELECT '{[1,5)}'::nummultirange <@ '{[1,9)}'; +SELECT '{[-4,-2), [1,5)}'::nummultirange <@ '{[1,9)}'; +SELECT '{[1,5), [8,9)}'::nummultirange <@ '{[1,9)}'; +SELECT '{[1,5), [6,9)}'::nummultirange <@ '{[1,9)}'; +SELECT '{[1,5), [6,10)}'::nummultirange <@ '{[1,9)}'; -- overleft SELECT 'empty'::numrange &< nummultirange();