Add support of multirange matching to the existing range GiST indexes
authorAlexander Korotkov <akorotkov@postgresql.org>
Tue, 29 Dec 2020 20:36:43 +0000 (23:36 +0300)
committerAlexander Korotkov <akorotkov@postgresql.org>
Tue, 29 Dec 2020 20:36:43 +0000 (23:36 +0300)
6df7a9698b has introduced a set of operators between ranges and multiranges.
Existing GiST indexes for ranges could easily support majority of them.
This commit adds support for new operators to the existing range GiST indexes.
New operators resides the same strategy numbers as existing ones.  Appropriate
check function is determined using the subtype.

Catversion is bumped.

15 files changed:
doc/src/sgml/gist.sgml
doc/src/sgml/rangetypes.sgml
src/backend/utils/adt/multirangetypes.c
src/backend/utils/adt/rangetypes_gist.c
src/include/catalog/catversion.h
src/include/catalog/pg_amop.dat
src/include/catalog/pg_amproc.dat
src/include/catalog/pg_opclass.dat
src/include/catalog/pg_opfamily.dat
src/include/catalog/pg_proc.dat
src/include/utils/multirangetypes.h
src/test/regress/expected/multirangetypes.out
src/test/regress/expected/rangetypes.out
src/test/regress/sql/multirangetypes.sql
src/test/regress/sql/rangetypes.sql

index d1b6cc9a01a31d26b4c301f4e6e82cf54bf83731..dc1f192bddfdf1320ba2a8b7339d9d353c777e3b 100644 (file)
      <row><entry><literal>&gt;= (inet,inet)</literal></entry></row>
      <row><entry><literal>&amp;&amp; (inet,inet)</literal></entry></row>
 
+     <row>
+      <entry valign="middle" morerows="17"><literal>multirange_ops</literal></entry>
+      <entry><literal>= (anymultirange,anymultirange)</literal></entry>
+      <entry valign="middle" morerows="17"></entry>
+     </row>
+     <row><entry><literal>&amp;&amp; (anymultirange,anymultirange)</literal></entry></row>
+     <row><entry><literal>&amp;&amp; (anymultirange,anyrange)</literal></entry></row>
+     <row><entry><literal>@&gt; (anymultirange,anyelement)</literal></entry></row>
+     <row><entry><literal>@&gt; (anymultirange,anymultirange)</literal></entry></row>
+     <row><entry><literal>@&gt; (anymultirange,anyrange)</literal></entry></row>
+     <row><entry><literal>&lt;@ (anymultirange,anymultirange)</literal></entry></row>
+     <row><entry><literal>&lt;@ (anymultirange,anyrange)</literal></entry></row>
+     <row><entry><literal>&lt;&lt; (anymultirange,anymultirange)</literal></entry></row>
+     <row><entry><literal>&lt;&lt; (anymultirange,anyrange)</literal></entry></row>
+     <row><entry><literal>&gt;&gt; (anymultirange,anymultirange)</literal></entry></row>
+     <row><entry><literal>&gt;&gt; (anymultirange,anyrange)</literal></entry></row>
+     <row><entry><literal>&amp;&lt; (anymultirange,anymultirange)</literal></entry></row>
+     <row><entry><literal>&amp;&lt; (anymultirange,anyrange)</literal></entry></row>
+     <row><entry><literal>&amp;&gt; (anymultirange,anymultirange)</literal></entry></row>
+     <row><entry><literal>&amp;&gt; (anymultirange,anyrange)</literal></entry></row>
+     <row><entry><literal>-|- (anymultirange,anymultirange)</literal></entry></row>
+     <row><entry><literal>-|- (anymultirange,anyrange)</literal></entry></row>
+
      <row>
       <entry valign="middle" morerows="7"><literal>point_ops</literal></entry>
       <entry><literal>|&gt;&gt; (point,point)</literal></entry>
      <row><entry><literal>~ (polygon,polygon)</literal></entry></row>
 
      <row>
-      <entry valign="middle" morerows="9"><literal>range_ops</literal></entry>
+      <entry valign="middle" morerows="17"><literal>range_ops</literal></entry>
       <entry><literal>= (anyrange,anyrange)</literal></entry>
-      <entry valign="middle" morerows="9"></entry>
+      <entry valign="middle" morerows="17"></entry>
      </row>
      <row><entry><literal>&amp;&amp; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&amp;&amp; (anyrange,anymultirange)</literal></entry></row>
      <row><entry><literal>@&gt; (anyrange,anyelement)</literal></entry></row>
      <row><entry><literal>@&gt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>@&gt; (anyrange,anymultirange)</literal></entry></row>
      <row><entry><literal>&lt;@ (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&lt;@ (anyrange,anymultirange)</literal></entry></row>
      <row><entry><literal>&lt;&lt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&lt;&lt; (anyrange,anymultirange)</literal></entry></row>
      <row><entry><literal>&gt;&gt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&gt;&gt; (anyrange,anymultirange)</literal></entry></row>
      <row><entry><literal>&amp;&lt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&amp;&lt; (anyrange,anymultirange)</literal></entry></row>
      <row><entry><literal>&amp;&gt; (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>&amp;&gt; (anyrange,anymultirange)</literal></entry></row>
      <row><entry><literal>-|- (anyrange,anyrange)</literal></entry></row>
+     <row><entry><literal>-|- (anyrange,anymultirange)</literal></entry></row>
 
      <row>
       <entry valign="middle" morerows="1"><literal>tsquery_ops</literal></entry>
index 859079c69cad948458931ed1aa5c78d70d7a810a..91e353d4fdb91f7bdbe8a87b101aec8fa60ce848 100644 (file)
@@ -469,11 +469,13 @@ SELECT '[11:10, 23:00]'::timerange;
 
   <para>
    GiST and SP-GiST indexes can be created for table columns of range types.
+   GiST indexes can be also created for table columns of multirange types.
    For instance, to create a GiST index:
 <programlisting>
 CREATE INDEX reservation_idx ON reservation USING GIST (during);
 </programlisting>
-   A GiST or SP-GiST index can accelerate queries involving these range operators:
+   A GiST or SP-GiST index on ranges can accelerate queries involving these
+   range operators:
    <literal>=</literal>,
    <literal>&amp;&amp;</literal>,
    <literal>&lt;@</literal>,
@@ -482,8 +484,21 @@ CREATE INDEX reservation_idx ON reservation USING GIST (during);
    <literal>&gt;&gt;</literal>,
    <literal>-|-</literal>,
    <literal>&amp;&lt;</literal>, and
-   <literal>&amp;&gt;</literal>
-   (see <xref linkend="range-operators-table"/> for more information).
+   <literal>&amp;&gt;</literal>.
+   A GiST index on multiranges can accelerate queries involving the same
+   set of multirange operators.
+   A GiST index on ranges and GiST index on multiranges can also accelerate
+   queries involving these cross-type range to multirange and multirange to
+   range operators correspondingly:
+   <literal>&amp;&amp;</literal>,
+   <literal>&lt;@</literal>,
+   <literal>@&gt;</literal>,
+   <literal>&lt;&lt;</literal>,
+   <literal>&gt;&gt;</literal>,
+   <literal>-|-</literal>,
+   <literal>&amp;&lt;</literal>, and
+   <literal>&amp;&gt;</literal>.
+   See <xref linkend="range-operators-table"/> for more information.
   </para>
 
   <para>
index a77299147e7fb3502fd3e1bc79a52136d48ca361..2d4cee92bcced86f0828e04d2a86ddddf411856f 100644 (file)
@@ -768,6 +768,27 @@ multirange_get_bounds(TypeCacheEntry *rangetyp,
    upper->lower = false;
 }
 
+/*
+ * Construct union range from the multirange.
+ */
+RangeType *
+multirange_get_union_range(TypeCacheEntry *rangetyp,
+                          const MultirangeType *mr)
+{
+   RangeBound  lower,
+               upper,
+               tmp;
+
+   if (MultirangeIsEmpty(mr))
+       return make_empty_range(rangetyp);
+
+   multirange_get_bounds(rangetyp, mr, 0, &lower, &tmp);
+   multirange_get_bounds(rangetyp, mr, mr->rangeCount - 1, &tmp, &upper);
+
+   return make_range(rangetyp, &lower, &upper, false);
+}
+
+
 /*
  * multirange_deserialize: deconstruct a multirange value
  *
index 75069c3ac2c8acae6555435cb974b802c152ace1..435b242c8abec3ea5ecebb94e28f320c39afd3d8 100644 (file)
@@ -19,6 +19,7 @@
 #include "utils/datum.h"
 #include "utils/float.h"
 #include "utils/fmgrprotos.h"
+#include "utils/multirangetypes.h"
 #include "utils/rangetypes.h"
 
 /*
@@ -135,12 +136,30 @@ typedef struct
 
 static RangeType *range_super_union(TypeCacheEntry *typcache, RangeType *r1,
                                    RangeType *r2);
-static bool range_gist_consistent_int(TypeCacheEntry *typcache,
-                                     StrategyNumber strategy, const RangeType *key,
-                                     Datum query);
-static bool range_gist_consistent_leaf(TypeCacheEntry *typcache,
-                                      StrategyNumber strategy, const RangeType *key,
-                                      Datum query);
+static bool range_gist_consistent_int_range(TypeCacheEntry *typcache,
+                                           StrategyNumber strategy,
+                                           const RangeType *key,
+                                           const RangeType *query);
+static bool range_gist_consistent_int_multirange(TypeCacheEntry *typcache,
+                                                StrategyNumber strategy,
+                                                const RangeType *key,
+                                                const MultirangeType *query);
+static bool range_gist_consistent_int_element(TypeCacheEntry *typcache,
+                                             StrategyNumber strategy,
+                                             const RangeType *key,
+                                             Datum query);
+static bool range_gist_consistent_leaf_range(TypeCacheEntry *typcache,
+                                            StrategyNumber strategy,
+                                            const RangeType *key,
+                                            const RangeType *query);
+static bool range_gist_consistent_leaf_multirange(TypeCacheEntry *typcache,
+                                                 StrategyNumber strategy,
+                                                 const RangeType *key,
+                                                 const MultirangeType *query);
+static bool range_gist_consistent_leaf_element(TypeCacheEntry *typcache,
+                                              StrategyNumber strategy,
+                                              const RangeType *key,
+                                              Datum query);
 static void range_gist_fallback_split(TypeCacheEntry *typcache,
                                      GistEntryVector *entryvec,
                                      GIST_SPLITVEC *v);
@@ -174,8 +193,8 @@ range_gist_consistent(PG_FUNCTION_ARGS)
    GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    Datum       query = PG_GETARG_DATUM(1);
    StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
-
-   /* Oid subtype = PG_GETARG_OID(3); */
+   bool        result;
+   Oid         subtype = PG_GETARG_OID(3);
    bool       *recheck = (bool *) PG_GETARG_POINTER(4);
    RangeType  *key = DatumGetRangeTypeP(entry->key);
    TypeCacheEntry *typcache;
@@ -185,12 +204,119 @@ range_gist_consistent(PG_FUNCTION_ARGS)
 
    typcache = range_get_typcache(fcinfo, RangeTypeGetOid(key));
 
+   /*
+    * Perform consistent checking using function corresponding to key type
+    * (leaf or internal) and query subtype (range, multirange, or element).
+    * Note that invalid subtype means that query type matches key type
+    * (range).
+    */
    if (GIST_LEAF(entry))
-       PG_RETURN_BOOL(range_gist_consistent_leaf(typcache, strategy,
-                                                 key, query));
+   {
+       if (!OidIsValid(subtype) || subtype == ANYRANGEOID)
+           result = range_gist_consistent_leaf_range(typcache, strategy, key,
+                                                     DatumGetRangeTypeP(query));
+       else if (subtype == ANYMULTIRANGEOID)
+           result = range_gist_consistent_leaf_multirange(typcache, strategy, key,
+                                                          DatumGetMultirangeTypeP(query));
+       else
+           result = range_gist_consistent_leaf_element(typcache, strategy,
+                                                       key, query);
+   }
    else
-       PG_RETURN_BOOL(range_gist_consistent_int(typcache, strategy,
-                                                key, query));
+   {
+       if (!OidIsValid(subtype) || subtype == ANYRANGEOID)
+           result = range_gist_consistent_int_range(typcache, strategy, key,
+                                                    DatumGetRangeTypeP(query));
+       else if (subtype == ANYMULTIRANGEOID)
+           result = range_gist_consistent_int_multirange(typcache, strategy, key,
+                                                         DatumGetMultirangeTypeP(query));
+       else
+           result = range_gist_consistent_int_element(typcache, strategy,
+                                                      key, query);
+   }
+   PG_RETURN_BOOL(result);
+}
+
+/*
+ * GiST compress method for multiranges: multirange is approximated as union
+ * range with no gaps.
+ */
+Datum
+multirange_gist_compress(PG_FUNCTION_ARGS)
+{
+   GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+
+   if (entry->leafkey)
+   {
+       MultirangeType *mr = DatumGetMultirangeTypeP(entry->key);
+       RangeType  *r;
+       TypeCacheEntry *typcache;
+       GISTENTRY  *retval = palloc(sizeof(GISTENTRY));
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+       r = multirange_get_union_range(typcache->rngtype, mr);
+
+       gistentryinit(*retval, RangeTypePGetDatum(r),
+                     entry->rel, entry->page, entry->offset, false);
+
+       PG_RETURN_POINTER(retval);
+   }
+
+   PG_RETURN_POINTER(entry);
+}
+
+/* GiST query consistency check for multiranges */
+Datum
+multirange_gist_consistent(PG_FUNCTION_ARGS)
+{
+   GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+   Datum       query = PG_GETARG_DATUM(1);
+   StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+   bool        result;
+   Oid         subtype = PG_GETARG_OID(3);
+   bool       *recheck = (bool *) PG_GETARG_POINTER(4);
+   RangeType  *key = DatumGetRangeTypeP(entry->key);
+   TypeCacheEntry *typcache;
+
+   /*
+    * All operators served by this function are inexact because multirange is
+    * approximated by union range with no gaps.
+    */
+   *recheck = true;
+
+   typcache = range_get_typcache(fcinfo, RangeTypeGetOid(key));
+
+   /*
+    * Perform consistent checking using function corresponding to key type
+    * (leaf or internal) and query subtype (range, multirange, or element).
+    * Note that invalid subtype means that query type matches key type
+    * (multirange).
+    */
+   if (GIST_LEAF(entry))
+   {
+       if (!OidIsValid(subtype) || subtype == ANYMULTIRANGEOID)
+           result = range_gist_consistent_leaf_multirange(typcache, strategy, key,
+                                                          DatumGetMultirangeTypeP(query));
+       else if (subtype == ANYRANGEOID)
+           result = range_gist_consistent_leaf_range(typcache, strategy, key,
+                                                     DatumGetRangeTypeP(query));
+       else
+           result = range_gist_consistent_leaf_element(typcache, strategy,
+                                                       key, query);
+   }
+   else
+   {
+       if (!OidIsValid(subtype) || subtype == ANYMULTIRANGEOID)
+           result = range_gist_consistent_int_multirange(typcache, strategy, key,
+                                                         DatumGetMultirangeTypeP(query));
+       else if (subtype == ANYRANGEOID)
+           result = range_gist_consistent_int_range(typcache, strategy, key,
+                                                    DatumGetRangeTypeP(query));
+       else
+           result = range_gist_consistent_int_element(typcache, strategy,
+                                                      key, query);
+   }
+   PG_RETURN_BOOL(result);
 }
 
 /* form union range */
@@ -758,49 +884,67 @@ range_super_union(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
    return result;
 }
 
+static bool
+multirange_union_range_equal(TypeCacheEntry *typcache,
+                            const RangeType *r,
+                            const MultirangeType *mr)
+{
+   RangeBound  lower1,
+               upper1,
+               lower2,
+               upper2,
+               tmp;
+   bool        empty;
+
+   if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+       return (RangeIsEmpty(r) && MultirangeIsEmpty(mr));
+
+   range_deserialize(typcache, r, &lower1, &upper1, &empty);
+   Assert(!empty);
+   multirange_get_bounds(typcache, mr, 0, &lower2, &tmp);
+   multirange_get_bounds(typcache, mr, mr->rangeCount - 1, &tmp, &upper2);
+
+   return (range_cmp_bounds(typcache, &lower1, &lower2) == 0 &&
+           range_cmp_bounds(typcache, &upper1, &upper2) == 0);
+}
+
 /*
- * GiST consistent test on an index internal page
+ * GiST consistent test on an index internal page with range query
  */
 static bool
-range_gist_consistent_int(TypeCacheEntry *typcache, StrategyNumber strategy,
-                         const RangeType *key, Datum query)
+range_gist_consistent_int_range(TypeCacheEntry *typcache,
+                               StrategyNumber strategy,
+                               const RangeType *key,
+                               const RangeType *query)
 {
    switch (strategy)
    {
        case RANGESTRAT_BEFORE:
-           if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeTypeP(query)))
+           if (RangeIsEmpty(key) || RangeIsEmpty(query))
                return false;
-           return (!range_overright_internal(typcache, key,
-                                             DatumGetRangeTypeP(query)));
+           return (!range_overright_internal(typcache, key, query));
        case RANGESTRAT_OVERLEFT:
-           if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeTypeP(query)))
+           if (RangeIsEmpty(key) || RangeIsEmpty(query))
                return false;
-           return (!range_after_internal(typcache, key,
-                                         DatumGetRangeTypeP(query)));
+           return (!range_after_internal(typcache, key, query));
        case RANGESTRAT_OVERLAPS:
-           return range_overlaps_internal(typcache, key,
-                                          DatumGetRangeTypeP(query));
+           return range_overlaps_internal(typcache, key, query);
        case RANGESTRAT_OVERRIGHT:
-           if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeTypeP(query)))
+           if (RangeIsEmpty(key) || RangeIsEmpty(query))
                return false;
-           return (!range_before_internal(typcache, key,
-                                          DatumGetRangeTypeP(query)));
+           return (!range_before_internal(typcache, key, query));
        case RANGESTRAT_AFTER:
-           if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeTypeP(query)))
+           if (RangeIsEmpty(key) || RangeIsEmpty(query))
                return false;
-           return (!range_overleft_internal(typcache, key,
-                                            DatumGetRangeTypeP(query)));
+           return (!range_overleft_internal(typcache, key, query));
        case RANGESTRAT_ADJACENT:
-           if (RangeIsEmpty(key) || RangeIsEmpty(DatumGetRangeTypeP(query)))
+           if (RangeIsEmpty(key) || RangeIsEmpty(query))
                return false;
-           if (range_adjacent_internal(typcache, key,
-                                       DatumGetRangeTypeP(query)))
+           if (range_adjacent_internal(typcache, key, query))
                return true;
-           return range_overlaps_internal(typcache, key,
-                                          DatumGetRangeTypeP(query));
+           return range_overlaps_internal(typcache, key, query);
        case RANGESTRAT_CONTAINS:
-           return range_contains_internal(typcache, key,
-                                          DatumGetRangeTypeP(query));
+           return range_contains_internal(typcache, key, query);
        case RANGESTRAT_CONTAINED_BY:
 
            /*
@@ -810,20 +954,16 @@ range_gist_consistent_int(TypeCacheEntry *typcache, StrategyNumber strategy,
             */
            if (RangeIsOrContainsEmpty(key))
                return true;
-           return range_overlaps_internal(typcache, key,
-                                          DatumGetRangeTypeP(query));
-       case RANGESTRAT_CONTAINS_ELEM:
-           return range_contains_elem_internal(typcache, key, query);
+           return range_overlaps_internal(typcache, key, query);
        case RANGESTRAT_EQ:
 
            /*
             * If query is empty, descend only if the key is or contains any
             * empty ranges.  Otherwise, descend if key contains query.
             */
-           if (RangeIsEmpty(DatumGetRangeTypeP(query)))
+           if (RangeIsEmpty(query))
                return RangeIsOrContainsEmpty(key);
-           return range_contains_internal(typcache, key,
-                                          DatumGetRangeTypeP(query));
+           return range_contains_internal(typcache, key, query);
        default:
            elog(ERROR, "unrecognized range strategy: %d", strategy);
            return false;       /* keep compiler quiet */
@@ -831,42 +971,169 @@ range_gist_consistent_int(TypeCacheEntry *typcache, StrategyNumber strategy,
 }
 
 /*
- * GiST consistent test on an index leaf page
+ * GiST consistent test on an index internal page with multirange query
  */
 static bool
-range_gist_consistent_leaf(TypeCacheEntry *typcache, StrategyNumber strategy,
-                          const RangeType *key, Datum query)
+range_gist_consistent_int_multirange(TypeCacheEntry *typcache,
+                                    StrategyNumber strategy,
+                                    const RangeType *key,
+                                    const MultirangeType *query)
 {
    switch (strategy)
    {
        case RANGESTRAT_BEFORE:
-           return range_before_internal(typcache, key,
-                                        DatumGetRangeTypeP(query));
+           if (RangeIsEmpty(key) || MultirangeIsEmpty(query))
+               return false;
+           return (!range_overright_multirange_internal(typcache, key, query));
        case RANGESTRAT_OVERLEFT:
-           return range_overleft_internal(typcache, key,
-                                          DatumGetRangeTypeP(query));
+           if (RangeIsEmpty(key) || MultirangeIsEmpty(query))
+               return false;
+           return (!range_after_multirange_internal(typcache, key, query));
        case RANGESTRAT_OVERLAPS:
-           return range_overlaps_internal(typcache, key,
-                                          DatumGetRangeTypeP(query));
+           return range_overlaps_multirange_internal(typcache, key, query);
        case RANGESTRAT_OVERRIGHT:
-           return range_overright_internal(typcache, key,
-                                           DatumGetRangeTypeP(query));
+           if (RangeIsEmpty(key) || MultirangeIsEmpty(query))
+               return false;
+           return (!range_before_multirange_internal(typcache, key, query));
        case RANGESTRAT_AFTER:
-           return range_after_internal(typcache, key,
-                                       DatumGetRangeTypeP(query));
+           if (RangeIsEmpty(key) || MultirangeIsEmpty(query))
+               return false;
+           return (!range_overleft_multirange_internal(typcache, key, query));
        case RANGESTRAT_ADJACENT:
-           return range_adjacent_internal(typcache, key,
-                                          DatumGetRangeTypeP(query));
+           if (RangeIsEmpty(key) || MultirangeIsEmpty(query))
+               return false;
+           if (range_adjacent_multirange_internal(typcache, key, query))
+               return true;
+           return range_overlaps_multirange_internal(typcache, key, query);
        case RANGESTRAT_CONTAINS:
-           return range_contains_internal(typcache, key,
-                                          DatumGetRangeTypeP(query));
+           return range_contains_multirange_internal(typcache, key, query);
        case RANGESTRAT_CONTAINED_BY:
-           return range_contained_by_internal(typcache, key,
-                                              DatumGetRangeTypeP(query));
+
+           /*
+            * Empty ranges are contained by anything, so if key is or
+            * contains any empty ranges, we must descend into it.  Otherwise,
+            * descend only if key overlaps the query.
+            */
+           if (RangeIsOrContainsEmpty(key))
+               return true;
+           return range_overlaps_multirange_internal(typcache, key, query);
+       case RANGESTRAT_EQ:
+
+           /*
+            * If query is empty, descend only if the key is or contains any
+            * empty ranges.  Otherwise, descend if key contains query.
+            */
+           if (MultirangeIsEmpty(query))
+               return RangeIsOrContainsEmpty(key);
+           return range_contains_multirange_internal(typcache, key, query);
+       default:
+           elog(ERROR, "unrecognized range strategy: %d", strategy);
+           return false;       /* keep compiler quiet */
+   }
+}
+
+/*
+ * GiST consistent test on an index internal page with element query
+ */
+static bool
+range_gist_consistent_int_element(TypeCacheEntry *typcache,
+                                 StrategyNumber strategy,
+                                 const RangeType *key,
+                                 Datum query)
+{
+   switch (strategy)
+   {
        case RANGESTRAT_CONTAINS_ELEM:
            return range_contains_elem_internal(typcache, key, query);
+       default:
+           elog(ERROR, "unrecognized range strategy: %d", strategy);
+           return false;       /* keep compiler quiet */
+   }
+}
+
+/*
+ * GiST consistent test on an index leaf page with range query
+ */
+static bool
+range_gist_consistent_leaf_range(TypeCacheEntry *typcache,
+                                StrategyNumber strategy,
+                                const RangeType *key,
+                                const RangeType *query)
+{
+   switch (strategy)
+   {
+       case RANGESTRAT_BEFORE:
+           return range_before_internal(typcache, key, query);
+       case RANGESTRAT_OVERLEFT:
+           return range_overleft_internal(typcache, key, query);
+       case RANGESTRAT_OVERLAPS:
+           return range_overlaps_internal(typcache, key, query);
+       case RANGESTRAT_OVERRIGHT:
+           return range_overright_internal(typcache, key, query);
+       case RANGESTRAT_AFTER:
+           return range_after_internal(typcache, key, query);
+       case RANGESTRAT_ADJACENT:
+           return range_adjacent_internal(typcache, key, query);
+       case RANGESTRAT_CONTAINS:
+           return range_contains_internal(typcache, key, query);
+       case RANGESTRAT_CONTAINED_BY:
+           return range_contained_by_internal(typcache, key, query);
+       case RANGESTRAT_EQ:
+           return range_eq_internal(typcache, key, query);
+       default:
+           elog(ERROR, "unrecognized range strategy: %d", strategy);
+           return false;       /* keep compiler quiet */
+   }
+}
+
+/*
+ * GiST consistent test on an index leaf page with multirange query
+ */
+static bool
+range_gist_consistent_leaf_multirange(TypeCacheEntry *typcache,
+                                     StrategyNumber strategy,
+                                     const RangeType *key,
+                                     const MultirangeType *query)
+{
+   switch (strategy)
+   {
+       case RANGESTRAT_BEFORE:
+           return range_before_multirange_internal(typcache, key, query);
+       case RANGESTRAT_OVERLEFT:
+           return range_overleft_multirange_internal(typcache, key, query);
+       case RANGESTRAT_OVERLAPS:
+           return range_overlaps_multirange_internal(typcache, key, query);
+       case RANGESTRAT_OVERRIGHT:
+           return range_overright_multirange_internal(typcache, key, query);
+       case RANGESTRAT_AFTER:
+           return range_after_multirange_internal(typcache, key, query);
+       case RANGESTRAT_ADJACENT:
+           return range_adjacent_multirange_internal(typcache, key, query);
+       case RANGESTRAT_CONTAINS:
+           return range_contains_multirange_internal(typcache, key, query);
+       case RANGESTRAT_CONTAINED_BY:
+           return multirange_contains_range_internal(typcache, query, key);
        case RANGESTRAT_EQ:
-           return range_eq_internal(typcache, key, DatumGetRangeTypeP(query));
+           return multirange_union_range_equal(typcache, key, query);
+       default:
+           elog(ERROR, "unrecognized range strategy: %d", strategy);
+           return false;       /* keep compiler quiet */
+   }
+}
+
+/*
+ * GiST consistent test on an index leaf page with element query
+ */
+static bool
+range_gist_consistent_leaf_element(TypeCacheEntry *typcache,
+                                  StrategyNumber strategy,
+                                  const RangeType *key,
+                                  Datum query)
+{
+   switch (strategy)
+   {
+       case RANGESTRAT_CONTAINS_ELEM:
+           return range_contains_elem_internal(typcache, key, query);
        default:
            elog(ERROR, "unrecognized range strategy: %d", strategy);
            return false;       /* keep compiler quiet */
index 4fd88a477306c52e26eed099fe83c06b1247025b..db4814e57afa5f2ecddd917c976953ce851213d9 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202012291
+#define CATALOG_VERSION_NO 202012293
 
 #endif
index 78d7d2c52329c50a012ff787a3b00cd3984515c9..bbe1a6ddf8e64a35660285c334a8a3b53f681f3d 100644 (file)
 { amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
   amoprighttype => 'anyrange', amopstrategy => '1',
   amopopr => '<<(anyrange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
+  amoprighttype => 'anymultirange', amopstrategy => '1',
+  amopopr => '<<(anyrange,anymultirange)', amopmethod => 'gist' },
 { amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
   amoprighttype => 'anyrange', amopstrategy => '2',
   amopopr => '&<(anyrange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
+  amoprighttype => 'anymultirange', amopstrategy => '2',
+  amopopr => '&<(anyrange,anymultirange)', amopmethod => 'gist' },
 { amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
   amoprighttype => 'anyrange', amopstrategy => '3',
   amopopr => '&&(anyrange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
+  amoprighttype => 'anymultirange', amopstrategy => '3',
+  amopopr => '&&(anyrange,anymultirange)', amopmethod => 'gist' },
 { amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
   amoprighttype => 'anyrange', amopstrategy => '4',
   amopopr => '&>(anyrange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
+  amoprighttype => 'anymultirange', amopstrategy => '4',
+  amopopr => '&>(anyrange,anymultirange)', amopmethod => 'gist' },
 { amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
   amoprighttype => 'anyrange', amopstrategy => '5',
   amopopr => '>>(anyrange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
+  amoprighttype => 'anymultirange', amopstrategy => '5',
+  amopopr => '>>(anyrange,anymultirange)', amopmethod => 'gist' },
 { amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
   amoprighttype => 'anyrange', amopstrategy => '6',
   amopopr => '-|-(anyrange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
+  amoprighttype => 'anymultirange', amopstrategy => '6',
+  amopopr => '-|-(anyrange,anymultirange)', amopmethod => 'gist' },
 { amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
   amoprighttype => 'anyrange', amopstrategy => '7',
   amopopr => '@>(anyrange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
+  amoprighttype => 'anymultirange', amopstrategy => '7',
+  amopopr => '@>(anyrange,anymultirange)', amopmethod => 'gist' },
 { amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
   amoprighttype => 'anyrange', amopstrategy => '8',
   amopopr => '<@(anyrange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
+  amoprighttype => 'anymultirange', amopstrategy => '8',
+  amopopr => '<@(anyrange,anymultirange)', amopmethod => 'gist' },
 { amopfamily => 'gist/range_ops', amoplefttype => 'anyrange',
   amoprighttype => 'anyelement', amopstrategy => '16',
   amopopr => '@>(anyrange,anyelement)', amopmethod => 'gist' },
   amoprighttype => 'anyrange', amopstrategy => '18',
   amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
 
+# GiST multirange_ops
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '1',
+  amopopr => '<<(anymultirange,anymultirange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anyrange', amopstrategy => '1',
+  amopopr => '<<(anymultirange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '2',
+  amopopr => '&<(anymultirange,anymultirange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anyrange', amopstrategy => '2',
+  amopopr => '&<(anymultirange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '3',
+  amopopr => '&&(anymultirange,anymultirange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anyrange', amopstrategy => '3',
+  amopopr => '&&(anymultirange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '4',
+  amopopr => '&>(anymultirange,anymultirange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anyrange', amopstrategy => '4',
+  amopopr => '&>(anymultirange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '5',
+  amopopr => '>>(anymultirange,anymultirange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anyrange', amopstrategy => '5',
+  amopopr => '>>(anymultirange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '6',
+  amopopr => '-|-(anymultirange,anymultirange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anyrange', amopstrategy => '6',
+  amopopr => '-|-(anymultirange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '7',
+  amopopr => '@>(anymultirange,anymultirange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anyrange', amopstrategy => '7',
+  amopopr => '@>(anymultirange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '8',
+  amopopr => '<@(anymultirange,anymultirange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anyrange', amopstrategy => '8',
+  amopopr => '<@(anymultirange,anyrange)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anyelement', amopstrategy => '16',
+  amopopr => '@>(anymultirange,anyelement)', amopmethod => 'gist' },
+{ amopfamily => 'gist/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '18',
+  amopopr => '=(anymultirange,anymultirange)', amopmethod => 'gist' },
+
 # btree multirange_ops
 { amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
   amoprighttype => 'anymultirange', amopstrategy => '1',
index 9d423d535cd59985cc7b4154c1e43325178f020a..68d72ec732a446d192d929c099341a1b67296919 100644 (file)
   amprocrighttype => 'inet', amprocnum => '7', amproc => 'inet_gist_same' },
 { amprocfamily => 'gist/network_ops', amproclefttype => 'inet',
   amprocrighttype => 'inet', amprocnum => '9', amproc => 'inet_gist_fetch' },
+{ amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange',
+  amprocrighttype => 'anymultirange', amprocnum => '1',
+  amproc => 'multirange_gist_consistent' },
+{ amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange',
+  amprocrighttype => 'anymultirange', amprocnum => '2',
+  amproc => 'range_gist_union' },
+{ amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange',
+  amprocrighttype => 'anymultirange', amprocnum => '3',
+  amproc => 'multirange_gist_compress' },
+{ amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange',
+  amprocrighttype => 'anymultirange', amprocnum => '5',
+  amproc => 'range_gist_penalty' },
+{ amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange',
+  amprocrighttype => 'anymultirange', amprocnum => '6',
+  amproc => 'range_gist_picksplit' },
+{ amprocfamily => 'gist/multirange_ops', amproclefttype => 'anymultirange',
+  amprocrighttype => 'anymultirange', amprocnum => '7',
+  amproc => 'range_gist_same' },
 
 # gin
 { amprocfamily => 'gin/array_ops', amproclefttype => 'anyarray',
index 4c5e475ff7b5982aeedfca595d27eb6258230312..12cad6941866f21d18f515c4df9ed8b608449e01 100644 (file)
   opcintype => 'anymultirange' },
 { opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
   opcintype => 'anymultirange' },
+{ opcmethod => 'gist', opcname => 'multirange_ops', opcfamily => 'gist/multirange_ops',
+  opcintype => 'anymultirange', opckeytype => 'anyrange' },
 { opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
   opcintype => 'box' },
 { opcmethod => 'spgist', opcname => 'quad_point_ops',
index fe42dfc0f8ffbeee759e298087d6d6e4e81c7738..ac8338f34b12085d47b3887bfa1783a9028584a5 100644 (file)
   opfmethod => 'btree', opfname => 'multirange_ops' },
 { oid => '4225',
   opfmethod => 'hash', opfname => 'multirange_ops' },
+{ oid => '8021',
+  opfmethod => 'gist', opfname => 'multirange_ops' },
 
 ]
index 834ee86c791b4d2f8a6cb812ddfba995e5e3ffa6..139f4a08bd5b749a57035d23976a4d40dbb45907 100644 (file)
 { oid => '3881', descr => 'GiST support',
   proname => 'range_gist_same', prorettype => 'internal',
   proargtypes => 'anyrange anyrange internal', prosrc => 'range_gist_same' },
+{ oid => '8017', descr => 'GiST support',
+  proname => 'multirange_gist_consistent', prorettype => 'bool',
+  proargtypes => 'internal anymultirange int2 oid internal',
+  prosrc => 'multirange_gist_consistent' },
+{ oid => '8019', descr => 'GiST support',
+  proname => 'multirange_gist_compress', prorettype => 'internal',
+  proargtypes => 'internal',
+  prosrc => 'multirange_gist_compress' },
 { oid => '3902', descr => 'hash a range',
   proname => 'hash_range', prorettype => 'int4', proargtypes => 'anyrange',
   prosrc => 'hash_range' },
index ff2e58744a908eb80307d0cb166c12c12249fc70..1d877f08b56be3aafba7f35c293ea21c9f30452f 100644 (file)
@@ -129,5 +129,7 @@ extern void multirange_get_bounds(TypeCacheEntry *rangetyp,
                                  RangeBound *lower, RangeBound *upper);
 extern RangeType *multirange_get_range(TypeCacheEntry *rangetyp,
                                       const MultirangeType *multirange, int i);
+extern RangeType *multirange_get_union_range(TypeCacheEntry *rangetyp,
+                                            const MultirangeType *mr);
 
 #endif                         /* MULTIRANGETYPES_H */
index aa7232efc5779c870eb8aeaff934eb2f01ff441c..86011a02a1e69d1fee425d46df96af645890631f 100644 (file)
@@ -2219,6 +2219,263 @@ SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirang
  {[1,2),[3,4),[7,8),[9,10)}
 (1 row)
 
+-- test GiST index
+create table test_multirange_gist(mr int4multirange);
+insert into test_multirange_gist select int4multirange(int4range(g, g+10),int4range(g+20, g+30),int4range(g+40, g+50)) from generate_series(1,2000) g;
+insert into test_multirange_gist select '{}'::int4multirange from generate_series(1,500) g;
+insert into test_multirange_gist select int4multirange(int4range(g, g+10000)) from generate_series(1,1000) g;
+insert into test_multirange_gist select int4multirange(int4range(NULL, g*10, '(]'), int4range(g*10, g*20, '(]')) from generate_series(1,100) g;
+insert into test_multirange_gist select int4multirange(int4range(g*10, g*20, '(]'), int4range(g*20, NULL, '(]')) from generate_series(1,100) g;
+create index test_mulrirange_gist_idx on test_multirange_gist using gist (mr);
+-- first, verify non-indexed results
+SET enable_seqscan    = t;
+SET enable_indexscan  = f;
+SET enable_bitmapscan = f;
+select count(*) from test_multirange_gist where mr @> 'empty'::int4range;
+ count 
+-------
+  3700
+(1 row)
+
+select count(*) from test_multirange_gist where mr = int4multirange(int4range(10,20), int4range(30,40), int4range(50,60));
+ count 
+-------
+     1
+(1 row)
+
+select count(*) from test_multirange_gist where mr @> 10;
+ count 
+-------
+   120
+(1 row)
+
+select count(*) from test_multirange_gist where mr @> int4range(10,20);
+ count 
+-------
+   111
+(1 row)
+
+select count(*) from test_multirange_gist where mr && int4range(10,20);
+ count 
+-------
+   139
+(1 row)
+
+select count(*) from test_multirange_gist where mr <@ int4range(10,50);
+ count 
+-------
+   500
+(1 row)
+
+select count(*) from test_multirange_gist where mr << int4range(100,500);
+ count 
+-------
+    54
+(1 row)
+
+select count(*) from test_multirange_gist where mr >> int4range(100,500);
+ count 
+-------
+  2053
+(1 row)
+
+select count(*) from test_multirange_gist where mr &< int4range(100,500);
+ count 
+-------
+   474
+(1 row)
+
+select count(*) from test_multirange_gist where mr &> int4range(100,500);
+ count 
+-------
+  2893
+(1 row)
+
+select count(*) from test_multirange_gist where mr -|- int4range(100,500);
+ count 
+-------
+     3
+(1 row)
+
+select count(*) from test_multirange_gist where mr @> '{}'::int4multirange;
+ count 
+-------
+  3700
+(1 row)
+
+select count(*) from test_multirange_gist where mr @> int4multirange(int4range(10,20), int4range(30,40));
+ count 
+-------
+   110
+(1 row)
+
+select count(*) from test_multirange_gist where mr && '{(10,20),(30,40),(50,60)}'::int4multirange;
+ count 
+-------
+   218
+(1 row)
+
+select count(*) from test_multirange_gist where mr <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+ count 
+-------
+   500
+(1 row)
+
+select count(*) from test_multirange_gist where mr << int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+    54
+(1 row)
+
+select count(*) from test_multirange_gist where mr >> int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  2053
+(1 row)
+
+select count(*) from test_multirange_gist where mr &< int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+   474
+(1 row)
+
+select count(*) from test_multirange_gist where mr &> int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  2893
+(1 row)
+
+select count(*) from test_multirange_gist where mr -|- int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+     3
+(1 row)
+
+-- now check same queries using index
+SET enable_seqscan    = f;
+SET enable_indexscan  = t;
+SET enable_bitmapscan = f;
+select count(*) from test_multirange_gist where mr @> 'empty'::int4range;
+ count 
+-------
+  3700
+(1 row)
+
+select count(*) from test_multirange_gist where mr = int4multirange(int4range(10,20), int4range(30,40), int4range(50,60));
+ count 
+-------
+     1
+(1 row)
+
+select count(*) from test_multirange_gist where mr @> 10;
+ count 
+-------
+   120
+(1 row)
+
+select count(*) from test_multirange_gist where mr @> int4range(10,20);
+ count 
+-------
+   111
+(1 row)
+
+select count(*) from test_multirange_gist where mr && int4range(10,20);
+ count 
+-------
+   139
+(1 row)
+
+select count(*) from test_multirange_gist where mr <@ int4range(10,50);
+ count 
+-------
+   500
+(1 row)
+
+select count(*) from test_multirange_gist where mr << int4range(100,500);
+ count 
+-------
+    54
+(1 row)
+
+select count(*) from test_multirange_gist where mr >> int4range(100,500);
+ count 
+-------
+  2053
+(1 row)
+
+select count(*) from test_multirange_gist where mr &< int4range(100,500);
+ count 
+-------
+   474
+(1 row)
+
+select count(*) from test_multirange_gist where mr &> int4range(100,500);
+ count 
+-------
+  2893
+(1 row)
+
+select count(*) from test_multirange_gist where mr -|- int4range(100,500);
+ count 
+-------
+     3
+(1 row)
+
+select count(*) from test_multirange_gist where mr @> '{}'::int4multirange;
+ count 
+-------
+  3700
+(1 row)
+
+select count(*) from test_multirange_gist where mr @> int4multirange(int4range(10,20), int4range(30,40));
+ count 
+-------
+   110
+(1 row)
+
+select count(*) from test_multirange_gist where mr && '{(10,20),(30,40),(50,60)}'::int4multirange;
+ count 
+-------
+   218
+(1 row)
+
+select count(*) from test_multirange_gist where mr <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+ count 
+-------
+   500
+(1 row)
+
+select count(*) from test_multirange_gist where mr << int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+    54
+(1 row)
+
+select count(*) from test_multirange_gist where mr >> int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  2053
+(1 row)
+
+select count(*) from test_multirange_gist where mr &< int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+   474
+(1 row)
+
+select count(*) from test_multirange_gist where mr &> int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  2893
+(1 row)
+
+select count(*) from test_multirange_gist where mr -|- int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+     3
+(1 row)
+
+drop table test_multirange_gist;
 --
 -- range_agg function
 --
index 6a1bbadc91af2c7f19a59cd5641ea13096032271..28dc995e5994fa4552884f14e86cd6a92809249d 100644 (file)
@@ -862,6 +862,60 @@ select count(*) from test_range_gist where ir -|- int4range(100,500);
      5
 (1 row)
 
+select count(*) from test_range_gist where ir @> '{}'::int4multirange;
+ count 
+-------
+  6200
+(1 row)
+
+select count(*) from test_range_gist where ir @> int4multirange(int4range(10,20), int4range(30,40));
+ count 
+-------
+   107
+(1 row)
+
+select count(*) from test_range_gist where ir && '{(10,20),(30,40),(50,60)}'::int4multirange;
+ count 
+-------
+   271
+(1 row)
+
+select count(*) from test_range_gist where ir <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+ count 
+-------
+  1060
+(1 row)
+
+select count(*) from test_range_gist where ir << int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+   189
+(1 row)
+
+select count(*) from test_range_gist where ir >> int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  3554
+(1 row)
+
+select count(*) from test_range_gist where ir &< int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  1029
+(1 row)
+
+select count(*) from test_range_gist where ir &> int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  4794
+(1 row)
+
+select count(*) from test_range_gist where ir -|- int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+     5
+(1 row)
+
 -- now check same queries using index
 SET enable_seqscan    = f;
 SET enable_indexscan  = t;
@@ -932,6 +986,60 @@ select count(*) from test_range_gist where ir -|- int4range(100,500);
      5
 (1 row)
 
+select count(*) from test_range_gist where ir @> '{}'::int4multirange;
+ count 
+-------
+  6200
+(1 row)
+
+select count(*) from test_range_gist where ir @> int4multirange(int4range(10,20), int4range(30,40));
+ count 
+-------
+   107
+(1 row)
+
+select count(*) from test_range_gist where ir && '{(10,20),(30,40),(50,60)}'::int4multirange;
+ count 
+-------
+   271
+(1 row)
+
+select count(*) from test_range_gist where ir <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+ count 
+-------
+  1060
+(1 row)
+
+select count(*) from test_range_gist where ir << int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+   189
+(1 row)
+
+select count(*) from test_range_gist where ir >> int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  3554
+(1 row)
+
+select count(*) from test_range_gist where ir &< int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  1029
+(1 row)
+
+select count(*) from test_range_gist where ir &> int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  4794
+(1 row)
+
+select count(*) from test_range_gist where ir -|- int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+     5
+(1 row)
+
 -- now check same queries using a bulk-loaded index
 drop index test_range_gist_idx;
 create index test_range_gist_idx on test_range_gist using gist (ir);
@@ -1001,6 +1109,60 @@ select count(*) from test_range_gist where ir -|- int4range(100,500);
      5
 (1 row)
 
+select count(*) from test_range_gist where ir @> '{}'::int4multirange;
+ count 
+-------
+  6200
+(1 row)
+
+select count(*) from test_range_gist where ir @> int4multirange(int4range(10,20), int4range(30,40));
+ count 
+-------
+   107
+(1 row)
+
+select count(*) from test_range_gist where ir && '{(10,20),(30,40),(50,60)}'::int4multirange;
+ count 
+-------
+   271
+(1 row)
+
+select count(*) from test_range_gist where ir <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+ count 
+-------
+  1060
+(1 row)
+
+select count(*) from test_range_gist where ir << int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+   189
+(1 row)
+
+select count(*) from test_range_gist where ir >> int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  3554
+(1 row)
+
+select count(*) from test_range_gist where ir &< int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  1029
+(1 row)
+
+select count(*) from test_range_gist where ir &> int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+  4794
+(1 row)
+
+select count(*) from test_range_gist where ir -|- int4multirange(int4range(100,200), int4range(400,500));
+ count 
+-------
+     5
+(1 row)
+
 -- test SP-GiST index that's been built incrementally
 create table test_range_spgist(ir int4range);
 create index test_range_spgist_idx on test_range_spgist using spgist (ir);
index 9d7af404c98c2630532175ad1f7d1a27116c44a9..2a2ee4dcdfd8fe70989d67df0e32880301e6708a 100644 (file)
@@ -414,6 +414,69 @@ SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultira
 SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
 SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
 
+-- test GiST index
+create table test_multirange_gist(mr int4multirange);
+insert into test_multirange_gist select int4multirange(int4range(g, g+10),int4range(g+20, g+30),int4range(g+40, g+50)) from generate_series(1,2000) g;
+insert into test_multirange_gist select '{}'::int4multirange from generate_series(1,500) g;
+insert into test_multirange_gist select int4multirange(int4range(g, g+10000)) from generate_series(1,1000) g;
+insert into test_multirange_gist select int4multirange(int4range(NULL, g*10, '(]'), int4range(g*10, g*20, '(]')) from generate_series(1,100) g;
+insert into test_multirange_gist select int4multirange(int4range(g*10, g*20, '(]'), int4range(g*20, NULL, '(]')) from generate_series(1,100) g;
+create index test_mulrirange_gist_idx on test_multirange_gist using gist (mr);
+
+-- first, verify non-indexed results
+SET enable_seqscan    = t;
+SET enable_indexscan  = f;
+SET enable_bitmapscan = f;
+
+select count(*) from test_multirange_gist where mr @> 'empty'::int4range;
+select count(*) from test_multirange_gist where mr = int4multirange(int4range(10,20), int4range(30,40), int4range(50,60));
+select count(*) from test_multirange_gist where mr @> 10;
+select count(*) from test_multirange_gist where mr @> int4range(10,20);
+select count(*) from test_multirange_gist where mr && int4range(10,20);
+select count(*) from test_multirange_gist where mr <@ int4range(10,50);
+select count(*) from test_multirange_gist where mr << int4range(100,500);
+select count(*) from test_multirange_gist where mr >> int4range(100,500);
+select count(*) from test_multirange_gist where mr &< int4range(100,500);
+select count(*) from test_multirange_gist where mr &> int4range(100,500);
+select count(*) from test_multirange_gist where mr -|- int4range(100,500);
+select count(*) from test_multirange_gist where mr @> '{}'::int4multirange;
+select count(*) from test_multirange_gist where mr @> int4multirange(int4range(10,20), int4range(30,40));
+select count(*) from test_multirange_gist where mr && '{(10,20),(30,40),(50,60)}'::int4multirange;
+select count(*) from test_multirange_gist where mr <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+select count(*) from test_multirange_gist where mr << int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_multirange_gist where mr >> int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_multirange_gist where mr &< int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_multirange_gist where mr &> int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_multirange_gist where mr -|- int4multirange(int4range(100,200), int4range(400,500));
+
+-- now check same queries using index
+SET enable_seqscan    = f;
+SET enable_indexscan  = t;
+SET enable_bitmapscan = f;
+
+select count(*) from test_multirange_gist where mr @> 'empty'::int4range;
+select count(*) from test_multirange_gist where mr = int4multirange(int4range(10,20), int4range(30,40), int4range(50,60));
+select count(*) from test_multirange_gist where mr @> 10;
+select count(*) from test_multirange_gist where mr @> int4range(10,20);
+select count(*) from test_multirange_gist where mr && int4range(10,20);
+select count(*) from test_multirange_gist where mr <@ int4range(10,50);
+select count(*) from test_multirange_gist where mr << int4range(100,500);
+select count(*) from test_multirange_gist where mr >> int4range(100,500);
+select count(*) from test_multirange_gist where mr &< int4range(100,500);
+select count(*) from test_multirange_gist where mr &> int4range(100,500);
+select count(*) from test_multirange_gist where mr -|- int4range(100,500);
+select count(*) from test_multirange_gist where mr @> '{}'::int4multirange;
+select count(*) from test_multirange_gist where mr @> int4multirange(int4range(10,20), int4range(30,40));
+select count(*) from test_multirange_gist where mr && '{(10,20),(30,40),(50,60)}'::int4multirange;
+select count(*) from test_multirange_gist where mr <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+select count(*) from test_multirange_gist where mr << int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_multirange_gist where mr >> int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_multirange_gist where mr &< int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_multirange_gist where mr &> int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_multirange_gist where mr -|- int4multirange(int4range(100,200), int4range(400,500));
+
+drop table test_multirange_gist;
+
 --
 -- range_agg function
 --
index b69efede3ae4977e322c1349c02a5dc2f74b7cc4..51eddabf60f4e46266fa977d33b11bb4eb44fb9f 100644 (file)
@@ -232,6 +232,15 @@ select count(*) from test_range_gist where ir >> int4range(100,500);
 select count(*) from test_range_gist where ir &< int4range(100,500);
 select count(*) from test_range_gist where ir &> int4range(100,500);
 select count(*) from test_range_gist where ir -|- int4range(100,500);
+select count(*) from test_range_gist where ir @> '{}'::int4multirange;
+select count(*) from test_range_gist where ir @> int4multirange(int4range(10,20), int4range(30,40));
+select count(*) from test_range_gist where ir && '{(10,20),(30,40),(50,60)}'::int4multirange;
+select count(*) from test_range_gist where ir <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+select count(*) from test_range_gist where ir << int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir >> int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir &< int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir &> int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir -|- int4multirange(int4range(100,200), int4range(400,500));
 
 -- now check same queries using index
 SET enable_seqscan    = f;
@@ -249,6 +258,15 @@ select count(*) from test_range_gist where ir >> int4range(100,500);
 select count(*) from test_range_gist where ir &< int4range(100,500);
 select count(*) from test_range_gist where ir &> int4range(100,500);
 select count(*) from test_range_gist where ir -|- int4range(100,500);
+select count(*) from test_range_gist where ir @> '{}'::int4multirange;
+select count(*) from test_range_gist where ir @> int4multirange(int4range(10,20), int4range(30,40));
+select count(*) from test_range_gist where ir && '{(10,20),(30,40),(50,60)}'::int4multirange;
+select count(*) from test_range_gist where ir <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+select count(*) from test_range_gist where ir << int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir >> int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir &< int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir &> int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir -|- int4multirange(int4range(100,200), int4range(400,500));
 
 -- now check same queries using a bulk-loaded index
 drop index test_range_gist_idx;
@@ -265,6 +283,15 @@ select count(*) from test_range_gist where ir >> int4range(100,500);
 select count(*) from test_range_gist where ir &< int4range(100,500);
 select count(*) from test_range_gist where ir &> int4range(100,500);
 select count(*) from test_range_gist where ir -|- int4range(100,500);
+select count(*) from test_range_gist where ir @> '{}'::int4multirange;
+select count(*) from test_range_gist where ir @> int4multirange(int4range(10,20), int4range(30,40));
+select count(*) from test_range_gist where ir && '{(10,20),(30,40),(50,60)}'::int4multirange;
+select count(*) from test_range_gist where ir <@ '{(10,30),(40,60),(70,90)}'::int4multirange;
+select count(*) from test_range_gist where ir << int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir >> int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir &< int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir &> int4multirange(int4range(100,200), int4range(400,500));
+select count(*) from test_range_gist where ir -|- int4multirange(int4range(100,200), int4range(400,500));
 
 -- test SP-GiST index that's been built incrementally
 create table test_range_spgist(ir int4range);