Implement operators for checking if the range contains a multirange
authorAlexander Korotkov <akorotkov@postgresql.org>
Tue, 29 Dec 2020 20:35:33 +0000 (23:35 +0300)
committerAlexander Korotkov <akorotkov@postgresql.org>
Tue, 29 Dec 2020 20:35:33 +0000 (23:35 +0300)
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
src/backend/utils/adt/multirangetypes.c
src/backend/utils/adt/multirangetypes_selfuncs.c
src/include/catalog/catversion.h
src/include/catalog/pg_operator.dat
src/include/catalog/pg_proc.dat
src/include/utils/multirangetypes.h
src/test/regress/expected/multirangetypes.out
src/test/regress/sql/multirangetypes.sql

index 93d17e4b558afce969ec0eab424850848c2f4d1d..5021ac1ca96a42c4d18d889009abfdff9da5fe71 100644 (file)
@@ -18182,6 +18182,20 @@ SELECT NULLIF(value, '(none)') ...
        </para></entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anyrange</type> <literal>@&gt;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the range contain the multirange?
+       </para>
+       <para>
+        <literal>'[2,4)'::int4range @&gt; '{[2,3)}'::int4multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <type>anymultirange</type> <literal>&lt;@</literal> <type>anymultirange</type>
index 46f661fee49e4a392cd5891dbc2eb0f43bdfb8f9..4b86be583ef714fb7a979c1889e19e4c815e31fe 100644 (file)
@@ -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 */
 
index 7259af0b853058a3a1b6932f1bb1c276e6e8ee68..bb016b6e9877062038e1e68d21ea7e6d0b7455ae 100644 (file)
@@ -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)
        {
index 40fd5d434758c4df118c629d2c41858249e32847..4fd88a477306c52e26eed099fe83c06b1247025b 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202012201
+#define CATALOG_VERSION_NO     202012291
 
 #endif
index bd0c3d0f81a8a3a689536e1b56319a2219c29032..9c6bf6c9d115dfd4f9ffeaff931b0ac9a07f5edc 100644 (file)
   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',
index 22970f46cd7befb2cfb745e75528afc3aa7a439d..834ee86c791b4d2f8a6cb812ddfba995e5e3ffa6 100644 (file)
 { 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' },
index 4cf72415701975399f46e09e25aca53ad97d0f07..f2290aac274802dcafcc82c8da3ccf917323a1e7 100644 (file)
@@ -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);
index 180aa1e8a535e398dbc1751f512066ec8ac3a8e3..aa7232efc5779c870eb8aeaff934eb2f01ff441c 100644 (file)
@@ -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? 
index c9f84cf81d4672b8bed2e3d56e8341ad6a166652..9d7af404c98c2630532175ad1f7d1a27116c44a9 100644 (file)
@@ -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();