Multirange datatypes
authorAlexander Korotkov <[email protected]>
Sun, 20 Dec 2020 04:20:33 +0000 (07:20 +0300)
committerAlexander Korotkov <[email protected]>
Sun, 20 Dec 2020 04:20:33 +0000 (07:20 +0300)
Multiranges are basically sorted arrays of non-overlapping ranges with
set-theoretic operations defined over them.

Since v14, each range type automatically gets a corresponding multirange
datatype.  There are both manual and automatic mechanisms for naming multirange
types.  Once can specify multirange type name using multirange_type_name
attribute in CREATE TYPE.  Otherwise, a multirange type name is generated
automatically.  If the range type name contains "range" then we change that to
"multirange".  Otherwise, we add "_multirange" to the end.

Implementation of multiranges comes with a space-efficient internal
representation format, which evades extra paddings and duplicated storage of
oids.  Altogether this format allows fetching a particular range by its index
in O(n).

Statistic gathering and selectivity estimation are implemented for multiranges.
For this purpose, stored multirange is approximated as union range without gaps.
This field will likely need improvements in the future.

Catversion is bumped.

Discussion: https://postgr.es/m/CALNJ-vSUpQ_Y%3DjXvTxt1VYFztaBSsWVXeF1y6gTYQ4bOiWDLgQ%40mail.gmail.com
Discussion: https://postgr.es/m/a0b8026459d1e6167933be2104a6174e7d40d0ab.camel%40j-davis.com#fe7218c83b08068bfffb0c5293eceda0
Author: Paul Jungwirth, revised by me
Reviewed-by: David Fetter, Corey Huinker, Jeff Davis, Pavel Stehule
Reviewed-by: Alvaro Herrera, Tom Lane, Isaac Morland, David G. Johnston
Reviewed-by: Zhihong Yu, Alexander Korotkov
67 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/datatype.sgml
doc/src/sgml/extend.sgml
doc/src/sgml/func.sgml
doc/src/sgml/rangetypes.sgml
doc/src/sgml/ref/create_type.sgml
src/backend/catalog/pg_range.c
src/backend/catalog/pg_type.c
src/backend/commands/typecmds.c
src/backend/executor/functions.c
src/backend/parser/parse_coerce.c
src/backend/utils/adt/Makefile
src/backend/utils/adt/multirangetypes.c [new file with mode: 0644]
src/backend/utils/adt/multirangetypes_selfuncs.c [new file with mode: 0644]
src/backend/utils/adt/pg_upgrade_support.c
src/backend/utils/adt/pseudotypes.c
src/backend/utils/adt/rangetypes.c
src/backend/utils/adt/rangetypes_typanalyze.c
src/backend/utils/cache/lsyscache.c
src/backend/utils/cache/syscache.c
src/backend/utils/cache/typcache.c
src/backend/utils/fmgr/funcapi.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h
src/bin/pg_dump/t/002_pg_dump.pl
src/include/access/tupmacs.h
src/include/catalog/binary_upgrade.h
src/include/catalog/catversion.h
src/include/catalog/pg_aggregate.dat
src/include/catalog/pg_amop.dat
src/include/catalog/pg_amproc.dat
src/include/catalog/pg_cast.dat
src/include/catalog/pg_opclass.dat
src/include/catalog/pg_operator.dat
src/include/catalog/pg_opfamily.dat
src/include/catalog/pg_proc.dat
src/include/catalog/pg_range.dat
src/include/catalog/pg_range.h
src/include/catalog/pg_type.dat
src/include/catalog/pg_type.h
src/include/commands/typecmds.h
src/include/utils/lsyscache.h
src/include/utils/multirangetypes.h [new file with mode: 0644]
src/include/utils/rangetypes.h
src/include/utils/selfuncs.h
src/include/utils/syscache.h
src/include/utils/typcache.h
src/pl/plpgsql/src/pl_comp.c
src/test/regress/expected/dependency.out
src/test/regress/expected/hash_func.out
src/test/regress/expected/multirangetypes.out [new file with mode: 0644]
src/test/regress/expected/opr_sanity.out
src/test/regress/expected/plpgsql.out
src/test/regress/expected/polymorphism.out
src/test/regress/expected/rangefuncs.out
src/test/regress/expected/rangetypes.out
src/test/regress/expected/sanity_check.out
src/test/regress/expected/type_sanity.out
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/hash_func.sql
src/test/regress/sql/multirangetypes.sql [new file with mode: 0644]
src/test/regress/sql/opr_sanity.sql
src/test/regress/sql/polymorphism.sql
src/test/regress/sql/rangetypes.sql
src/test/regress/sql/type_sanity.sql
src/tools/pgindent/typedefs.list

index 89ca59b92b5837456e508afbdc145a60ac2588da..d98863604673a7629c4b787120d8d748664080fc 100644 (file)
@@ -6237,6 +6237,16 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       </para></entry>
      </row>
 
+     <row>
+      <entry role="catalog_table_entry"><para role="column_definition">
+       <structfield>rngmultitypid</structfield> <type>oid</type>
+       (references <link linkend="catalog-pg-type"><structname>pg_type</structname></link>.<structfield>oid</structfield>)
+      </para>
+      <para>
+       OID of the multirange type for this range type
+      </para></entry>
+     </row>
+
      <row>
       <entry role="catalog_table_entry"><para role="column_definition">
        <structfield>rngcollation</structfield> <type>oid</type>
@@ -8671,8 +8681,9 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
        <literal>c</literal> for a composite type (e.g., a table's row type),
        <literal>d</literal> for a domain,
        <literal>e</literal> for an enum type,
-       <literal>p</literal> for a pseudo-type, or
-       <literal>r</literal> for a range type.
+       <literal>p</literal> for a pseudo-type,
+       <literal>r</literal> for a range type, or
+       <literal>m</literal> for a multirange type.
        See also <structfield>typrelid</structfield> and
        <structfield>typbasetype</structfield>.
       </para></entry>
index 9eb19a1c616303536508eadc7fc3081f3fa1ab80..58d168c763e96577ae93f6bcb8d1923a134de085 100644 (file)
@@ -4907,6 +4907,10 @@ SELECT * FROM pg_attribute
     <primary>anyrange</primary>
    </indexterm>
 
+   <indexterm zone="datatype-pseudo">
+    <primary>anymultirange</primary>
+   </indexterm>
+
    <indexterm zone="datatype-pseudo">
     <primary>anycompatible</primary>
    </indexterm>
@@ -4923,6 +4927,10 @@ SELECT * FROM pg_attribute
     <primary>anycompatiblerange</primary>
    </indexterm>
 
+   <indexterm zone="datatype-pseudo">
+    <primary>anycompatiblemultirange</primary>
+   </indexterm>
+
    <indexterm zone="datatype-pseudo">
     <primary>void</primary>
    </indexterm>
@@ -5034,6 +5042,13 @@ SELECT * FROM pg_attribute
         <xref linkend="rangetypes"/>).</entry>
        </row>
 
+       <row>
+        <entry><type>anymultirange</type></entry>
+        <entry>Indicates that a function accepts any multirange data type
+        (see <xref linkend="extend-types-polymorphic"/> and
+        <xref linkend="rangetypes"/>).</entry>
+       </row>
+
        <row>
         <entry><type>anycompatible</type></entry>
         <entry>Indicates that a function accepts any data type,
@@ -5063,6 +5078,14 @@ SELECT * FROM pg_attribute
         <xref linkend="rangetypes"/>).</entry>
        </row>
 
+       <row>
+        <entry><type>anycompatiblemultirange</type></entry>
+        <entry>Indicates that a function accepts any multirange data type,
+        with automatic promotion of multiple arguments to a common data type
+        (see <xref linkend="extend-types-polymorphic"/> and
+        <xref linkend="rangetypes"/>).</entry>
+       </row>
+
        <row>
         <entry><type>cstring</type></entry>
         <entry>Indicates that a function accepts or returns a null-terminated C string.</entry>
index 1c37026bb0508dab3d95675fc5282fe77a4c2901..6e3d82b85b804d5388b7dce6cfb3bc5c1764221c 100644 (file)
         </entry>
        </row>
 
+       <row>
+        <entry><type>anymultirange</type></entry>
+        <entry>Simple</entry>
+        <entry>Indicates that a function accepts any multirange data type
+        (see <xref linkend="rangetypes"/>)
+        </entry>
+       </row>
+
        <row>
         <entry><type>anycompatible</type></entry>
         <entry>Common</entry>
         with automatic promotion of multiple arguments to a common data type
         </entry>
        </row>
+
+       <row>
+        <entry><type>anycompatiblemultirange</type></entry>
+        <entry>Common</entry>
+        <entry>Indicates that a function accepts any multirange data type,
+        with automatic promotion of multiple arguments to a common data type
+        </entry>
+       </row>
       </tbody>
      </tgroup>
     </table>
      position declared as <type>anyarray</type> can have any array data type,
      but similarly they must all be the same type.  And similarly,
      positions declared as <type>anyrange</type> must all be the same range
-     type.  Furthermore, if there are
+     type.  Likewise for <type>anymultirange</type>.
+    </para>
+
+    <para>
+     Furthermore, if there are
      positions declared <type>anyarray</type> and others declared
      <type>anyelement</type>, the actual array type in the
      <type>anyarray</type> positions must be an array whose elements are
      the same type appearing in the <type>anyelement</type> positions.
-     Similarly, if there are positions declared <type>anyrange</type>
-     and others declared <type>anyelement</type> or <type>anyarray</type>,
-     the actual range type in the <type>anyrange</type> positions must be a
-     range whose subtype is the same type appearing in
-     the <type>anyelement</type> positions and the same as the element type
-     of the <type>anyarray</type> positions.
      <type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
      but adds the additional constraint that the actual type must not be
      an array type.
      be an enum type.
     </para>
 
+    <para>
+     Similarly, if there are positions declared <type>anyrange</type>
+     and others declared <type>anyelement</type> or <type>anyarray</type>,
+     the actual range type in the <type>anyrange</type> positions must be a
+     range whose subtype is the same type appearing in
+     the <type>anyelement</type> positions and the same as the element type
+     of the <type>anyarray</type> positions.
+     If there are positions declared <type>anymultirange</type>,
+     their actual multirange type must contain ranges matching parameters declared
+     <type>anyrange</type> and base elements matching parameters declared
+     <type>anyelement</type> and <type>anyarray</type>.
+    </para>
+
     <para>
      Thus, when more than one argument position is declared with a polymorphic
      type, the net effect is that only certain combinations of actual argument
      Selection of the common type considers the actual types
      of <type>anycompatible</type> and <type>anycompatiblenonarray</type>
      inputs, the array element types of <type>anycompatiblearray</type>
-     inputs, and the range subtypes of <type>anycompatiblerange</type>
+     inputs, the range subtypes of <type>anycompatiblerange</type> inputs,
+     and the multirange subtypes of <type>anycompatiablemultirange</type>
      inputs.  If <type>anycompatiblenonarray</type> is present then the
      common type is required to be a non-array type.  Once a common type is
      identified, arguments in <type>anycompatible</type>
 
     <para>
      Since there is no way to select a range type knowing only its subtype,
-     use of <type>anycompatiblerange</type> requires that all arguments
-     declared with that type have the same actual range type, and that that
-     type's subtype agree with the selected common type, so that no casting
-     of the range values is required.  As with <type>anyrange</type>, use
-     of <type>anycompatiblerange</type> as a function result type requires
-     that there be an <type>anycompatiblerange</type> argument.
+     use of <type>anycompatiblerange</type> and/or
+     <type>anycompatiblemultirange</type> requires that all arguments declared
+     with that type have the same actual range and/or multirange type, and that
+     that type's subtype agree with the selected common type, so that no casting
+     of the range values is required.  As with <type>anyrange</type> and
+     <type>anymultirange</type>, use of <type>anycompatiblerange</type> and
+     <type>anymultirange</type> as a function result type requires that there be
+     an <type>anycompatiblerange</type> or <type>anycompatiblemultirange</type>
+     argument.
     </para>
 
     <para>
index df29af6371a296c02e506fd4851d218b6efc8a03..d5cd705eebb7f0562ab1a4f67739e8223aca45c2 100644 (file)
@@ -17884,12 +17884,15 @@ SELECT NULLIF(value, '(none)') ...
   <para>
    <xref linkend="range-operators-table"/> shows the specialized operators
    available for range types.
+   <xref linkend="multirange-operators-table"/> shows the specialized operators
+   available for multirange types.
    In addition to those, the usual comparison operators shown in
    <xref linkend="functions-comparison-op-table"/> are available for range
-   types.  The comparison operators order first by the range lower bounds, and
-   only if those are equal do they compare the upper bounds.  This does not
-   usually result in a useful overall ordering, but the operators are provided
-   to allow unique indexes to be constructed on ranges.
+   and multirange types.  The comparison operators order first by the range lower
+   bounds, and only if those are equal do they compare the upper bounds.  The
+   multirange operators compare each range until one is unequal. This
+   does not usually result in a useful overall ordering, but the operators are
+   provided to allow unique indexes to be constructed on ranges.
   </para>
 
    <table id="range-operators-table">
@@ -18099,15 +18102,449 @@ SELECT NULLIF(value, '(none)') ...
     </tgroup>
    </table>
 
+   <table id="multirange-operators-table">
+    <title>Multirange Operators</title>
+    <tgroup cols="1">
+     <thead>
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        Operator
+       </para>
+       <para>
+        Description
+       </para>
+       <para>
+        Example(s)
+       </para></entry>
+      </row>
+     </thead>
+
+     <tbody>
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>@&gt;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the first multirange contain the second?
+       </para>
+       <para>
+        <literal>'{[2,4)}'::int4multirange @&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>@&gt;</literal> <type>anyrange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the multirange contain the range?
+       </para>
+       <para>
+        <literal>'{[2,4)}'::int4multirange @&gt; int4range(2,3)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>@&gt;</literal> <type>anyelement</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the multirange contain the element?
+       </para>
+       <para>
+        <literal>'{[2011-01-01,2011-03-01)}'::tsmultirange @&gt; '2011-01-10'::timestamp</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>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the first multirange contained by the second?
+       </para>
+       <para>
+        <literal>'{[2,4)}'::int4multirange &lt;@ '{[1,7)}'::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>anyrange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the multirange contained by the range?
+       </para>
+       <para>
+        <literal>'{[2,4)}'::int4multirange &lt;@ int4range(1,7)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anyrange</type> <literal>&lt;@</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the range contained by the multirange?
+       </para>
+       <para>
+        <literal>int4range(2,4) &lt;@ '{[1,7)}'::int4multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anyelement</type> <literal>&lt;@</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the element contained by the multirange?
+       </para>
+       <para>
+        <literal>42 &lt;@ '{[1,7)}'::int4multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>&amp;&amp;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Do the multiranges overlap, that is, have any elements in common?
+       </para>
+       <para>
+        <literal>'{[3,7)}'::int8multirange &amp;&amp; '{[4,12)}'::int8multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>&amp;&amp;</literal> <type>anyrange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the multirange overlap the range?
+       </para>
+       <para>
+        <literal>'{[3,7)}'::int8multirange &amp;&amp; int8range(4,12)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anyrange</type> <literal>&amp;&amp;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the range overlap the multirange?
+       </para>
+       <para>
+        <literal>int8range(3,7) &amp;&amp; '{[4,12)}'::int8multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>&lt;&lt;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the first multirange strictly left of the second?
+       </para>
+       <para>
+        <literal>'{[1,10)}'::int8multirange &lt;&lt; '{[100,110)}'::int8multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>&lt;&lt;</literal> <type>anyrange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the multirange strictly left of the range?
+       </para>
+       <para>
+        <literal>'{[1,10)}'::int8multirange &lt;&lt; int8range(100,110)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anyrange</type> <literal>&lt;&lt;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the range strictly left of the multirange?
+       </para>
+       <para>
+        <literal>int8range(1,10) &lt;&lt; '{[100,110)}'::int8multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>&gt;&gt;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the first multirange strictly right of the second?
+       </para>
+       <para>
+        <literal>'{[50,60)}'::int8multirange &gt;&gt; '{[20,30)}'::int8multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>&gt;&gt;</literal> <type>anyrange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the multirange strictly right of the range?
+       </para>
+       <para>
+        <literal>'{[50,60)}'::int8multirange &gt;&gt; int8range(20,30)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anyrange</type> <literal>&gt;&gt;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the range strictly right of the multirange?
+       </para>
+       <para>
+        <literal>int8range(50,60) &gt;&gt; '{[20,30)}'::int8multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>&amp;&lt;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the first multirange not extend to the right of the second?
+       </para>
+       <para>
+        <literal>'{[1,20)}'::int8multirange &amp;&lt; '{[18,20)}'::int8multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>&amp;&lt;</literal> <type>anyrange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the multirange not extend to the right of the range?
+       </para>
+       <para>
+        <literal>'{[1,20)}'::int8multirange &amp;&lt; int8range(18,20)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anyrange</type> <literal>&amp;&lt;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the range not extend to the right of the multirange?
+       </para>
+       <para>
+        <literal>int8range(1,20) &amp;&lt; '{[18,20)}'::int8multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>&amp;&gt;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the first multirange not extend to the left of the second?
+       </para>
+       <para>
+        <literal>'{[7,20)}'::int8multirange &amp;&gt; '{[5,10)}'::int8multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>&amp;&gt;</literal> <type>anyrange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the multirange not extend to the left of the range?
+       </para>
+       <para>
+        <literal>'{[7,20)}'::int8multirange &amp;&gt; int8range(5,10)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anyrange</type> <literal>&amp;&gt;</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Does the range not extend to the left of the multirange?
+       </para>
+       <para>
+        <literal>int8range(7,20) &amp;&gt; '{[5,10)}'::int8multirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>-|-</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Are the multiranges adjacent?
+       </para>
+       <para>
+        <literal>'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>-|-</literal> <type>anyrange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the multirange adjacent to the range?
+       </para>
+       <para>
+        <literal>'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anyrange</type> <literal>-|-</literal> <type>anymultirange</type>
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the range adjacent to the multirange?
+       </para>
+       <para>
+        <literal>numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>+</literal> <type>anymultirange</type>
+        <returnvalue>anymultirange</returnvalue>
+       </para>
+       <para>
+        Computes the union of the multiranges.  The multiranges need not overlap
+        or be adjacent.
+       </para>
+       <para>
+        <literal>'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literal>
+        <returnvalue>{[5,10), [15,20)}</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>*</literal> <type>anymultirange</type>
+        <returnvalue>anymultirange</returnvalue>
+       </para>
+       <para>
+        Computes the intersection of the multiranges.
+       </para>
+       <para>
+        <literal>'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literal>
+        <returnvalue>{[10,15)}</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <type>anymultirange</type> <literal>-</literal> <type>anymultirange</type>
+        <returnvalue>anymultirange</returnvalue>
+       </para>
+       <para>
+        Computes the difference of the multiranges.
+       </para>
+       <para>
+        <literal>'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literal>
+        <returnvalue>{[5,10), [15,20)}</returnvalue>
+       </para></entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
   <para>
    The left-of/right-of/adjacent operators always return false when an empty
-   range is involved; that is, an empty range is not considered to be either
-   before or after any other range.
+   range or multirange is involved; that is, an empty range is not considered to
+   be either before or after any other range.
+  </para>
+
+  <para>
+   Elsewhere empty ranges and multiranges are treated as the additive identity:
+   anything unioned with an empty value is itself. Anything minus an empty
+   value is itself. An empty multirange has exactly the same points as an empty
+   range. Every range contains the empty range. Every multirange contains as many
+   empty ranges as you like.
+  </para>
+
+  <para>
+   The range union and difference operators will fail if the resulting range would
+   need to contain two disjoint sub-ranges, as such a range cannot be
+   represented. There are separate operators for union and difference that take
+   multirange parameters and return a multirange, and they do not fail even if
+   their arguments are disjoint. So if you need a union or difference operation
+   for ranges that may be disjoint, you can avoid errors by first casting your
+   ranges to multiranges.
   </para>
 
   <para>
    <xref linkend="range-functions-table"/> shows the functions
    available for use with range types.
+   <xref linkend="multirange-functions-table"/> shows the functions
+   available for use with multirange types.
   </para>
 
    <table id="range-functions-table">
@@ -18269,10 +18706,185 @@ SELECT NULLIF(value, '(none)') ...
     </tgroup>
    </table>
 
+   <table id="multirange-functions-table">
+    <title>Multirange Functions</title>
+    <tgroup cols="1">
+     <thead>
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        Function
+       </para>
+       <para>
+        Description
+       </para>
+       <para>
+        Example(s)
+       </para></entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>lower</primary>
+        </indexterm>
+        <function>lower</function> ( <type>anymultirange</type> )
+        <returnvalue>anyelement</returnvalue>
+       </para>
+       <para>
+        Extracts the lower bound of the multirange (<literal>NULL</literal> if the
+        multirange is empty or the lower bound is infinite).
+       </para>
+       <para>
+        <literal>lower('{[1.1,2.2)}'::nummultirange)</literal>
+        <returnvalue>1.1</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>upper</primary>
+        </indexterm>
+        <function>upper</function> ( <type>anymultirange</type> )
+        <returnvalue>anyelement</returnvalue>
+       </para>
+       <para>
+        Extracts the upper bound of the multirange (<literal>NULL</literal> if the
+        multirange is empty or the upper bound is infinite).
+       </para>
+       <para>
+        <literal>upper('{[1.1,2.2)}'::nummultirange)</literal>
+        <returnvalue>2.2</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>isempty</primary>
+        </indexterm>
+        <function>isempty</function> ( <type>anymultirange</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the multirange empty?
+       </para>
+       <para>
+        <literal>isempty('{[1.1,2.2)}'::nummultirange)</literal>
+        <returnvalue>f</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>lower_inc</primary>
+        </indexterm>
+        <function>lower_inc</function> ( <type>anymultirange</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the multirange's lower bound inclusive?
+       </para>
+       <para>
+        <literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>upper_inc</primary>
+        </indexterm>
+        <function>upper_inc</function> ( <type>anymultirange</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the multirange's upper bound inclusive?
+       </para>
+       <para>
+        <literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal>
+        <returnvalue>f</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>lower_inf</primary>
+        </indexterm>
+        <function>lower_inf</function> ( <type>anymultirange</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the multirange's lower bound infinite?
+       </para>
+       <para>
+        <literal>lower_inf('{(,)}'::datemultirange)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>upper_inf</primary>
+        </indexterm>
+        <function>upper_inf</function> ( <type>anymultirange</type> )
+        <returnvalue>boolean</returnvalue>
+       </para>
+       <para>
+        Is the multirange's upper bound infinite?
+       </para>
+       <para>
+        <literal>upper_inf('{(,)}'::datemultirange)</literal>
+        <returnvalue>t</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>range_merge</primary>
+        </indexterm>
+        <function>range_merge</function> ( <type>anymultirange</type> )
+        <returnvalue>anyrange</returnvalue>
+       </para>
+       <para>
+        Computes the smallest range that includes the entire multirange.
+       </para>
+       <para>
+        <literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal>
+        <returnvalue>[1,4)</returnvalue>
+       </para></entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>multirange</primary>
+        </indexterm>
+        <function>multirange</function> ( <type>anyrange</type> )
+        <returnvalue>anymultirange</returnvalue>
+       </para>
+       <para>
+        Returns a multirange containing just the given range.
+       </para>
+       <para>
+        <literal>multirange('[1,2)'::int4range)</literal>
+        <returnvalue>{[1,2)}</returnvalue>
+       </para></entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
   <para>
    The <function>lower_inc</function>, <function>upper_inc</function>,
    <function>lower_inf</function>, and <function>upper_inf</function>
-   functions all return false for an empty range.
+   functions all return false for an empty range or multirange.
   </para>
   </sect1>
 
@@ -18604,6 +19216,36 @@ SELECT NULLIF(value, '(none)') ...
        <entry>Yes</entry>
       </row>
 
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>range_agg</primary>
+        </indexterm>
+        <function>range_agg</function> ( <parameter>value</parameter>
+         <type>anyrange</type> )
+        <returnvalue>anymultirange</returnvalue>
+       </para>
+       <para>
+        Computes the union of the non-null input values.
+       </para></entry>
+       <entry>No</entry>
+      </row>
+
+      <row>
+       <entry role="func_table_entry"><para role="func_signature">
+        <indexterm>
+         <primary>range_intersect_agg</primary>
+        </indexterm>
+        <function>range_intersect_agg</function> ( <parameter>value</parameter>
+         <type>anyrange</type> )
+        <returnvalue>anymultirange</returnvalue>
+       </para>
+       <para>
+        Computes the intersection of the non-null input values.
+       </para></entry>
+       <entry>No</entry>
+      </row>
+
       <row>
        <entry role="func_table_entry"><para role="func_signature">
         <indexterm>
index b75fb3a3929b3bd9eb74432aeccb22ba925f44c8..83aa9bc4e9e40d5a0aa7c162134ea49de77e8955 100644 (file)
   ranges from an instrument, and so forth can also be useful.
  </para>
 
+ <para>
+  Every range type has a corresponding multirange type. A multirange is
+  an ordered list of non-continguous, non-empty, non-null ranges. Most
+  range operators also work on multiranges, and they have a few functions
+  of their own.
+ </para>
+
  <sect2 id="rangetypes-builtin">
-  <title>Built-in Range Types</title>
+  <title>Built-in Range and Multirange Types</title>
 
  <para>
   PostgreSQL comes with the following built-in range types:
   <itemizedlist>
     <listitem>
       <para>
-       <type>int4range</type> &mdash; Range of <type>integer</type>
+       <type>int4range</type> &mdash; Range of <type>integer</type>,
+       <type>int4multirange</type> &mdash; corresponding Multirange
       </para>
     </listitem>
     <listitem>
       <para>
-       <type>int8range</type> &mdash; Range of <type>bigint</type>
+       <type>int8range</type> &mdash; Range of <type>bigint</type>,
+       <type>int8multirange</type> &mdash; corresponding Multirange
       </para>
     </listitem>
     <listitem>
       <para>
-       <type>numrange</type> &mdash; Range of <type>numeric</type>
+       <type>numrange</type> &mdash; Range of <type>numeric</type>,
+       <type>nummultirange</type> &mdash; corresponding Multirange
       </para>
     </listitem>
     <listitem>
       <para>
-       <type>tsrange</type> &mdash; Range of <type>timestamp without time zone</type>
+       <type>tsrange</type> &mdash; Range of <type>timestamp without time zone</type>,
+       <type>tsmultirange</type> &mdash; corresponding Multirange
       </para>
     </listitem>
     <listitem>
       <para>
-       <type>tstzrange</type> &mdash; Range of <type>timestamp with time zone</type>
+       <type>tstzrange</type> &mdash; Range of <type>timestamp with time zone</type>,
+       <type>tstzmultirange</type> &mdash; corresponding Multirange
       </para>
     </listitem>
     <listitem>
       <para>
-       <type>daterange</type> &mdash; Range of <type>date</type>
+       <type>daterange</type> &mdash; Range of <type>date</type>,
+       <type>datemultirange</type> &mdash; corresponding Multirange
       </para>
     </listitem>
   </itemizedlist>
@@ -232,10 +245,30 @@ SELECT '[4,4]'::int4range;
 SELECT '[4,4)'::int4range;
 </programlisting>
   </para>
+
+  <para>
+   The input for a multirange is curly brackets (<literal>{</literal> and
+   <literal>}</literal>) containing zero or more valid ranges,
+   separated by commas. Whitespace is permitted around the brackets and
+   commas. This is intended to be reminiscent of array syntax, although
+   multiranges are much simpler: they have just one dimension and there is
+   no need to quote their contents. (The bounds of their ranges may be
+   quoted as above however.)
+  </para>
+
+  <para>
+  Examples:
+<programlisting>
+SELECT '{}'::int4multirange;
+SELECT '{[3,7)}'::int4multirange;
+SELECT '{[3,7), [8,9)}'::int4multirange;
+</programlisting>
+  </para>
+
  </sect2>
 
  <sect2 id="rangetypes-construct">
-  <title>Constructing Ranges</title>
+  <title>Constructing Ranges and Multiranges</title>
 
   <para>
    Each range type has a constructor function with the same name as the range
@@ -267,6 +300,19 @@ SELECT int8range(1, 14, '(]');
 
 -- Using NULL for either bound causes the range to be unbounded on that side.
 SELECT numrange(NULL, 2.2);
+</programlisting>
+  </para>
+
+  <para>
+   Each range type also has a multirange constructor with the same name as the
+   multirange type.  The constructor function takes zero or more arguments
+   which are all ranges of the appropriate type.
+   For example:
+
+<programlisting>
+SELECT nummultirange();
+SELECT nummultirange(numrange(1.0, 14.0));
+SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
 </programlisting>
   </para>
  </sect2>
@@ -341,6 +387,11 @@ SELECT '[1.234, 5.678]'::floatrange;
    function in this example.
   </para>
 
+  <para>
+   When you define your own range you automatically get a corresponding
+   multirange type.
+  </para>
+
   <para>
    Defining your own range type also allows you to specify a different
    subtype B-tree operator class or collation to use, so as to change the sort
index d575f166142451273d50dba85e7cf29915fc3e02..7d2d6aa0af846bf6ce8beac3674aa689fbb17be9 100644 (file)
@@ -33,6 +33,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> AS RANGE (
     [ , COLLATION = <replaceable class="parameter">collation</replaceable> ]
     [ , CANONICAL = <replaceable class="parameter">canonical_function</replaceable> ]
     [ , SUBTYPE_DIFF = <replaceable class="parameter">subtype_diff_function</replaceable> ]
+    [ , MULTIRANGE_TYPE_NAME = <replaceable class="parameter">multirange_type_name</replaceable> ]
 )
 
 CREATE TYPE <replaceable class="parameter">name</replaceable> (
@@ -174,6 +175,17 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
     the range type.  See <xref linkend="rangetypes-defining"/> for more
     information.
    </para>
+
+   <para>
+    The optional <replaceable class="parameter">multirange_type_name</replaceable>
+    parameter specifies the name of the corresponding multirange type.  If not
+    specified, this name is chosen automatically as follows.
+    If range type name contains <literal>range</literal> substring, then
+    multirange type name is formed by replacement of the <literal>range</literal>
+    substring with <literal>multirange</literal> substring in the range
+    type name.  Otherwise, multirange type name is formed by appending
+    <literal>_multirange</literal> suffix to the range type name.
+   </para>
   </refsect2>
 
   <refsect2>
@@ -630,6 +642,15 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><replaceable class="parameter">multirange_type_name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the corresponding multirange type.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">input_function</replaceable></term>
     <listitem>
index a606d8c3adb43ea2b3aa9626500053797c95ed8d..91b0fb0611a78369201e60990b9a7b0e55dce217 100644 (file)
@@ -35,7 +35,7 @@
 void
 RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
                        Oid rangeSubOpclass, RegProcedure rangeCanonical,
-                       RegProcedure rangeSubDiff)
+                       RegProcedure rangeSubDiff, Oid multirangeTypeOid)
 {
        Relation        pg_range;
        Datum           values[Natts_pg_range];
@@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
        HeapTuple       tup;
        ObjectAddress myself;
        ObjectAddress referenced;
+       ObjectAddress referencing;
        ObjectAddresses *addrs;
 
        pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -55,6 +56,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
        values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
        values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
        values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
+       values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
 
        tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
 
@@ -93,6 +95,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
        record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
        free_object_addresses(addrs);
 
+       /* record multirange type's dependency on the range type */
+       referencing.classId = TypeRelationId;
+       referencing.objectId = multirangeTypeOid;
+       referencing.objectSubId = 0;
+       recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
+
        table_close(pg_range, RowExclusiveLock);
 }
 
index 4252875ef5013ab70b6e0d9ea40eef9b7d2402c1..2aa686a640fbd4aa07b8a2039de62d8980f2710a 100644 (file)
@@ -28,6 +28,7 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "commands/typecmds.h"
+#include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "parser/scansup.h"
 #include "utils/acl.h"
@@ -37,6 +38,9 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
+static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
+                                                               bool tryOriginal);
+
 /* Potentially set by pg_upgrade_support functions */
 Oid                    binary_upgrade_next_pg_type_oid = InvalidOid;
 
@@ -863,31 +867,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
 char *
 makeArrayTypeName(const char *typeName, Oid typeNamespace)
 {
-       char       *arr = (char *) palloc(NAMEDATALEN);
-       int                     namelen = strlen(typeName);
-       int                     i;
-
-       /*
-        * The idea is to prepend underscores as needed until we make a name that
-        * doesn't collide with anything...
-        */
-       for (i = 1; i < NAMEDATALEN - 1; i++)
-       {
-               arr[i - 1] = '_';
-               if (i + namelen < NAMEDATALEN)
-                       strcpy(arr + i, typeName);
-               else
-               {
-                       memcpy(arr + i, typeName, NAMEDATALEN - i);
-                       truncate_identifier(arr, NAMEDATALEN, false);
-               }
-               if (!SearchSysCacheExists2(TYPENAMENSP,
-                                                                  CStringGetDatum(arr),
-                                                                  ObjectIdGetDatum(typeNamespace)))
-                       break;
-       }
+       char       *arr;
 
-       if (i >= NAMEDATALEN - 1)
+       arr = makeUniqueTypeName(typeName, typeNamespace, false);
+       if (arr == NULL)
                ereport(ERROR,
                                (errcode(ERRCODE_DUPLICATE_OBJECT),
                                 errmsg("could not form array type name for type \"%s\"",
@@ -958,3 +941,90 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
 
        return true;
 }
+
+
+/*
+ * makeMultirangeTypeName
+ *       - given a range type name, make a multirange type name for it
+ *
+ * caller is responsible for pfreeing the result
+ */
+char *
+makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
+{
+       char       *buf;
+       char       *rangestr;
+
+       /*
+        * If the range type name contains "range" then change that to
+        * "multirange". Otherwise add "_multirange" to the end.
+        */
+       rangestr = strstr(rangeTypeName, "range");
+       if (rangestr)
+       {
+               char       *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
+
+               buf = psprintf("%s%s%s", prefix, "multi", rangestr);
+       }
+       else
+               buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
+
+       /* clip it at NAMEDATALEN-1 bytes */
+       buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
+
+       if (SearchSysCacheExists2(TYPENAMENSP,
+                                                         CStringGetDatum(buf),
+                                                         ObjectIdGetDatum(typeNamespace)))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                errmsg("type \"%s\" already exists", buf),
+                                errdetail("Failed while creating a multirange type for type \"%s\".", rangeTypeName),
+                                errhint("You can manually specify a multirange type name using the \"multirange_type_name\" attribute")));
+
+       return pstrdup(buf);
+}
+
+/*
+ * makeUniqueTypeName
+ *             Generate a unique name for a prospective new type
+ *
+ * Given a typeName, return a new palloc'ed name by preprending underscores
+ * until a non-conflicting name results.
+ *
+ * If tryOriginal, first try with zero underscores.
+ */
+static char *
+makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
+{
+       int                     i;
+       int                     namelen;
+       char            dest[NAMEDATALEN];
+
+       Assert(strlen(typeName) <= NAMEDATALEN - 1);
+
+       if (tryOriginal &&
+               !SearchSysCacheExists2(TYPENAMENSP,
+                                                          CStringGetDatum(typeName),
+                                                          ObjectIdGetDatum(typeNamespace)))
+               return pstrdup(typeName);
+
+       /*
+        * The idea is to prepend underscores as needed until we make a name that
+        * doesn't collide with anything ...
+        */
+       namelen = strlen(typeName);
+       for (i = 1; i < NAMEDATALEN - 1; i++)
+       {
+               dest[i - 1] = '_';
+               strlcpy(dest + i, typeName, NAMEDATALEN - i);
+               if (namelen + i >= NAMEDATALEN)
+                       truncate_identifier(dest, NAMEDATALEN, false);
+
+               if (!SearchSysCacheExists2(TYPENAMENSP,
+                                                                  CStringGetDatum(dest),
+                                                                  ObjectIdGetDatum(typeNamespace)))
+                       return pstrdup(dest);
+       }
+
+       return NULL;
+}
index 7c0b2c3bf0223c790274dba417e85632d7202a81..0fcd8c8f16e2bffec502aa982079e01d9e38491c 100644 (file)
@@ -107,9 +107,14 @@ typedef struct
 
 /* Potentially set by pg_upgrade_support functions */
 Oid                    binary_upgrade_next_array_pg_type_oid = InvalidOid;
+Oid                    binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+Oid                    binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
 
 static void makeRangeConstructors(const char *name, Oid namespace,
                                                                  Oid rangeOid, Oid subtype);
+static void makeMultirangeConstructors(const char *name, Oid namespace,
+                                                                          Oid multirangeOid, Oid rangeOid,
+                                                                          Oid rangeArrayOid, Oid *castFuncOid);
 static Oid     findTypeInputFunction(List *procname, Oid typeOid);
 static Oid     findTypeOutputFunction(List *procname, Oid typeOid);
 static Oid     findTypeReceiveFunction(List *procname, Oid typeOid);
@@ -772,7 +777,8 @@ DefineDomain(CreateDomainStmt *stmt)
                typtype != TYPTYPE_COMPOSITE &&
                typtype != TYPTYPE_DOMAIN &&
                typtype != TYPTYPE_ENUM &&
-               typtype != TYPTYPE_RANGE)
+               typtype != TYPTYPE_RANGE &&
+               typtype != TYPTYPE_MULTIRANGE)
                ereport(ERROR,
                                (errcode(ERRCODE_DATATYPE_MISMATCH),
                                 errmsg("\"%s\" is not a valid base type for a domain",
@@ -1323,6 +1329,11 @@ checkEnumOwner(HeapTuple tup)
 /*
  * DefineRange
  *             Registers a new range type.
+ *
+ * Perhaps it might be worthwhile to set pg_type.typelem to the base type,
+ * and likewise on multiranges to set it to the range type. But having a
+ * non-zero typelem is treated elsewhere as a synonym for being an array,
+ * and users might have queries with that same assumption.
  */
 ObjectAddress
 DefineRange(CreateRangeStmt *stmt)
@@ -1331,7 +1342,12 @@ DefineRange(CreateRangeStmt *stmt)
        Oid                     typeNamespace;
        Oid                     typoid;
        char       *rangeArrayName;
+       char       *multirangeTypeName = NULL;
+       char       *multirangeArrayName;
+       Oid                     multirangeNamespace = InvalidOid;
        Oid                     rangeArrayOid;
+       Oid                     multirangeOid;
+       Oid                     multirangeArrayOid;
        Oid                     rangeSubtype = InvalidOid;
        List       *rangeSubOpclassName = NIL;
        List       *rangeCollationName = NIL;
@@ -1348,6 +1364,8 @@ DefineRange(CreateRangeStmt *stmt)
        AclResult       aclresult;
        ListCell   *lc;
        ObjectAddress address;
+       ObjectAddress mltrngaddress;
+       Oid                     castFuncOid;
 
        /* Convert list of names to a name and namespace */
        typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@@ -1431,6 +1449,16 @@ DefineRange(CreateRangeStmt *stmt)
                                                 errmsg("conflicting or redundant options")));
                        rangeSubtypeDiffName = defGetQualifiedName(defel);
                }
+               else if (strcmp(defel->defname, "multirange_type_name") == 0)
+               {
+                       if (multirangeTypeName != NULL)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_SYNTAX_ERROR),
+                                                errmsg("conflicting or redundant options")));
+                       /* we can look up the subtype name immediately */
+                       multirangeNamespace = QualifiedNameGetCreationNamespace(defGetQualifiedName(defel),
+                                                                                                                                       &multirangeTypeName);
+               }
                else
                        ereport(ERROR,
                                        (errcode(ERRCODE_SYNTAX_ERROR),
@@ -1496,8 +1524,10 @@ DefineRange(CreateRangeStmt *stmt)
        /* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
        alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
 
-       /* Allocate OID for array type */
+       /* Allocate OID for array type, its multirange, and its multirange array */
        rangeArrayOid = AssignTypeArrayOid();
+       multirangeOid = AssignTypeMultirangeOid();
+       multirangeArrayOid = AssignTypeMultirangeArrayOid();
 
        /* Create the pg_type entry */
        address =
@@ -1536,9 +1566,75 @@ DefineRange(CreateRangeStmt *stmt)
        Assert(typoid == InvalidOid || typoid == address.objectId);
        typoid = address.objectId;
 
+       /* Create the multirange that goes with it */
+       if (multirangeTypeName)
+       {
+               Oid             old_typoid;
+
+               /*
+                * Look to see if multirange type already exists.
+                */
+               old_typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
+                                                                        CStringGetDatum(multirangeTypeName),
+                                                                        ObjectIdGetDatum(multirangeNamespace));
+
+               /*
+                * If it's not a shell, see if it's an autogenerated array type, and if so
+                * rename it out of the way.
+                */
+               if (OidIsValid(old_typoid) && get_typisdefined(old_typoid))
+               {
+                       if (!moveArrayTypeName(old_typoid, multirangeTypeName, multirangeNamespace))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DUPLICATE_OBJECT),
+                                                errmsg("type \"%s\" already exists", multirangeTypeName)));
+               }
+       }
+       else
+       {
+               /* Generate multirange name automatically */
+               multirangeNamespace = typeNamespace;
+               multirangeTypeName = makeMultirangeTypeName(typeName, multirangeNamespace);
+       }
+
+       mltrngaddress =
+               TypeCreate(multirangeOid,       /* force assignment of this type OID */
+                                  multirangeTypeName,  /* type name */
+                                  multirangeNamespace, /* namespace */
+                                  InvalidOid,  /* relation oid (n/a here) */
+                                  0,                   /* relation kind (ditto) */
+                                  GetUserId(), /* owner's ID */
+                                  -1,                  /* internal size (always varlena) */
+                                  TYPTYPE_MULTIRANGE,  /* type-type (multirange type) */
+                                  TYPCATEGORY_RANGE,   /* type-category (range type) */
+                                  false,               /* multirange types are never preferred */
+                                  DEFAULT_TYPDELIM,    /* array element delimiter */
+                                  F_MULTIRANGE_IN, /* input procedure */
+                                  F_MULTIRANGE_OUT,    /* output procedure */
+                                  F_MULTIRANGE_RECV,   /* receive procedure */
+                                  F_MULTIRANGE_SEND,   /* send procedure */
+                                  InvalidOid,  /* typmodin procedure - none */
+                                  InvalidOid,  /* typmodout procedure - none */
+                                  F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
+                                  InvalidOid,  /* subscript procedure - none */
+                                  InvalidOid,  /* element type ID - none */
+                                  false,               /* this is not an array type */
+                                  multirangeArrayOid,  /* array type we are about to create */
+                                  InvalidOid,  /* base type ID (only for domains) */
+                                  NULL,                /* never a default type value */
+                                  NULL,                /* no binary form available either */
+                                  false,               /* never passed by value */
+                                  alignment,   /* alignment */
+                                  'x',                 /* TOAST strategy (always extended) */
+                                  -1,                  /* typMod (Domains only) */
+                                  0,                   /* Array dimensions of typbasetype */
+                                  false,               /* Type NOT NULL */
+                                  InvalidOid); /* type's collation (ranges never have one) */
+       Assert(multirangeOid == mltrngaddress.objectId);
+
        /* Create the entry in pg_range */
        RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
-                               rangeCanonical, rangeSubtypeDiff);
+                               rangeCanonical, rangeSubtypeDiff, multirangeOid);
 
        /*
         * Create the array type that goes with it.
@@ -1580,8 +1676,54 @@ DefineRange(CreateRangeStmt *stmt)
 
        pfree(rangeArrayName);
 
+       /* Create the multirange's array type */
+
+       multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
+
+       TypeCreate(multirangeArrayOid,  /* force assignment of this type OID */
+                          multirangeArrayName, /* type name */
+                          multirangeNamespace, /* namespace */
+                          InvalidOid,          /* relation oid (n/a here) */
+                          0,                           /* relation kind (ditto) */
+                          GetUserId(),         /* owner's ID */
+                          -1,                          /* internal size (always varlena) */
+                          TYPTYPE_BASE,        /* type-type (base type) */
+                          TYPCATEGORY_ARRAY,   /* type-category (array) */
+                          false,                       /* array types are never preferred */
+                          DEFAULT_TYPDELIM,    /* array element delimiter */
+                          F_ARRAY_IN,          /* input procedure */
+                          F_ARRAY_OUT,         /* output procedure */
+                          F_ARRAY_RECV,        /* receive procedure */
+                          F_ARRAY_SEND,        /* send procedure */
+                          InvalidOid,          /* typmodin procedure - none */
+                          InvalidOid,          /* typmodout procedure - none */
+                          F_ARRAY_TYPANALYZE,  /* analyze procedure */
+                          F_ARRAY_SUBSCRIPT_HANDLER,   /* array subscript procedure */
+                          multirangeOid,       /* element type ID */
+                          true,                        /* yes this is an array type */
+                          InvalidOid,          /* no further array type */
+                          InvalidOid,          /* base type ID */
+                          NULL,                        /* never a default type value */
+                          NULL,                        /* binary default isn't sent either */
+                          false,                       /* never passed by value */
+                          alignment,           /* alignment - same as range's */
+                          'x',                         /* ARRAY is always toastable */
+                          -1,                          /* typMod (Domains only) */
+                          0,                           /* Array dimensions of typbasetype */
+                          false,                       /* Type NOT NULL */
+                          InvalidOid);         /* typcollation */
+
        /* And create the constructor functions for this range type */
        makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
+       makeMultirangeConstructors(multirangeTypeName, typeNamespace,
+                                                          multirangeOid, typoid, rangeArrayOid,
+                                                          &castFuncOid);
+
+       /* Create cast from the range type to its multirange type */
+       CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
+
+       pfree(multirangeTypeName);
+       pfree(multirangeArrayName);
 
        return address;
 }
@@ -1659,6 +1801,149 @@ makeRangeConstructors(const char *name, Oid namespace,
        }
 }
 
+/*
+ * We make a separate multirange constructor for each range type
+ * so its name can include the base type, like range constructors do.
+ * If we had an anyrangearray polymorphic type we could use it here,
+ * but since each type has its own constructor name there's no need.
+ *
+ * Sets castFuncOid to the oid of the new constructor that can be used
+ * to cast from a range to a multirange.
+ */
+static void
+makeMultirangeConstructors(const char *name, Oid namespace,
+                                                  Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
+                                                  Oid *castFuncOid)
+{
+       ObjectAddress myself,
+                               referenced;
+       oidvector  *argtypes;
+       Datum           allParamTypes;
+       ArrayType  *allParameterTypes;
+       Datum           paramModes;
+       ArrayType  *parameterModes;
+
+       referenced.classId = TypeRelationId;
+       referenced.objectId = multirangeOid;
+       referenced.objectSubId = 0;
+
+       /* 0-arg constructor - for empty multiranges */
+       argtypes = buildoidvector(NULL, 0);
+       myself = ProcedureCreate(name,  /* name: same as multirange type */
+                                                        namespace,
+                                                        false, /* replace */
+                                                        false, /* returns set */
+                                                        multirangeOid, /* return type */
+                                                        BOOTSTRAP_SUPERUSERID, /* proowner */
+                                                        INTERNALlanguageId,    /* language */
+                                                        F_FMGR_INTERNAL_VALIDATOR,
+                                                        "multirange_constructor0", /* prosrc */
+                                                        NULL,  /* probin */
+                                                        PROKIND_FUNCTION,
+                                                        false, /* security_definer */
+                                                        false, /* leakproof */
+                                                        false, /* isStrict */
+                                                        PROVOLATILE_IMMUTABLE, /* volatility */
+                                                        PROPARALLEL_SAFE,      /* parallel safety */
+                                                        argtypes,      /* parameterTypes */
+                                                        PointerGetDatum(NULL), /* allParameterTypes */
+                                                        PointerGetDatum(NULL), /* parameterModes */
+                                                        PointerGetDatum(NULL), /* parameterNames */
+                                                        NIL,   /* parameterDefaults */
+                                                        PointerGetDatum(NULL), /* trftypes */
+                                                        PointerGetDatum(NULL), /* proconfig */
+                                                        InvalidOid,    /* prosupport */
+                                                        1.0,   /* procost */
+                                                        0.0);  /* prorows */
+
+       /*
+        * Make the constructor internally-dependent on the multirange type so
+        * that they go away silently when the type is dropped.  Note that pg_dump
+        * depends on this choice to avoid dumping the constructors.
+        */
+       recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+       pfree(argtypes);
+
+       /*
+        * 1-arg constructor - for casts
+        *
+        * In theory we shouldn't need both this and the vararg (n-arg)
+        * constructor, but having a separate 1-arg function lets us define casts
+        * against it.
+        */
+       argtypes = buildoidvector(&rangeOid, 1);
+       myself = ProcedureCreate(name,  /* name: same as multirange type */
+                                                        namespace,
+                                                        false, /* replace */
+                                                        false, /* returns set */
+                                                        multirangeOid, /* return type */
+                                                        BOOTSTRAP_SUPERUSERID, /* proowner */
+                                                        INTERNALlanguageId,    /* language */
+                                                        F_FMGR_INTERNAL_VALIDATOR,
+                                                        "multirange_constructor1", /* prosrc */
+                                                        NULL,  /* probin */
+                                                        PROKIND_FUNCTION,
+                                                        false, /* security_definer */
+                                                        false, /* leakproof */
+                                                        true,  /* isStrict */
+                                                        PROVOLATILE_IMMUTABLE, /* volatility */
+                                                        PROPARALLEL_SAFE,      /* parallel safety */
+                                                        argtypes,      /* parameterTypes */
+                                                        PointerGetDatum(NULL), /* allParameterTypes */
+                                                        PointerGetDatum(NULL), /* parameterModes */
+                                                        PointerGetDatum(NULL), /* parameterNames */
+                                                        NIL,   /* parameterDefaults */
+                                                        PointerGetDatum(NULL), /* trftypes */
+                                                        PointerGetDatum(NULL), /* proconfig */
+                                                        InvalidOid,    /* prosupport */
+                                                        1.0,   /* procost */
+                                                        0.0);  /* prorows */
+       /* ditto */
+       recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+       pfree(argtypes);
+       *castFuncOid = myself.objectId;
+
+       /* n-arg constructor - vararg */
+       argtypes = buildoidvector(&rangeArrayOid, 1);
+       allParamTypes = ObjectIdGetDatum(rangeArrayOid);
+       allParameterTypes = construct_array(&allParamTypes,
+                                                                               1, OIDOID,
+                                                                               sizeof(Oid), true, 'i');
+       paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
+       parameterModes = construct_array(&paramModes, 1, CHAROID,
+                                                                        1, true, 'c');
+       myself = ProcedureCreate(name,  /* name: same as multirange type */
+                                                        namespace,
+                                                        false, /* replace */
+                                                        false, /* returns set */
+                                                        multirangeOid, /* return type */
+                                                        BOOTSTRAP_SUPERUSERID, /* proowner */
+                                                        INTERNALlanguageId,    /* language */
+                                                        F_FMGR_INTERNAL_VALIDATOR,
+                                                        "multirange_constructor2", /* prosrc */
+                                                        NULL,  /* probin */
+                                                        PROKIND_FUNCTION,
+                                                        false, /* security_definer */
+                                                        false, /* leakproof */
+                                                        false, /* isStrict */
+                                                        PROVOLATILE_IMMUTABLE, /* volatility */
+                                                        PROPARALLEL_SAFE,      /* parallel safety */
+                                                        argtypes,      /* parameterTypes */
+                                                        PointerGetDatum(allParameterTypes),    /* allParameterTypes */
+                                                        PointerGetDatum(parameterModes),       /* parameterModes */
+                                                        PointerGetDatum(NULL), /* parameterNames */
+                                                        NIL,   /* parameterDefaults */
+                                                        PointerGetDatum(NULL), /* trftypes */
+                                                        PointerGetDatum(NULL), /* proconfig */
+                                                        InvalidOid,    /* prosupport */
+                                                        1.0,   /* procost */
+                                                        0.0);  /* prorows */
+       /* ditto */
+       recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
+       pfree(argtypes);
+       pfree(allParameterTypes);
+       pfree(parameterModes);
+}
 
 /*
  * Find suitable I/O and other support functions for a type.
@@ -2152,6 +2437,72 @@ AssignTypeArrayOid(void)
        return type_array_oid;
 }
 
+/*
+ *     AssignTypeMultirangeOid
+ *
+ *     Pre-assign the range type's multirange OID for use in pg_type.oid
+ */
+Oid
+AssignTypeMultirangeOid(void)
+{
+       Oid                     type_multirange_oid;
+
+       /* Use binary-upgrade override for pg_type.oid? */
+       if (IsBinaryUpgrade)
+       {
+               if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
+
+               type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
+               binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
+       }
+       else
+       {
+               Relation        pg_type = table_open(TypeRelationId, AccessShareLock);
+
+               type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+                                                                                                Anum_pg_type_oid);
+               table_close(pg_type, AccessShareLock);
+       }
+
+       return type_multirange_oid;
+}
+
+/*
+ *     AssignTypeMultirangeArrayOid
+ *
+ *     Pre-assign the range type's multirange array OID for use in pg_type.typarray
+ */
+Oid
+AssignTypeMultirangeArrayOid(void)
+{
+       Oid                     type_multirange_array_oid;
+
+       /* Use binary-upgrade override for pg_type.oid? */
+       if (IsBinaryUpgrade)
+       {
+               if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                        errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
+
+               type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
+               binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
+       }
+       else
+       {
+               Relation        pg_type = table_open(TypeRelationId, AccessShareLock);
+
+               type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
+                                                                                                          Anum_pg_type_oid);
+               table_close(pg_type, AccessShareLock);
+       }
+
+       return type_multirange_array_oid;
+}
+
 
 /*-------------------------------------------------------------------
  * DefineCompositeType
index 459a33375b14dcea4b829ce23af88096fd3e5834..ca8d637e73ab6f5668cd6fbdafebb635fd4bcc63 100644 (file)
@@ -1711,7 +1711,8 @@ check_sql_fn_retval(List *queryTreeLists,
        if (fn_typtype == TYPTYPE_BASE ||
                fn_typtype == TYPTYPE_DOMAIN ||
                fn_typtype == TYPTYPE_ENUM ||
-               fn_typtype == TYPTYPE_RANGE)
+               fn_typtype == TYPTYPE_RANGE ||
+               fn_typtype == TYPTYPE_MULTIRANGE)
        {
                /*
                 * For scalar-type returns, the target list must have exactly one
index da6c3ae4b5f6246ef8887fb9bd8469648fc612d2..e33618f9744d36e0b711cd4a2a6faf57fd818e8d 100644 (file)
@@ -190,19 +190,21 @@ coerce_type(ParseState *pstate, Node *node,
        if (targetTypeId == ANYARRAYOID ||
                targetTypeId == ANYENUMOID ||
                targetTypeId == ANYRANGEOID ||
+               targetTypeId == ANYMULTIRANGEOID ||
                targetTypeId == ANYCOMPATIBLEARRAYOID ||
-               targetTypeId == ANYCOMPATIBLERANGEOID)
+               targetTypeId == ANYCOMPATIBLERANGEOID ||
+               targetTypeId == ANYCOMPATIBLEMULTIRANGEOID)
        {
                /*
                 * Assume can_coerce_type verified that implicit coercion is okay.
                 *
                 * These cases are unlike the ones above because the exposed type of
-                * the argument must be an actual array, enum, or range type.  In
-                * particular the argument must *not* be an UNKNOWN constant.  If it
-                * is, we just fall through; below, we'll call the pseudotype's input
-                * function, which will produce an error.  Also, if what we have is a
-                * domain over array, enum, or range, we have to relabel it to its
-                * base type.
+                * the argument must be an actual array, enum, range, or multirange
+                * type.  In particular the argument must *not* be an UNKNOWN
+                * constant.  If it is, we just fall through; below, we'll call the
+                * pseudotype's input function, which will produce an error.  Also, if
+                * what we have is a domain over array, enum, range, or multirange, we
+                * have to relabel it to its base type.
                 *
                 * Note: currently, we can't actually see a domain-over-enum here,
                 * since the other functions in this file will not match such a
@@ -1570,8 +1572,8 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
  * 1) All arguments declared ANYELEMENT must have the same datatype.
  * 2) All arguments declared ANYARRAY must have the same datatype,
  *       which must be a varlena array type.
- * 3) All arguments declared ANYRANGE must have the same datatype,
- *       which must be a range type.
+ * 3) All arguments declared ANYRANGE or ANYMULTIRANGE must be a range or
+ *       multirange type, all derived from the same base datatype.
  * 4) If there are arguments of more than one of these polymorphic types,
  *       the array element type and/or range subtype must be the same as each
  *       other and the same as the ANYELEMENT type.
@@ -1586,8 +1588,8 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
  *       to a common supertype (chosen as per select_common_type's rules).
  *       ANYCOMPATIBLENONARRAY works like ANYCOMPATIBLE but also requires the
  *       common supertype to not be an array.  If there are ANYCOMPATIBLEARRAY
- *       or ANYCOMPATIBLERANGE arguments, their element types or subtypes are
- *       included while making the choice of common supertype.
+ *       or ANYCOMPATIBLERANGE or ANYCOMPATIBLEMULTIRANGE arguments, their element
+ *       types or subtypes are included while making the choice of common supertype.
  * 8) The resolved type of ANYCOMPATIBLEARRAY arguments will be the array
  *       type over the common supertype (which might not be the same array type
  *       as any of the original arrays).
@@ -1595,6 +1597,10 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
  *       (after domain flattening), since we have no preference rule that would
  *       let us choose one over another.  Furthermore, that range's subtype
  *       must exactly match the common supertype chosen by rule 7.
+ * 10) All ANYCOMPATIBLEMULTIRANGE arguments must be the exact same multirange
+ *       type (after domain flattening), since we have no preference rule that would
+ *       let us choose one over another.  Furthermore, that multirange's range's
+ *       subtype must exactly match the common supertype chosen by rule 7.
  *
  * Domains over arrays match ANYARRAY, and are immediately flattened to their
  * base type.  (Thus, for example, we will consider it a match if one ANYARRAY
@@ -1603,7 +1609,9 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
  * for ANYCOMPATIBLEARRAY and ANYCOMPATIBLENONARRAY.
  *
  * Similarly, domains over ranges match ANYRANGE or ANYCOMPATIBLERANGE,
- * and are immediately flattened to their base type.
+ * and are immediately flattened to their base type, and domains over
+ * multiranges match ANYMULTIRANGE or ANYCOMPATIBLEMULTIRANGE and are immediately
+ * flattened to their base type.
  *
  * Note that domains aren't currently considered to match ANYENUM,
  * even if their base type would match.
@@ -1621,8 +1629,12 @@ check_generic_type_consistency(const Oid *actual_arg_types,
        Oid                     elem_typeid = InvalidOid;
        Oid                     array_typeid = InvalidOid;
        Oid                     range_typeid = InvalidOid;
+       Oid                     multirange_typeid = InvalidOid;
        Oid                     anycompatible_range_typeid = InvalidOid;
        Oid                     anycompatible_range_typelem = InvalidOid;
+       Oid                     anycompatible_multirange_typeid = InvalidOid;
+       Oid                     anycompatible_multirange_typelem = InvalidOid;
+       Oid                     range_typelem = InvalidOid;
        bool            have_anynonarray = false;
        bool            have_anyenum = false;
        bool            have_anycompatible_nonarray = false;
@@ -1671,6 +1683,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
                                return false;
                        range_typeid = actual_type;
                }
+               else if (decl_type == ANYMULTIRANGEOID)
+               {
+                       if (actual_type == UNKNOWNOID)
+                               continue;
+                       actual_type = getBaseType(actual_type); /* flatten domains */
+                       if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+                               return false;
+                       multirange_typeid = actual_type;
+               }
                else if (decl_type == ANYCOMPATIBLEOID ||
                                 decl_type == ANYCOMPATIBLENONARRAYOID)
                {
@@ -1715,6 +1736,45 @@ check_generic_type_consistency(const Oid *actual_arg_types,
                                anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
                        }
                }
+               else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
+               {
+                       if (actual_type == UNKNOWNOID)
+                               continue;
+                       actual_type = getBaseType(actual_type); /* flatten domains */
+                       if (OidIsValid(anycompatible_multirange_typeid))
+                       {
+                               /* All ANYCOMPATIBLEMULTIRANGE arguments must be the same type */
+                               if (anycompatible_multirange_typeid != actual_type)
+                                       return false;
+                       }
+                       else
+                       {
+                               anycompatible_multirange_typeid = actual_type;
+                               anycompatible_multirange_typelem = get_multirange_range(actual_type);
+                               if (!OidIsValid(anycompatible_multirange_typelem))
+                                       return false;   /* not a multirange type */
+
+                               if (OidIsValid(anycompatible_range_typeid))
+                               {
+                                       /*
+                                        * ANYCOMPATIBLEMULTIRANGE and ANYCOMPATIBLERANGE
+                                        * arguments must match
+                                        */
+                                       if (anycompatible_range_typeid != anycompatible_multirange_typelem)
+                                               return false;
+                               }
+                               else
+                               {
+                                       anycompatible_range_typeid = anycompatible_multirange_typelem;
+                                       anycompatible_range_typelem = get_range_subtype(anycompatible_range_typeid);
+                                       if (!OidIsValid(anycompatible_range_typelem))
+                                               return false;   /* not a range type */
+                               }
+                               /* collect the subtype for common-supertype choice */
+                               anycompatible_actual_types[n_anycompatible_args++] =
+                                       anycompatible_range_typelem;
+                       }
+               }
        }
 
        /* Get the element type based on the array type, if we have one */
@@ -1761,8 +1821,6 @@ check_generic_type_consistency(const Oid *actual_arg_types,
        /* Get the element type based on the range type, if we have one */
        if (OidIsValid(range_typeid))
        {
-               Oid                     range_typelem;
-
                range_typelem = get_range_subtype(range_typeid);
                if (!OidIsValid(range_typelem))
                        return false;           /* should be a range, but isn't */
@@ -1781,6 +1839,45 @@ check_generic_type_consistency(const Oid *actual_arg_types,
                }
        }
 
+       /* Get the element type based on the multirange type, if we have one */
+       if (OidIsValid(multirange_typeid))
+       {
+               Oid                     multirange_typelem;
+
+               multirange_typelem = get_multirange_range(multirange_typeid);
+               if (!OidIsValid(multirange_typelem))
+                       return false;           /* should be a multirange, but isn't */
+
+               if (!OidIsValid(range_typeid))
+               {
+                       /*
+                        * If we don't have a range type yet, use the one we just got
+                        */
+                       range_typeid = multirange_typelem;
+                       range_typelem = get_range_subtype(multirange_typelem);
+                       if (!OidIsValid(range_typelem))
+                               return false;   /* should be a range, but isn't */
+               }
+               else if (multirange_typelem != range_typeid)
+               {
+                       /* otherwise, they better match */
+                       return false;
+               }
+
+               if (!OidIsValid(elem_typeid))
+               {
+                       /*
+                        * If we don't have an element type yet, use the one we just got
+                        */
+                       elem_typeid = range_typelem;
+               }
+               else if (range_typelem != elem_typeid)
+               {
+                       /* otherwise, they better match */
+                       return false;
+               }
+       }
+
        if (have_anynonarray)
        {
                /* require the element type to not be an array or domain over array */
@@ -1819,8 +1916,10 @@ check_generic_type_consistency(const Oid *actual_arg_types,
                }
 
                /*
-                * the anycompatible type must exactly match the range element type,
-                * if we were able to identify one
+                * The anycompatible type must exactly match the range element type,
+                * if we were able to identify one. This checks compatibility for
+                * anycompatiblemultirange too since that also sets
+                * anycompatible_range_typelem above.
                 */
                if (OidIsValid(anycompatible_range_typelem) &&
                        anycompatible_range_typelem != anycompatible_typeid)
@@ -1859,21 +1958,27 @@ check_generic_type_consistency(const Oid *actual_arg_types,
  *       argument's actual type as the function's return type.
  * 2) If return type is ANYARRAY, and any argument is ANYARRAY, use the
  *       argument's actual type as the function's return type.
- * 3) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE,
- *       use the argument's actual type as the function's return type.
- * 4) Otherwise, if return type is ANYELEMENT or ANYARRAY, and there is
+ * 3) Similarly, if return type is ANYRANGE or ANYMULTIRANGE, and any
+ *       argument is ANYRANGE or ANYMULTIRANGE, use that argument's
+ *       actual type, range type or multirange type as the function's return
+ *       type.
+ * 4) Otherwise, if return type is ANYMULTIRANGE, and any argument is
+ *       ANYMULTIRANGE, use the argument's actual type as the function's return
+ *       type. Or if any argument is ANYRANGE, use its multirange type as the
+ *       function's return type.
+ * 5) Otherwise, if return type is ANYELEMENT or ANYARRAY, and there is
  *       at least one ANYELEMENT, ANYARRAY, or ANYRANGE input, deduce the
  *       return type from those inputs, or throw error if we can't.
- * 5) Otherwise, if return type is ANYRANGE, throw error.  (We have no way to
- *       select a specific range type if the arguments don't include ANYRANGE.)
- * 6) ANYENUM is treated the same as ANYELEMENT except that if it is used
+ * 6) Otherwise, if return type is ANYRANGE or ANYMULTIRANGE, throw error.
+ *       (We have no way to select a specific range type if the arguments don't
+ *       include ANYRANGE.)
  *       (alone or in combination with plain ANYELEMENT), we add the extra
  *       condition that the ANYELEMENT type must be an enum.
- * 7) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
+ * 8) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
  *       we add the extra condition that the ANYELEMENT type must not be an array.
  *       (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
  *       is an extra restriction if not.)
- * 8) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLENONARRAY, and
+ * 9) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLENONARRAY, and
  *       ANYCOMPATIBLERANGE are handled by resolving the common supertype
  *       of those arguments (or their element types/subtypes, for array and range
  *       inputs), and then coercing all those arguments to the common supertype,
@@ -1927,10 +2032,15 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
        Oid                     elem_typeid = InvalidOid;
        Oid                     array_typeid = InvalidOid;
        Oid                     range_typeid = InvalidOid;
+       Oid                     multirange_typeid = InvalidOid;
        Oid                     anycompatible_typeid = InvalidOid;
        Oid                     anycompatible_array_typeid = InvalidOid;
        Oid                     anycompatible_range_typeid = InvalidOid;
        Oid                     anycompatible_range_typelem = InvalidOid;
+       Oid                     anycompatible_multirange_typeid = InvalidOid;
+       Oid                     anycompatible_multirange_typelem = InvalidOid;
+       Oid                     range_typelem;
+       Oid                     multirange_typelem;
        bool            have_anynonarray = (rettype == ANYNONARRAYOID);
        bool            have_anyenum = (rettype == ANYENUMOID);
        bool            have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID);
@@ -2015,6 +2125,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                                                   format_type_be(actual_type))));
                        range_typeid = actual_type;
                }
+               else if (decl_type == ANYMULTIRANGEOID)
+               {
+                       n_poly_args++;
+                       if (actual_type == UNKNOWNOID)
+                       {
+                               have_poly_unknowns = true;
+                               continue;
+                       }
+                       if (allow_poly && decl_type == actual_type)
+                               continue;               /* no new information here */
+                       actual_type = getBaseType(actual_type); /* flatten domains */
+                       if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("arguments declared \"anymultirange\" are not all alike"),
+                                                errdetail("%s versus %s",
+                                                                  format_type_be(multirange_typeid),
+                                                                  format_type_be(actual_type))));
+                       multirange_typeid = actual_type;
+               }
                else if (decl_type == ANYCOMPATIBLEOID ||
                                 decl_type == ANYCOMPATIBLENONARRAYOID)
                {
@@ -2083,6 +2213,40 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
                        }
                }
+               else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
+               {
+                       have_poly_anycompatible = true;
+                       if (actual_type == UNKNOWNOID)
+                               continue;
+                       if (allow_poly && decl_type == actual_type)
+                               continue;               /* no new information here */
+                       actual_type = getBaseType(actual_type); /* flatten domains */
+                       if (OidIsValid(anycompatible_multirange_typeid))
+                       {
+                               /* All ANYCOMPATIBLEMULTIRANGE arguments must be the same type */
+                               if (anycompatible_multirange_typeid != actual_type)
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                        errmsg("arguments declared \"anycompatiblemultirange\" are not all alike"),
+                                                        errdetail("%s versus %s",
+                                                                          format_type_be(anycompatible_multirange_typeid),
+                                                                          format_type_be(actual_type))));
+                       }
+                       else
+                       {
+                               anycompatible_multirange_typeid = actual_type;
+                               anycompatible_multirange_typelem = get_multirange_range(actual_type);
+                               anycompatible_range_typelem = get_range_subtype(anycompatible_multirange_typelem);
+                               if (!OidIsValid(anycompatible_multirange_typelem))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                        errmsg("argument declared %s is not a multirange type but type %s",
+                                                                       "anycompatiblemultirange",
+                                                                       format_type_be(actual_type))));
+                               /* collect the subtype for common-supertype choice */
+                               anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
+                       }
+               }
        }
 
        /*
@@ -2151,8 +2315,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                /* Get the element type based on the range type, if we have one */
                if (OidIsValid(range_typeid))
                {
-                       Oid                     range_typelem;
-
                        range_typelem = get_range_subtype(range_typeid);
                        if (!OidIsValid(range_typelem))
                                ereport(ERROR,
@@ -2181,6 +2343,61 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                                                   format_type_be(elem_typeid))));
                        }
                }
+               else
+                       range_typelem = InvalidOid;
+
+               /* Get the element type based on the multirange type, if we have one */
+               if (OidIsValid(multirange_typeid))
+               {
+                       multirange_typelem = get_multirange_range(multirange_typeid);
+                       if (!OidIsValid(multirange_typelem))
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("argument declared %s is not a multirange type but type %s",
+                                                               "anymultirange",
+                                                               format_type_be(multirange_typeid))));
+
+                       if (!OidIsValid(range_typeid))
+                       {
+                               /*
+                                * If we don't have a range type yet, use the one we just got
+                                */
+                               range_typeid = multirange_typelem;
+                               range_typelem = get_range_subtype(range_typeid);
+                       }
+                       else if (multirange_typelem != range_typeid)
+                       {
+                               /* otherwise, they better match */
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("argument declared %s is not consistent with argument declared %s",
+                                                               "anymultirange", "anyrange"),
+                                                errdetail("%s versus %s",
+                                                                  format_type_be(multirange_typeid),
+                                                                  format_type_be(range_typeid))));
+                       }
+
+                       if (!OidIsValid(elem_typeid))
+                       {
+                               /*
+                                * if we don't have an element type yet, use the one we just got
+                                */
+                               elem_typeid = range_typelem;
+                       }
+                       else if (range_typelem != elem_typeid)
+                       {
+                               /* otherwise, they better match */
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                                errmsg("argument declared %s is not consistent with argument declared %s",
+                                                               "anymultirange", "anyelement"),
+                                                errdetail("%s versus %s",
+                                                                  format_type_be(multirange_typeid),
+                                                                  format_type_be(elem_typeid))));
+                       }
+               }
+               else
+                       multirange_typelem = InvalidOid;
 
                if (!OidIsValid(elem_typeid))
                {
@@ -2189,6 +2406,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                elem_typeid = ANYELEMENTOID;
                                array_typeid = ANYARRAYOID;
                                range_typeid = ANYRANGEOID;
+                               multirange_typeid = ANYMULTIRANGEOID;
                        }
                        else
                        {
@@ -2288,6 +2506,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                anycompatible_typeid = ANYCOMPATIBLEOID;
                                anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID;
                                anycompatible_range_typeid = ANYCOMPATIBLERANGEOID;
+                               anycompatible_multirange_typeid = ANYCOMPATIBLEMULTIRANGEOID;
                        }
                        else
                        {
@@ -2319,6 +2538,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                declared_arg_types[j] = anycompatible_array_typeid;
                        else if (decl_type == ANYCOMPATIBLERANGEOID)
                                declared_arg_types[j] = anycompatible_range_typeid;
+                       else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
+                               declared_arg_types[j] = anycompatible_multirange_typeid;
                }
        }
 
@@ -2369,6 +2590,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                                }
                                declared_arg_types[j] = range_typeid;
                        }
+                       else if (decl_type == ANYMULTIRANGEOID)
+                       {
+                               if (!OidIsValid(multirange_typeid))
+                               {
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                        errmsg("could not find multirange type for data type %s",
+                                                                       format_type_be(elem_typeid))));
+                               }
+                               declared_arg_types[j] = multirange_typeid;
+                       }
                }
        }
 
@@ -2405,6 +2637,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                return range_typeid;
        }
 
+       /* if we return ANYMULTIRANGE use the appropriate argument type */
+       if (rettype == ANYMULTIRANGEOID)
+       {
+               if (!OidIsValid(multirange_typeid))
+               {
+                       if (OidIsValid(range_typeid))
+                               multirange_typeid = get_range_multirange(range_typeid);
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                                errmsg("could not find multirange type for data type %s",
+                                                               format_type_be(elem_typeid))));
+               }
+               return multirange_typeid;
+       }
+
        /* if we return ANYCOMPATIBLE use the appropriate type */
        if (rettype == ANYCOMPATIBLEOID ||
                rettype == ANYCOMPATIBLENONARRAYOID)
@@ -2439,6 +2687,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
                return anycompatible_range_typeid;
        }
 
+       /* if we return ANYCOMPATIBLEMULTIRANGE use the appropriate argument type */
+       if (rettype == ANYCOMPATIBLEMULTIRANGEOID)
+       {
+               /* this error is unreachable if the function signature is valid: */
+               if (!OidIsValid(anycompatible_multirange_typeid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg_internal("could not identify anycompatiblemultirange type")));
+               return anycompatible_multirange_typeid;
+       }
+
        /* we don't return a generic type; send back the original return type */
        return rettype;
 }
@@ -2456,20 +2715,38 @@ check_valid_polymorphic_signature(Oid ret_type,
                                                                  const Oid *declared_arg_types,
                                                                  int nargs)
 {
-       if (ret_type == ANYRANGEOID || ret_type == ANYCOMPATIBLERANGEOID)
+       if (ret_type == ANYRANGEOID || ret_type == ANYMULTIRANGEOID)
        {
                /*
-                * ANYRANGE requires an ANYRANGE input, else we can't tell which of
-                * several range types with the same element type to use.  Likewise
-                * for ANYCOMPATIBLERANGE.
+                * ANYRANGE and ANYMULTIRANGE require an ANYRANGE or ANYMULTIRANGE
+                * input, else we can't tell which of several range types with the
+                * same element type to use.
                 */
                for (int i = 0; i < nargs; i++)
                {
-                       if (declared_arg_types[i] == ret_type)
+                       if (declared_arg_types[i] == ANYRANGEOID ||
+                               declared_arg_types[i] == ANYMULTIRANGEOID)
                                return NULL;    /* OK */
                }
-               return psprintf(_("A result of type %s requires at least one input of type %s."),
-                                               format_type_be(ret_type), format_type_be(ret_type));
+               return psprintf(_("A result of type %s requires at least one input of type anyrange or anymultirange."),
+                                               format_type_be(ret_type));
+       }
+       else if (ret_type == ANYCOMPATIBLERANGEOID || ret_type == ANYCOMPATIBLEMULTIRANGEOID)
+       {
+               /*
+                * ANYCOMPATIBLERANGE and ANYCOMPATIBLEMULTIRANGE require an
+                * ANYCOMPATIBLERANGE or ANYCOMPATIBLEMULTIRANGE input, else we can't
+                * tell which of several range types with the same element type to
+                * use.
+                */
+               for (int i = 0; i < nargs; i++)
+               {
+                       if (declared_arg_types[i] == ANYCOMPATIBLERANGEOID ||
+                               declared_arg_types[i] == ANYCOMPATIBLEMULTIRANGEOID)
+                               return NULL;    /* OK */
+               }
+               return psprintf(_("A result of type %s requires at least one input of type anycompatiblerange or anycompatiblemultirange."),
+                                               format_type_be(ret_type));
        }
        else if (IsPolymorphicTypeFamily1(ret_type))
        {
@@ -2480,7 +2757,7 @@ check_valid_polymorphic_signature(Oid ret_type,
                                return NULL;    /* OK */
                }
                /* Keep this list in sync with IsPolymorphicTypeFamily1! */
-               return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange."),
+               return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange."),
                                                format_type_be(ret_type));
        }
        else if (IsPolymorphicTypeFamily2(ret_type))
@@ -2632,6 +2909,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
                if (type_is_range(srctype))
                        return true;
 
+       /* Also accept any multirange type as coercible to ANMULTIYRANGE */
+       if (targettype == ANYMULTIRANGEOID || targettype == ANYCOMPATIBLEMULTIRANGEOID)
+               if (type_is_multirange(srctype))
+                       return true;
+
        /* Also accept any composite type as coercible to RECORD */
        if (targettype == RECORDOID)
                if (ISCOMPLEX(srctype))
index ce09ad73754e05bd595a97eac502a85d246a2e32..82732146d3deb56f9012cad00ca260fe5016919b 100644 (file)
@@ -60,6 +60,8 @@ OBJS = \
        mac8.o \
        mcxtfuncs.o \
        misc.o \
+       multirangetypes.o \
+       multirangetypes_selfuncs.o \
        name.o \
        network.o \
        network_gist.o \
diff --git a/src/backend/utils/adt/multirangetypes.c b/src/backend/utils/adt/multirangetypes.c
new file mode 100644 (file)
index 0000000..a4dc439
--- /dev/null
@@ -0,0 +1,2679 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ *       I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ *     12 bytes: MultirangeType struct including varlena header, multirange
+ *                       type's OID and the number of ranges in the multirange.
+ *     4 * (rangesCount - 1) bytes: 32-bit items pointing to the each range
+ *                                                              in the multirange starting from
+ *                                                              the second one.
+ *     1 * rangesCount bytes : 8-bit flags for each range in the multirange
+ *     The rest of the multirange are range bound values pointed by multirange
+ *     items.
+ *
+ *     Majority of items contain lengths of corresponding range bound values.
+ *     Thanks to that items are typically low numbers.  This makes multiranges
+ *     compression-friendly.  Every MULTIRANGE_ITEM_OFFSET_STRIDE item contains
+ *     an offset of the corresponding range bound values.  That allows fast lookups
+ *     for a particular range index.  Offsets are counted starting from the end of
+ *     flags aligned to the bound type.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/utils/adt/multirangetypes.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/tupmacs.h"
+#include "common/hashfn.h"
+#include "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/array.h"
+#include "utils/memutils.h"
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+       TypeCacheEntry *typcache;       /* multirange type's typcache entry */
+       FmgrInfo        typioproc;              /* range type's I/O proc */
+       Oid                     typioparam;             /* range type's I/O parameter */
+} MultirangeIOData;
+
+typedef enum
+{
+       MULTIRANGE_BEFORE_RANGE,
+       MULTIRANGE_IN_RANGE,
+       MULTIRANGE_IN_RANGE_ESCAPED,
+       MULTIRANGE_IN_RANGE_QUOTED,
+       MULTIRANGE_IN_RANGE_QUOTED_ESCAPED,
+       MULTIRANGE_AFTER_RANGE,
+       MULTIRANGE_FINISHED,
+} MultirangeParseState;
+
+/*
+ * Macros for accessing past MultirangeType parts of multirange: items, flags
+ * and boundaries.
+ */
+#define MultirangeGetItemsPtr(mr) ((uint32 *) ((Pointer) (mr) + \
+       sizeof(MultirangeType)))
+#define MultirangeGetFlagsPtr(mr) ((uint8 *) ((Pointer) (mr) + \
+       sizeof(MultirangeType) + ((mr)->rangeCount - 1) * sizeof(uint32)))
+#define MultirangeGetBoundariesPtr(mr, align) ((Pointer) (mr) + \
+       att_align_nominal(sizeof(MultirangeType) + \
+               ((mr)->rangeCount - 1) * sizeof(uint32) + \
+               (mr)->rangeCount * sizeof(uint8), (align)))
+
+#define MULTIRANGE_ITEM_OFF_BIT 0x80000000
+#define MULTIRANGE_ITEM_GET_OFFLEN(item) ((item) & 0x7FFFFFFF)
+#define MULTIRANGE_ITEM_HAS_OFF(item) ((item) & MULTIRANGE_ITEM_OFF_BIT)
+#define MULTIRANGE_ITEM_OFFSET_STRIDE 4
+
+typedef int (*multirange_bsearch_comparison) (TypeCacheEntry *typcache,
+                                                                                         RangeBound *lower,
+                                                                                         RangeBound *upper,
+                                                                                         void *key,
+                                                                                         bool *match);
+
+static MultirangeIOData *get_multirange_io_data(FunctionCallInfo fcinfo,
+                                                                                               Oid mltrngtypid,
+                                                                                               IOFuncSelector func);
+static int32 multirange_canonicalize(TypeCacheEntry *rangetyp,
+                                                                        int32 input_range_count,
+                                                                        RangeType **ranges);
+
+/*
+ *----------------------------------------------------------
+ * I/O FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Converts string to multirange.
+ *
+ * We expect curly brackets to bound the list, with zero or more ranges
+ * separated by commas.  We accept whitespace anywhere: before/after our
+ * brackets and around the commas.  Ranges can be the empty literal or some
+ * stuff inside parens/brackets.  Mostly we delegate parsing the individual
+ * range contents to range_in, but we have to detect quoting and
+ * backslash-escaping which can happen for range bounds.  Backslashes can
+ * escape something inside or outside a quoted string, and a quoted string
+ * can escape quote marks with either backslashes or double double-quotes.
+ */
+Datum
+multirange_in(PG_FUNCTION_ARGS)
+{
+       char       *input_str = PG_GETARG_CSTRING(0);
+       Oid                     mltrngtypoid = PG_GETARG_OID(1);
+       Oid                     typmod = PG_GETARG_INT32(2);
+       TypeCacheEntry *rangetyp;
+       int32           ranges_seen = 0;
+       int32           range_count = 0;
+       int32           range_capacity = 8;
+       RangeType  *range;
+       RangeType **ranges = palloc(range_capacity * sizeof(RangeType *));
+       MultirangeIOData *cache;
+       MultirangeType *ret;
+       MultirangeParseState parse_state;
+       const char *ptr = input_str;
+       const char *range_str = NULL;
+       int32           range_str_len;
+       char       *range_str_copy;
+
+       cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_input);
+       rangetyp = cache->typcache->rngtype;
+
+       /* consume whitespace */
+       while (*ptr != '\0' && isspace((unsigned char) *ptr))
+               ptr++;
+
+       if (*ptr == '{')
+               ptr++;
+       else
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                errmsg("malformed multirange literal: \"%s\"",
+                                               input_str),
+                                errdetail("Missing left bracket.")));
+
+       /* consume ranges */
+       parse_state = MULTIRANGE_BEFORE_RANGE;
+       for (; parse_state != MULTIRANGE_FINISHED; ptr++)
+       {
+               char            ch = *ptr;
+
+               if (ch == '\0')
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                        errmsg("malformed multirange literal: \"%s\"",
+                                                       input_str),
+                                        errdetail("Unexpected end of input.")));
+
+               /* skip whitespace */
+               if (isspace((unsigned char) ch))
+                       continue;
+
+               switch (parse_state)
+               {
+                       case MULTIRANGE_BEFORE_RANGE:
+                               if (ch == '[' || ch == '(')
+                               {
+                                       range_str = ptr;
+                                       parse_state = MULTIRANGE_IN_RANGE;
+                               }
+                               else if (ch == '}' && ranges_seen == 0)
+                                       parse_state = MULTIRANGE_FINISHED;
+                               else if (pg_strncasecmp(ptr, RANGE_EMPTY_LITERAL,
+                                                                               strlen(RANGE_EMPTY_LITERAL)) == 0)
+                               {
+                                       ranges_seen++;
+                                       /* nothing to do with an empty range */
+                                       ptr += strlen(RANGE_EMPTY_LITERAL) - 1;
+                                       parse_state = MULTIRANGE_AFTER_RANGE;
+                               }
+                               else
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                                        errmsg("malformed multirange literal: \"%s\"",
+                                                                       input_str),
+                                                        errdetail("Expected range start.")));
+                               break;
+                       case MULTIRANGE_IN_RANGE:
+                               if (ch == '"')
+                                       parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+                               else if (ch == '\\')
+                                       parse_state = MULTIRANGE_IN_RANGE_ESCAPED;
+                               else if (ch == ']' || ch == ')')
+                               {
+                                       range_str_len = ptr - range_str + 1;
+                                       range_str_copy = pnstrdup(range_str, range_str_len);
+                                       if (range_capacity == range_count)
+                                       {
+                                               range_capacity *= 2;
+                                               ranges = (RangeType **)
+                                                       repalloc(ranges, range_capacity * sizeof(RangeType *));
+                                       }
+                                       ranges_seen++;
+                                       range = DatumGetRangeTypeP(InputFunctionCall(&cache->typioproc,
+                                                                                                                                range_str_copy,
+                                                                                                                                cache->typioparam,
+                                                                                                                                typmod));
+                                       if (!RangeIsEmpty(range))
+                                               ranges[range_count++] = range;
+                                       parse_state = MULTIRANGE_AFTER_RANGE;
+                               }
+                               else
+                                        /* include it in range_str */ ;
+                               break;
+                       case MULTIRANGE_IN_RANGE_ESCAPED:
+                               /* include it in range_str */
+                               parse_state = MULTIRANGE_IN_RANGE;
+                               break;
+                       case MULTIRANGE_IN_RANGE_QUOTED:
+                               if (ch == '"')
+                                       if (*(ptr + 1) == '"')
+                                       {
+                                               /* two quote marks means an escaped quote mark */
+                                               ptr++;
+                                       }
+                                       else
+                                               parse_state = MULTIRANGE_IN_RANGE;
+                               else if (ch == '\\')
+                                       parse_state = MULTIRANGE_IN_RANGE_QUOTED_ESCAPED;
+                               else
+                                        /* include it in range_str */ ;
+                               break;
+                       case MULTIRANGE_AFTER_RANGE:
+                               if (ch == ',')
+                                       parse_state = MULTIRANGE_BEFORE_RANGE;
+                               else if (ch == '}')
+                                       parse_state = MULTIRANGE_FINISHED;
+                               else
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                                        errmsg("malformed multirange literal: \"%s\"",
+                                                                       input_str),
+                                                        errdetail("Expected comma or end of multirange.")));
+                               break;
+                       case MULTIRANGE_IN_RANGE_QUOTED_ESCAPED:
+                               /* include it in range_str */
+                               parse_state = MULTIRANGE_IN_RANGE_QUOTED;
+                               break;
+                       default:
+                               elog(ERROR, "unknown parse state: %d", parse_state);
+               }
+       }
+
+       /* consume whitespace */
+       while (*ptr != '\0' && isspace((unsigned char) *ptr))
+               ptr++;
+
+       if (*ptr != '\0')
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                                errmsg("malformed multirange literal: \"%s\"",
+                                               input_str),
+                                errdetail("Junk after right bracket.")));
+
+       ret = make_multirange(mltrngtypoid, rangetyp, range_count, ranges);
+       PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_out(PG_FUNCTION_ARGS)
+{
+       MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+       Oid                     mltrngtypoid = MultirangeTypeGetOid(multirange);
+       MultirangeIOData *cache;
+       StringInfoData buf;
+       RangeType  *range;
+       char       *rangeStr;
+       int32           range_count;
+       int32           i;
+       RangeType **ranges;
+
+       cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+       initStringInfo(&buf);
+
+       appendStringInfoChar(&buf, '{');
+
+       multirange_deserialize(cache->typcache->rngtype, multirange, &range_count, &ranges);
+       for (i = 0; i < range_count; i++)
+       {
+               if (i > 0)
+                       appendStringInfoChar(&buf, ',');
+               range = ranges[i];
+               rangeStr = OutputFunctionCall(&cache->typioproc, RangeTypePGetDatum(range));
+               appendStringInfoString(&buf, rangeStr);
+       }
+
+       appendStringInfoChar(&buf, '}');
+
+       PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: First a int32-sized count of ranges, followed by
+ * ranges in their native binary representation.
+ */
+Datum
+multirange_recv(PG_FUNCTION_ARGS)
+{
+       StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
+       Oid                     mltrngtypoid = PG_GETARG_OID(1);
+       int32           typmod = PG_GETARG_INT32(2);
+       MultirangeIOData *cache;
+       uint32          range_count;
+       RangeType **ranges;
+       MultirangeType *ret;
+       StringInfoData tmpbuf;
+
+       cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+
+       range_count = pq_getmsgint(buf, 4);
+       ranges = palloc(range_count * sizeof(RangeType *));
+
+       initStringInfo(&tmpbuf);
+       for (int i = 0; i < range_count; i++)
+       {
+               uint32          range_len = pq_getmsgint(buf, 4);
+               const char *range_data = pq_getmsgbytes(buf, range_len);
+
+               resetStringInfo(&tmpbuf);
+               appendBinaryStringInfo(&tmpbuf, range_data, range_len);
+
+               ranges[i] = DatumGetRangeTypeP(ReceiveFunctionCall(&cache->typioproc,
+                                                                                                                  &tmpbuf,
+                                                                                                                  cache->typioparam,
+                                                                                                                  typmod));
+       }
+       pfree(tmpbuf.data);
+
+       pq_getmsgend(buf);
+
+       ret = make_multirange(mltrngtypoid, cache->typcache->rngtype,
+                                                 range_count, ranges);
+       PG_RETURN_MULTIRANGE_P(ret);
+}
+
+Datum
+multirange_send(PG_FUNCTION_ARGS)
+{
+       MultirangeType *multirange = PG_GETARG_MULTIRANGE_P(0);
+       Oid                     mltrngtypoid = MultirangeTypeGetOid(multirange);
+       StringInfo      buf = makeStringInfo();
+       RangeType **ranges;
+       int32           range_count;
+       MultirangeIOData *cache;
+
+       cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+       /* construct output */
+       pq_begintypsend(buf);
+
+       pq_sendint32(buf, multirange->rangeCount);
+
+       multirange_deserialize(cache->typcache->rngtype, multirange, &range_count, &ranges);
+       for (int i = 0; i < range_count; i++)
+       {
+               Datum           range;
+
+               range = RangeTypePGetDatum(ranges[i]);
+               range = PointerGetDatum(SendFunctionCall(&cache->typioproc, range));
+
+               pq_sendint32(buf, VARSIZE(range) - VARHDRSZ);
+               pq_sendbytes(buf, VARDATA(range), VARSIZE(range) - VARHDRSZ);
+       }
+
+       PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+/*
+ * get_multirange_io_data: get cached information needed for multirange type I/O
+ *
+ * The multirange I/O functions need a bit more cached info than other multirange
+ * functions, so they store a MultirangeIOData struct in fn_extra, not just a
+ * pointer to a type cache entry.
+ */
+static MultirangeIOData *
+get_multirange_io_data(FunctionCallInfo fcinfo, Oid mltrngtypid, IOFuncSelector func)
+{
+       MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+       if (cache == NULL || cache->typcache->type_id != mltrngtypid)
+       {
+               Oid                     typiofunc;
+               int16           typlen;
+               bool            typbyval;
+               char            typalign;
+               char            typdelim;
+
+               cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+                                                                                                               sizeof(MultirangeIOData));
+               cache->typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+               if (cache->typcache->rngtype == NULL)
+                       elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+
+               /* get_type_io_data does more than we need, but is convenient */
+               get_type_io_data(cache->typcache->rngtype->type_id,
+                                                func,
+                                                &typlen,
+                                                &typbyval,
+                                                &typalign,
+                                                &typdelim,
+                                                &cache->typioparam,
+                                                &typiofunc);
+
+               if (!OidIsValid(typiofunc))
+               {
+                       /* this could only happen for receive or send */
+                       if (func == IOFunc_receive)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                                errmsg("no binary input function available for type %s",
+                                                               format_type_be(cache->typcache->rngtype->type_id))));
+                       else
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                                errmsg("no binary output function available for type %s",
+                                                               format_type_be(cache->typcache->rngtype->type_id))));
+               }
+               fmgr_info_cxt(typiofunc, &cache->typioproc,
+                                         fcinfo->flinfo->fn_mcxt);
+
+               fcinfo->flinfo->fn_extra = (void *) cache;
+       }
+
+       return cache;
+}
+
+/*
+ * Converts a list of arbitrary ranges into a list that is sorted and merged.
+ * Changes the contents of `ranges`.
+ *
+ * Returns the number of slots actually used, which may be less than
+ * input_range_count but never more.
+ *
+ * We assume that no input ranges are null, but empties are okay.
+ */
+static int32
+multirange_canonicalize(TypeCacheEntry *rangetyp, int32 input_range_count,
+                                               RangeType **ranges)
+{
+       RangeType  *lastRange = NULL;
+       RangeType  *currentRange;
+       int32           i;
+       int32           output_range_count = 0;
+
+       /* Sort the ranges so we can find the ones that overlap/meet. */
+       qsort_arg(ranges, input_range_count, sizeof(RangeType *), range_compare,
+                         rangetyp);
+
+       /* Now merge where possible: */
+       for (i = 0; i < input_range_count; i++)
+       {
+               currentRange = ranges[i];
+               if (RangeIsEmpty(currentRange))
+                       continue;
+
+               if (lastRange == NULL)
+               {
+                       ranges[output_range_count++] = lastRange = currentRange;
+                       continue;
+               }
+
+               /*
+                * range_adjacent_internal gives true if *either* A meets B or B meets
+                * A, which is not quite want we want, but we rely on the sorting
+                * above to rule out B meets A ever happening.
+                */
+               if (range_adjacent_internal(rangetyp, lastRange, currentRange))
+               {
+                       /* The two ranges touch (without overlap), so merge them: */
+                       ranges[output_range_count - 1] = lastRange =
+                               range_union_internal(rangetyp, lastRange, currentRange, false);
+               }
+               else if (range_before_internal(rangetyp, lastRange, currentRange))
+               {
+                       /* There's a gap, so make a new entry: */
+                       lastRange = ranges[output_range_count] = currentRange;
+                       output_range_count++;
+               }
+               else
+               {
+                       /* They must overlap, so merge them: */
+                       ranges[output_range_count - 1] = lastRange =
+                               range_union_internal(rangetyp, lastRange, currentRange, true);
+               }
+       }
+
+       return output_range_count;
+}
+
+/*
+ *----------------------------------------------------------
+ * SUPPORT FUNCTIONS
+ *
+ *      These functions aren't in pg_proc, but are useful for
+ *      defining new generic multirange functions in C.
+ *----------------------------------------------------------
+ */
+
+/*
+ * multirange_get_typcache: get cached information about a multirange type
+ *
+ * This is for use by multirange-related functions that follow the convention
+ * of using the fn_extra field as a pointer to the type cache entry for
+ * the multirange type.  Functions that need to cache more information than
+ * that must fend for themselves.
+ */
+TypeCacheEntry *
+multirange_get_typcache(FunctionCallInfo fcinfo, Oid mltrngtypid)
+{
+       TypeCacheEntry *typcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
+
+       if (typcache == NULL ||
+               typcache->type_id != mltrngtypid)
+       {
+               typcache = lookup_type_cache(mltrngtypid, TYPECACHE_MULTIRANGE_INFO);
+               if (typcache->rngtype == NULL)
+                       elog(ERROR, "type %u is not a multirange type", mltrngtypid);
+               fcinfo->flinfo->fn_extra = (void *) typcache;
+       }
+
+       return typcache;
+}
+
+
+/*
+ * Estimate size occupied by serialized multirage.
+ */
+static Size
+multirange_size_estimate(TypeCacheEntry *rangetyp, int32 range_count,
+                                                RangeType **ranges)
+{
+       char            elemalign = rangetyp->rngelemtype->typalign;
+       Size            size;
+       int32           i;
+
+       /*
+        * Count space for MultirangeType struct, items and flags.
+        */
+       size = att_align_nominal(sizeof(MultirangeType) +
+                                                        Max(range_count - 1, 0) * sizeof(uint32) +
+                                                        range_count * sizeof(uint8), elemalign);
+
+       /* Count space for range bounds */
+       for (i = 0; i < range_count; i++)
+               size += att_align_nominal(VARSIZE(ranges[i]) -
+                                                                 sizeof(RangeType) -
+                                                                 sizeof(char), elemalign);
+
+       return size;
+}
+
+/*
+ * Write multirange data into pre-allocated space.
+ */
+static void
+write_multirange_data(MultirangeType *multirange, TypeCacheEntry *rangetyp,
+                                         int32 range_count, RangeType **ranges)
+{
+       uint32     *items;
+       uint32          prev_offset = 0;
+       uint8      *flags;
+       int32           i;
+       Pointer         begin,
+                               ptr;
+       char            elemalign = rangetyp->rngelemtype->typalign;
+
+       items = MultirangeGetItemsPtr(multirange);
+       flags = MultirangeGetFlagsPtr(multirange);
+       ptr = begin = MultirangeGetBoundariesPtr(multirange, elemalign);
+       for (i = 0; i < range_count; i++)
+       {
+               uint32          len;
+
+               if (i > 0)
+               {
+                       /*
+                        * Every range, except the first one, has an item.  Every
+                        * MULTIRANGE_ITEM_OFFSET_STRIDE item contains an offset, others
+                        * contain lengths.
+                        */
+                       items[i - 1] = ptr - begin;
+                       if ((i % MULTIRANGE_ITEM_OFFSET_STRIDE) != 0)
+                               items[i - 1] -= prev_offset;
+                       else
+                               items[i - 1] |= MULTIRANGE_ITEM_OFF_BIT;
+                       prev_offset = ptr - begin;
+               }
+               flags[i] = *((Pointer) ranges[i] + VARSIZE(ranges[i]) - sizeof(char));
+               len = VARSIZE(ranges[i]) - sizeof(RangeType) - sizeof(char);
+               memcpy(ptr, (Pointer) (ranges[i] + 1), len);
+               ptr += att_align_nominal(len, elemalign);
+       }
+}
+
+
+/*
+ * This serializes the multirange from a list of non-null ranges.  It also
+ * sorts the ranges and merges any that touch.  The ranges should already be
+ * detoasted, and there should be no NULLs.  This should be used by most
+ * callers.
+ *
+ * Note that we may change the `ranges` parameter (the pointers, but not
+ * any already-existing RangeType contents).
+ */
+MultirangeType *
+make_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp, int32 range_count,
+                               RangeType **ranges)
+{
+       MultirangeType *multirange;
+       Size            size;
+
+       /* Sort and merge input ranges. */
+       range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+       /* Note: zero-fill is required here, just as in heap tuples */
+       size = multirange_size_estimate(rangetyp, range_count, ranges);
+       multirange = palloc0(size);
+       SET_VARSIZE(multirange, size);
+
+       /* Now fill in the datum */
+       multirange->multirangetypid = mltrngtypoid;
+       multirange->rangeCount = range_count;
+
+       write_multirange_data(multirange, rangetyp, range_count, ranges);
+
+       return multirange;
+}
+
+/*
+ * Get offset of bounds values of the i'th range in the multirange.
+ */
+static uint32
+multirange_get_bounds_offset(const MultirangeType *multirange, int32 i)
+{
+       uint32     *items = MultirangeGetItemsPtr(multirange);
+       uint32          offset = 0;
+
+       /*
+        * Summarize lengths till we meet an offset.
+        */
+       while (i > 0)
+       {
+               offset += MULTIRANGE_ITEM_GET_OFFLEN(items[i - 1]);
+               if (MULTIRANGE_ITEM_HAS_OFF(items[i - 1]))
+                       break;
+               i--;
+       }
+       return offset;
+}
+
+/*
+ * Fetch the i'th range from the multirange.
+ */
+RangeType *
+multirange_get_range(TypeCacheEntry *rangetyp,
+                                        const MultirangeType *multirange, int i)
+{
+       uint32          offset;
+       uint8           flags;
+       Pointer         begin,
+                               ptr;
+       int16           typlen = rangetyp->rngelemtype->typlen;
+       char            typalign = rangetyp->rngelemtype->typalign;
+       uint32          len;
+       RangeType  *range;
+
+       Assert(i < multirange->rangeCount);
+
+       offset = multirange_get_bounds_offset(multirange, i);
+       flags = MultirangeGetFlagsPtr(multirange)[i];
+       ptr = begin = MultirangeGetBoundariesPtr(multirange, typalign) + offset;
+
+       /*
+        * Calculate the size of bound values.  In principle, we could get offset
+        * of the next range bound values and calculate accordingly.  But range
+        * bound values are aligned, so we have to walk the values to get the
+        * exact size.
+        */
+       if (RANGE_HAS_LBOUND(flags))
+               ptr = (Pointer) att_addlength_pointer(ptr, typlen, ptr);
+       if (RANGE_HAS_UBOUND(flags))
+               ptr = (Pointer) att_addlength_pointer(ptr, typlen, ptr);
+       len = (ptr - begin) + sizeof(RangeType) + sizeof(uint8);
+
+       range = palloc0(len);
+       SET_VARSIZE(range, len);
+       range->rangetypid = rangetyp->type_id;
+
+       memcpy(range + 1, begin, ptr - begin);
+       *((uint8 *) (range + 1) + (ptr - begin)) = flags;
+
+       return range;
+}
+
+/*
+ * Fetch bounds from the i'th range of the multirange.  This is the shortcut for
+ * doing the same thing as multirange_get_range() + range_deserialize(), but
+ * performing fewer operations.
+ */
+void
+multirange_get_bounds(TypeCacheEntry *rangetyp,
+                                         const MultirangeType *multirange,
+                                         uint32 i, RangeBound *lower, RangeBound *upper)
+{
+       uint32          offset;
+       uint8           flags;
+       Pointer         ptr;
+       int16           typlen = rangetyp->rngelemtype->typlen;
+       char            typalign = rangetyp->rngelemtype->typalign;
+       bool            typbyval = rangetyp->rngelemtype->typbyval;
+       Datum           lbound;
+       Datum           ubound;
+
+       Assert(i < multirange->rangeCount);
+
+       offset = multirange_get_bounds_offset(multirange, i);
+       flags = MultirangeGetFlagsPtr(multirange)[i];
+       ptr = MultirangeGetBoundariesPtr(multirange, typalign) + offset;
+
+       /* multirange can't contain empty ranges */
+       Assert((flags & RANGE_EMPTY) == 0);
+
+       /* fetch lower bound, if any */
+       if (RANGE_HAS_LBOUND(flags))
+       {
+               /* att_align_pointer cannot be necessary here */
+               lbound = fetch_att(ptr, typbyval, typlen);
+               ptr = (Pointer) att_addlength_pointer(ptr, typlen, ptr);
+       }
+       else
+               lbound = (Datum) 0;
+
+       /* fetch upper bound, if any */
+       if (RANGE_HAS_UBOUND(flags))
+       {
+               ptr = (Pointer) att_align_pointer(ptr, typalign, typlen, ptr);
+               ubound = fetch_att(ptr, typbyval, typlen);
+               /* no need for att_addlength_pointer */
+       }
+       else
+               ubound = (Datum) 0;
+
+       /* emit results */
+       lower->val = lbound;
+       lower->infinite = (flags & RANGE_LB_INF) != 0;
+       lower->inclusive = (flags & RANGE_LB_INC) != 0;
+       lower->lower = true;
+
+       upper->val = ubound;
+       upper->infinite = (flags & RANGE_UB_INF) != 0;
+       upper->inclusive = (flags & RANGE_UB_INC) != 0;
+       upper->lower = false;
+}
+
+/*
+ * multirange_deserialize: deconstruct a multirange value
+ *
+ * NB: the given multirange object must be fully detoasted; it cannot have a
+ * short varlena header.
+ */
+void
+multirange_deserialize(TypeCacheEntry *rangetyp,
+                                          const MultirangeType *multirange, int32 *range_count,
+                                          RangeType ***ranges)
+{
+       *range_count = multirange->rangeCount;
+
+       /* Convert each ShortRangeType into a RangeType */
+       if (*range_count > 0)
+       {
+               int                     i;
+
+               *ranges = palloc(*range_count * sizeof(RangeType *));
+               for (i = 0; i < *range_count; i++)
+                       (*ranges)[i] = multirange_get_range(rangetyp, multirange, i);
+       }
+       else
+       {
+               *ranges = NULL;
+       }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+       return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ * Similar to range_overlaps_internal(), but takes range bounds instead of
+ * ranges as arguments.
+ */
+static bool
+range_bounds_overlaps(TypeCacheEntry *typcache,
+                                         RangeBound *lower1, RangeBound *upper1,
+                                         RangeBound *lower2, RangeBound *upper2)
+{
+       if (range_cmp_bounds(typcache, lower1, lower2) >= 0 &&
+               range_cmp_bounds(typcache, lower1, upper2) <= 0)
+               return true;
+
+       if (range_cmp_bounds(typcache, lower2, lower1) >= 0 &&
+               range_cmp_bounds(typcache, lower2, upper1) <= 0)
+               return true;
+
+       return false;
+}
+
+/*
+ * Similar to range_contains_internal(), but takes range bounds instead of
+ * ranges as arguments.
+ */
+static bool
+range_bounds_contains(TypeCacheEntry *typcache,
+                                         RangeBound *lower1, RangeBound *upper1,
+                                         RangeBound *lower2, RangeBound *upper2)
+{
+       if (range_cmp_bounds(typcache, lower1, lower2) <= 0 &&
+               range_cmp_bounds(typcache, upper1, upper2) >= 0)
+               return true;
+
+       return false;
+}
+
+/*
+ * Check if the given key matches any range in multirange using binary search.
+ * If the required range isn't found, that counts as a mismatch.  When the
+ * required range is found, the comparison function can still report this as
+ * either match or mismatch.  For instance, if we search for containment, we can
+ * found a range, which is overlapping but not containing the key range, and
+ * that would count as a mismatch.
+ */
+static bool
+multirange_bsearch_match(TypeCacheEntry *typcache, MultirangeType *mr,
+                                                void *key, multirange_bsearch_comparison cmp_func)
+{
+       uint32          l,
+                               u,
+                               idx;
+       int                     comparison;
+       bool            match = false;
+
+       l = 0;
+       u = mr->rangeCount;
+       while (l < u)
+       {
+               RangeBound      lower,
+                                       upper;
+
+               idx = (l + u) / 2;
+               multirange_get_bounds(typcache, mr, idx, &lower, &upper);
+               comparison = (*cmp_func) (typcache, &lower, &upper, key, &match);
+
+               if (comparison < 0)
+                       u = idx;
+               else if (comparison > 0)
+                       l = idx + 1;
+               else
+                       return match;
+       }
+
+       return false;
+}
+
+/*
+ *----------------------------------------------------------
+ * GENERIC FUNCTIONS
+ *----------------------------------------------------------
+ */
+
+/*
+ * Construct multirange value from zero or more ranges.  Since this is a
+ * variadic function we get passed an array.  The array must contain ranges
+ * that match our return value, and there must be no NULLs.
+ */
+Datum
+multirange_constructor2(PG_FUNCTION_ARGS)
+{
+       Oid                     mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+       Oid                     rngtypid;
+       TypeCacheEntry *typcache;
+       TypeCacheEntry *rangetyp;
+       ArrayType  *rangeArray;
+       int                     range_count;
+       Datum      *elements;
+       bool       *nulls;
+       RangeType **ranges;
+       int                     dims;
+       int                     i;
+
+       typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+       rangetyp = typcache->rngtype;
+
+       /*
+        * A no-arg invocation should call multirange_constructor0 instead, but
+        * returning an empty range is what that does.
+        */
+
+       if (PG_NARGS() == 0)
+               PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+
+       /*
+        * These checks should be guaranteed by our signature, but let's do them
+        * just in case.
+        */
+
+       if (PG_ARGISNULL(0))
+               ereport(ERROR,
+                               (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                errmsg("multirange values cannot contain NULL members")));
+
+       rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+       dims = ARR_NDIM(rangeArray);
+       if (dims > 1)
+               ereport(ERROR,
+                               (errcode(ERRCODE_CARDINALITY_VIOLATION),
+                                errmsg("multiranges cannot be constructed from multi-dimensional arrays")));
+
+       rngtypid = ARR_ELEMTYPE(rangeArray);
+       if (rngtypid != rangetyp->type_id)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("type %u does not match constructor type", rngtypid)));
+
+       /*
+        * Be careful: we can still be called with zero ranges, like this:
+        * `int4multirange(variadic '{}'::int4range[])
+        */
+       if (dims == 0)
+       {
+               range_count = 0;
+               ranges = NULL;
+       }
+       else
+       {
+               deconstruct_array(rangeArray, rngtypid, rangetyp->typlen, rangetyp->typbyval,
+                                                 rangetyp->typalign, &elements, &nulls, &range_count);
+
+               ranges = palloc0(range_count * sizeof(RangeType *));
+               for (i = 0; i < range_count; i++)
+               {
+                       if (nulls[i])
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                                errmsg("multirange values cannot contain NULL members")));
+
+                       /* make_multirange will do its own copy */
+                       ranges[i] = DatumGetRangeTypeP(elements[i]);
+               }
+       }
+
+       PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, range_count, ranges));
+}
+
+/*
+ * Construct multirange value from a single range.  It'd be nice if we could
+ * just use multirange_constructor2 for this case, but we need a non-variadic
+ * single-arg function to let us define a CAST from a range to its multirange.
+ */
+Datum
+multirange_constructor1(PG_FUNCTION_ARGS)
+{
+       Oid                     mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+       Oid                     rngtypid;
+       TypeCacheEntry *typcache;
+       TypeCacheEntry *rangetyp;
+       RangeType  *range;
+
+       typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+       rangetyp = typcache->rngtype;
+
+       /*
+        * These checks should be guaranteed by our signature, but let's do them
+        * just in case.
+        */
+
+       if (PG_ARGISNULL(0))
+               ereport(ERROR,
+                               (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+                                errmsg("multirange values cannot contain NULL members")));
+
+       range = PG_GETARG_RANGE_P(0);
+
+       /* Make sure the range type matches. */
+       rngtypid = RangeTypeGetOid(range);
+       if (rngtypid != rangetyp->type_id)
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("type %u does not match constructor type", rngtypid)));
+
+       PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 1, &range));
+}
+
+/*
+ * Constructor just like multirange_constructor1, but opr_sanity gets angry
+ * if the same internal function handles multiple functions with different arg
+ * counts.
+ */
+Datum
+multirange_constructor0(PG_FUNCTION_ARGS)
+{
+       Oid                     mltrngtypid = get_fn_expr_rettype(fcinfo->flinfo);
+       TypeCacheEntry *typcache;
+       TypeCacheEntry *rangetyp;
+
+       typcache = multirange_get_typcache(fcinfo, mltrngtypid);
+       rangetyp = typcache->rngtype;
+
+       /* We should always be called with no arguments */
+
+       if (PG_NARGS() == 0)
+               PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypid, rangetyp, 0, NULL));
+       else
+               elog(ERROR,                             /* can't happen */
+                        "niladic multirange constructor must not receive arguments");
+}
+
+
+/* multirange, multirange -> multirange type functions */
+
+/* multirange union */
+Datum
+multirange_union(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+       int32           range_count1;
+       int32           range_count2;
+       int32           range_count3;
+       RangeType **ranges1;
+       RangeType **ranges2;
+       RangeType **ranges3;
+
+       if (MultirangeIsEmpty(mr1))
+               PG_RETURN_MULTIRANGE_P(mr2);
+       if (MultirangeIsEmpty(mr2))
+               PG_RETURN_MULTIRANGE_P(mr1);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       multirange_deserialize(typcache->rngtype, mr1, &range_count1, &ranges1);
+       multirange_deserialize(typcache->rngtype, mr2, &range_count2, &ranges2);
+
+       range_count3 = range_count1 + range_count2;
+       ranges3 = palloc0(range_count3 * sizeof(RangeType *));
+       memcpy(ranges3, ranges1, range_count1 * sizeof(RangeType *));
+       memcpy(ranges3 + range_count1, ranges2, range_count2 * sizeof(RangeType *));
+       PG_RETURN_MULTIRANGE_P(make_multirange(typcache->type_id, typcache->rngtype,
+                                                                                  range_count3, ranges3));
+}
+
+/* multirange minus */
+Datum
+multirange_minus(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       Oid                     mltrngtypoid = MultirangeTypeGetOid(mr1);
+       TypeCacheEntry *typcache;
+       TypeCacheEntry *rangetyp;
+       int32           range_count1;
+       int32           range_count2;
+       RangeType **ranges1;
+       RangeType **ranges2;
+
+       typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+       rangetyp = typcache->rngtype;
+
+       if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+               PG_RETURN_MULTIRANGE_P(mr1);
+
+       multirange_deserialize(typcache->rngtype, mr1, &range_count1, &ranges1);
+       multirange_deserialize(typcache->rngtype, mr2, &range_count2, &ranges2);
+
+       PG_RETURN_MULTIRANGE_P(multirange_minus_internal(mltrngtypoid,
+                                                                                                        rangetyp,
+                                                                                                        range_count1,
+                                                                                                        ranges1,
+                                                                                                        range_count2,
+                                                                                                        ranges2));
+}
+
+MultirangeType *
+multirange_minus_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+                                                 int32 range_count1, RangeType **ranges1,
+                                                 int32 range_count2, RangeType **ranges2)
+{
+       RangeType  *r1;
+       RangeType  *r2;
+       RangeType **ranges3;
+       int32           range_count3;
+       int32           i1;
+       int32           i2;
+
+       /*
+        * Worst case: every range in ranges1 makes a different cut to some range
+        * in ranges2.
+        */
+       ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+       range_count3 = 0;
+
+       /*
+        * For each range in mr1, keep subtracting until it's gone or the ranges
+        * in mr2 have passed it. After a subtraction we assign what's left back
+        * to r1. The parallel progress through mr1 and mr2 is similar to
+        * multirange_overlaps_multirange_internal.
+        */
+       r2 = ranges2[0];
+       for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+       {
+               r1 = ranges1[i1];
+
+               /* Discard r2s while r2 << r1 */
+               while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+               {
+                       r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+               }
+
+               while (r2 != NULL)
+               {
+                       if (range_split_internal(rangetyp, r1, r2, &ranges3[range_count3], &r1))
+                       {
+                               /*
+                                * If r2 takes a bite out of the middle of r1, we need two
+                                * outputs
+                                */
+                               range_count3++;
+                               r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+                       }
+                       else if (range_overlaps_internal(rangetyp, r1, r2))
+                       {
+                               /*
+                                * If r2 overlaps r1, replace r1 with r1 - r2.
+                                */
+                               r1 = range_minus_internal(rangetyp, r1, r2);
+
+                               /*
+                                * If r2 goes past r1, then we need to stay with it, in case
+                                * it hits future r1s. Otherwise we need to keep r1, in case
+                                * future r2s hit it. Since we already subtracted, there's no
+                                * point in using the overright/overleft calls.
+                                */
+                               if (RangeIsEmpty(r1) || range_before_internal(rangetyp, r1, r2))
+                                       break;
+                               else
+                                       r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+                       }
+                       else
+                       {
+                               /*
+                                * This and all future r2s are past r1, so keep them. Also
+                                * assign whatever is left of r1 to the result.
+                                */
+                               break;
+                       }
+               }
+
+               /*
+                * Nothing else can remove anything from r1, so keep it. Even if r1 is
+                * empty here, make_multirange will remove it.
+                */
+               ranges3[range_count3++] = r1;
+       }
+
+       return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/* multirange intersection */
+Datum
+multirange_intersect(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       Oid                     mltrngtypoid = MultirangeTypeGetOid(mr1);
+       TypeCacheEntry *typcache;
+       TypeCacheEntry *rangetyp;
+       int32           range_count1;
+       int32           range_count2;
+       RangeType **ranges1;
+       RangeType **ranges2;
+
+       typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+       rangetyp = typcache->rngtype;
+
+       if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+               PG_RETURN_MULTIRANGE_P(make_empty_multirange(mltrngtypoid, rangetyp));
+
+       multirange_deserialize(rangetyp, mr1, &range_count1, &ranges1);
+       multirange_deserialize(rangetyp, mr2, &range_count2, &ranges2);
+
+       PG_RETURN_MULTIRANGE_P(multirange_intersect_internal(mltrngtypoid,
+                                                                                                                rangetyp,
+                                                                                                                range_count1,
+                                                                                                                ranges1,
+                                                                                                                range_count2,
+                                                                                                                ranges2));
+}
+
+MultirangeType *
+multirange_intersect_internal(Oid mltrngtypoid, TypeCacheEntry *rangetyp,
+                                                         int32 range_count1, RangeType **ranges1,
+                                                         int32 range_count2, RangeType **ranges2)
+{
+       RangeType  *r1;
+       RangeType  *r2;
+       RangeType **ranges3;
+       int32           range_count3;
+       int32           i1;
+       int32           i2;
+
+       if (range_count1 == 0 || range_count2 == 0)
+               return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+
+       /*-----------------------------------------------
+        * Worst case is a stitching pattern like this:
+        *
+        * mr1: --- --- --- ---
+        * mr2:   --- --- ---
+        * mr3:   - - - - - -
+        *
+        * That seems to be range_count1 + range_count2 - 1,
+        * but one extra won't hurt.
+        *-----------------------------------------------
+        */
+       ranges3 = palloc0((range_count1 + range_count2) * sizeof(RangeType *));
+       range_count3 = 0;
+
+       /*
+        * For each range in mr1, keep intersecting until the ranges in mr2 have
+        * passed it. The parallel progress through mr1 and mr2 is similar to
+        * multirange_minus_multirange_internal, but we don't have to assign back
+        * to r1.
+        */
+       r2 = ranges2[0];
+       for (i1 = 0, i2 = 0; i1 < range_count1; i1++)
+       {
+               r1 = ranges1[i1];
+
+               /* Discard r2s while r2 << r1 */
+               while (r2 != NULL && range_before_internal(rangetyp, r2, r1))
+               {
+                       r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+               }
+
+               while (r2 != NULL)
+               {
+                       if (range_overlaps_internal(rangetyp, r1, r2))
+                       {
+                               /* Keep the overlapping part */
+                               ranges3[range_count3++] = range_intersect_internal(rangetyp, r1, r2);
+
+                               /* If we "used up" all of r2, go to the next one... */
+                               if (range_overleft_internal(rangetyp, r2, r1))
+                                       r2 = ++i2 >= range_count2 ? NULL : ranges2[i2];
+
+                               /* ...otherwise go to the next r1 */
+                               else
+                                       break;
+                       }
+                       else
+                               /* We're past r1, so move to the next one */
+                               break;
+               }
+
+               /* If we're out of r2s, there can be no more intersections */
+               if (r2 == NULL)
+                       break;
+       }
+
+       return make_multirange(mltrngtypoid, rangetyp, range_count3, ranges3);
+}
+
+/*
+ * range_agg_transfn: combine adjacent/overlapping ranges.
+ *
+ * All we do here is gather the input ranges into an array
+ * so that the finalfn can sort and combine them.
+ */
+Datum
+range_agg_transfn(PG_FUNCTION_ARGS)
+{
+       MemoryContext aggContext;
+       Oid                     rngtypoid;
+       ArrayBuildState *state;
+
+       if (!AggCheckCallContext(fcinfo, &aggContext))
+               elog(ERROR, "range_agg_transfn called in non-aggregate context");
+
+       rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+       if (!type_is_range(rngtypoid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("range_agg must be called with a range")));
+
+       if (PG_ARGISNULL(0))
+               state = initArrayResult(rngtypoid, aggContext, false);
+       else
+               state = (ArrayBuildState *) PG_GETARG_POINTER(0);
+
+       /* skip NULLs */
+       if (!PG_ARGISNULL(1))
+               accumArrayResult(state, PG_GETARG_DATUM(1), false, rngtypoid, aggContext);
+
+       PG_RETURN_POINTER(state);
+}
+
+/*
+ * range_agg_finalfn: use our internal array to merge touching ranges.
+ */
+Datum
+range_agg_finalfn(PG_FUNCTION_ARGS)
+{
+       MemoryContext aggContext;
+       Oid                     mltrngtypoid;
+       TypeCacheEntry *typcache;
+       ArrayBuildState *state;
+       int32           range_count;
+       RangeType **ranges;
+       int                     i;
+
+       if (!AggCheckCallContext(fcinfo, &aggContext))
+               elog(ERROR, "range_agg_finalfn called in non-aggregate context");
+
+       state = PG_ARGISNULL(0) ? NULL : (ArrayBuildState *) PG_GETARG_POINTER(0);
+       if (state == NULL)
+               /* This shouldn't be possible, but just in case.... */
+               PG_RETURN_NULL();
+
+       /* Also return NULL if we had zero inputs, like other aggregates */
+       range_count = state->nelems;
+       if (range_count == 0)
+               PG_RETURN_NULL();
+
+       mltrngtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+       typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+       ranges = palloc0(range_count * sizeof(RangeType *));
+       for (i = 0; i < range_count; i++)
+               ranges[i] = DatumGetRangeTypeP(state->dvalues[i]);
+
+       PG_RETURN_MULTIRANGE_P(make_multirange(mltrngtypoid, typcache->rngtype, range_count, ranges));
+}
+
+Datum
+multirange_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+       MemoryContext aggContext;
+       Oid                     mltrngtypoid;
+       TypeCacheEntry *typcache;
+       MultirangeType *result;
+       MultirangeType *current;
+       int32           range_count1;
+       int32           range_count2;
+       RangeType **ranges1;
+       RangeType **ranges2;
+
+       if (!AggCheckCallContext(fcinfo, &aggContext))
+               elog(ERROR, "multirange_intersect_agg_transfn called in non-aggregate context");
+
+       mltrngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+       if (!type_is_multirange(mltrngtypoid))
+               ereport(ERROR,
+                               (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                errmsg("range_intersect_agg must be called with a multirange")));
+
+       typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+       /* strictness ensures these are non-null */
+       result = PG_GETARG_MULTIRANGE_P(0);
+       current = PG_GETARG_MULTIRANGE_P(1);
+
+       multirange_deserialize(typcache->rngtype, result, &range_count1, &ranges1);
+       multirange_deserialize(typcache->rngtype, current, &range_count2, &ranges2);
+
+       result = multirange_intersect_internal(mltrngtypoid,
+                                                                                  typcache->rngtype,
+                                                                                  range_count1,
+                                                                                  ranges1,
+                                                                                  range_count2,
+                                                                                  ranges2);
+       PG_RETURN_RANGE_P(result);
+}
+
+
+/* multirange -> element type functions */
+
+/* extract lower bound value */
+Datum
+multirange_lower(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       TypeCacheEntry *typcache;
+       RangeBound      lower;
+       RangeBound      upper;
+
+       if (MultirangeIsEmpty(mr))
+               PG_RETURN_NULL();
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+       multirange_get_bounds(typcache->rngtype, mr, 0,
+                                                 &lower, &upper);
+
+       if (!lower.infinite)
+               PG_RETURN_DATUM(lower.val);
+       else
+               PG_RETURN_NULL();
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       TypeCacheEntry *typcache;
+       RangeBound      lower;
+       RangeBound      upper;
+
+       if (MultirangeIsEmpty(mr))
+               PG_RETURN_NULL();
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+       multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
+                                                 &lower, &upper);
+
+       if (!upper.infinite)
+               PG_RETURN_DATUM(upper.val);
+       else
+               PG_RETURN_NULL();
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+       PG_RETURN_BOOL(MultirangeIsEmpty(mr));
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       TypeCacheEntry *typcache;
+       RangeBound      lower;
+       RangeBound      upper;
+
+       if (MultirangeIsEmpty(mr))
+               PG_RETURN_BOOL(false);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+       multirange_get_bounds(typcache->rngtype, mr, 0,
+                                                 &lower, &upper);
+
+       PG_RETURN_BOOL(lower.inclusive);
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       TypeCacheEntry *typcache;
+       RangeBound      lower;
+       RangeBound      upper;
+
+       if (MultirangeIsEmpty(mr))
+               PG_RETURN_BOOL(false);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+       multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
+                                                 &lower, &upper);
+
+       PG_RETURN_BOOL(upper.inclusive);
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       TypeCacheEntry *typcache;
+       RangeBound      lower;
+       RangeBound      upper;
+
+       if (MultirangeIsEmpty(mr))
+               PG_RETURN_BOOL(false);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+       multirange_get_bounds(typcache->rngtype, mr, 0,
+                                                 &lower, &upper);
+
+       PG_RETURN_BOOL(lower.infinite);
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       TypeCacheEntry *typcache;
+       RangeBound      lower;
+       RangeBound      upper;
+
+       if (MultirangeIsEmpty(mr))
+               PG_RETURN_BOOL(false);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+       multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
+                                                 &lower, &upper);
+
+       PG_RETURN_BOOL(upper.infinite);
+}
+
+
+
+/* multirange, element -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_elem(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       Datum           val = PG_GETARG_DATUM(1);
+       TypeCacheEntry *typcache;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+       PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/* contained by? */
+Datum
+elem_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+       Datum           val = PG_GETARG_DATUM(0);
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+       PG_RETURN_BOOL(multirange_contains_elem_internal(typcache, mr, val));
+}
+
+/*
+ * Comparison function for checking if any range of multirange contains given
+ * key element using binary search.
+ */
+static int
+multirange_elem_bsearch_comparison(TypeCacheEntry *typcache,
+                                                                  RangeBound *lower, RangeBound *upper,
+                                                                  void *key, bool *match)
+{
+       Datum           val = *((Datum *) key);
+       int                     cmp;
+
+       if (!lower->infinite)
+       {
+               cmp = DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
+                                                                                         typcache->rng_collation,
+                                                                                         lower->val, val));
+               if (cmp > 0 || (cmp == 0 && !lower->inclusive))
+                       return -1;
+       }
+
+       if (!upper->infinite)
+       {
+               cmp = DatumGetInt32(FunctionCall2Coll(&typcache->rng_cmp_proc_finfo,
+                                                                                         typcache->rng_collation,
+                                                                                         upper->val, val));
+               if (cmp < 0 || (cmp == 0 && !upper->inclusive))
+                       return 1;
+       }
+
+       *match = true;
+       return 0;
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache,
+                                                                 MultirangeType *mr, Datum val)
+{
+       if (MultirangeIsEmpty(mr))
+               return false;
+
+       return multirange_bsearch_match(typcache->rngtype, mr, &val,
+                                                                       multirange_elem_bsearch_comparison);
+}
+
+/* multirange, range -> bool functions */
+
+/* contains? */
+Datum
+multirange_contains_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(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/* contained by? */
+Datum
+range_contained_by_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(multirange_contains_range_internal(typcache, mr, r));
+}
+
+/*
+ * Comparison function for checking if any range of multirange contains given
+ * key range using binary search.
+ */
+static int
+multirange_range_contains_bsearch_comparison(TypeCacheEntry *typcache,
+                                                                                        RangeBound *lower, RangeBound *upper,
+                                                                                        void *key, bool *match)
+{
+       RangeBound *keyLower = (RangeBound *) key;
+       RangeBound *keyUpper = (RangeBound *) key + 1;
+
+       /* Check if key range is strictly in the left or in the right */
+       if (range_cmp_bounds(typcache, keyUpper, lower) < 0)
+               return -1;
+       if (range_cmp_bounds(typcache, keyLower, upper) > 0)
+               return -1;
+
+       /*
+        * At this point we found overlapping range.  But we have to check if it
+        * really contains the key range.  Anyway, we have to stop our search
+        * here, because multirange contains only non-overlapping ranges.
+        */
+       *match = range_bounds_contains(typcache, lower, upper, keyLower, keyUpper);
+
+       return 0;
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr, RangeType *r)
+{
+       TypeCacheEntry *rangetyp;
+       RangeBound      bounds[2];
+       bool            empty;
+
+       rangetyp = typcache->rngtype;
+
+       /*
+        * Every multirange contains an infinite number of empty ranges, even an
+        * empty one.
+        */
+       if (RangeIsEmpty(r))
+               return true;
+
+       if (MultirangeIsEmpty(mr))
+               return false;
+
+       range_deserialize(rangetyp, r, &bounds[0], &bounds[1], &empty);
+       Assert(!empty);
+
+       return multirange_bsearch_match(rangetyp, mr, bounds,
+                                                                       multirange_range_contains_bsearch_comparison);
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+       TypeCacheEntry *rangetyp = typcache->rngtype;
+       int32           range_count_1;
+       int32           range_count_2;
+       int32           i;
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+
+       /* Different types should be prevented by ANYMULTIRANGE matching rules */
+       if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+               elog(ERROR, "multirange types do not match");
+
+       range_count_1 = mr1->rangeCount;
+       range_count_2 = mr2->rangeCount;
+
+       if (range_count_1 != range_count_2)
+               return false;
+
+       for (i = 0; i < range_count_1; i++)
+       {
+               multirange_get_bounds(rangetyp, mr1, i, &lower1, &upper1);
+               multirange_get_bounds(rangetyp, mr2, i, &lower2, &upper2);
+
+               if (range_cmp_bounds(rangetyp, &lower1, &lower2) != 0 ||
+                       range_cmp_bounds(rangetyp, &upper1, &upper2) != 0)
+                       return false;
+       }
+
+       return true;
+}
+
+/* equality */
+Datum
+multirange_eq(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       PG_RETURN_BOOL(multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality (internal version) */
+bool
+multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1, MultirangeType *mr2)
+{
+       return (!multirange_eq_internal(typcache, mr1, mr2));
+}
+
+/* inequality */
+Datum
+multirange_ne(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       PG_RETURN_BOOL(multirange_ne_internal(typcache, mr1, mr2));
+}
+
+/* overlaps? */
+Datum
+range_overlaps_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_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_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_overlaps_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_overlaps_multirange(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       PG_RETURN_BOOL(multirange_overlaps_multirange_internal(typcache, mr1, mr2));
+}
+
+/*
+ * Comparison function for checking if any range of multirange overlaps given
+ * key range using binary search.
+ */
+static int
+multirange_range_overlaps_bsearch_comparison(TypeCacheEntry *typcache,
+                                                                                        RangeBound *lower, RangeBound *upper,
+                                                                                        void *key, bool *match)
+{
+       RangeBound *keyLower = (RangeBound *) key;
+       RangeBound *keyUpper = (RangeBound *) key + 1;
+
+       if (range_cmp_bounds(typcache, keyUpper, lower) < 0)
+               return -1;
+       if (range_cmp_bounds(typcache, keyLower, upper) > 0)
+               return -1;
+
+       *match = true;
+       return 0;
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType *mr)
+{
+       TypeCacheEntry *rangetyp;
+       RangeBound      bounds[2];
+       bool            empty;
+
+       rangetyp = typcache->rngtype;
+
+       /*
+        * Empties never overlap, even with empties. (This seems strange since
+        * they *do* contain each other, but we want to follow how ranges work.)
+        */
+       if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+               return false;
+
+       range_deserialize(rangetyp, r, &bounds[0], &bounds[1], &empty);
+       Assert(!empty);
+
+       return multirange_bsearch_match(rangetyp, mr, bounds,
+                                                                       multirange_range_overlaps_bsearch_comparison);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+                                                                               MultirangeType *mr2)
+{
+       TypeCacheEntry *rangetyp;
+       int32           range_count1;
+       int32           range_count2;
+       int32           i1;
+       int32           i2;
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+
+       /*
+        * Empties never overlap, even with empties. (This seems strange since
+        * they *do* contain each other, but we want to follow how ranges work.)
+        */
+       if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+               return false;
+
+       rangetyp = typcache->rngtype;
+
+       range_count1 = mr1->rangeCount;
+       range_count2 = mr2->rangeCount;
+
+       /*
+        * Every range in mr1 gets a chance to overlap with the ranges in mr2, but
+        * we can use their ordering to avoid O(n^2). This is similar to
+        * range_overlaps_multirange where r1 : r2 :: mrr : r, but there if we
+        * don't find an overlap with r we're done, and here if we don't find an
+        * overlap with r2 we try the next r2.
+        */
+       i1 = 0;
+       multirange_get_bounds(rangetyp, mr1, i1, &lower1, &upper1);
+       for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+       {
+               multirange_get_bounds(rangetyp, mr2, i2, &lower2, &upper2);
+
+               /* Discard r1s while r1 << r2 */
+               while (range_cmp_bounds(rangetyp, &upper1, &lower2) < 0)
+               {
+                       if (++i1 >= range_count1)
+                               return false;
+                       multirange_get_bounds(rangetyp, mr1, i1, &lower1, &upper1);
+               }
+
+               /*
+                * If r1 && r2, we're done, otherwise we failed to find an overlap for
+                * r2, so go to the next one.
+                */
+               if (range_bounds_overlaps(rangetyp, &lower1, &upper1, &lower2, &upper2))
+                       return true;
+       }
+
+       /* We looked through all of mr2 without finding an overlap */
+       return false;
+}
+
+/* does not extend to right of? */
+Datum
+range_overleft_multirange(PG_FUNCTION_ARGS)
+{
+       RangeType  *r = PG_GETARG_RANGE_P(0);
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+       bool            empty;
+
+       if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+               PG_RETURN_BOOL(false);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+       range_deserialize(typcache->rngtype, r, &lower1, &upper1, &empty);
+       Assert(!empty);
+       multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
+                                                 &lower2, &upper2);
+
+       PG_RETURN_BOOL(range_cmp_bounds(typcache->rngtype, &upper1, &upper2) <= 0);
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       RangeType  *r = PG_GETARG_RANGE_P(1);
+       TypeCacheEntry *typcache;
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+       bool            empty;
+
+       if (MultirangeIsEmpty(mr) || RangeIsEmpty(r))
+               PG_RETURN_BOOL(false);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+       multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
+                                                 &lower1, &upper1);
+       range_deserialize(typcache->rngtype, r, &lower2, &upper2, &empty);
+       Assert(!empty);
+
+       PG_RETURN_BOOL(range_cmp_bounds(typcache->rngtype, &upper1, &upper2) <= 0);
+}
+
+Datum
+multirange_overleft_multirange(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+
+       if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+               PG_RETURN_BOOL(false);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       multirange_get_bounds(typcache->rngtype, mr1, mr1->rangeCount - 1,
+                                                 &lower1, &upper1);
+       multirange_get_bounds(typcache->rngtype, mr2, mr2->rangeCount - 1,
+                                                 &lower2, &upper2);
+
+       PG_RETURN_BOOL(range_cmp_bounds(typcache->rngtype, &upper1, &upper2) <= 0);
+}
+
+/* does not extend to left of? */
+Datum
+range_overright_multirange(PG_FUNCTION_ARGS)
+{
+       RangeType  *r = PG_GETARG_RANGE_P(0);
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+       bool            empty;
+
+       if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+               PG_RETURN_BOOL(false);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+       range_deserialize(typcache->rngtype, r, &lower1, &upper1, &empty);
+       Assert(!empty);
+       multirange_get_bounds(typcache->rngtype, mr, 0, &lower2, &upper2);
+
+       PG_RETURN_BOOL(range_cmp_bounds(typcache->rngtype, &lower1, &lower2) >= 0);
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       RangeType  *r = PG_GETARG_RANGE_P(1);
+       TypeCacheEntry *typcache;
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+       bool            empty;
+
+       if (MultirangeIsEmpty(mr) || RangeIsEmpty(r))
+               PG_RETURN_BOOL(false);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+       multirange_get_bounds(typcache->rngtype, mr, 0, &lower1, &upper1);
+       range_deserialize(typcache->rngtype, r, &lower2, &upper2, &empty);
+       Assert(!empty);
+
+       PG_RETURN_BOOL(range_cmp_bounds(typcache->rngtype, &lower1, &lower2) >= 0);
+}
+
+Datum
+multirange_overright_multirange(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+
+       if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+               PG_RETURN_BOOL(false);
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       multirange_get_bounds(typcache->rngtype, mr1, 0, &lower1, &upper1);
+       multirange_get_bounds(typcache->rngtype, mr2, 0, &lower2, &upper2);
+
+       PG_RETURN_BOOL(range_cmp_bounds(typcache->rngtype, &lower1, &lower2) >= 0);
+}
+
+/* contains? */
+Datum
+multirange_contains_multirange(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr1, mr2));
+}
+
+/* contained by? */
+Datum
+multirange_contained_by_multirange(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       PG_RETURN_BOOL(multirange_contains_multirange_internal(typcache, mr2, mr1));
+}
+
+/*
+ * Test whether multirange mr1 contains every range from another multirange mr2.
+ */
+bool
+multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+                                                                               MultirangeType *mr1, MultirangeType *mr2)
+{
+       TypeCacheEntry *rangetyp;
+       int32           range_count1 = mr1->rangeCount;
+       int32           range_count2 = mr2->rangeCount;
+       int                     i1,
+                               i2;
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+
+       rangetyp = typcache->rngtype;
+
+       /*
+        * We follow the same logic for empties as ranges: - an empty multirange
+        * contains an empty range/multirange. - an empty multirange can't contain
+        * any other range/multirange. - an empty multirange is contained by any
+        * other range/multirange.
+        */
+
+       if (range_count2 == 0)
+               return true;
+       if (range_count1 == 0)
+               return false;
+
+       /*
+        * Every range in mr2 must be contained by some range in mr1. To avoid
+        * O(n^2) we walk through both ranges in tandem.
+        */
+       i1 = 0;
+       multirange_get_bounds(rangetyp, mr1, i1, &lower1, &upper1);
+       for (i2 = 0; i2 < range_count2; i2++)
+       {
+               multirange_get_bounds(rangetyp, mr2, i2, &lower2, &upper2);
+
+               /* Discard r1s while r1 << r2 */
+               while (range_cmp_bounds(rangetyp, &upper1, &lower2) < 0)
+               {
+                       if (++i1 >= range_count1)
+                               return false;
+                       multirange_get_bounds(rangetyp, mr1, i1, &lower1, &upper1);
+               }
+
+               /*
+                * If r1 @> r2, go to the next r2, otherwise return false (since every
+                * r1[n] and r1[n+1] must have a gap). Note this will give weird
+                * answers if you don't canonicalize, e.g. with a custom
+                * int2multirange {[1,1], [2,2]} there is a "gap". But that is
+                * consistent with other range operators, e.g. '[1,1]'::int2range -|-
+                * '[2,2]'::int2range is false.
+                */
+               if (!range_bounds_contains(rangetyp, &lower1, &upper1,
+                                                                  &lower2, &upper2))
+                       return false;
+       }
+
+       /* All ranges in mr2 are satisfied */
+       return true;
+}
+
+/* strictly left of? */
+Datum
+range_before_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_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_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_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_before_multirange(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr1, mr2));
+}
+
+/* strictly right of? */
+Datum
+range_after_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_after_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_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_before_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_after_multirange(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       PG_RETURN_BOOL(multirange_before_multirange_internal(typcache, mr2, mr1));
+}
+
+/* strictly left of? (internal version) */
+bool
+range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+                                                                MultirangeType *mr)
+{
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+       bool            empty;
+
+       if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+               return false;
+
+       range_deserialize(typcache->rngtype, r, &lower1, &upper1, &empty);
+       Assert(!empty);
+
+       multirange_get_bounds(typcache->rngtype, mr, 0,
+                                                 &lower2, &upper2);
+
+       return (range_cmp_bounds(typcache->rngtype, &upper1, &lower2) < 0);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache,
+                                                                         MultirangeType *mr1,
+                                                                         MultirangeType *mr2)
+{
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+
+       if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+               return false;
+
+       multirange_get_bounds(typcache->rngtype, mr1, mr1->rangeCount - 1,
+                                                 &lower1, &upper1);
+       multirange_get_bounds(typcache->rngtype, mr2, 0,
+                                                 &lower2, &upper2);
+
+       return (range_cmp_bounds(typcache->rngtype, &upper1, &lower2) < 0);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+                                                               MultirangeType *mr)
+{
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+       bool            empty;
+       int32           range_count;
+
+       if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+               return false;
+
+       range_deserialize(typcache->rngtype, r, &lower1, &upper1, &empty);
+       Assert(!empty);
+
+       range_count = mr->rangeCount;
+       multirange_get_bounds(typcache->rngtype, mr, range_count - 1,
+                                                 &lower2, &upper2);
+
+       return (range_cmp_bounds(typcache->rngtype, &lower1, &upper2) > 0);
+}
+
+bool
+range_adjacent_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+                                                                  MultirangeType *mr)
+{
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+       bool            empty;
+       int32           range_count;
+
+       if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+               return false;
+
+       range_deserialize(typcache->rngtype, r, &lower1, &upper1, &empty);
+       Assert(!empty);
+
+       range_count = mr->rangeCount;
+       multirange_get_bounds(typcache->rngtype, mr, 0,
+                                                 &lower2, &upper2);
+
+       if (bounds_adjacent(typcache->rngtype, upper1, lower2))
+               return true;
+
+       if (range_count > 1)
+               multirange_get_bounds(typcache->rngtype, mr, range_count - 1,
+                                                         &lower2, &upper2);
+
+       if (bounds_adjacent(typcache->rngtype, upper2, lower1))
+               return true;
+
+       return false;
+}
+
+/* adjacent to? */
+Datum
+range_adjacent_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_adjacent_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       RangeType  *r = PG_GETARG_RANGE_P(1);
+       TypeCacheEntry *typcache;
+
+       if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+               return false;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+       PG_RETURN_BOOL(range_adjacent_multirange_internal(typcache, r, mr));
+}
+
+Datum
+multirange_adjacent_multirange(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       TypeCacheEntry *typcache;
+       int32           range_count1;
+       int32           range_count2;
+       RangeBound      lower1,
+                               upper1,
+                               lower2,
+                               upper2;
+
+       if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+               return false;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       range_count1 = mr1->rangeCount;
+       range_count2 = mr2->rangeCount;
+       multirange_get_bounds(typcache->rngtype, mr1, range_count1 - 1,
+                                                 &lower1, &upper1);
+       multirange_get_bounds(typcache->rngtype, mr2, 0,
+                                                 &lower2, &upper2);
+       if (bounds_adjacent(typcache->rngtype, upper1, lower2))
+               PG_RETURN_BOOL(true);
+
+       if (range_count1 > 1)
+               multirange_get_bounds(typcache->rngtype, mr1, 0,
+                                                         &lower1, &upper1);
+       if (range_count2 > 1)
+               multirange_get_bounds(typcache->rngtype, mr2, range_count2 - 1,
+                                                         &lower2, &upper2);
+       if (bounds_adjacent(typcache->rngtype, upper2, lower1))
+               PG_RETURN_BOOL(true);
+       PG_RETURN_BOOL(false);
+}
+
+/* Btree support */
+
+/* btree comparator */
+Datum
+multirange_cmp(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr1 = PG_GETARG_MULTIRANGE_P(0);
+       MultirangeType *mr2 = PG_GETARG_MULTIRANGE_P(1);
+       int32           range_count_1;
+       int32           range_count_2;
+       int32           range_count_max;
+       int32           i;
+       TypeCacheEntry *typcache;
+       int                     cmp = 0;                /* If both are empty we'll use this. */
+
+       /* Different types should be prevented by ANYMULTIRANGE matching rules */
+       if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+               elog(ERROR, "multirange types do not match");
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+       range_count_1 = mr1->rangeCount;
+       range_count_2 = mr2->rangeCount;
+
+       /* Loop over source data */
+       range_count_max = Max(range_count_1, range_count_2);
+       for (i = 0; i < range_count_max; i++)
+       {
+               RangeBound      lower1,
+                                       upper1,
+                                       lower2,
+                                       upper2;
+
+               /*
+                * If one multirange is shorter, it's as if it had empty ranges at the
+                * end to extend its length. An empty range compares earlier than any
+                * other range, so the shorter multirange comes before the longer.
+                * This is the same behavior as in other types, e.g. in strings 'aaa'
+                * < 'aaaaaa'.
+                */
+               if (i >= range_count_1)
+               {
+                       cmp = -1;
+                       break;
+               }
+               if (i >= range_count_2)
+               {
+                       cmp = 1;
+                       break;
+               }
+
+               multirange_get_bounds(typcache->rngtype, mr1, i, &lower1, &upper1);
+               multirange_get_bounds(typcache->rngtype, mr2, i, &lower2, &upper2);
+
+               cmp = range_cmp_bounds(typcache->rngtype, &lower1, &lower2);
+               if (cmp == 0)
+                       cmp = range_cmp_bounds(typcache->rngtype, &upper1, &upper2);
+               if (cmp != 0)
+                       break;
+       }
+
+       PG_FREE_IF_COPY(mr1, 0);
+       PG_FREE_IF_COPY(mr2, 1);
+
+       PG_RETURN_INT32(cmp);
+}
+
+/* inequality operators using the multirange_cmp function */
+Datum
+multirange_lt(PG_FUNCTION_ARGS)
+{
+       int                     cmp = multirange_cmp(fcinfo);
+
+       PG_RETURN_BOOL(cmp < 0);
+}
+
+Datum
+multirange_le(PG_FUNCTION_ARGS)
+{
+       int                     cmp = multirange_cmp(fcinfo);
+
+       PG_RETURN_BOOL(cmp <= 0);
+}
+
+Datum
+multirange_ge(PG_FUNCTION_ARGS)
+{
+       int                     cmp = multirange_cmp(fcinfo);
+
+       PG_RETURN_BOOL(cmp >= 0);
+}
+
+Datum
+multirange_gt(PG_FUNCTION_ARGS)
+{
+       int                     cmp = multirange_cmp(fcinfo);
+
+       PG_RETURN_BOOL(cmp > 0);
+}
+
+/* multirange -> range functions */
+
+/* Find the smallest range that includes everything in the multirange */
+Datum
+range_merge_from_multirange(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       Oid                     mltrngtypoid = MultirangeTypeGetOid(mr);
+       TypeCacheEntry *typcache;
+       RangeType  *result;
+
+       typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+       if (MultirangeIsEmpty(mr))
+       {
+               result = make_empty_range(typcache->rngtype);
+       }
+       else if (mr->rangeCount == 1)
+       {
+               result = multirange_get_range(typcache->rngtype, mr, 0);
+       }
+       else
+       {
+               RangeBound      firstLower,
+                                       firstUpper,
+                                       lastLower,
+                                       lastUpper;
+
+               multirange_get_bounds(typcache->rngtype, mr, 0,
+                                                         &firstLower, &firstUpper);
+               multirange_get_bounds(typcache->rngtype, mr, mr->rangeCount - 1,
+                                                         &lastLower, &lastUpper);
+
+               result = make_range(typcache->rngtype, &firstLower, &lastUpper, false);
+       }
+
+       PG_RETURN_RANGE_P(result);
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       uint32          result = 1;
+       TypeCacheEntry *typcache,
+                          *scache;
+       int32           range_count,
+                               i;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+       scache = typcache->rngtype->rngelemtype;
+       if (!OidIsValid(scache->hash_proc_finfo.fn_oid))
+       {
+               scache = lookup_type_cache(scache->type_id,
+                                                                  TYPECACHE_HASH_PROC_FINFO);
+               if (!OidIsValid(scache->hash_proc_finfo.fn_oid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                        errmsg("could not identify a hash function for type %s",
+                                                       format_type_be(scache->type_id))));
+       }
+
+       range_count = mr->rangeCount;
+       for (i = 0; i < range_count; i++)
+       {
+               RangeBound      lower,
+                                       upper;
+               uint8           flags = MultirangeGetFlagsPtr(mr)[i];
+               uint32          lower_hash;
+               uint32          upper_hash;
+               uint32          range_hash;
+
+               multirange_get_bounds(typcache->rngtype, mr, i, &lower, &upper);
+
+               if (RANGE_HAS_LBOUND(flags))
+                       lower_hash = DatumGetUInt32(FunctionCall1Coll(&scache->hash_proc_finfo,
+                                                                                                                 typcache->rngtype->rng_collation,
+                                                                                                                 lower.val));
+               else
+                       lower_hash = 0;
+
+               if (RANGE_HAS_UBOUND(flags))
+                       upper_hash = DatumGetUInt32(FunctionCall1Coll(&scache->hash_proc_finfo,
+                                                                                                                 typcache->rngtype->rng_collation,
+                                                                                                                 upper.val));
+               else
+                       upper_hash = 0;
+
+               /* Merge hashes of flags and bounds */
+               range_hash = hash_uint32((uint32) flags);
+               range_hash ^= lower_hash;
+               range_hash = (range_hash << 1) | (range_hash >> 31);
+               range_hash ^= upper_hash;
+
+               /*
+                * Use the same approach as hash_array to combine the individual
+                * elements' hash values:
+                */
+               result = (result << 5) - result + range_hash;
+       }
+
+       PG_FREE_IF_COPY(mr, 0);
+
+       PG_RETURN_UINT32(result);
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_multirange.
+ */
+Datum
+hash_multirange_extended(PG_FUNCTION_ARGS)
+{
+       MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+       Datum           seed = PG_GETARG_DATUM(1);
+       uint64          result = 1;
+       TypeCacheEntry *typcache,
+                          *scache;
+       int32           range_count,
+                               i;
+
+       typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+       scache = typcache->rngtype->rngelemtype;
+       if (!OidIsValid(scache->hash_extended_proc_finfo.fn_oid))
+       {
+               scache = lookup_type_cache(scache->type_id,
+                                                                  TYPECACHE_HASH_EXTENDED_PROC_FINFO);
+               if (!OidIsValid(scache->hash_extended_proc_finfo.fn_oid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_FUNCTION),
+                                        errmsg("could not identify a hash function for type %s",
+                                                       format_type_be(scache->type_id))));
+       }
+
+       range_count = mr->rangeCount;
+       for (i = 0; i < range_count; i++)
+       {
+               RangeBound      lower,
+                                       upper;
+               uint8           flags = MultirangeGetFlagsPtr(mr)[i];
+               uint64          lower_hash;
+               uint64          upper_hash;
+               uint64          range_hash;
+
+               multirange_get_bounds(typcache->rngtype, mr, i, &lower, &upper);
+
+               if (RANGE_HAS_LBOUND(flags))
+                       lower_hash = DatumGetUInt64(FunctionCall2Coll(&scache->hash_extended_proc_finfo,
+                                                                                                                 typcache->rngtype->rng_collation,
+                                                                                                                 lower.val,
+                                                                                                                 seed));
+               else
+                       lower_hash = 0;
+
+               if (RANGE_HAS_UBOUND(flags))
+                       upper_hash = DatumGetUInt64(FunctionCall2Coll(&scache->hash_extended_proc_finfo,
+                                                                                                                 typcache->rngtype->rng_collation,
+                                                                                                                 upper.val,
+                                                                                                                 seed));
+               else
+                       upper_hash = 0;
+
+               /* Merge hashes of flags and bounds */
+               range_hash = DatumGetUInt64(hash_uint32_extended((uint32) flags,
+                                                                                                                DatumGetInt64(seed)));
+               range_hash ^= lower_hash;
+               range_hash = ROTATE_HIGH_AND_LOW_32BITS(range_hash);
+               range_hash ^= upper_hash;
+
+               /*
+                * Use the same approach as hash_array to combine the individual
+                * elements' hash values:
+                */
+               result = (result << 5) - result + range_hash;
+       }
+
+       PG_FREE_IF_COPY(mr, 0);
+
+       PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/multirangetypes_selfuncs.c b/src/backend/utils/adt/multirangetypes_selfuncs.c
new file mode 100644 (file)
index 0000000..7259af0
--- /dev/null
@@ -0,0 +1,1320 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes_selfuncs.c
+ *       Functions for selectivity estimation of multirange operators
+ *
+ * Estimates are based on histograms of lower and upper bounds, and the
+ * fraction of empty multiranges.
+ *
+ * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ *       src/backend/utils/adt/multirangetypes_selfuncs.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include <math.h>
+
+#include "access/htup_details.h"
+#include "catalog/pg_operator.h"
+#include "catalog/pg_statistic.h"
+#include "catalog/pg_type.h"
+#include "utils/float.h"
+#include "utils/fmgrprotos.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/selfuncs.h"
+#include "utils/typcache.h"
+
+static double calc_multirangesel(TypeCacheEntry *typcache,
+                                                                VariableStatData *vardata,
+                                                                const MultirangeType *constval, Oid operator);
+static double default_multirange_selectivity(Oid operator);
+static double default_multirange_selectivity(Oid operator);
+static double calc_hist_selectivity(TypeCacheEntry *typcache,
+                                                                       VariableStatData *vardata,
+                                                                       const MultirangeType *constval,
+                                                                       Oid operator);
+static double calc_hist_selectivity_scalar(TypeCacheEntry *typcache,
+                                                                                  const RangeBound *constbound,
+                                                                                  const RangeBound *hist,
+                                                                                  int hist_nvalues, bool equal);
+static int     rbound_bsearch(TypeCacheEntry *typcache, const RangeBound *value,
+                                                  const RangeBound *hist, int hist_length, bool equal);
+static float8 get_position(TypeCacheEntry *typcache, const RangeBound *value,
+                                                  const RangeBound *hist1, const RangeBound *hist2);
+static float8 get_len_position(double value, double hist1, double hist2);
+static float8 get_distance(TypeCacheEntry *typcache, const RangeBound *bound1,
+                                                  const RangeBound *bound2);
+static int     length_hist_bsearch(Datum *length_hist_values,
+                                                               int length_hist_nvalues, double value,
+                                                               bool equal);
+static double calc_length_hist_frac(Datum *length_hist_values,
+                                                                       int length_hist_nvalues, double length1,
+                                                                       double length2, bool equal);
+static double calc_hist_selectivity_contained(TypeCacheEntry *typcache,
+                                                                                         const RangeBound *lower,
+                                                                                         RangeBound *upper,
+                                                                                         const RangeBound *hist_lower,
+                                                                                         int hist_nvalues,
+                                                                                         Datum *length_hist_values,
+                                                                                         int length_hist_nvalues);
+static double calc_hist_selectivity_contains(TypeCacheEntry *typcache,
+                                                                                        const RangeBound *lower,
+                                                                                        const RangeBound *upper,
+                                                                                        const RangeBound *hist_lower,
+                                                                                        int hist_nvalues,
+                                                                                        Datum *length_hist_values,
+                                                                                        int length_hist_nvalues);
+
+/*
+ * Returns a default selectivity estimate for given operator, when we don't
+ * have statistics or cannot use them for some reason.
+ */
+static double
+default_multirange_selectivity(Oid operator)
+{
+       switch (operator)
+       {
+               case OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_RANGE_OP:
+               case OID_RANGE_OVERLAPS_MULTIRANGE_OP:
+                       return 0.01;
+
+               case OID_MULTIRANGE_CONTAINS_RANGE_OP:
+               case OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP:
+               case OID_MULTIRANGE_RANGE_CONTAINED_OP:
+               case OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP:
+                       return 0.005;
+
+               case OID_MULTIRANGE_CONTAINS_ELEM_OP:
+               case OID_MULTIRANGE_ELEM_CONTAINED_OP:
+
+                       /*
+                        * "multirange @> elem" is more or less identical to a scalar
+                        * inequality "A >= b AND A <= c".
+                        */
+                       return DEFAULT_MULTIRANGE_INEQ_SEL;
+
+               case OID_MULTIRANGE_LESS_OP:
+               case OID_MULTIRANGE_LESS_EQUAL_OP:
+               case OID_MULTIRANGE_GREATER_OP:
+               case OID_MULTIRANGE_GREATER_EQUAL_OP:
+               case OID_MULTIRANGE_LEFT_RANGE_OP:
+               case OID_MULTIRANGE_LEFT_MULTIRANGE_OP:
+               case OID_RANGE_LEFT_MULTIRANGE_OP:
+               case OID_MULTIRANGE_RIGHT_RANGE_OP:
+               case OID_MULTIRANGE_RIGHT_MULTIRANGE_OP:
+               case OID_RANGE_RIGHT_MULTIRANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP:
+               case OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP:
+               case OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
+                       /* these are similar to regular scalar inequalities */
+                       return DEFAULT_INEQ_SEL;
+
+               default:
+
+                       /*
+                        * all multirange operators should be handled above, but just in
+                        * case
+                        */
+                       return 0.01;
+       }
+}
+
+/*
+ * multirangesel -- restriction selectivity for multirange operators
+ */
+Datum
+multirangesel(PG_FUNCTION_ARGS)
+{
+       PlannerInfo *root = (PlannerInfo *) PG_GETARG_POINTER(0);
+       Oid                     operator = PG_GETARG_OID(1);
+       List       *args = (List *) PG_GETARG_POINTER(2);
+       int                     varRelid = PG_GETARG_INT32(3);
+       VariableStatData vardata;
+       Node       *other;
+       bool            varonleft;
+       Selectivity selec;
+       TypeCacheEntry *typcache = NULL;
+       MultirangeType *constmultirange = NULL;
+       RangeType  *constrange = NULL;
+
+       /*
+        * If expression is not (variable op something) or (something op
+        * variable), then punt and return a default estimate.
+        */
+       if (!get_restriction_variable(root, args, varRelid,
+                                                                 &vardata, &other, &varonleft))
+               PG_RETURN_FLOAT8(default_multirange_selectivity(operator));
+
+       /*
+        * Can't do anything useful if the something is not a constant, either.
+        */
+       if (!IsA(other, Const))
+       {
+               ReleaseVariableStats(vardata);
+               PG_RETURN_FLOAT8(default_multirange_selectivity(operator));
+       }
+
+       /*
+        * All the multirange operators are strict, so we can cope with a NULL
+        * constant right away.
+        */
+       if (((Const *) other)->constisnull)
+       {
+               ReleaseVariableStats(vardata);
+               PG_RETURN_FLOAT8(0.0);
+       }
+
+       /*
+        * If var is on the right, commute the operator, so that we can assume the
+        * var is on the left in what follows.
+        */
+       if (!varonleft)
+       {
+               /* we have other Op var, commute to make var Op other */
+               operator = get_commutator(operator);
+               if (!operator)
+               {
+                       /* Use default selectivity (should we raise an error instead?) */
+                       ReleaseVariableStats(vardata);
+                       PG_RETURN_FLOAT8(default_multirange_selectivity(operator));
+               }
+       }
+
+       /*
+        * OK, there's a Var and a Const we're dealing with here.  We need the
+        * Const to be of same multirange type as the column, else we can't do
+        * anything useful. (Such cases will likely fail at runtime, but here we'd
+        * rather just return a default estimate.)
+        *
+        * If the operator is "multirange @> element", the constant should be of
+        * the element type of the multirange column. Convert it to a multirange
+        * that includes only that single point, so that we don't need special
+        * handling for that in what follows.
+        */
+       if (operator == OID_MULTIRANGE_CONTAINS_ELEM_OP)
+       {
+               typcache = multirange_get_typcache(fcinfo, vardata.vartype);
+
+               if (((Const *) other)->consttype == typcache->rngtype->rngelemtype->type_id)
+               {
+                       RangeBound      lower,
+                                               upper;
+
+                       lower.inclusive = true;
+                       lower.val = ((Const *) other)->constvalue;
+                       lower.infinite = false;
+                       lower.lower = true;
+                       upper.inclusive = true;
+                       upper.val = ((Const *) other)->constvalue;
+                       upper.infinite = false;
+                       upper.lower = false;
+                       constrange = range_serialize(typcache->rngtype, &lower, &upper, false);
+                       constmultirange = make_multirange(typcache->type_id, typcache->rngtype,
+                                                                                         1, &constrange);
+               }
+       }
+       else if (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 ||
+                        operator == OID_MULTIRANGE_LEFT_RANGE_OP ||
+                        operator == OID_MULTIRANGE_RIGHT_RANGE_OP)
+       {
+               /*
+                * Promote a range in "multirange OP range" just like we do an element
+                * in "multirange OP element".
+                */
+               typcache = multirange_get_typcache(fcinfo, vardata.vartype);
+               if (((Const *) other)->consttype == typcache->rngtype->type_id)
+               {
+                       constrange = DatumGetRangeTypeP(((Const *) other)->constvalue);
+                       constmultirange = make_multirange(typcache->type_id, typcache->rngtype,
+                                                                                         1, &constrange);
+               }
+       }
+       else if (operator == OID_RANGE_OVERLAPS_MULTIRANGE_OP ||
+                        operator == OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP ||
+                        operator == OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP ||
+                        operator == OID_RANGE_LEFT_MULTIRANGE_OP ||
+                        operator == OID_RANGE_RIGHT_MULTIRANGE_OP ||
+                        operator == OID_MULTIRANGE_ELEM_CONTAINED_OP ||
+                        operator == OID_MULTIRANGE_RANGE_CONTAINED_OP)
+       {
+               /*
+                * Here, the Var is the elem/range, not the multirange.  For now we
+                * just punt and return the default estimate.  In future we could
+                * disassemble the multirange constant to do something more
+                * intelligent.
+                */
+       }
+       else if (((Const *) other)->consttype == vardata.vartype)
+       {
+               /* Both sides are the same multirange type */
+               typcache = multirange_get_typcache(fcinfo, vardata.vartype);
+
+               constmultirange = DatumGetMultirangeTypeP(((Const *) other)->constvalue);
+       }
+
+       /*
+        * If we got a valid constant on one side of the operator, proceed to
+        * estimate using statistics. Otherwise punt and return a default constant
+        * estimate.  Note that calc_multirangesel need not handle
+        * OID_MULTIRANGE_*_CONTAINED_OP.
+        */
+       if (constmultirange)
+               selec = calc_multirangesel(typcache, &vardata, constmultirange, operator);
+       else
+               selec = default_multirange_selectivity(operator);
+
+       ReleaseVariableStats(vardata);
+
+       CLAMP_PROBABILITY(selec);
+
+       PG_RETURN_FLOAT8((float8) selec);
+}
+
+static double
+calc_multirangesel(TypeCacheEntry *typcache, VariableStatData *vardata,
+                                  const MultirangeType *constval, Oid operator)
+{
+       double          hist_selec;
+       double          selec;
+       float4          empty_frac,
+                               null_frac;
+
+       /*
+        * First look up the fraction of NULLs and empty multiranges from
+        * pg_statistic.
+        */
+       if (HeapTupleIsValid(vardata->statsTuple))
+       {
+               Form_pg_statistic stats;
+               AttStatsSlot sslot;
+
+               stats = (Form_pg_statistic) GETSTRUCT(vardata->statsTuple);
+               null_frac = stats->stanullfrac;
+
+               /* Try to get fraction of empty multiranges */
+               if (get_attstatsslot(&sslot, vardata->statsTuple,
+                                                        STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
+                                                        InvalidOid,
+                                                        ATTSTATSSLOT_NUMBERS))
+               {
+                       if (sslot.nnumbers != 1)
+                               elog(ERROR, "invalid empty fraction statistic");        /* shouldn't happen */
+                       empty_frac = sslot.numbers[0];
+                       free_attstatsslot(&sslot);
+               }
+               else
+               {
+                       /* No empty fraction statistic. Assume no empty ranges. */
+                       empty_frac = 0.0;
+               }
+       }
+       else
+       {
+               /*
+                * No stats are available. Follow through the calculations below
+                * anyway, assuming no NULLs and no empty multiranges. This still
+                * allows us to give a better-than-nothing estimate based on whether
+                * the constant is an empty multirange or not.
+                */
+               null_frac = 0.0;
+               empty_frac = 0.0;
+       }
+
+       if (MultirangeIsEmpty(constval))
+       {
+               /*
+                * An empty multirange matches all multiranges, all empty multiranges,
+                * or nothing, depending on the operator
+                */
+               switch (operator)
+               {
+                               /* these return false if either argument is empty */
+                       case OID_RANGE_OVERLAPS_MULTIRANGE_OP:
+                       case OID_MULTIRANGE_OVERLAPS_RANGE_OP:
+                       case OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP:
+                       case OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
+                       case OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP:
+                       case OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
+                       case OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
+                       case OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP:
+                       case OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
+                       case OID_MULTIRANGE_LEFT_MULTIRANGE_OP:
+                       case OID_MULTIRANGE_RIGHT_MULTIRANGE_OP:
+                               /* nothing is less than an empty multirange */
+                       case OID_MULTIRANGE_LESS_OP:
+                               selec = 0.0;
+                               break;
+
+                               /*
+                                * only empty multiranges can be contained by an empty
+                                * multirange
+                                */
+                       case OID_MULTIRANGE_RANGE_CONTAINED_OP:
+                       case OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP:
+                               /* only empty ranges are <= an empty multirange */
+                       case OID_MULTIRANGE_LESS_EQUAL_OP:
+                               selec = empty_frac;
+                               break;
+
+                               /* everything contains an empty multirange */
+                       case OID_MULTIRANGE_CONTAINS_RANGE_OP:
+                       case OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP:
+                               /* everything is >= an empty multirange */
+                       case OID_MULTIRANGE_GREATER_EQUAL_OP:
+                               selec = 1.0;
+                               break;
+
+                               /* all non-empty multiranges are > an empty multirange */
+                       case OID_MULTIRANGE_GREATER_OP:
+                               selec = 1.0 - empty_frac;
+                               break;
+
+                               /* an element cannot be empty */
+                       case OID_MULTIRANGE_ELEM_CONTAINED_OP:
+                       case OID_MULTIRANGE_CONTAINS_ELEM_OP:
+                       default:
+                               elog(ERROR, "unexpected operator %u", operator);
+                               selec = 0.0;    /* keep compiler quiet */
+                               break;
+               }
+       }
+       else
+       {
+               /*
+                * Calculate selectivity using bound histograms. If that fails for
+                * some reason, e.g no histogram in pg_statistic, use the default
+                * constant estimate for the fraction of non-empty values. This is
+                * still somewhat better than just returning the default estimate,
+                * because this still takes into account the fraction of empty and
+                * NULL tuples, if we had statistics for them.
+                */
+               hist_selec = calc_hist_selectivity(typcache, vardata, constval,
+                                                                                  operator);
+               if (hist_selec < 0.0)
+                       hist_selec = default_multirange_selectivity(operator);
+
+               /*
+                * Now merge the results for the empty multiranges and histogram
+                * calculations, realizing that the histogram covers only the
+                * non-null, non-empty values.
+                */
+               if (operator == OID_MULTIRANGE_ELEM_CONTAINED_OP ||
+                       operator == OID_MULTIRANGE_RANGE_CONTAINED_OP ||
+                       operator == OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP)
+               {
+                       /* empty is contained by anything non-empty */
+                       selec = (1.0 - empty_frac) * hist_selec + empty_frac;
+               }
+               else
+               {
+                       /* with any other operator, empty Op non-empty matches nothing */
+                       selec = (1.0 - empty_frac) * hist_selec;
+               }
+       }
+
+       /* all multirange operators are strict */
+       selec *= (1.0 - null_frac);
+
+       /* result should be in range, but make sure... */
+       CLAMP_PROBABILITY(selec);
+
+       return selec;
+}
+
+/*
+ * Calculate multirange operator selectivity using histograms of multirange bounds.
+ *
+ * This estimate is for the portion of values that are not empty and not
+ * NULL.
+ */
+static double
+calc_hist_selectivity(TypeCacheEntry *typcache, VariableStatData *vardata,
+                                         const MultirangeType *constval, Oid operator)
+{
+       TypeCacheEntry *rng_typcache = typcache->rngtype;
+       AttStatsSlot hslot;
+       AttStatsSlot lslot;
+       int                     nhist;
+       RangeBound *hist_lower;
+       RangeBound *hist_upper;
+       int                     i;
+       RangeBound      const_lower;
+       RangeBound      const_upper;
+       RangeBound      tmp;
+       double          hist_selec;
+
+       /* Can't use the histogram with insecure multirange support functions */
+       if (!statistic_proc_security_check(vardata,
+                                                                          rng_typcache->rng_cmp_proc_finfo.fn_oid))
+               return -1;
+       if (OidIsValid(rng_typcache->rng_subdiff_finfo.fn_oid) &&
+               !statistic_proc_security_check(vardata,
+                                                                          rng_typcache->rng_subdiff_finfo.fn_oid))
+               return -1;
+
+       /* Try to get histogram of ranges */
+       if (!(HeapTupleIsValid(vardata->statsTuple) &&
+                 get_attstatsslot(&hslot, vardata->statsTuple,
+                                                  STATISTIC_KIND_BOUNDS_HISTOGRAM, InvalidOid,
+                                                  ATTSTATSSLOT_VALUES)))
+               return -1.0;
+
+       /* check that it's a histogram, not just a dummy entry */
+       if (hslot.nvalues < 2)
+       {
+               free_attstatsslot(&hslot);
+               return -1.0;
+       }
+
+       /*
+        * Convert histogram of ranges into histograms of its lower and upper
+        * bounds.
+        */
+       nhist = hslot.nvalues;
+       hist_lower = (RangeBound *) palloc(sizeof(RangeBound) * nhist);
+       hist_upper = (RangeBound *) palloc(sizeof(RangeBound) * nhist);
+       for (i = 0; i < nhist; i++)
+       {
+               bool            empty;
+
+               range_deserialize(rng_typcache, DatumGetRangeTypeP(hslot.values[i]),
+                                                 &hist_lower[i], &hist_upper[i], &empty);
+               /* The histogram should not contain any empty ranges */
+               if (empty)
+                       elog(ERROR, "bounds histogram contains an empty range");
+       }
+
+       /* @> and @< also need a histogram of range lengths */
+       if (operator == OID_MULTIRANGE_CONTAINS_RANGE_OP ||
+               operator == OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP ||
+               operator == OID_MULTIRANGE_RANGE_CONTAINED_OP ||
+               operator == OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP)
+       {
+               if (!(HeapTupleIsValid(vardata->statsTuple) &&
+                         get_attstatsslot(&lslot, vardata->statsTuple,
+                                                          STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM,
+                                                          InvalidOid,
+                                                          ATTSTATSSLOT_VALUES)))
+               {
+                       free_attstatsslot(&hslot);
+                       return -1.0;
+               }
+
+               /* check that it's a histogram, not just a dummy entry */
+               if (lslot.nvalues < 2)
+               {
+                       free_attstatsslot(&lslot);
+                       free_attstatsslot(&hslot);
+                       return -1.0;
+               }
+       }
+       else
+               memset(&lslot, 0, sizeof(lslot));
+
+       /* Extract the bounds of the constant value. */
+       Assert(constval->rangeCount > 0);
+       multirange_get_bounds(rng_typcache, constval, 0,
+                                                 &const_lower, &tmp);
+       multirange_get_bounds(rng_typcache, constval, constval->rangeCount - 1,
+                                                 &tmp, &const_upper);
+
+       /*
+        * Calculate selectivity comparing the lower or upper bound of the
+        * constant with the histogram of lower or upper bounds.
+        */
+       switch (operator)
+       {
+               case OID_MULTIRANGE_LESS_OP:
+
+                       /*
+                        * The regular b-tree comparison operators (<, <=, >, >=) compare
+                        * the lower bounds first, and the upper bounds for values with
+                        * equal lower bounds. Estimate that by comparing the lower bounds
+                        * only. This gives a fairly accurate estimate assuming there
+                        * aren't many rows with a lower bound equal to the constant's
+                        * lower bound.
+                        */
+                       hist_selec =
+                               calc_hist_selectivity_scalar(rng_typcache, &const_lower,
+                                                                                        hist_lower, nhist, false);
+                       break;
+
+               case OID_MULTIRANGE_LESS_EQUAL_OP:
+                       hist_selec =
+                               calc_hist_selectivity_scalar(rng_typcache, &const_lower,
+                                                                                        hist_lower, nhist, true);
+                       break;
+
+               case OID_MULTIRANGE_GREATER_OP:
+                       hist_selec =
+                               1 - calc_hist_selectivity_scalar(rng_typcache, &const_lower,
+                                                                                                hist_lower, nhist, false);
+                       break;
+
+               case OID_MULTIRANGE_GREATER_EQUAL_OP:
+                       hist_selec =
+                               1 - calc_hist_selectivity_scalar(rng_typcache, &const_lower,
+                                                                                                hist_lower, nhist, true);
+                       break;
+
+               case OID_RANGE_LEFT_MULTIRANGE_OP:
+               case OID_MULTIRANGE_LEFT_RANGE_OP:
+               case OID_MULTIRANGE_LEFT_MULTIRANGE_OP:
+                       /* var << const when upper(var) < lower(const) */
+                       hist_selec =
+                               calc_hist_selectivity_scalar(rng_typcache, &const_lower,
+                                                                                        hist_upper, nhist, false);
+                       break;
+
+               case OID_RANGE_RIGHT_MULTIRANGE_OP:
+               case OID_MULTIRANGE_RIGHT_RANGE_OP:
+               case OID_MULTIRANGE_RIGHT_MULTIRANGE_OP:
+                       /* var >> const when lower(var) > upper(const) */
+                       hist_selec =
+                               1 - calc_hist_selectivity_scalar(rng_typcache, &const_upper,
+                                                                                                hist_lower, nhist, true);
+                       break;
+
+               case OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP:
+                       /* compare lower bounds */
+                       hist_selec =
+                               1 - calc_hist_selectivity_scalar(rng_typcache, &const_lower,
+                                                                                                hist_lower, nhist, false);
+                       break;
+
+               case OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP:
+                       /* compare upper bounds */
+                       hist_selec =
+                               calc_hist_selectivity_scalar(rng_typcache, &const_upper,
+                                                                                        hist_upper, nhist, true);
+                       break;
+
+               case OID_RANGE_OVERLAPS_MULTIRANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_RANGE_OP:
+               case OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP:
+               case OID_MULTIRANGE_CONTAINS_ELEM_OP:
+
+                       /*
+                        * A && B <=> NOT (A << B OR A >> B).
+                        *
+                        * Since A << B and A >> B are mutually exclusive events we can
+                        * sum their probabilities to find probability of (A << B OR A >>
+                        * B).
+                        *
+                        * "multirange @> elem" is equivalent to "multirange &&
+                        * {[elem,elem]}". The caller already constructed the singular
+                        * range from the element constant, so just treat it the same as
+                        * &&.
+                        */
+                       hist_selec =
+                               calc_hist_selectivity_scalar(rng_typcache,
+                                                                                        &const_lower, hist_upper,
+                                                                                        nhist, false);
+                       hist_selec +=
+                               (1.0 - calc_hist_selectivity_scalar(rng_typcache,
+                                                                                                       &const_upper, hist_lower,
+                                                                                                       nhist, true));
+                       hist_selec = 1.0 - hist_selec;
+                       break;
+
+               case OID_MULTIRANGE_CONTAINS_RANGE_OP:
+               case OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP:
+                       hist_selec =
+                               calc_hist_selectivity_contains(rng_typcache, &const_lower,
+                                                                                          &const_upper, hist_lower, nhist,
+                                                                                          lslot.values, lslot.nvalues);
+                       break;
+
+               case OID_MULTIRANGE_RANGE_CONTAINED_OP:
+               case OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP:
+                       if (const_lower.infinite)
+                       {
+                               /*
+                                * Lower bound no longer matters. Just estimate the fraction
+                                * with an upper bound <= const upper bound
+                                */
+                               hist_selec =
+                                       calc_hist_selectivity_scalar(rng_typcache, &const_upper,
+                                                                                                hist_upper, nhist, true);
+                       }
+                       else if (const_upper.infinite)
+                       {
+                               hist_selec =
+                                       1.0 - calc_hist_selectivity_scalar(rng_typcache, &const_lower,
+                                                                                                          hist_lower, nhist, false);
+                       }
+                       else
+                       {
+                               hist_selec =
+                                       calc_hist_selectivity_contained(rng_typcache, &const_lower,
+                                                                                                       &const_upper, hist_lower, nhist,
+                                                                                                       lslot.values, lslot.nvalues);
+                       }
+                       break;
+
+               default:
+                       elog(ERROR, "unknown multirange operator %u", operator);
+                       hist_selec = -1.0;      /* keep compiler quiet */
+                       break;
+       }
+
+       free_attstatsslot(&lslot);
+       free_attstatsslot(&hslot);
+
+       return hist_selec;
+}
+
+
+/*
+ * Look up the fraction of values less than (or equal, if 'equal' argument
+ * is true) a given const in a histogram of range bounds.
+ */
+static double
+calc_hist_selectivity_scalar(TypeCacheEntry *typcache, const RangeBound *constbound,
+                                                        const RangeBound *hist, int hist_nvalues, bool equal)
+{
+       Selectivity selec;
+       int                     index;
+
+       /*
+        * Find the histogram bin the given constant falls into. Estimate
+        * selectivity as the number of preceding whole bins.
+        */
+       index = rbound_bsearch(typcache, constbound, hist, hist_nvalues, equal);
+       selec = (Selectivity) (Max(index, 0)) / (Selectivity) (hist_nvalues - 1);
+
+       /* Adjust using linear interpolation within the bin */
+       if (index >= 0 && index < hist_nvalues - 1)
+               selec += get_position(typcache, constbound, &hist[index],
+                                                         &hist[index + 1]) / (Selectivity) (hist_nvalues - 1);
+
+       return selec;
+}
+
+/*
+ * Binary search on an array of range bounds. Returns greatest index of range
+ * bound in array which is less(less or equal) than given range bound. If all
+ * range bounds in array are greater or equal(greater) than given range bound,
+ * return -1. When "equal" flag is set conditions in brackets are used.
+ *
+ * This function is used in scalar operator selectivity estimation. Another
+ * goal of this function is to find a histogram bin where to stop
+ * interpolation of portion of bounds which are less or equal to given bound.
+ */
+static int
+rbound_bsearch(TypeCacheEntry *typcache, const RangeBound *value, const RangeBound *hist,
+                          int hist_length, bool equal)
+{
+       int                     lower = -1,
+                               upper = hist_length - 1,
+                               cmp,
+                               middle;
+
+       while (lower < upper)
+       {
+               middle = (lower + upper + 1) / 2;
+               cmp = range_cmp_bounds(typcache, &hist[middle], value);
+
+               if (cmp < 0 || (equal && cmp == 0))
+                       lower = middle;
+               else
+                       upper = middle - 1;
+       }
+       return lower;
+}
+
+
+/*
+ * Binary search on length histogram. Returns greatest index of range length in
+ * histogram which is less than (less than or equal) the given length value. If
+ * all lengths in the histogram are greater than (greater than or equal) the
+ * given length, returns -1.
+ */
+static int
+length_hist_bsearch(Datum *length_hist_values, int length_hist_nvalues,
+                                       double value, bool equal)
+{
+       int                     lower = -1,
+                               upper = length_hist_nvalues - 1,
+                               middle;
+
+       while (lower < upper)
+       {
+               double          middleval;
+
+               middle = (lower + upper + 1) / 2;
+
+               middleval = DatumGetFloat8(length_hist_values[middle]);
+               if (middleval < value || (equal && middleval <= value))
+                       lower = middle;
+               else
+                       upper = middle - 1;
+       }
+       return lower;
+}
+
+/*
+ * Get relative position of value in histogram bin in [0,1] range.
+ */
+static float8
+get_position(TypeCacheEntry *typcache, const RangeBound *value, const RangeBound *hist1,
+                        const RangeBound *hist2)
+{
+       bool            has_subdiff = OidIsValid(typcache->rng_subdiff_finfo.fn_oid);
+       float8          position;
+
+       if (!hist1->infinite && !hist2->infinite)
+       {
+               float8          bin_width;
+
+               /*
+                * Both bounds are finite. Assuming the subtype's comparison function
+                * works sanely, the value must be finite, too, because it lies
+                * somewhere between the bounds.  If it doesn't, arbitrarily return
+                * 0.5.
+                */
+               if (value->infinite)
+                       return 0.5;
+
+               /* Can't interpolate without subdiff function */
+               if (!has_subdiff)
+                       return 0.5;
+
+               /* Calculate relative position using subdiff function. */
+               bin_width = DatumGetFloat8(FunctionCall2Coll(&typcache->rng_subdiff_finfo,
+                                                                                                        typcache->rng_collation,
+                                                                                                        hist2->val,
+                                                                                                        hist1->val));
+               if (isnan(bin_width) || bin_width <= 0.0)
+                       return 0.5;                     /* punt for NaN or zero-width bin */
+
+               position = DatumGetFloat8(FunctionCall2Coll(&typcache->rng_subdiff_finfo,
+                                                                                                       typcache->rng_collation,
+                                                                                                       value->val,
+                                                                                                       hist1->val))
+                       / bin_width;
+
+               if (isnan(position))
+                       return 0.5;                     /* punt for NaN from subdiff, Inf/Inf, etc */
+
+               /* Relative position must be in [0,1] range */
+               position = Max(position, 0.0);
+               position = Min(position, 1.0);
+               return position;
+       }
+       else if (hist1->infinite && !hist2->infinite)
+       {
+               /*
+                * Lower bin boundary is -infinite, upper is finite. If the value is
+                * -infinite, return 0.0 to indicate it's equal to the lower bound.
+                * Otherwise return 1.0 to indicate it's infinitely far from the lower
+                * bound.
+                */
+               return ((value->infinite && value->lower) ? 0.0 : 1.0);
+       }
+       else if (!hist1->infinite && hist2->infinite)
+       {
+               /* same as above, but in reverse */
+               return ((value->infinite && !value->lower) ? 1.0 : 0.0);
+       }
+       else
+       {
+               /*
+                * If both bin boundaries are infinite, they should be equal to each
+                * other, and the value should also be infinite and equal to both
+                * bounds. (But don't Assert that, to avoid crashing if a user creates
+                * a datatype with a broken comparison function).
+                *
+                * Assume the value to lie in the middle of the infinite bounds.
+                */
+               return 0.5;
+       }
+}
+
+
+/*
+ * Get relative position of value in a length histogram bin in [0,1] range.
+ */
+static double
+get_len_position(double value, double hist1, double hist2)
+{
+       if (!isinf(hist1) && !isinf(hist2))
+       {
+               /*
+                * Both bounds are finite. The value should be finite too, because it
+                * lies somewhere between the bounds. If it doesn't, just return
+                * something.
+                */
+               if (isinf(value))
+                       return 0.5;
+
+               return 1.0 - (hist2 - value) / (hist2 - hist1);
+       }
+       else if (isinf(hist1) && !isinf(hist2))
+       {
+               /*
+                * Lower bin boundary is -infinite, upper is finite. Return 1.0 to
+                * indicate the value is infinitely far from the lower bound.
+                */
+               return 1.0;
+       }
+       else if (isinf(hist1) && isinf(hist2))
+       {
+               /* same as above, but in reverse */
+               return 0.0;
+       }
+       else
+       {
+               /*
+                * If both bin boundaries are infinite, they should be equal to each
+                * other, and the value should also be infinite and equal to both
+                * bounds. (But don't Assert that, to avoid crashing unnecessarily if
+                * the caller messes up)
+                *
+                * Assume the value to lie in the middle of the infinite bounds.
+                */
+               return 0.5;
+       }
+}
+
+/*
+ * Measure distance between two range bounds.
+ */
+static float8
+get_distance(TypeCacheEntry *typcache, const RangeBound *bound1, const RangeBound *bound2)
+{
+       bool            has_subdiff = OidIsValid(typcache->rng_subdiff_finfo.fn_oid);
+
+       if (!bound1->infinite && !bound2->infinite)
+       {
+               /*
+                * Neither bound is infinite, use subdiff function or return default
+                * value of 1.0 if no subdiff is available.
+                */
+               if (has_subdiff)
+               {
+                       float8          res;
+
+                       res = DatumGetFloat8(FunctionCall2Coll(&typcache->rng_subdiff_finfo,
+                                                                                                  typcache->rng_collation,
+                                                                                                  bound2->val,
+                                                                                                  bound1->val));
+                       /* Reject possible NaN result, also negative result */
+                       if (isnan(res) || res < 0.0)
+                               return 1.0;
+                       else
+                               return res;
+               }
+               else
+                       return 1.0;
+       }
+       else if (bound1->infinite && bound2->infinite)
+       {
+               /* Both bounds are infinite */
+               if (bound1->lower == bound2->lower)
+                       return 0.0;
+               else
+                       return get_float8_infinity();
+       }
+       else
+       {
+               /* One bound is infinite, the other is not */
+               return get_float8_infinity();
+       }
+}
+
+/*
+ * Calculate the average of function P(x), in the interval [length1, length2],
+ * where P(x) is the fraction of tuples with length < x (or length <= x if
+ * 'equal' is true).
+ */
+static double
+calc_length_hist_frac(Datum *length_hist_values, int length_hist_nvalues,
+                                         double length1, double length2, bool equal)
+{
+       double          frac;
+       double          A,
+                               B,
+                               PA,
+                               PB;
+       double          pos;
+       int                     i;
+       double          area;
+
+       Assert(length2 >= length1);
+
+       if (length2 < 0.0)
+               return 0.0;                             /* shouldn't happen, but doesn't hurt to check */
+
+       /* All lengths in the table are <= infinite. */
+       if (isinf(length2) && equal)
+               return 1.0;
+
+       /*----------
+        * The average of a function between A and B can be calculated by the
+        * formula:
+        *
+        *                      B
+        *        1             /
+        * -------      | P(x)dx
+        *      B - A   /
+        *                      A
+        *
+        * The geometrical interpretation of the integral is the area under the
+        * graph of P(x). P(x) is defined by the length histogram. We calculate
+        * the area in a piecewise fashion, iterating through the length histogram
+        * bins. Each bin is a trapezoid:
+        *
+        *               P(x2)
+        *                /|
+        *               / |
+        * P(x1)/  |
+        *         |   |
+        *         |   |
+        *      ---+---+--
+        *         x1  x2
+        *
+        * where x1 and x2 are the boundaries of the current histogram, and P(x1)
+        * and P(x1) are the cumulative fraction of tuples at the boundaries.
+        *
+        * The area of each trapezoid is 1/2 * (P(x2) + P(x1)) * (x2 - x1)
+        *
+        * The first bin contains the lower bound passed by the caller, so we
+        * use linear interpolation between the previous and next histogram bin
+        * boundary to calculate P(x1). Likewise for the last bin: we use linear
+        * interpolation to calculate P(x2). For the bins in between, x1 and x2
+        * lie on histogram bin boundaries, so P(x1) and P(x2) are simply:
+        * P(x1) =        (bin index) / (number of bins)
+        * P(x2) = (bin index + 1 / (number of bins)
+        */
+
+       /* First bin, the one that contains lower bound */
+       i = length_hist_bsearch(length_hist_values, length_hist_nvalues, length1, equal);
+       if (i >= length_hist_nvalues - 1)
+               return 1.0;
+
+       if (i < 0)
+       {
+               i = 0;
+               pos = 0.0;
+       }
+       else
+       {
+               /* interpolate length1's position in the bin */
+               pos = get_len_position(length1,
+                                                          DatumGetFloat8(length_hist_values[i]),
+                                                          DatumGetFloat8(length_hist_values[i + 1]));
+       }
+       PB = (((double) i) + pos) / (double) (length_hist_nvalues - 1);
+       B = length1;
+
+       /*
+        * In the degenerate case that length1 == length2, simply return
+        * P(length1). This is not merely an optimization: if length1 == length2,
+        * we'd divide by zero later on.
+        */
+       if (length2 == length1)
+               return PB;
+
+       /*
+        * Loop through all the bins, until we hit the last bin, the one that
+        * contains the upper bound. (if lower and upper bounds are in the same
+        * bin, this falls out immediately)
+        */
+       area = 0.0;
+       for (; i < length_hist_nvalues - 1; i++)
+       {
+               double          bin_upper = DatumGetFloat8(length_hist_values[i + 1]);
+
+               /* check if we've reached the last bin */
+               if (!(bin_upper < length2 || (equal && bin_upper <= length2)))
+                       break;
+
+               /* the upper bound of previous bin is the lower bound of this bin */
+               A = B;
+               PA = PB;
+
+               B = bin_upper;
+               PB = (double) i / (double) (length_hist_nvalues - 1);
+
+               /*
+                * Add the area of this trapezoid to the total. The point of the
+                * if-check is to avoid NaN, in the corner case that PA == PB == 0,
+                * and B - A == Inf. The area of a zero-height trapezoid (PA == PB ==
+                * 0) is zero, regardless of the width (B - A).
+                */
+               if (PA > 0 || PB > 0)
+                       area += 0.5 * (PB + PA) * (B - A);
+       }
+
+       /* Last bin */
+       A = B;
+       PA = PB;
+
+       B = length2;                            /* last bin ends at the query upper bound */
+       if (i >= length_hist_nvalues - 1)
+               pos = 0.0;
+       else
+       {
+               if (DatumGetFloat8(length_hist_values[i]) == DatumGetFloat8(length_hist_values[i + 1]))
+                       pos = 0.0;
+               else
+                       pos = get_len_position(length2,
+                                                                  DatumGetFloat8(length_hist_values[i]),
+                                                                  DatumGetFloat8(length_hist_values[i + 1]));
+       }
+       PB = (((double) i) + pos) / (double) (length_hist_nvalues - 1);
+
+       if (PA > 0 || PB > 0)
+               area += 0.5 * (PB + PA) * (B - A);
+
+       /*
+        * Ok, we have calculated the area, ie. the integral. Divide by width to
+        * get the requested average.
+        *
+        * Avoid NaN arising from infinite / infinite. This happens at least if
+        * length2 is infinite. It's not clear what the correct value would be in
+        * that case, so 0.5 seems as good as any value.
+        */
+       if (isinf(area) && isinf(length2))
+               frac = 0.5;
+       else
+               frac = area / (length2 - length1);
+
+       return frac;
+}
+
+/*
+ * Calculate selectivity of "var <@ const" operator, ie. estimate the fraction
+ * of multiranges that fall within the constant lower and upper bounds. This uses
+ * the histograms of range lower bounds and range lengths, on the assumption
+ * that the range lengths are independent of the lower bounds.
+ *
+ * The caller has already checked that constant lower and upper bounds are
+ * finite.
+ */
+static double
+calc_hist_selectivity_contained(TypeCacheEntry *typcache,
+                                                               const RangeBound *lower, RangeBound *upper,
+                                                               const RangeBound *hist_lower, int hist_nvalues,
+                                                               Datum *length_hist_values, int length_hist_nvalues)
+{
+       int                     i,
+                               upper_index;
+       float8          prev_dist;
+       double          bin_width;
+       double          upper_bin_width;
+       double          sum_frac;
+
+       /*
+        * Begin by finding the bin containing the upper bound, in the lower bound
+        * histogram. Any range with a lower bound > constant upper bound can't
+        * match, ie. there are no matches in bins greater than upper_index.
+        */
+       upper->inclusive = !upper->inclusive;
+       upper->lower = true;
+       upper_index = rbound_bsearch(typcache, upper, hist_lower, hist_nvalues,
+                                                                false);
+
+       /*
+        * If the upper bound value is below the histogram's lower limit, there
+        * are no matches.
+        */
+       if (upper_index < 0)
+               return 0.0;
+
+       /*
+        * If the upper bound value is at or beyond the histogram's upper limit,
+        * start our loop at the last actual bin, as though the upper bound were
+        * within that bin; get_position will clamp its result to 1.0 anyway.
+        * (This corresponds to assuming that the data population above the
+        * histogram's upper limit is empty, exactly like what we just assumed for
+        * the lower limit.)
+        */
+       upper_index = Min(upper_index, hist_nvalues - 2);
+
+       /*
+        * Calculate upper_bin_width, ie. the fraction of the (upper_index,
+        * upper_index + 1) bin which is greater than upper bound of query range
+        * using linear interpolation of subdiff function.
+        */
+       upper_bin_width = get_position(typcache, upper,
+                                                                  &hist_lower[upper_index],
+                                                                  &hist_lower[upper_index + 1]);
+
+       /*
+        * In the loop, dist and prev_dist are the distance of the "current" bin's
+        * lower and upper bounds from the constant upper bound.
+        *
+        * bin_width represents the width of the current bin. Normally it is 1.0,
+        * meaning a full width bin, but can be less in the corner cases: start
+        * and end of the loop. We start with bin_width = upper_bin_width, because
+        * we begin at the bin containing the upper bound.
+        */
+       prev_dist = 0.0;
+       bin_width = upper_bin_width;
+
+       sum_frac = 0.0;
+       for (i = upper_index; i >= 0; i--)
+       {
+               double          dist;
+               double          length_hist_frac;
+               bool            final_bin = false;
+
+               /*
+                * dist -- distance from upper bound of query range to lower bound of
+                * the current bin in the lower bound histogram. Or to the lower bound
+                * of the constant range, if this is the final bin, containing the
+                * constant lower bound.
+                */
+               if (range_cmp_bounds(typcache, &hist_lower[i], lower) < 0)
+               {
+                       dist = get_distance(typcache, lower, upper);
+
+                       /*
+                        * Subtract from bin_width the portion of this bin that we want to
+                        * ignore.
+                        */
+                       bin_width -= get_position(typcache, lower, &hist_lower[i],
+                                                                         &hist_lower[i + 1]);
+                       if (bin_width < 0.0)
+                               bin_width = 0.0;
+                       final_bin = true;
+               }
+               else
+                       dist = get_distance(typcache, &hist_lower[i], upper);
+
+               /*
+                * Estimate the fraction of tuples in this bin that are narrow enough
+                * to not exceed the distance to the upper bound of the query range.
+                */
+               length_hist_frac = calc_length_hist_frac(length_hist_values,
+                                                                                                length_hist_nvalues,
+                                                                                                prev_dist, dist, true);
+
+               /*
+                * Add the fraction of tuples in this bin, with a suitable length, to
+                * the total.
+                */
+               sum_frac += length_hist_frac * bin_width / (double) (hist_nvalues - 1);
+
+               if (final_bin)
+                       break;
+
+               bin_width = 1.0;
+               prev_dist = dist;
+       }
+
+       return sum_frac;
+}
+
+/*
+ * Calculate selectivity of "var @> const" operator, ie. estimate the fraction
+ * of multiranges that contain the constant lower and upper bounds. This uses
+ * the histograms of range lower bounds and range lengths, on the assumption
+ * that the range lengths are independent of the lower bounds.
+ */
+static double
+calc_hist_selectivity_contains(TypeCacheEntry *typcache,
+                                                          const RangeBound *lower, const RangeBound *upper,
+                                                          const RangeBound *hist_lower, int hist_nvalues,
+                                                          Datum *length_hist_values, int length_hist_nvalues)
+{
+       int                     i,
+                               lower_index;
+       double          bin_width,
+                               lower_bin_width;
+       double          sum_frac;
+       float8          prev_dist;
+
+       /* Find the bin containing the lower bound of query range. */
+       lower_index = rbound_bsearch(typcache, lower, hist_lower, hist_nvalues,
+                                                                true);
+
+       /*
+        * If the lower bound value is below the histogram's lower limit, there
+        * are no matches.
+        */
+       if (lower_index < 0)
+               return 0.0;
+
+       /*
+        * If the lower bound value is at or beyond the histogram's upper limit,
+        * start our loop at the last actual bin, as though the upper bound were
+        * within that bin; get_position will clamp its result to 1.0 anyway.
+        * (This corresponds to assuming that the data population above the
+        * histogram's upper limit is empty, exactly like what we just assumed for
+        * the lower limit.)
+        */
+       lower_index = Min(lower_index, hist_nvalues - 2);
+
+       /*
+        * Calculate lower_bin_width, ie. the fraction of the of (lower_index,
+        * lower_index + 1) bin which is greater than lower bound of query range
+        * using linear interpolation of subdiff function.
+        */
+       lower_bin_width = get_position(typcache, lower, &hist_lower[lower_index],
+                                                                  &hist_lower[lower_index + 1]);
+
+       /*
+        * Loop through all the lower bound bins, smaller than the query lower
+        * bound. In the loop, dist and prev_dist are the distance of the
+        * "current" bin's lower and upper bounds from the constant upper bound.
+        * We begin from query lower bound, and walk backwards, so the first bin's
+        * upper bound is the query lower bound, and its distance to the query
+        * upper bound is the length of the query range.
+        *
+        * bin_width represents the width of the current bin. Normally it is 1.0,
+        * meaning a full width bin, except for the first bin, which is only
+        * counted up to the constant lower bound.
+        */
+       prev_dist = get_distance(typcache, lower, upper);
+       sum_frac = 0.0;
+       bin_width = lower_bin_width;
+       for (i = lower_index; i >= 0; i--)
+       {
+               float8          dist;
+               double          length_hist_frac;
+
+               /*
+                * dist -- distance from upper bound of query range to current value
+                * of lower bound histogram or lower bound of query range (if we've
+                * reach it).
+                */
+               dist = get_distance(typcache, &hist_lower[i], upper);
+
+               /*
+                * Get average fraction of length histogram which covers intervals
+                * longer than (or equal to) distance to upper bound of query range.
+                */
+               length_hist_frac =
+                       1.0 - calc_length_hist_frac(length_hist_values,
+                                                                               length_hist_nvalues,
+                                                                               prev_dist, dist, false);
+
+               sum_frac += length_hist_frac * bin_width / (double) (hist_nvalues - 1);
+
+               bin_width = 1.0;
+               prev_dist = dist;
+       }
+
+       return sum_frac;
+}
index 8a5953881eebd7794262acceabda3b31b63493e7..521ebaaab6d4ee94e34404912d05018bb01bd190 100644 (file)
@@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
        PG_RETURN_VOID();
 }
 
+Datum
+binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
+{
+       Oid                     typoid = PG_GETARG_OID(0);
+
+       CHECK_IS_BINARY_UPGRADE;
+       binary_upgrade_next_mrng_pg_type_oid = typoid;
+
+       PG_RETURN_VOID();
+}
+
+Datum
+binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
+{
+       Oid                     typoid = PG_GETARG_OID(0);
+
+       CHECK_IS_BINARY_UPGRADE;
+       binary_upgrade_next_mrng_array_pg_type_oid = typoid;
+
+       PG_RETURN_VOID();
+}
+
 Datum
 binary_upgrade_set_next_heap_pg_class_oid(PG_FUNCTION_ARGS)
 {
index 3d6b2f909353bec4ad227e54a4cb603fdf03fd25..99a93271fe1c0f56c8f2146432c13cb6038b1318 100644 (file)
@@ -26,6 +26,7 @@
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
 
 
 /*
@@ -227,6 +228,43 @@ anycompatiblerange_out(PG_FUNCTION_ARGS)
        return range_out(fcinfo);
 }
 
+/*
+ * anycompatiblemultirange
+ *
+ * We may as well allow output, since multirange_out will in fact work.
+ */
+PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblemultirange);
+
+Datum
+anycompatiblemultirange_out(PG_FUNCTION_ARGS)
+{
+       return multirange_out(fcinfo);
+}
+
+/*
+ * anymultirange_in            - input routine for pseudo-type ANYMULTIRANGE.
+ */
+Datum
+anymultirange_in(PG_FUNCTION_ARGS)
+{
+       ereport(ERROR,
+                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                        errmsg("cannot accept a value of type %s", "anymultirange")));
+
+       PG_RETURN_VOID();                       /* keep compiler quiet */
+}
+
+/*
+ * anymultirange_out           - output routine for pseudo-type ANYMULTIRANGE.
+ *
+ * We may as well allow this, since multirange_out will in fact work.
+ */
+Datum
+anymultirange_out(PG_FUNCTION_ARGS)
+{
+       return multirange_out(fcinfo);
+}
+
 /*
  * void
  *
index 01ad8bc240b43b025555996e6c82e06c2553ded2..8957cc19842279a346128be57ce832b0af8c9edb 100644 (file)
@@ -43,8 +43,6 @@
 #include "utils/timestamp.h"
 
 
-#define RANGE_EMPTY_LITERAL "empty"
-
 /* fn_extra cache entry for one of the range I/O functions */
 typedef struct RangeIOData
 {
@@ -957,7 +955,25 @@ range_minus(PG_FUNCTION_ARGS)
 {
        RangeType  *r1 = PG_GETARG_RANGE_P(0);
        RangeType  *r2 = PG_GETARG_RANGE_P(1);
+       RangeType  *ret;
        TypeCacheEntry *typcache;
+
+       /* Different types should be prevented by ANYRANGE matching rules */
+       if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+               elog(ERROR, "range types do not match");
+
+       typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+       ret = range_minus_internal(typcache, r1, r2);
+       if (ret)
+               PG_RETURN_RANGE_P(ret);
+       else
+               PG_RETURN_NULL();
+}
+
+RangeType *
+range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
+{
        RangeBound      lower1,
                                lower2;
        RangeBound      upper1,
@@ -969,18 +985,12 @@ range_minus(PG_FUNCTION_ARGS)
                                cmp_u1l2,
                                cmp_u1u2;
 
-       /* Different types should be prevented by ANYRANGE matching rules */
-       if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
-               elog(ERROR, "range types do not match");
-
-       typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
        range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
        range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
 
        /* if either is empty, r1 is the correct answer */
        if (empty1 || empty2)
-               PG_RETURN_RANGE_P(r1);
+               return r1;
 
        cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
        cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@@ -993,34 +1003,34 @@ range_minus(PG_FUNCTION_ARGS)
                                 errmsg("result of range difference would not be contiguous")));
 
        if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
-               PG_RETURN_RANGE_P(r1);
+               return r1;
 
        if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
-               PG_RETURN_RANGE_P(make_empty_range(typcache));
+               return make_empty_range(typcache);
 
        if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
        {
                lower2.inclusive = !lower2.inclusive;
                lower2.lower = false;   /* it will become the upper bound */
-               PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
+               return make_range(typcache, &lower1, &lower2, false);
        }
 
        if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
        {
                upper2.inclusive = !upper2.inclusive;
                upper2.lower = true;    /* it will become the lower bound */
-               PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
+               return make_range(typcache, &upper2, &upper1, false);
        }
 
        elog(ERROR, "unexpected case in range_minus");
-       PG_RETURN_NULL();
+       return NULL;
 }
 
 /*
  * Set union.  If strict is true, it is an error that the two input ranges
  * are not adjacent or overlapping.
  */
-static RangeType *
+RangeType *
 range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
                                         bool strict)
 {
@@ -1101,6 +1111,19 @@ range_intersect(PG_FUNCTION_ARGS)
        RangeType  *r1 = PG_GETARG_RANGE_P(0);
        RangeType  *r2 = PG_GETARG_RANGE_P(1);
        TypeCacheEntry *typcache;
+
+       /* Different types should be prevented by ANYRANGE matching rules */
+       if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
+               elog(ERROR, "range types do not match");
+
+       typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+       PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
+}
+
+RangeType *
+range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
        RangeBound      lower1,
                                lower2;
        RangeBound      upper1,
@@ -1110,17 +1133,11 @@ range_intersect(PG_FUNCTION_ARGS)
        RangeBound *result_lower;
        RangeBound *result_upper;
 
-       /* Different types should be prevented by ANYRANGE matching rules */
-       if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
-               elog(ERROR, "range types do not match");
-
-       typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
        range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
        range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
 
-       if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
-               PG_RETURN_RANGE_P(make_empty_range(typcache));
+       if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
+               return make_empty_range(typcache);
 
        if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
                result_lower = &lower1;
@@ -1132,9 +1149,81 @@ range_intersect(PG_FUNCTION_ARGS)
        else
                result_upper = &upper2;
 
-       PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
+       return make_range(typcache, result_lower, result_upper, false);
 }
 
+/* range, range -> range, range functions */
+
+/*
+ * range_split_internal - if r2 intersects the middle of r1, leaving non-empty
+ * ranges on both sides, then return true and set output1 and output2 to the
+ * results of r1 - r2 (in order). Otherwise return false and don't set output1
+ * or output2. Neither input range should be empty.
+ */
+bool
+range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
+                                        RangeType **output1, RangeType **output2)
+{
+       RangeBound      lower1,
+                               lower2;
+       RangeBound      upper1,
+                               upper2;
+       bool            empty1,
+                               empty2;
+
+       range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+       range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+       if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
+               range_cmp_bounds(typcache, &upper1, &upper2) > 0)
+       {
+               /*
+                * Need to invert inclusive/exclusive for the lower2 and upper2
+                * points. They can't be infinite though. We're allowed to overwrite
+                * these RangeBounds since they only exist locally.
+                */
+               lower2.inclusive = !lower2.inclusive;
+               lower2.lower = false;
+               upper2.inclusive = !upper2.inclusive;
+               upper2.lower = true;
+
+               *output1 = make_range(typcache, &lower1, &lower2, false);
+               *output2 = make_range(typcache, &upper2, &upper1, false);
+               return true;
+       }
+
+       return false;
+}
+
+/* range -> range aggregate functions */
+
+Datum
+range_intersect_agg_transfn(PG_FUNCTION_ARGS)
+{
+       MemoryContext aggContext;
+       Oid                     rngtypoid;
+       TypeCacheEntry *typcache;
+       RangeType  *result;
+       RangeType  *current;
+
+       if (!AggCheckCallContext(fcinfo, &aggContext))
+               elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
+
+       rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
+       if (!type_is_range(rngtypoid))
+               ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
+
+       typcache = range_get_typcache(fcinfo, rngtypoid);
+
+       /* strictness ensures these are non-null */
+       result = PG_GETARG_RANGE_P(0);
+       current = PG_GETARG_RANGE_P(1);
+
+       result = range_intersect_internal(typcache, result, current);
+       PG_RETURN_RANGE_P(result);
+}
+
+
 /* Btree support */
 
 /* btree comparator */
@@ -1937,6 +2026,46 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
                                                                                   b1->val, b2->val));
 }
 
+/*
+ * qsort callback for sorting ranges.
+ *
+ * Two empty ranges compare equal; an empty range sorts to the left of any
+ * non-empty range.  Two non-empty ranges are sorted by lower bound first
+ * and by upper bound next.
+ */
+int
+range_compare(const void *key1, const void *key2, void *arg)
+{
+       RangeType  *r1 = *(RangeType **) key1;
+       RangeType  *r2 = *(RangeType **) key2;
+       TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
+       RangeBound      lower1;
+       RangeBound      upper1;
+       RangeBound      lower2;
+       RangeBound      upper2;
+       bool            empty1;
+       bool            empty2;
+       int                     cmp;
+
+       range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
+       range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
+
+       if (empty1 && empty2)
+               cmp = 0;
+       else if (empty1)
+               cmp = -1;
+       else if (empty2)
+               cmp = 1;
+       else
+       {
+               cmp = range_cmp_bounds(typcache, &lower1, &lower2);
+               if (cmp == 0)
+                       cmp = range_cmp_bounds(typcache, &upper1, &upper2);
+       }
+
+       return cmp;
+}
+
 /*
  * Build an empty range value of the type indicated by the typcache entry.
  */
index 57413b07201fb40166f317dffda0f16e6a182fb6..1376cf06940994d75196ea1ce79f053097ce77de 100644 (file)
 #include "utils/fmgrprotos.h"
 #include "utils/lsyscache.h"
 #include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
 
 static int     float8_qsort_cmp(const void *a1, const void *a2);
 static int     range_bound_qsort_cmp(const void *a1, const void *a2, void *arg);
 static void compute_range_stats(VacAttrStats *stats,
-                                                               AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows);
+                                                               AnalyzeAttrFetchFunc fetchfunc, int samplerows,
+                                                               double totalrows);
 
 /*
  * range_typanalyze -- typanalyze function for range columns
@@ -60,6 +62,33 @@ range_typanalyze(PG_FUNCTION_ARGS)
        PG_RETURN_BOOL(true);
 }
 
+/*
+ * multirange_typanalyze -- typanalyze function for multirange columns
+ *
+ * We do the same analysis as for ranges, but on the smallest range that
+ * completely includes the multirange.
+ */
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+       VacAttrStats *stats = (VacAttrStats *) PG_GETARG_POINTER(0);
+       TypeCacheEntry *typcache;
+       Form_pg_attribute attr = stats->attr;
+
+       /* Get information about multirange type; note column might be a domain */
+       typcache = multirange_get_typcache(fcinfo, getBaseType(stats->attrtypid));
+
+       if (attr->attstattarget < 0)
+               attr->attstattarget = default_statistics_target;
+
+       stats->compute_stats = compute_range_stats;
+       stats->extra_data = typcache;
+       /* same as in std_typanalyze */
+       stats->minrows = 300 * attr->attstattarget;
+
+       PG_RETURN_BOOL(true);
+}
+
 /*
  * Comparison function for sorting float8s, used for range lengths.
  */
@@ -98,7 +127,8 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
                                        int samplerows, double totalrows)
 {
        TypeCacheEntry *typcache = (TypeCacheEntry *) stats->extra_data;
-       bool            has_subdiff = OidIsValid(typcache->rng_subdiff_finfo.fn_oid);
+       TypeCacheEntry *mltrng_typcache = NULL;
+       bool            has_subdiff;
        int                     null_cnt = 0;
        int                     non_null_cnt = 0;
        int                     non_empty_cnt = 0;
@@ -112,6 +142,15 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
                           *uppers;
        double          total_width = 0;
 
+       if (typcache->typtype == TYPTYPE_MULTIRANGE)
+       {
+               mltrng_typcache = typcache;
+               typcache = typcache->rngtype;
+       }
+       else
+               Assert(typcache->typtype == TYPTYPE_RANGE);
+       has_subdiff = OidIsValid(typcache->rng_subdiff_finfo.fn_oid);
+
        /* Allocate memory to hold range bounds and lengths of the sample ranges. */
        lowers = (RangeBound *) palloc(sizeof(RangeBound) * samplerows);
        uppers = (RangeBound *) palloc(sizeof(RangeBound) * samplerows);
@@ -123,6 +162,7 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
                Datum           value;
                bool            isnull,
                                        empty;
+               MultirangeType *multirange;
                RangeType  *range;
                RangeBound      lower,
                                        upper;
@@ -145,8 +185,31 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
                total_width += VARSIZE_ANY(DatumGetPointer(value));
 
                /* Get range and deserialize it for further analysis. */
-               range = DatumGetRangeTypeP(value);
-               range_deserialize(typcache, range, &lower, &upper, &empty);
+               if (mltrng_typcache != NULL)
+               {
+                       /* Treat multiranges like a big range without gaps. */
+                       multirange = DatumGetMultirangeTypeP(value);
+                       if (!MultirangeIsEmpty(multirange))
+                       {
+                               RangeBound      tmp;
+
+                               multirange_get_bounds(typcache, multirange, 0,
+                                                                         &lower, &tmp);
+                               multirange_get_bounds(typcache, multirange,
+                                                                         multirange->rangeCount - 1,
+                                                                         &tmp, &upper);
+                               empty = false;
+                       }
+                       else
+                       {
+                               empty = true;
+                       }
+               }
+               else
+               {
+                       range = DatumGetRangeTypeP(value);
+                       range_deserialize(typcache, range, &lower, &upper, &empty);
+               }
 
                if (!empty)
                {
@@ -262,6 +325,13 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
                        stats->stakind[slot_idx] = STATISTIC_KIND_BOUNDS_HISTOGRAM;
                        stats->stavalues[slot_idx] = bound_hist_values;
                        stats->numvalues[slot_idx] = num_hist;
+
+                       /* Store ranges even if we're analyzing a multirange column */
+                       stats->statypid[slot_idx] = typcache->type_id;
+                       stats->statyplen[slot_idx] = typcache->typlen;
+                       stats->statypbyval[slot_idx] = typcache->typbyval;
+                       stats->statypalign[slot_idx] = 'd';
+
                        slot_idx++;
                }
 
index 204bcd03c0b3b0709a999e4a286492226dc59b86..ad92636f7f19d95de71ea9f66d0c88eee08fdcce 100644 (file)
@@ -2610,6 +2610,16 @@ type_is_range(Oid typid)
        return (get_typtype(typid) == TYPTYPE_RANGE);
 }
 
+/*
+ * type_is_multirange
+ *       Returns true if the given type is a multirange type.
+ */
+bool
+type_is_multirange(Oid typid)
+{
+       return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
+}
+
 /*
  * get_type_category_preferred
  *
@@ -3308,7 +3318,7 @@ get_namespace_name_or_temp(Oid nspid)
                return get_namespace_name(nspid);
 }
 
-/*                             ---------- PG_RANGE CACHE ----------                             */
+/*                             ---------- PG_RANGE CACHES ----------                            */
 
 /*
  * get_range_subtype
@@ -3361,6 +3371,56 @@ get_range_collation(Oid rangeOid)
                return InvalidOid;
 }
 
+/*
+ * get_range_multirange
+ *             Returns the multirange type of a given range type
+ *
+ * Returns InvalidOid if the type is not a range type.
+ */
+Oid
+get_range_multirange(Oid rangeOid)
+{
+       HeapTuple       tp;
+
+       tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+               Oid                     result;
+
+               result = rngtup->rngmultitypid;
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return InvalidOid;
+}
+
+/*
+ * get_multirange_range
+ *             Returns the range type of a given multirange
+ *
+ * Returns InvalidOid if the type is not a multirange.
+ */
+Oid
+get_multirange_range(Oid multirangeOid)
+{
+       HeapTuple       tp;
+
+       tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
+       if (HeapTupleIsValid(tp))
+       {
+               Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
+               Oid                     result;
+
+               result = rngtup->rngtypid;
+               ReleaseSysCache(tp);
+               return result;
+       }
+       else
+               return InvalidOid;
+}
+
 /*                             ---------- PG_INDEX CACHE ----------                             */
 
 /*
index 809b27a038fac3a0395403c46adc4ae3ac656d08..89f08a4f43ca43ba84bb998bb2e29f277a77e68f 100644 (file)
@@ -650,6 +650,18 @@ static const struct cachedesc cacheinfo[] = {
                },
                64
        },
+       {RangeRelationId,                       /* RANGEMULTIRANGE */
+               RangeMultirangeTypidIndexId,
+               1,
+               {
+                       Anum_pg_range_rngmultitypid,
+                       0,
+                       0,
+                       0
+               },
+               4
+       },
+
        {RangeRelationId,                       /* RANGETYPE */
                RangeTypidIndexId,
                1,
index 1e331098c0db5369a749b58b97948f5b0b22dd56..8c97ef39557723774a24183bad327e094c9863f7 100644 (file)
@@ -287,6 +287,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
 
 static void load_typcache_tupdesc(TypeCacheEntry *typentry);
 static void load_rangetype_info(TypeCacheEntry *typentry);
+static void load_multirangetype_info(TypeCacheEntry *typentry);
 static void load_domaintype_info(TypeCacheEntry *typentry);
 static int     dcs_cmp(const void *a, const void *b);
 static void decr_dcc_refcount(DomainConstraintCache *dcc);
@@ -305,6 +306,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
 static bool range_element_has_hashing(TypeCacheEntry *typentry);
 static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
 static void cache_range_element_properties(TypeCacheEntry *typentry);
+static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
+static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
+static void cache_multirange_element_properties(TypeCacheEntry *typentry);
 static void TypeCacheRelCallback(Datum arg, Oid relid);
 static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
 static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
@@ -557,8 +561,8 @@ lookup_type_cache(Oid type_id, int flags)
                 * to see if the element type or column types support equality.  If
                 * not, array_eq or record_eq would fail at runtime, so we don't want
                 * to report that the type has equality.  (We can omit similar
-                * checking for ranges because ranges can't be created in the first
-                * place unless their subtypes support equality.)
+                * checking for ranges and multiranges because ranges can't be created
+                * in the first place unless their subtypes support equality.)
                 */
                if (eq_opr == ARRAY_EQ_OP &&
                        !array_element_has_equality(typentry))
@@ -595,7 +599,7 @@ lookup_type_cache(Oid type_id, int flags)
 
                /*
                 * As above, make sure array_cmp or record_cmp will succeed; but again
-                * we need no special check for ranges.
+                * we need no special check for ranges or multiranges.
                 */
                if (lt_opr == ARRAY_LT_OP &&
                        !array_element_has_compare(typentry))
@@ -620,7 +624,7 @@ lookup_type_cache(Oid type_id, int flags)
 
                /*
                 * As above, make sure array_cmp or record_cmp will succeed; but again
-                * we need no special check for ranges.
+                * we need no special check for ranges or multiranges.
                 */
                if (gt_opr == ARRAY_GT_OP &&
                        !array_element_has_compare(typentry))
@@ -645,7 +649,7 @@ lookup_type_cache(Oid type_id, int flags)
 
                /*
                 * As above, make sure array_cmp or record_cmp will succeed; but again
-                * we need no special check for ranges.
+                * we need no special check for ranges or multiranges.
                 */
                if (cmp_proc == F_BTARRAYCMP &&
                        !array_element_has_compare(typentry))
@@ -695,6 +699,13 @@ lookup_type_cache(Oid type_id, int flags)
                        !range_element_has_hashing(typentry))
                        hash_proc = InvalidOid;
 
+               /*
+                * Likewise for hash_multirange.
+                */
+               if (hash_proc == F_HASH_MULTIRANGE &&
+                       !multirange_element_has_hashing(typentry))
+                       hash_proc = InvalidOid;
+
                /* Force update of hash_proc_finfo only if we're changing state */
                if (typentry->hash_proc != hash_proc)
                        typentry->hash_proc_finfo.fn_oid = InvalidOid;
@@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags)
                        !range_element_has_extended_hashing(typentry))
                        hash_extended_proc = InvalidOid;
 
+               /*
+                * Likewise for hash_multirange_extended.
+                */
+               if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
+                       !multirange_element_has_extended_hashing(typentry))
+                       hash_extended_proc = InvalidOid;
+
                /* Force update of proc finfo only if we're changing state */
                if (typentry->hash_extended_proc != hash_extended_proc)
                        typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@@ -816,6 +834,16 @@ lookup_type_cache(Oid type_id, int flags)
                        (void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
        }
 
+       /*
+        * If requested, get information about a multirange type
+        */
+       if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
+               typentry->rngtype == NULL &&
+               typentry->typtype == TYPTYPE_MULTIRANGE)
+       {
+               load_multirangetype_info(typentry);
+       }
+
        /*
         * If requested, get information about a domain type
         */
@@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry)
        typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
 }
 
+/*
+ * load_multirangetype_info --- helper routine to set up multirange type
+ * information
+ */
+static void
+load_multirangetype_info(TypeCacheEntry *typentry)
+{
+       Oid                     rangetypeOid;
+
+       rangetypeOid = get_multirange_range(typentry->type_id);
+       if (!OidIsValid(rangetypeOid))
+               elog(ERROR, "cache lookup failed for multirange type %u",
+                        typentry->type_id);
+
+       typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
+}
 
 /*
  * load_domaintype_info --- helper routine to set up domain constraint info
@@ -1559,11 +1603,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
 }
 
 /*
- * Likewise, some helper functions for range types.
+ * Likewise, some helper functions for range and multirange types.
  *
  * We can borrow the flag bits for array element properties to use for range
  * element properties, since those flag bits otherwise have no use in a
- * range type's typcache entry.
+ * range or multirange type's typcache entry.
  */
 
 static bool
@@ -1606,6 +1650,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
        typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
 }
 
+static bool
+multirange_element_has_hashing(TypeCacheEntry *typentry)
+{
+       if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+               cache_multirange_element_properties(typentry);
+       return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
+
+static bool
+multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
+{
+       if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+               cache_multirange_element_properties(typentry);
+       return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
+}
+
+static void
+cache_multirange_element_properties(TypeCacheEntry *typentry)
+{
+       /* load up range link if we didn't already */
+       if (typentry->rngtype == NULL &&
+               typentry->typtype == TYPTYPE_MULTIRANGE)
+               load_multirangetype_info(typentry);
+
+       if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
+       {
+               TypeCacheEntry *elementry;
+
+               /* might need to calculate subtype's hash function properties */
+               elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
+                                                                         TYPECACHE_HASH_PROC |
+                                                                         TYPECACHE_HASH_EXTENDED_PROC);
+               if (OidIsValid(elementry->hash_proc))
+                       typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
+               if (OidIsValid(elementry->hash_extended_proc))
+                       typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
+       }
+       typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
+
 /*
  * Make sure that RecordCacheArray and RecordIdentifierArray are large enough
  * to store 'typmod'.
index 9696c88f2415c4073ae57ea7344aded4a3de698f..f6fa4ab2fb26ee87a91c8db8338f4353ceb89325 100644 (file)
@@ -35,6 +35,7 @@ typedef struct polymorphic_actuals
        Oid                     anyelement_type;        /* anyelement mapping, if known */
        Oid                     anyarray_type;  /* anyarray mapping, if known */
        Oid                     anyrange_type;  /* anyrange mapping, if known */
+       Oid                     anymultirange_type; /* anymultirange mapping, if known */
 } polymorphic_actuals;
 
 static void shutdown_MultiFuncCall(Datum arg);
@@ -46,6 +47,7 @@ static TypeFuncClass internal_get_result_type(Oid funcid,
 static void resolve_anyelement_from_others(polymorphic_actuals *actuals);
 static void resolve_anyarray_from_others(polymorphic_actuals *actuals);
 static void resolve_anyrange_from_others(polymorphic_actuals *actuals);
+static void resolve_anymultirange_from_others(polymorphic_actuals *actuals);
 static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
                                                                                oidvector *declared_args,
                                                                                Node *call_expr);
@@ -503,6 +505,34 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
                                                        format_type_be(range_base_type))));
                actuals->anyelement_type = range_typelem;
        }
+       else if (OidIsValid(actuals->anymultirange_type))
+       {
+               /* Use the element type based on the multirange type */
+               Oid                     multirange_base_type;
+               Oid                     multirange_typelem;
+               Oid                     range_base_type;
+               Oid                     range_typelem;
+
+               multirange_base_type = getBaseType(actuals->anymultirange_type);
+               multirange_typelem = get_multirange_range(multirange_base_type);
+               if (!OidIsValid(multirange_typelem))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("argument declared %s is not a multirange type but type %s",
+                                                       "anymultirange",
+                                                       format_type_be(multirange_base_type))));
+
+               range_base_type = getBaseType(multirange_typelem);
+               range_typelem = get_range_subtype(range_base_type);
+
+               if (!OidIsValid(range_typelem))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("argument declared %s does not contain a range type but type %s",
+                                                       "anymultirange",
+                                                       format_type_be(range_base_type))));
+               actuals->anyelement_type = range_typelem;
+       }
        else
                elog(ERROR, "could not determine polymorphic type");
 }
@@ -540,10 +570,53 @@ static void
 resolve_anyrange_from_others(polymorphic_actuals *actuals)
 {
        /*
-        * We can't deduce a range type from other polymorphic inputs, because
-        * there may be multiple range types with the same subtype.
+        * We can't deduce a range type from other polymorphic array or base
+        * types, because there may be multiple range types with the same subtype,
+        * but we can deduce it from a polymorphic multirange type.
+        */
+       if (OidIsValid(actuals->anymultirange_type))
+       {
+               /* Use the element type based on the multirange type */
+               Oid                     multirange_base_type = getBaseType(actuals->anymultirange_type);
+               Oid                     multirange_typelem = get_multirange_range(multirange_base_type);
+
+               if (!OidIsValid(multirange_typelem))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_DATATYPE_MISMATCH),
+                                        errmsg("argument declared %s is not a multirange type but type %s",
+                                                       "anymultirange",
+                                                       format_type_be(multirange_base_type))));
+               actuals->anyrange_type = multirange_typelem;
+       }
+       else
+               elog(ERROR, "could not determine polymorphic type");
+}
+
+/*
+ * Resolve actual type of ANYMULTIRANGE from other polymorphic inputs
+ */
+static void
+resolve_anymultirange_from_others(polymorphic_actuals *actuals)
+{
+       /*
+        * We can't deduce a multirange type from polymorphic array or base types,
+        * because there may be multiple range types with the same subtype, but we
+        * can deduce it from a polymorphic range type.
         */
-       elog(ERROR, "could not determine polymorphic type");
+       if (OidIsValid(actuals->anyrange_type))
+       {
+               Oid                     range_base_type = getBaseType(actuals->anyrange_type);
+               Oid                     multirange_typeid = get_range_multirange(range_base_type);
+
+               if (!OidIsValid(multirange_typeid))
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                        errmsg("could not find multirange type for data type %s",
+                                                       format_type_be(actuals->anyrange_type))));
+               actuals->anymultirange_type = multirange_typeid;
+       }
+       else
+               elog(ERROR, "could not determine polymorphic type");
 }
 
 /*
@@ -566,9 +639,11 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
        bool            have_anyelement_result = false;
        bool            have_anyarray_result = false;
        bool            have_anyrange_result = false;
+       bool            have_anymultirange_result = false;
        bool            have_anycompatible_result = false;
        bool            have_anycompatible_array_result = false;
        bool            have_anycompatible_range_result = false;
+       bool            have_anycompatible_multirange_result = false;
        polymorphic_actuals poly_actuals;
        polymorphic_actuals anyc_actuals;
        Oid                     anycollation = InvalidOid;
@@ -594,6 +669,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                                have_polymorphic_result = true;
                                have_anyrange_result = true;
                                break;
+                       case ANYMULTIRANGEOID:
+                               have_polymorphic_result = true;
+                               have_anymultirange_result = true;
+                               break;
                        case ANYCOMPATIBLEOID:
                        case ANYCOMPATIBLENONARRAYOID:
                                have_polymorphic_result = true;
@@ -607,6 +686,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                                have_polymorphic_result = true;
                                have_anycompatible_range_result = true;
                                break;
+                       case ANYCOMPATIBLEMULTIRANGEOID:
+                               have_polymorphic_result = true;
+                               have_anycompatible_multirange_result = true;
+                               break;
                        default:
                                break;
                }
@@ -660,6 +743,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                                                return false;
                                }
                                break;
+                       case ANYMULTIRANGEOID:
+                               if (!OidIsValid(poly_actuals.anymultirange_type))
+                               {
+                                       poly_actuals.anymultirange_type =
+                                               get_call_expr_argtype(call_expr, i);
+                                       if (!OidIsValid(poly_actuals.anymultirange_type))
+                                               return false;
+                               }
+                               break;
                        case ANYCOMPATIBLEOID:
                        case ANYCOMPATIBLENONARRAYOID:
                                if (!OidIsValid(anyc_actuals.anyelement_type))
@@ -688,6 +780,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                                                return false;
                                }
                                break;
+                       case ANYCOMPATIBLEMULTIRANGEOID:
+                               if (!OidIsValid(anyc_actuals.anymultirange_type))
+                               {
+                                       anyc_actuals.anymultirange_type =
+                                               get_call_expr_argtype(call_expr, i);
+                                       if (!OidIsValid(anyc_actuals.anymultirange_type))
+                                               return false;
+                               }
+                               break;
                        default:
                                break;
                }
@@ -703,6 +804,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
        if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
                resolve_anyrange_from_others(&poly_actuals);
 
+       if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+               resolve_anymultirange_from_others(&poly_actuals);
+
        if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
                resolve_anyelement_from_others(&anyc_actuals);
 
@@ -712,6 +816,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
        if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
                resolve_anyrange_from_others(&anyc_actuals);
 
+       if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
+               resolve_anymultirange_from_others(&anyc_actuals);
+
        /*
         * Identify the collation to use for polymorphic OUT parameters. (It'll
         * necessarily be the same for both anyelement and anyarray, likewise for
@@ -780,6 +887,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                                                                   0);
                                /* no collation should be attached to a range type */
                                break;
+                       case ANYMULTIRANGEOID:
+                               TupleDescInitEntry(tupdesc, i + 1,
+                                                                  NameStr(att->attname),
+                                                                  poly_actuals.anymultirange_type,
+                                                                  -1,
+                                                                  0);
+                               /* no collation should be attached to a multirange type */
+                               break;
                        case ANYCOMPATIBLEOID:
                        case ANYCOMPATIBLENONARRAYOID:
                                TupleDescInitEntry(tupdesc, i + 1,
@@ -805,6 +920,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                                                                   0);
                                /* no collation should be attached to a range type */
                                break;
+                       case ANYCOMPATIBLEMULTIRANGEOID:
+                               TupleDescInitEntry(tupdesc, i + 1,
+                                                                  NameStr(att->attname),
+                                                                  anyc_actuals.anymultirange_type,
+                                                                  -1,
+                                                                  0);
+                               /* no collation should be attached to a multirange type */
+                               break;
                        default:
                                break;
                }
@@ -834,9 +957,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
        bool            have_anyelement_result = false;
        bool            have_anyarray_result = false;
        bool            have_anyrange_result = false;
+       bool            have_anymultirange_result = false;
        bool            have_anycompatible_result = false;
        bool            have_anycompatible_array_result = false;
        bool            have_anycompatible_range_result = false;
+       bool            have_anycompatible_multirange_result = false;
        polymorphic_actuals poly_actuals;
        polymorphic_actuals anyc_actuals;
        int                     inargno;
@@ -912,6 +1037,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
                                        argtypes[i] = poly_actuals.anyrange_type;
                                }
                                break;
+                       case ANYMULTIRANGEOID:
+                               if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+                               {
+                                       have_polymorphic_result = true;
+                                       have_anymultirange_result = true;
+                               }
+                               else
+                               {
+                                       if (!OidIsValid(poly_actuals.anymultirange_type))
+                                       {
+                                               poly_actuals.anymultirange_type =
+                                                       get_call_expr_argtype(call_expr, inargno);
+                                               if (!OidIsValid(poly_actuals.anymultirange_type))
+                                                       return false;
+                                       }
+                                       argtypes[i] = poly_actuals.anymultirange_type;
+                               }
+                               break;
                        case ANYCOMPATIBLEOID:
                        case ANYCOMPATIBLENONARRAYOID:
                                if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
@@ -967,6 +1110,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
                                        argtypes[i] = anyc_actuals.anyrange_type;
                                }
                                break;
+                       case ANYCOMPATIBLEMULTIRANGEOID:
+                               if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+                               {
+                                       have_polymorphic_result = true;
+                                       have_anycompatible_multirange_result = true;
+                               }
+                               else
+                               {
+                                       if (!OidIsValid(anyc_actuals.anymultirange_type))
+                                       {
+                                               anyc_actuals.anymultirange_type =
+                                                       get_call_expr_argtype(call_expr, inargno);
+                                               if (!OidIsValid(anyc_actuals.anymultirange_type))
+                                                       return false;
+                                       }
+                                       argtypes[i] = anyc_actuals.anymultirange_type;
+                               }
+                               break;
                        default:
                                break;
                }
@@ -988,6 +1149,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
        if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
                resolve_anyrange_from_others(&poly_actuals);
 
+       if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
+               resolve_anymultirange_from_others(&poly_actuals);
+
        if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
                resolve_anyelement_from_others(&anyc_actuals);
 
@@ -997,6 +1161,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
        if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
                resolve_anyrange_from_others(&anyc_actuals);
 
+       if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
+               resolve_anymultirange_from_others(&anyc_actuals);
+
        /* And finally replace the output column types as needed */
        for (i = 0; i < numargs; i++)
        {
@@ -1013,6 +1180,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
                        case ANYRANGEOID:
                                argtypes[i] = poly_actuals.anyrange_type;
                                break;
+                       case ANYMULTIRANGEOID:
+                               argtypes[i] = poly_actuals.anymultirange_type;
+                               break;
                        case ANYCOMPATIBLEOID:
                        case ANYCOMPATIBLENONARRAYOID:
                                argtypes[i] = anyc_actuals.anyelement_type;
@@ -1023,6 +1193,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
                        case ANYCOMPATIBLERANGEOID:
                                argtypes[i] = anyc_actuals.anyrange_type;
                                break;
+                       case ANYCOMPATIBLEMULTIRANGEOID:
+                               argtypes[i] = anyc_actuals.anymultirange_type;
+                               break;
                        default:
                                break;
                }
@@ -1052,6 +1225,7 @@ get_type_func_class(Oid typid, Oid *base_typeid)
                case TYPTYPE_BASE:
                case TYPTYPE_ENUM:
                case TYPTYPE_RANGE:
+               case TYPTYPE_MULTIRANGE:
                        return TYPEFUNC_SCALAR;
                case TYPTYPE_DOMAIN:
                        *base_typeid = typid = getBaseType(typid);
index 673a6703475ad26ef6fdb97e5c95187e0befc7e1..03023a382c0e3dffd94ca4f2ebdb1e3d5e9041f9 100644 (file)
@@ -272,7 +272,8 @@ static void dumpSearchPath(Archive *AH);
 static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
                                                                                                         PQExpBuffer upgrade_buffer,
                                                                                                         Oid pg_type_oid,
-                                                                                                        bool force_array_type);
+                                                                                                        bool force_array_type,
+                                                                                                        bool include_multirange_type);
 static void binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
                                                                                                        PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
 static void binary_upgrade_set_pg_class_oids(Archive *fout,
@@ -1653,7 +1654,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
        }
 
        /* skip auto-generated array types */
-       if (tyinfo->isArray)
+       if (tyinfo->isArray || tyinfo->isMultirange)
        {
                tyinfo->dobj.objType = DO_DUMMY_TYPE;
 
@@ -4461,16 +4462,49 @@ append_depends_on_extension(Archive *fout,
        }
 }
 
+static Oid
+get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
+{
+       /*
+        * If the old version didn't assign an array type, but the new version
+        * does, we must select an unused type OID to assign.  This currently only
+        * happens for domains, when upgrading pre-v11 to v11 and up.
+        *
+        * Note: local state here is kind of ugly, but we must have some, since we
+        * mustn't choose the same unused OID more than once.
+        */
+       static Oid      next_possible_free_oid = FirstNormalObjectId;
+       PGresult   *res;
+       bool            is_dup;
+
+       do
+       {
+               ++next_possible_free_oid;
+               printfPQExpBuffer(upgrade_query,
+                                                 "SELECT EXISTS(SELECT 1 "
+                                                 "FROM pg_catalog.pg_type "
+                                                 "WHERE oid = '%u'::pg_catalog.oid);",
+                                                 next_possible_free_oid);
+               res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+               is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
+               PQclear(res);
+       } while (is_dup);
+
+       return next_possible_free_oid;
+}
 
 static void
 binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
                                                                                 PQExpBuffer upgrade_buffer,
                                                                                 Oid pg_type_oid,
-                                                                                bool force_array_type)
+                                                                                bool force_array_type,
+                                                                                bool include_multirange_type)
 {
        PQExpBuffer upgrade_query = createPQExpBuffer();
        PGresult   *res;
        Oid                     pg_type_array_oid;
+       Oid                     pg_type_multirange_oid;
+       Oid                     pg_type_multirange_array_oid;
 
        appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
        appendPQExpBuffer(upgrade_buffer,
@@ -4491,33 +4525,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
        PQclear(res);
 
        if (!OidIsValid(pg_type_array_oid) && force_array_type)
-       {
-               /*
-                * If the old version didn't assign an array type, but the new version
-                * does, we must select an unused type OID to assign.  This currently
-                * only happens for domains, when upgrading pre-v11 to v11 and up.
-                *
-                * Note: local state here is kind of ugly, but we must have some,
-                * since we mustn't choose the same unused OID more than once.
-                */
-               static Oid      next_possible_free_oid = FirstNormalObjectId;
-               bool            is_dup;
-
-               do
-               {
-                       ++next_possible_free_oid;
-                       printfPQExpBuffer(upgrade_query,
-                                                         "SELECT EXISTS(SELECT 1 "
-                                                         "FROM pg_catalog.pg_type "
-                                                         "WHERE oid = '%u'::pg_catalog.oid);",
-                                                         next_possible_free_oid);
-                       res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
-                       is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
-                       PQclear(res);
-               } while (is_dup);
-
-               pg_type_array_oid = next_possible_free_oid;
-       }
+               pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
 
        if (OidIsValid(pg_type_array_oid))
        {
@@ -4528,6 +4536,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
                                                  pg_type_array_oid);
        }
 
+       /*
+        * Pre-set the multirange type oid and its own array type oid.
+        */
+       if (include_multirange_type)
+       {
+               if (fout->remoteVersion >= 130000)
+               {
+                       appendPQExpBuffer(upgrade_query,
+                                                         "SELECT t.oid, t.typarray "
+                                                         "FROM pg_catalog.pg_type t "
+                                                         "JOIN pg_catalog.pg_range r "
+                                                         "ON t.oid = r.rngmultitypid "
+                                                         "WHERE r.rngtypid = '%u'::pg_catalog.oid;",
+                                                         pg_type_oid);
+
+                       res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
+
+                       pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
+                       pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
+
+                       PQclear(res);
+               }
+               else
+               {
+                       pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+                       pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
+               }
+
+               appendPQExpBufferStr(upgrade_buffer,
+                                                        "\n-- For binary upgrade, must preserve multirange pg_type oid\n");
+               appendPQExpBuffer(upgrade_buffer,
+                                                 "SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+                                                 pg_type_multirange_oid);
+               appendPQExpBufferStr(upgrade_buffer,
+                                                        "\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
+               appendPQExpBuffer(upgrade_buffer,
+                                                 "SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
+                                                 pg_type_multirange_array_oid);
+       }
+
        destroyPQExpBuffer(upgrade_query);
 }
 
@@ -4552,7 +4600,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
 
        if (OidIsValid(pg_type_oid))
                binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
-                                                                                                pg_type_oid, false);
+                                                                                                pg_type_oid, false, false);
 
        PQclear(upgrade_res);
        destroyPQExpBuffer(upgrade_query);
@@ -5097,6 +5145,11 @@ getTypes(Archive *fout, int *numTypes)
                else
                        tyinfo[i].isArray = false;
 
+               if (tyinfo[i].typtype == 'm')
+                       tyinfo[i].isMultirange = true;
+               else
+                       tyinfo[i].isMultirange = false;
+
                /* Decide whether we want to dump it */
                selectDumpableType(&tyinfo[i], fout);
 
@@ -8326,9 +8379,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
 
 /*
  * getCasts
- *       get basic information about every cast in the system
+ *       get basic information about most casts in the system
  *
  * numCasts is set to the number of casts read in
+ *
+ * Skip casts from a range to its multirange, since we'll create those
+ * automatically.
  */
 CastInfo *
 getCasts(Archive *fout, int *numCasts)
@@ -8346,7 +8402,20 @@ getCasts(Archive *fout, int *numCasts)
        int                     i_castcontext;
        int                     i_castmethod;
 
-       if (fout->remoteVersion >= 80400)
+       if (fout->remoteVersion >= 130000)
+       {
+               appendPQExpBufferStr(query, "SELECT tableoid, oid, "
+                                                        "castsource, casttarget, castfunc, castcontext, "
+                                                        "castmethod "
+                                                        "FROM pg_cast c "
+                                                        "WHERE NOT EXISTS ( "
+                                                        "SELECT 1 FROM pg_range r "
+                                                        "WHERE c.castsource = r.rngtypid "
+                                                        "AND c.casttarget = r.rngmultitypid "
+                                                        ") "
+                                                        "ORDER BY 3,4");
+       }
+       else if (fout->remoteVersion >= 80400)
        {
                appendPQExpBufferStr(query, "SELECT tableoid, oid, "
                                                         "castsource, casttarget, castfunc, castcontext, "
@@ -10496,7 +10565,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
        if (dopt->binary_upgrade)
                binary_upgrade_set_type_oids_by_type_oid(fout, q,
                                                                                                 tyinfo->dobj.catId.oid,
-                                                                                                false);
+                                                                                                false, false);
 
        appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
                                          qualtypname);
@@ -10594,7 +10663,17 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
        char       *procname;
 
        appendPQExpBuffer(query,
-                                         "SELECT pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
+                                         "SELECT ");
+
+       if (fout->remoteVersion >= 140000)
+               appendPQExpBuffer(query,
+                                                 "pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
+       else
+               appendPQExpBuffer(query,
+                                                 "NULL AS rngmultitype, ");
+
+       appendPQExpBuffer(query,
+                                         "pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
                                          "opc.opcname AS opcname, "
                                          "(SELECT nspname FROM pg_catalog.pg_namespace nsp "
                                          "  WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
@@ -10622,7 +10701,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
        if (dopt->binary_upgrade)
                binary_upgrade_set_type_oids_by_type_oid(fout, q,
                                                                                                 tyinfo->dobj.catId.oid,
-                                                                                                false);
+                                                                                                false, true);
 
        appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
                                          qualtypname);
@@ -10630,6 +10709,10 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
        appendPQExpBuffer(q, "\n    subtype = %s",
                                          PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
 
+       if (PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")))
+               appendPQExpBuffer(q, ",\n    multirange_type_name = %s",
+                                                 PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
+
        /* print subtype_opclass only if not default for subtype */
        if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
        {
@@ -10728,7 +10811,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
        if (dopt->binary_upgrade)
                binary_upgrade_set_type_oids_by_type_oid(fout, q,
                                                                                                 tyinfo->dobj.catId.oid,
-                                                                                                false);
+                                                                                                false, false);
 
        appendPQExpBuffer(q, "CREATE TYPE %s;\n",
                                          qualtypname);
@@ -10913,7 +10996,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
        if (dopt->binary_upgrade)
                binary_upgrade_set_type_oids_by_type_oid(fout, q,
                                                                                                 tyinfo->dobj.catId.oid,
-                                                                                                false);
+                                                                                                false, false);
 
        appendPQExpBuffer(q,
                                          "CREATE TYPE %s (\n"
@@ -11103,7 +11186,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
        if (dopt->binary_upgrade)
                binary_upgrade_set_type_oids_by_type_oid(fout, q,
                                                                                                 tyinfo->dobj.catId.oid,
-                                                                                                true); /* force array type */
+                                                                                                true,          /* force array type */
+                                                                                                false);        /* force multirange type */
 
        qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
        qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@@ -11291,7 +11375,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
        {
                binary_upgrade_set_type_oids_by_type_oid(fout, q,
                                                                                                 tyinfo->dobj.catId.oid,
-                                                                                                false);
+                                                                                                false, false);
                binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
        }
 
@@ -11565,7 +11649,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
        if (dopt->binary_upgrade)
                binary_upgrade_set_type_oids_by_type_oid(fout, q,
                                                                                                 stinfo->baseType->dobj.catId.oid,
-                                                                                                false);
+                                                                                                false, false);
 
        appendPQExpBuffer(q, "CREATE TYPE %s;\n",
                                          fmtQualifiedDumpable(stinfo));
index 317bb839702e76d4dd4822d58c6f4a024f9a5e3f..d7f77f1d3e005775fcd8a52ff0ff69e6f37fa8d2 100644 (file)
@@ -176,6 +176,7 @@ typedef struct _typeInfo
        char            typrelkind;             /* 'r', 'v', 'c', etc */
        char            typtype;                /* 'b', 'c', etc */
        bool            isArray;                /* true if auto-generated array type */
+       bool            isMultirange;   /* true if auto-generated multirange type */
        bool            isDefined;              /* true if typisdefined */
        /* If needed, we'll create a "shell type" entry for it; link that here: */
        struct _shellTypeInfo *shellType;       /* shell-type entry, or NULL */
index ec636620601e218785a8f194883ed7c119d6489c..11dc98ee0a5ee63f93fa6fdc2f9419c598cb6e4b 100644 (file)
@@ -1601,6 +1601,7 @@ my %tests = (
                        \QOPERATOR 3 dump_test.~~(integer,integer);\E\n.+
                        \QCREATE TYPE dump_test.range_type_custom AS RANGE (\E\n\s+
                        \Qsubtype = integer,\E\n\s+
+                       \Qmultirange_type_name = dump_test.multirange_type_custom,\E\n\s+
                        \Qsubtype_opclass = dump_test.op_class_custom\E\n
                        \Q);\E
                        /xms,
@@ -1698,6 +1699,7 @@ my %tests = (
                regexp => qr/^
                        \QCREATE TYPE dump_test.textrange AS RANGE (\E
                        \n\s+\Qsubtype = text,\E
+                       \n\s+\Qmultirange_type_name = dump_test.textmultirange,\E
                        \n\s+\Qcollation = pg_catalog."C"\E
                        \n\);/xm,
                like =>
index 70157cf90ab113e85eab00cfefb55593fe9831c7..c262265b95138d952e613312877a2ad28c5043b7 100644 (file)
  *     * we need to estimate alignment padding cost abstractly, ie without
  *       reference to a real tuple.  We must assume the worst case that
  *       all varlenas are aligned.
- *     * within arrays, we unconditionally align varlenas (XXX this should be
- *       revisited, probably).
+ *     * within arrays and multiranges, we unconditionally align varlenas (XXX this
+ *       should be revisited, probably).
  *
  * The attalign cases are tested in what is hopefully something like their
  * frequency of occurrence.
index 02fecb90f79af448698e1c604f17c8cf41a61ba0..1ff62e9f8e63278aa93073a4e9d726a8b1e849b0 100644 (file)
@@ -16,6 +16,8 @@
 
 extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
 extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
+extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
 
 extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
 extern PGDLLIMPORT Oid binary_upgrade_next_index_pg_class_oid;
index 7ca030d46001dc02b62d4c6483e2cb3e3dbcecb1..40fd5d434758c4df118c629d2c41858249e32847 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     202012091
+#define CATALOG_VERSION_NO     202012201
 
 #endif
index ffabe275c002866b98e223ca447ae534fc9d484a..03bab74253d1efad85c6458421ece3a3babd18b5 100644 (file)
   aggtransfn => 'bytea_string_agg_transfn',
   aggfinalfn => 'bytea_string_agg_finalfn', aggtranstype => 'internal' },
 
+# range
+{ aggfnoid => 'range_intersect_agg(anyrange)',
+  aggtransfn => 'range_intersect_agg_transfn',
+  aggcombinefn => 'range_intersect_agg_transfn', aggtranstype => 'anyrange' },
+{ aggfnoid => 'range_intersect_agg(anymultirange)',
+  aggtransfn => 'multirange_intersect_agg_transfn',
+  aggcombinefn => 'multirange_intersect_agg_transfn', aggtranstype => 'anymultirange' },
+{ aggfnoid => 'range_agg(anyrange)', aggtransfn => 'range_agg_transfn',
+  aggfinalfn => 'range_agg_finalfn', aggfinalextra => 't',
+  aggtranstype => 'internal' },
+
 # json
 { aggfnoid => 'json_agg', aggtransfn => 'json_agg_transfn',
   aggfinalfn => 'json_agg_finalfn', aggtranstype => 'internal' },
index 2c899f19d92da6edf60d75e9abcabbb269142020..78d7d2c52329c50a012ff787a3b00cd3984515c9 100644 (file)
   amoprighttype => 'anyrange', amopstrategy => '18',
   amopopr => '=(anyrange,anyrange)', amopmethod => 'gist' },
 
+# btree multirange_ops
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '1',
+  amopopr => '<(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '2',
+  amopopr => '<=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '3',
+  amopopr => '=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '4',
+  amopopr => '>=(anymultirange,anymultirange)', amopmethod => 'btree' },
+{ amopfamily => 'btree/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '5',
+  amopopr => '>(anymultirange,anymultirange)', amopmethod => 'btree' },
+
+# hash multirange_ops
+{ amopfamily => 'hash/multirange_ops', amoplefttype => 'anymultirange',
+  amoprighttype => 'anymultirange', amopstrategy => '1',
+  amopopr => '=(anymultirange,anymultirange)', amopmethod => 'hash' },
+
 # SP-GiST quad_point_ops
 { amopfamily => 'spgist/quad_point_ops', amoplefttype => 'point',
   amoprighttype => 'point', amopstrategy => '11', amopopr => '|>>(point,point)',
index db3e8c2d01a5d7e6069b933cf2ae2e578e23abdf..9d423d535cd59985cc7b4154c1e43325178f020a 100644 (file)
   amprocrighttype => 'tsquery', amprocnum => '1', amproc => 'tsquery_cmp' },
 { amprocfamily => 'btree/range_ops', amproclefttype => 'anyrange',
   amprocrighttype => 'anyrange', amprocnum => '1', amproc => 'range_cmp' },
+{ amprocfamily => 'btree/multirange_ops', amproclefttype => 'anymultirange',
+  amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'multirange_cmp' },
 { amprocfamily => 'btree/jsonb_ops', amproclefttype => 'jsonb',
   amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_cmp' },
 { amprocfamily => 'btree/xid8_ops', amproclefttype => 'xid8',
 { amprocfamily => 'hash/range_ops', amproclefttype => 'anyrange',
   amprocrighttype => 'anyrange', amprocnum => '2',
   amproc => 'hash_range_extended' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+  amprocrighttype => 'anymultirange', amprocnum => '1', amproc => 'hash_multirange' },
+{ amprocfamily => 'hash/multirange_ops', amproclefttype => 'anymultirange',
+  amprocrighttype => 'anymultirange', amprocnum => '2',
+  amproc => 'hash_multirange_extended' },
 { amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
   amprocrighttype => 'jsonb', amprocnum => '1', amproc => 'jsonb_hash' },
 { amprocfamily => 'hash/jsonb_ops', amproclefttype => 'jsonb',
   amprocrighttype => 'anyrange', amprocnum => '4',
   amproc => 'brin_inclusion_union' },
 { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
-  amprocrighttype => 'anyrange', amprocnum => '11', amproc => 'range_merge' },
+  amprocrighttype => 'anyrange', amprocnum => '11',
+  amproc => 'range_merge(anyrange,anyrange)' },
 { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
   amprocrighttype => 'anyrange', amprocnum => '13',
   amproc => 'range_contains' },
 { amprocfamily => 'brin/range_inclusion_ops', amproclefttype => 'anyrange',
-  amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty' },
+  amprocrighttype => 'anyrange', amprocnum => '14', amproc => 'isempty(anyrange)' },
 
 # minmax pg_lsn
 { amprocfamily => 'brin/pg_lsn_minmax_ops', amproclefttype => 'pg_lsn',
index 5a58f50fbb2367d85051ad4a469c6dfeb2910b24..d6ca624addc9991f02bce9969cb1585a916a0004 100644 (file)
 { castsource => 'jsonb', casttarget => 'float8', castfunc => 'float8(jsonb)',
   castcontext => 'e', castmethod => 'f' },
 
+# range to multirange
+{ castsource => 'int4range', casttarget => 'int4multirange', castfunc => 'int4multirange(int4range)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'int8range', casttarget => 'int8multirange', castfunc => 'int8multirange(int8range)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'numrange', casttarget => 'nummultirange', castfunc => 'nummultirange(numrange)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'daterange', casttarget => 'datemultirange', castfunc => 'datemultirange(daterange)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tsrange', casttarget => 'tsmultirange', castfunc => 'tsmultirange(tsrange)',
+  castcontext => 'e', castmethod => 'f' },
+{ castsource => 'tstzrange', casttarget => 'tstzmultirange', castfunc => 'tstzmultirange(tstzrange)',
+  castcontext => 'e', castmethod => 'f' },
 ]
index be5712692fef4bf4a4644126ee44239a95e06617..4c5e475ff7b5982aeedfca595d27eb6258230312 100644 (file)
   opcintype => 'anyrange' },
 { opcmethod => 'spgist', opcname => 'range_ops',
   opcfamily => 'spgist/range_ops', opcintype => 'anyrange' },
+{ opcmethod => 'btree', opcname => 'multirange_ops', opcfamily => 'btree/multirange_ops',
+  opcintype => 'anymultirange' },
+{ opcmethod => 'hash', opcname => 'multirange_ops', opcfamily => 'hash/multirange_ops',
+  opcintype => 'anymultirange' },
 { opcmethod => 'spgist', opcname => 'box_ops', opcfamily => 'spgist/box_ops',
   opcintype => 'box' },
 { opcmethod => 'spgist', opcname => 'quad_point_ops',
index 7ae19235ee2140d05f1c22fb1440dbc6b9cec799..bd0c3d0f81a8a3a689536e1b56319a2219c29032 100644 (file)
   oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
   oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
   oprrest => 'matchingsel', oprjoin => 'matchingjoinsel' },
+{ oid => '2860', descr => 'equal',
+  oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
+  oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
+  oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
+  oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
+{ oid => '2861', descr => 'not equal',
+  oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+  oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+  oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '2862', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
+  oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
+  oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
+  oprrest => 'multirangesel', oprjoin => 'scalarltjoinsel' },
+{ oid => '2863', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
+  descr => 'less than or equal',
+  oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
+  oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
+  oprrest => 'multirangesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '2864', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
+  descr => 'greater than or equal',
+  oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
+  oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
+  oprrest => 'multirangesel', oprjoin => 'scalargejoinsel' },
+{ oid => '2865', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
+  descr => 'greater than',
+  oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
+  oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
+  oprrest => 'multirangesel', oprjoin => 'scalargtjoinsel' },
+{ oid => '2866', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+  descr => 'overlaps',
+  oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+  oprcode => 'range_overlaps_multirange', oprrest => 'multirangesel',
+  oprjoin => 'areajoinsel' },
+{ oid => '2867', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+  oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+  oprcode => 'multirange_overlaps_range', oprrest => 'multirangesel',
+  oprjoin => 'areajoinsel' },
+{ oid => '2868', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+  oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+  oprcode => 'multirange_overlaps_multirange', oprrest => 'multirangesel',
+  oprjoin => 'areajoinsel' },
+{ oid => '2869', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+  descr => 'contains',
+  oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+  oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+  oprcode => 'multirange_contains_elem', oprrest => 'multirangesel',
+  oprjoin => 'contjoinsel' },
+{ oid => '2870', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+  oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+  oprcode => 'multirange_contains_range', oprrest => 'multirangesel',
+  oprjoin => 'contjoinsel' },
+{ oid => '2871', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+  oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+  oprcode => 'multirange_contains_multirange', oprrest => 'multirangesel',
+  oprjoin => 'contjoinsel' },
+{ oid => '2872', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
+  descr => 'is contained by',
+  oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
+  oprcode => 'elem_contained_by_multirange', oprrest => 'multirangesel',
+  oprjoin => 'contjoinsel' },
+{ oid => '2873', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
+  descr => 'is contained by',
+  oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
+  oprcode => 'range_contained_by_multirange', oprrest => 'multirangesel',
+  oprjoin => 'contjoinsel' },
+{ oid => '2874', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
+  descr => 'is contained by',
+  oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
+  oprcode => 'multirange_contained_by_multirange', 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',
+  oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'multirangesel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '2876', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
+  descr => 'overlaps or is left of',
+  oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'multirangesel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '2877', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
+  descr => 'overlaps or is left of',
+  oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'multirangesel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '3585', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+  descr => 'overlaps or is right of',
+  oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'multirangesel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '4035', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
+  descr => 'overlaps or is right of',
+  oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'multirangesel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '4142', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
+  descr => 'overlaps or is right of',
+  oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'multirangesel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '4179', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+  oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
+  oprcode => 'range_adjacent_multirange', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },
+{ oid => '4180', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
+  oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
+  oprcode => 'multirange_adjacent_range', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },
+{ oid => '4198', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
+  oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
+  oprcode => 'multirange_adjacent_multirange', oprrest => 'matchingsel',
+  oprjoin => 'matchingjoinsel' },
+{ oid => '4392', descr => 'multirange union',
+  oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+  oprcode => 'multirange_union' },
+{ oid => '4393', descr => 'multirange minus',
+  oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '4394', descr => 'multirange intersect',
+  oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+  oprcode => 'multirange_intersect' },
+{ oid => '4395', oid_symbol => 'OID_RANGE_LEFT_MULTIRANGE_OP', descr => 'is left of',
+  oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+  oprcode => 'range_before_multirange', oprrest => 'multirangesel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '4396', oid_symbol => 'OID_MULTIRANGE_LEFT_RANGE_OP', descr => 'is left of',
+  oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+  oprcode => 'multirange_before_range', oprrest => 'multirangesel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '4397', oid_symbol => 'OID_MULTIRANGE_LEFT_MULTIRANGE_OP', descr => 'is left of',
+  oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+  oprcode => 'multirange_before_multirange', oprrest => 'multirangesel',
+  oprjoin => 'scalarltjoinsel' },
+{ oid => '4398', oid_symbol => 'OID_RANGE_RIGHT_MULTIRANGE_OP', descr => 'is right of',
+  oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+  oprcode => 'range_after_multirange', oprrest => 'multirangesel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '4399', oid_symbol => 'OID_MULTIRANGE_RIGHT_RANGE_OP', descr => 'is right of',
+  oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+  oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+  oprcode => 'multirange_after_range', oprrest => 'multirangesel',
+  oprjoin => 'scalargtjoinsel' },
+{ oid => '4400', oid_symbol => 'OID_MULTIRANGE_RIGHT_MULTIRANGE_OP', descr => 'is right of',
+  oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+  oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+  oprcode => 'multirange_after_multirange', oprrest => 'multirangesel',
+  oprjoin => 'scalargtjoinsel' },
 
 ]
index 11c7ad2c14541a7123c848e81a3964bc5423b8d3..fe42dfc0f8ffbeee759e298087d6d6e4e81c7738 100644 (file)
   opfmethod => 'spgist', opfname => 'box_ops' },
 { oid => '5008',
   opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '4199',
+  opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '4225',
+  opfmethod => 'hash', opfname => 'multirange_ops' },
 
 ]
index e6c7b070f6478df088000fe8cc89e2cf618a5a8a..22970f46cd7befb2cfb745e75528afc3aa7a439d 100644 (file)
   proname => 'anycompatiblerange_out', provolatile => 's',
   prorettype => 'cstring', proargtypes => 'anycompatiblerange',
   prosrc => 'anycompatiblerange_out' },
+{ oid => '4226', descr => 'I/O',
+  proname => 'anycompatiblemultirange_in', provolatile => 's',
+  prorettype => 'anycompatiblemultirange', proargtypes => 'cstring oid int4',
+  prosrc => 'anycompatiblemultirange_in' },
+{ oid => '4227', descr => 'I/O',
+  proname => 'anycompatiblemultirange_out', provolatile => 's',
+  prorettype => 'cstring', proargtypes => 'anycompatiblemultirange',
+  prosrc => 'anycompatiblemultirange_out' },
 
 # tablesample method handlers
 { oid => '3313', descr => 'BERNOULLI tablesample method handler',
   descr => 'the smallest range which includes both of the given ranges',
   proname => 'range_merge', prorettype => 'anyrange',
   proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '4228',
+  descr => 'the smallest range which includes the whole multirange',
+  proname => 'range_merge', prorettype => 'anyrange',
+  proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
 { oid => '3868',
   proname => 'range_intersect', prorettype => 'anyrange',
   proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
 { oid => '3169', descr => 'restriction selectivity for range operators',
   proname => 'rangesel', provolatile => 's', prorettype => 'float8',
   proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '4401', descr => 'range aggregate by intersecting',
+  proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+  proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '4450', descr => 'range aggregate by intersecting',
+  proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+  prorettype => 'anyrange', proargtypes => 'anyrange',
+  prosrc => 'aggregate_dummy'},
 
 { oid => '3914', descr => 'convert an int4 range to canonical form',
   proname => 'int4range_canonical', prorettype => 'int4range',
   proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
   proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
 
+# functions for multiranges
+{ oid => '4229', descr => 'I/O',
+  proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+  proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '4230', descr => 'I/O',
+  proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+  proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '4231', descr => 'I/O',
+  proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+  proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '4232', descr => 'I/O',
+  proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+  proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '4233', descr => 'I/O',
+  proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+  proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '4234', descr => 'I/O',
+  proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+  proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '4235', descr => 'lower bound of multirange',
+  proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+  prosrc => 'multirange_lower' },
+{ oid => '4236', descr => 'upper bound of multirange',
+  proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+  prosrc => 'multirange_upper' },
+{ oid => '4237', descr => 'is the multirange empty?',
+  proname => 'isempty', prorettype => 'bool',
+  proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '4238', descr => 'is the multirange\'s lower bound inclusive?',
+  proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+  prosrc => 'multirange_lower_inc' },
+{ oid => '4239', descr => 'is the multirange\'s upper bound inclusive?',
+  proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+  prosrc => 'multirange_upper_inc' },
+{ oid => '4240', descr => 'is the multirange\'s lower bound infinite?',
+  proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+  prosrc => 'multirange_lower_inf' },
+{ oid => '4241', descr => 'is the multirange\'s upper bound infinite?',
+  proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+  prosrc => 'multirange_upper_inf' },
+{ oid => '4242', descr => 'multirange typanalyze',
+  proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+  proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '4243', descr => 'restriction selectivity for multirange operators',
+  proname => 'multirangesel', provolatile => 's', prorettype => 'float8',
+  proargtypes => 'internal oid internal int4', prosrc => 'multirangesel' },
+{ oid => '4244',
+  proname => 'multirange_eq', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '4245',
+  proname => 'multirange_ne', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '4246',
+  proname => 'range_overlaps_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '4247',
+  proname => 'multirange_overlaps_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '4248',
+  proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '4249',
+  proname => 'multirange_contains_elem', prorettype => 'bool',
+  proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '4250',
+  proname => 'multirange_contains_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '4251',
+  proname => 'multirange_contains_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '4252',
+  proname => 'elem_contained_by_multirange', prorettype => 'bool',
+  proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '4253',
+  proname => 'range_contained_by_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '4254',
+  proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '4255',
+  proname => 'range_adjacent_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '4256',
+  proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '4257',
+  proname => 'multirange_adjacent_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '4258',
+  proname => 'range_before_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '4259',
+  proname => 'multirange_before_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '4260',
+  proname => 'multirange_before_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '4261',
+  proname => 'range_after_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '4262',
+  proname => 'multirange_after_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '4263',
+  proname => 'multirange_after_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '4264',
+  proname => 'range_overleft_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '4265',
+  proname => 'multirange_overleft_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '4266',
+  proname => 'multirange_overleft_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '4267',
+  proname => 'range_overright_multirange', prorettype => 'bool',
+  proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '4268',
+  proname => 'multirange_overright_range', prorettype => 'bool',
+  proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '4269',
+  proname => 'multirange_overright_multirange', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '4270',
+  proname => 'multirange_union', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '4271',
+  proname => 'multirange_minus', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '4272',
+  proname => 'multirange_intersect', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '4273', descr => 'less-equal-greater',
+  proname => 'multirange_cmp', prorettype => 'int4',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '4274',
+  proname => 'multirange_lt', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '4275',
+  proname => 'multirange_le', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '4276',
+  proname => 'multirange_ge', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '4277',
+  proname => 'multirange_gt', prorettype => 'bool',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '4278', descr => 'hash a multirange',
+  proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+  prosrc => 'hash_multirange' },
+{ oid => '4279', descr => 'hash a multirange',
+  proname => 'hash_multirange_extended', prorettype => 'int8',
+  proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '4280', descr => 'int4multirange constructor',
+  proname => 'int4multirange', proisstrict => 'f',
+  prorettype => 'int4multirange', proargtypes => '',
+  prosrc => 'multirange_constructor0' },
+{ oid => '4281', descr => 'int4multirange constructor',
+  proname => 'int4multirange', proisstrict => 't',
+  prorettype => 'int4multirange', proargtypes => 'int4range',
+  prosrc => 'multirange_constructor1' },
+{ oid => '4282', descr => 'int4multirange constructor',
+  proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+  prorettype => 'int4multirange', proargtypes => '_int4range',
+  proallargtypes => '{_int4range}', proargmodes => '{v}',
+  prosrc => 'multirange_constructor2' },
+{ oid => '4283', descr => 'nummultirange constructor',
+  proname => 'nummultirange', proisstrict => 'f',
+  prorettype => 'nummultirange', proargtypes => '',
+  prosrc => 'multirange_constructor0' },
+{ oid => '4284', descr => 'nummultirange constructor',
+  proname => 'nummultirange', proisstrict => 't',
+  prorettype => 'nummultirange', proargtypes => 'numrange',
+  prosrc => 'multirange_constructor1' },
+{ oid => '4285', descr => 'nummultirange constructor',
+  proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+  prorettype => 'nummultirange', proargtypes => '_numrange',
+  proallargtypes => '{_numrange}', proargmodes => '{v}',
+  prosrc => 'multirange_constructor2' },
+{ oid => '4286', descr => 'tsmultirange constructor',
+  proname => 'tsmultirange', proisstrict => 'f',
+  prorettype => 'tsmultirange', proargtypes => '',
+  prosrc => 'multirange_constructor0' },
+{ oid => '4287', descr => 'tsmultirange constructor',
+  proname => 'tsmultirange', proisstrict => 't',
+  prorettype => 'tsmultirange', proargtypes => 'tsrange',
+  prosrc => 'multirange_constructor1' },
+{ oid => '4288', descr => 'tsmultirange constructor',
+  proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+  prorettype => 'tsmultirange', proargtypes => '_tsrange',
+  proallargtypes => '{_tsrange}', proargmodes => '{v}',
+  prosrc => 'multirange_constructor2' },
+{ oid => '4289', descr => 'tstzmultirange constructor',
+  proname => 'tstzmultirange', proisstrict => 'f',
+  prorettype => 'tstzmultirange', proargtypes => '',
+  prosrc => 'multirange_constructor0' },
+{ oid => '4290', descr => 'tstzmultirange constructor',
+  proname => 'tstzmultirange', proisstrict => 't',
+  prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+  prosrc => 'multirange_constructor1' },
+{ oid => '4291', descr => 'tstzmultirange constructor',
+  proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+  prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+  proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+  prosrc => 'multirange_constructor2' },
+{ oid => '4292', descr => 'datemultirange constructor',
+  proname => 'datemultirange', proisstrict => 'f',
+  prorettype => 'datemultirange', proargtypes => '',
+  prosrc => 'multirange_constructor0' },
+{ oid => '4293', descr => 'datemultirange constructor',
+  proname => 'datemultirange', proisstrict => 't',
+  prorettype => 'datemultirange', proargtypes => 'daterange',
+  prosrc => 'multirange_constructor1' },
+{ oid => '4294', descr => 'datemultirange constructor',
+  proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+  prorettype => 'datemultirange', proargtypes => '_daterange',
+  proallargtypes => '{_daterange}', proargmodes => '{v}',
+  prosrc => 'multirange_constructor2' },
+{ oid => '4295', descr => 'int8multirange constructor',
+  proname => 'int8multirange', proisstrict => 'f',
+  prorettype => 'int8multirange', proargtypes => '',
+  prosrc => 'multirange_constructor0' },
+{ oid => '4296', descr => 'int8multirange constructor',
+  proname => 'int8multirange', proisstrict => 't',
+  prorettype => 'int8multirange', proargtypes => 'int8range',
+  prosrc => 'multirange_constructor1' },
+{ oid => '4297', descr => 'int8multirange constructor',
+  proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+  prorettype => 'int8multirange', proargtypes => '_int8range',
+  proallargtypes => '{_int8range}', proargmodes => '{v}',
+  prosrc => 'multirange_constructor2' },
+{ oid => '4298', descr => 'anymultirange cast',
+  proname => 'multirange', proisstrict => 't',
+  prorettype => 'anymultirange', proargtypes => 'anyrange',
+  prosrc => 'multirange_constructor1' },
+{ oid => '4299', descr => 'aggregate transition function',
+  proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+  proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '4300', descr => 'aggregate final function',
+  proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+  proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '4301', descr => 'combine aggregate input into a multirange',
+  proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+  prorettype => 'anymultirange', proargtypes => 'anyrange',
+  prosrc => 'aggregate_dummy' },
+{ oid => '4388', descr => 'range aggregate by intersecting',
+  proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+  proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '4389', descr => 'range aggregate by intersecting',
+  proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
+  prorettype => 'anymultirange', proargtypes => 'anymultirange',
+  prosrc => 'aggregate_dummy'},
+
 # date, time, timestamp constructors
 { oid => '3846', descr => 'construct date',
   proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
   proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
   proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
   prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
+{ oid => '4390', descr => 'for use by pg_upgrade',
+  proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
+  proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+  prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
+{ oid => '4391', descr => 'for use by pg_upgrade',
+  proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
+  proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
+  prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
 { oid => '3586', descr => 'for use by pg_upgrade',
   proname => 'binary_upgrade_set_next_heap_pg_class_oid', provolatile => 'v',
   proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
index 479754c24557898508a9093ccc6eceacbe3cfc15..10060255c95c000f7dd0f8d6f3fb20acc036e6a5 100644 (file)
 [
 
 { rngtypid => 'int4range', rngsubtype => 'int4', rngsubopc => 'btree/int4_ops',
-  rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff' },
+  rngcanonical => 'int4range_canonical', rngsubdiff => 'int4range_subdiff',
+  rngmultitypid => 'int4multirange' },
 { rngtypid => 'numrange', rngsubtype => 'numeric',
   rngsubopc => 'btree/numeric_ops', rngcanonical => '-',
-  rngsubdiff => 'numrange_subdiff' },
+  rngsubdiff => 'numrange_subdiff', rngmultitypid => 'nummultirange' },
 { rngtypid => 'tsrange', rngsubtype => 'timestamp',
   rngsubopc => 'btree/timestamp_ops', rngcanonical => '-',
-  rngsubdiff => 'tsrange_subdiff' },
+  rngsubdiff => 'tsrange_subdiff', rngmultitypid => 'tsmultirange' },
 { rngtypid => 'tstzrange', rngsubtype => 'timestamptz',
   rngsubopc => 'btree/timestamptz_ops', rngcanonical => '-',
-  rngsubdiff => 'tstzrange_subdiff' },
+  rngsubdiff => 'tstzrange_subdiff', rngmultitypid => 'tstzmultirange' },
 { rngtypid => 'daterange', rngsubtype => 'date', rngsubopc => 'btree/date_ops',
-  rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff' },
+  rngcanonical => 'daterange_canonical', rngsubdiff => 'daterange_subdiff',
+  rngmultitypid => 'datemultirange' },
 { rngtypid => 'int8range', rngsubtype => 'int8', rngsubopc => 'btree/int8_ops',
-  rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff' },
+  rngcanonical => 'int8range_canonical', rngsubdiff => 'int8range_subdiff',
+  rngmultitypid => 'int8multirange' },
 
 ]
index c28bb57b6c9625187165663e8b2e4185ecfa8735..76ad4000bf794e385bd34d612054743eb2fd3b5a 100644 (file)
@@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
        /* OID of range's element type (subtype) */
        Oid                     rngsubtype BKI_LOOKUP(pg_type);
 
+       /* OID of the range's multirange type */
+       Oid                     rngmultitypid BKI_LOOKUP(pg_type);
+
        /* collation for this range type, or 0 */
        Oid                     rngcollation BKI_DEFAULT(0);
 
@@ -57,13 +60,17 @@ typedef FormData_pg_range *Form_pg_range;
 DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
 #define RangeTypidIndexId                                      3542
 
+DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 2228, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId                    2228
+
+
 /*
  * prototypes for functions in pg_range.c
  */
 
 extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
                                                Oid rangeSubOpclass, RegProcedure rangeCanonical,
-                                               RegProcedure rangeSubDiff);
+                                               RegProcedure rangeSubDiff, Oid multirangeTypeOid);
 extern void RangeDelete(Oid rangeTypeOid);
 
 #endif                                                 /* PG_RANGE_H */
index 28240bdce39cf636bc5266cc3b10c826db20f6b1..62018f063a8395f30d7a434b64e430d6a7263d2a 100644 (file)
   typreceive => 'range_recv', typsend => 'range_send',
   typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
 
+# multirange types
+{ oid => '4451', array_type_oid => '8010', descr => 'multirange of integers',
+  typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+  typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+  typreceive => 'multirange_recv', typsend => 'multirange_send',
+  typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '4532', array_type_oid => '8012', descr => 'multirange of numerics',
+  typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+  typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+  typreceive => 'multirange_recv', typsend => 'multirange_send',
+  typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '4533', array_type_oid => '8014',
+  descr => 'multirange of timestamps without time zone',
+  typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+  typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+  typreceive => 'multirange_recv', typsend => 'multirange_send',
+  typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '4534', array_type_oid => '8016',
+  descr => 'multirange of timestamps with time zone',
+  typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+  typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+  typreceive => 'multirange_recv', typsend => 'multirange_send',
+  typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+{ oid => '4535', array_type_oid => '8018', descr => 'multirange of dates',
+  typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+  typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+  typreceive => 'multirange_recv', typsend => 'multirange_send',
+  typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
+{ oid => '4536', array_type_oid => '8020', descr => 'multirange of bigints',
+  typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
+  typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
+  typreceive => 'multirange_recv', typsend => 'multirange_send',
+  typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
+
 # pseudo-types
 # types with typtype='p' represent various special cases in the type system.
 # These cannot be used to define table columns, but are valid as function
   typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
   typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
   typalign => 'd', typstorage => 'x' },
+{ oid => '4537',
+  descr => 'pseudo-type representing a polymorphic base type that is a multirange',
+  typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
+  typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
+  typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '4538',
+  descr => 'pseudo-type representing a multirange over a polymorphic common type',
+  typname => 'anycompatiblemultirange', typlen => '-1', typbyval => 'f',
+  typtype => 'p', typcategory => 'P', typinput => 'anycompatiblemultirange_in',
+  typoutput => 'anycompatiblemultirange_out', typreceive => '-', typsend => '-',
+  typalign => 'd', typstorage => 'x' },
 
 ]
index 70563a6408bd0b7a190ef3208a9bfd8a646dbd25..545b789608a034647fa81ab04f03ed5d4fd598ef 100644 (file)
@@ -276,6 +276,7 @@ DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typ
 #define  TYPTYPE_COMPOSITE     'c' /* composite (e.g., table's rowtype) */
 #define  TYPTYPE_DOMAIN                'd' /* domain over another type */
 #define  TYPTYPE_ENUM          'e' /* enumerated type */
+#define  TYPTYPE_MULTIRANGE    'm' /* multirange type */
 #define  TYPTYPE_PSEUDO                'p' /* pseudo-type */
 #define  TYPTYPE_RANGE         'r' /* range type */
 
@@ -317,13 +318,15 @@ DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typ
         (typid) == ANYARRAYOID || \
         (typid) == ANYNONARRAYOID || \
         (typid) == ANYENUMOID || \
-        (typid) == ANYRANGEOID)
+        (typid) == ANYRANGEOID || \
+        (typid) == ANYMULTIRANGEOID)
 
 #define IsPolymorphicTypeFamily2(typid)  \
        ((typid) == ANYCOMPATIBLEOID || \
         (typid) == ANYCOMPATIBLEARRAYOID || \
         (typid) == ANYCOMPATIBLENONARRAYOID || \
-        (typid) == ANYCOMPATIBLERANGEOID)
+        (typid) == ANYCOMPATIBLERANGEOID || \
+        (typid) == ANYCOMPATIBLEMULTIRANGEOID)
 
 /* Is this a "true" array type?  (Requires fmgroids.h) */
 #define IsTrueArrayType(typeForm)  \
@@ -397,4 +400,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
 extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
                                                          Oid typeNamespace);
 
+extern char *makeMultirangeTypeName(const char *rangeTypeName,
+                                                                       Oid typeNamespace);
+
 #endif                                                 /* PG_TYPE_H */
index 23130895af4e0c550e1169e805845221f7146be9..989914dfe1131508519d0a073804763f00e4c90e 100644 (file)
@@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
 extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
 extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
 extern Oid     AssignTypeArrayOid(void);
+extern Oid     AssignTypeMultirangeOid(void);
+extern Oid     AssignTypeMultirangeArrayOid(void);
 
 extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
 extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);
index a990d11ea8693108aa8c752c7ce105a18ec7b3ba..e5ad7b95d17158f860811c425d32a7f148d44a02 100644 (file)
@@ -161,6 +161,7 @@ extern char get_typtype(Oid typid);
 extern bool type_is_rowtype(Oid typid);
 extern bool type_is_enum(Oid typid);
 extern bool type_is_range(Oid typid);
+extern bool type_is_multirange(Oid typid);
 extern void get_type_category_preferred(Oid typid,
                                                                                char *typcategory,
                                                                                bool *typispreferred);
@@ -190,6 +191,8 @@ extern char *get_namespace_name(Oid nspid);
 extern char *get_namespace_name_or_temp(Oid nspid);
 extern Oid     get_range_subtype(Oid rangeOid);
 extern Oid     get_range_collation(Oid rangeOid);
+extern Oid     get_range_multirange(Oid rangeOid);
+extern Oid     get_multirange_range(Oid multirangeOid);
 extern Oid     get_index_column_opclass(Oid index_oid, int attno);
 extern bool get_index_isreplident(Oid index_oid);
 extern bool get_index_isvalid(Oid index_oid);
diff --git a/src/include/utils/multirangetypes.h b/src/include/utils/multirangetypes.h
new file mode 100644 (file)
index 0000000..4cf7241
--- /dev/null
@@ -0,0 +1,116 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.h
+ *       Declarations for Postgres multirange types.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/utils/multirangetypes.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef MULTIRANGETYPES_H
+#define MULTIRANGETYPES_H
+
+#include "utils/typcache.h"
+#include "utils/expandeddatum.h"
+
+
+/*
+ * Multiranges are varlena objects, so must meet the varlena convention that
+ * the first int32 of the object contains the total object size in bytes.
+ * Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
+ */
+typedef struct
+{
+       int32           vl_len_;                /* varlena header (do not touch directly!) */
+       Oid                     multirangetypid;        /* multirange type's own OID */
+       uint32          rangeCount;             /* the number of ranges */
+
+       /*
+        * Following the count are the range objects themselves, as ShortRangeType
+        * structs. Note that ranges are varlena too, depending on whether they
+        * have lower/upper bounds and because even their base types can be
+        * varlena. So we can't really index into this list.
+        */
+} MultirangeType;
+
+/* Use these macros in preference to accessing these fields directly */
+#define MultirangeTypeGetOid(mr)       ((mr)->multirangetypid)
+#define MultirangeIsEmpty(mr)  ((mr)->rangeCount == 0)
+
+/*
+ * fmgr macros for multirange type objects
+ */
+#define DatumGetMultirangeTypeP(X)             ((MultirangeType *) PG_DETOAST_DATUM(X))
+#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
+#define MultirangeTypePGetDatum(X)             PointerGetDatum(X)
+#define PG_GETARG_MULTIRANGE_P(n)              DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
+#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
+#define PG_RETURN_MULTIRANGE_P(x)              return MultirangeTypePGetDatum(x)
+
+/*
+ * prototypes for functions defined in multirangetypes.c
+ */
+
+/* internal versions of the above */
+extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+                                                                  MultirangeType *mr2);
+extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
+                                                                  MultirangeType *mr2);
+extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+                                                                                         Datum elem);
+extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr,
+                                                                                          RangeType *r);
+extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
+                                                                                                       MultirangeType *mr1,
+                                                                                                       MultirangeType *mr2);
+extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+                                                                                          MultirangeType *mr);
+extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
+                                                                                                       MultirangeType *mr1,
+                                                                                                       MultirangeType *mr2);
+extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+                                                                                        MultirangeType *mr);
+extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+                                                                                       MultirangeType *mr);
+extern bool range_adjacent_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+                                                                                       MultirangeType *mr);
+extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
+                                                                                                 MultirangeType *mr1,
+                                                                                                 MultirangeType *mr2);
+extern MultirangeType *multirange_minus_internal(Oid mltrngtypoid,
+                                                                                                TypeCacheEntry *rangetyp,
+                                                                                                int32 range_count1,
+                                                                                                RangeType **ranges1,
+                                                                                                int32 range_count2,
+                                                                                                RangeType **ranges2);
+extern MultirangeType *multirange_intersect_internal(Oid mltrngtypoid,
+                                                                                                        TypeCacheEntry *rangetyp,
+                                                                                                        int32 range_count1,
+                                                                                                        RangeType **ranges1,
+                                                                                                        int32 range_count2,
+                                                                                                        RangeType **ranges2);
+
+/* assorted support functions */
+extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
+                                                                                          Oid mltrngtypid);
+extern void multirange_deserialize(TypeCacheEntry *rangetyp,
+                                                                  const MultirangeType *range,
+                                                                  int32 *range_count,
+                                                                  RangeType ***ranges);
+extern MultirangeType *make_multirange(Oid mltrngtypoid,
+                                                                          TypeCacheEntry *typcache,
+                                                                          int32 range_count, RangeType **ranges);
+extern MultirangeType *make_empty_multirange(Oid mltrngtypoid,
+                                                                                        TypeCacheEntry *rangetyp);
+extern void multirange_get_bounds(TypeCacheEntry *rangetyp,
+                                                                 const MultirangeType *multirange,
+                                                                 uint32 i,
+                                                                 RangeBound *lower, RangeBound *upper);
+extern RangeType *multirange_get_range(TypeCacheEntry *rangetyp,
+                                                                          const MultirangeType *multirange, int i);
+
+#endif                                                 /* MULTIRANGETYPES_H */
index b77c41cf1b85897d61d0cfd9a0cbda408be4d32f..8459a3d6e7a203d2f5d599e1c4a7bf9866b8e54a 100644 (file)
@@ -29,6 +29,8 @@ typedef struct
        /* Following the OID are zero to two bound values, then a flags byte */
 } RangeType;
 
+#define RANGE_EMPTY_LITERAL "empty"
+
 /* Use this macro in preference to fetching rangetypid field directly */
 #define RangeTypeGetOid(r)     ((r)->rangetypid)
 
@@ -115,6 +117,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
                                                                        const RangeType *r2);
 extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
                                                                         const RangeType *r2);
+extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
+                                                                          RangeType *r2, bool strict);
+extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
+                                                                          RangeType *r2);
+extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
+                                                                                  const RangeType *r2);
 
 /* assorted support functions */
 extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@@ -132,8 +140,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
                                                         const RangeBound *b2);
 extern int     range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
                                                                   const RangeBound *b2);
+extern int     range_compare(const void *key1, const void *key2, void *arg);
 extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
                                                        RangeBound bound2);
 extern RangeType *make_empty_range(TypeCacheEntry *typcache);
+extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
+                                                                const RangeType *r2, RangeType **output1,
+                                                                RangeType **output2);
 
 #endif                                                 /* RANGETYPES_H */
index 3a2cfb7efa665c995c2ad690a7e29c220663624e..68ffd7761f1de4bbc023c13bc2262de70e6c1131 100644 (file)
@@ -39,6 +39,9 @@
 /* default selectivity estimate for range inequalities "A > b AND A < c" */
 #define DEFAULT_RANGE_INEQ_SEL 0.005
 
+/* default selectivity estimate for multirange inequalities "A > b AND A < c" */
+#define DEFAULT_MULTIRANGE_INEQ_SEL    0.005
+
 /* default selectivity estimate for pattern-match operators such as LIKE */
 #define DEFAULT_MATCH_SEL      0.005
 
index f27b73d76d06d0bf7b53f6becdaa83685e001548..e8f393520a3306a4cae0692fe5100757c8d2ec25 100644 (file)
@@ -79,6 +79,7 @@ enum SysCacheIdentifier
        PUBLICATIONOID,
        PUBLICATIONREL,
        PUBLICATIONRELMAP,
+       RANGEMULTIRANGE,
        RANGETYPE,
        RELNAMENSP,
        RELOID,
index 38c8fe0192927c01580ae4e45feee6b589c27614..dd69a06342fd18299aab91da32820a8fa62ceda2 100644 (file)
@@ -101,6 +101,11 @@ typedef struct TypeCacheEntry
        FmgrInfo        rng_canonical_finfo;    /* canonicalization function, if any */
        FmgrInfo        rng_subdiff_finfo;      /* difference function, if any */
 
+       /*
+        * Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
+        */
+       struct TypeCacheEntry *rngtype; /* multirange's range underlying type */
+
        /*
         * Domain's base type and typmod if it's a domain type.  Zeroes if not
         * domain, or if information hasn't been requested.
@@ -128,22 +133,23 @@ typedef struct TypeCacheEntry
 } TypeCacheEntry;
 
 /* Bit flags to indicate which fields a given caller needs to have set */
-#define TYPECACHE_EQ_OPR                       0x0001
-#define TYPECACHE_LT_OPR                       0x0002
-#define TYPECACHE_GT_OPR                       0x0004
-#define TYPECACHE_CMP_PROC                     0x0008
-#define TYPECACHE_HASH_PROC                    0x0010
-#define TYPECACHE_EQ_OPR_FINFO         0x0020
-#define TYPECACHE_CMP_PROC_FINFO       0x0040
-#define TYPECACHE_HASH_PROC_FINFO      0x0080
-#define TYPECACHE_TUPDESC                      0x0100
-#define TYPECACHE_BTREE_OPFAMILY       0x0200
-#define TYPECACHE_HASH_OPFAMILY                0x0400
-#define TYPECACHE_RANGE_INFO           0x0800
-#define TYPECACHE_DOMAIN_BASE_INFO                     0x1000
-#define TYPECACHE_DOMAIN_CONSTR_INFO           0x2000
-#define TYPECACHE_HASH_EXTENDED_PROC           0x4000
-#define TYPECACHE_HASH_EXTENDED_PROC_FINFO     0x8000
+#define TYPECACHE_EQ_OPR                       0x00001
+#define TYPECACHE_LT_OPR                       0x00002
+#define TYPECACHE_GT_OPR                       0x00004
+#define TYPECACHE_CMP_PROC                     0x00008
+#define TYPECACHE_HASH_PROC                    0x00010
+#define TYPECACHE_EQ_OPR_FINFO         0x00020
+#define TYPECACHE_CMP_PROC_FINFO       0x00040
+#define TYPECACHE_HASH_PROC_FINFO      0x00080
+#define TYPECACHE_TUPDESC                      0x00100
+#define TYPECACHE_BTREE_OPFAMILY       0x00200
+#define TYPECACHE_HASH_OPFAMILY                0x00400
+#define TYPECACHE_RANGE_INFO           0x00800
+#define TYPECACHE_DOMAIN_BASE_INFO                     0x01000
+#define TYPECACHE_DOMAIN_CONSTR_INFO           0x02000
+#define TYPECACHE_HASH_EXTENDED_PROC           0x04000
+#define TYPECACHE_HASH_EXTENDED_PROC_FINFO     0x08000
+#define TYPECACHE_MULTIRANGE_INFO                      0x10000
 
 /* This value will not equal any valid tupledesc identifier, nor 0 */
 #define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
index 555da952e1b4d912fa35149434c7869bedf0f27c..042deb2a96c2c05e4085d5e1fe7d820b9c4c7c83 100644 (file)
@@ -515,6 +515,8 @@ do_compile(FunctionCallInfo fcinfo,
                                        else if (rettypeid == ANYRANGEOID ||
                                                         rettypeid == ANYCOMPATIBLERANGEOID)
                                                rettypeid = INT4RANGEOID;
+                                       else if (rettypeid == ANYMULTIRANGEOID)
+                                               rettypeid = INT4MULTIRANGEOID;
                                        else            /* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
                                                rettypeid = INT4OID;
                                        /* XXX what could we use for ANYENUM? */
@@ -2109,6 +2111,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
                case TYPTYPE_BASE:
                case TYPTYPE_ENUM:
                case TYPTYPE_RANGE:
+               case TYPTYPE_MULTIRANGE:
                        typ->ttype = PLPGSQL_TTYPE_SCALAR;
                        break;
                case TYPTYPE_COMPOSITE:
@@ -2526,6 +2529,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
                                case ANYCOMPATIBLERANGEOID:
                                        argtypes[i] = INT4RANGEOID;
                                        break;
+                               case ANYMULTIRANGEOID:
+                                       argtypes[i] = INT4MULTIRANGEOID;
+                                       break;
                                default:
                                        break;
                        }
index b4a11b8aa9b63b6a84c9062a0bff2bdc65110f7f..823279514878190e33ad03fd9807dbc15c7c8e74 100644 (file)
@@ -140,6 +140,7 @@ owner of sequence deptest_a_seq
 owner of table deptest
 owner of function deptest_func()
 owner of type deptest_enum
+owner of type deptest_multirange
 owner of type deptest_range
 owner of table deptest2
 owner of sequence ss1
index 827e69fc7abf03f9d77d791329727d848e2f958f..dca31365bcfce4390b65bf85e5c81b158cd0ee6a 100644 (file)
@@ -305,6 +305,19 @@ WHERE  hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
 -------+----------+-----------+-----------
 (0 rows)
 
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+          hash_multirange_extended(v, 0)::bit(32) as extended0,
+          hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+         ('{[5675, 550273)}'::int4multirange),
+                ('{[550274, 1550274)}'::int4multirange),
+                ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE  hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+       OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+ value | standard | extended0 | extended1 
+-------+----------+-----------+-----------
+(0 rows)
+
 CREATE TYPE hash_test_t1 AS (a int, b text);
 SELECT v as value, hash_record(v)::bit(32) as standard,
        hash_record_extended(v, 0)::bit(32) as extended0,
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644 (file)
index 0000000..e81e565
--- /dev/null
@@ -0,0 +1,2466 @@
+-- Tests for multirange data types.
+--
+-- test input parser
+--
+-- negative tests; should fail
+select ''::textmultirange;
+ERROR:  malformed multirange literal: ""
+LINE 1: select ''::textmultirange;
+               ^
+DETAIL:  Missing left bracket.
+select '{,}'::textmultirange;
+ERROR:  malformed multirange literal: "{,}"
+LINE 1: select '{,}'::textmultirange;
+               ^
+DETAIL:  Expected range start.
+select '{[a,c),}'::textmultirange;
+ERROR:  malformed multirange literal: "{[a,c),}"
+LINE 1: select '{[a,c),}'::textmultirange;
+               ^
+DETAIL:  Expected range start.
+select '{,[a,c)}'::textmultirange;
+ERROR:  malformed multirange literal: "{,[a,c)}"
+LINE 1: select '{,[a,c)}'::textmultirange;
+               ^
+DETAIL:  Expected range start.
+select '{-[a,z)}'::textmultirange;
+ERROR:  malformed multirange literal: "{-[a,z)}"
+LINE 1: select '{-[a,z)}'::textmultirange;
+               ^
+DETAIL:  Expected range start.
+select '{[a,z) - }'::textmultirange;
+ERROR:  malformed multirange literal: "{[a,z) - }"
+LINE 1: select '{[a,z) - }'::textmultirange;
+               ^
+DETAIL:  Expected comma or end of multirange.
+select '{(",a)}'::textmultirange;
+ERROR:  malformed multirange literal: "{(",a)}"
+LINE 1: select '{(",a)}'::textmultirange;
+               ^
+DETAIL:  Unexpected end of input.
+select '{(,,a)}'::textmultirange;
+ERROR:  malformed range literal: "(,,a)"
+LINE 1: select '{(,,a)}'::textmultirange;
+               ^
+DETAIL:  Too many commas.
+select '{(),a)}'::textmultirange;
+ERROR:  malformed range literal: "()"
+LINE 1: select '{(),a)}'::textmultirange;
+               ^
+DETAIL:  Missing comma after lower bound.
+select '{(a,))}'::textmultirange;
+ERROR:  malformed multirange literal: "{(a,))}"
+LINE 1: select '{(a,))}'::textmultirange;
+               ^
+DETAIL:  Expected comma or end of multirange.
+select '{(],a)}'::textmultirange;
+ERROR:  malformed range literal: "(]"
+LINE 1: select '{(],a)}'::textmultirange;
+               ^
+DETAIL:  Missing comma after lower bound.
+select '{(a,])}'::textmultirange;
+ERROR:  malformed multirange literal: "{(a,])}"
+LINE 1: select '{(a,])}'::textmultirange;
+               ^
+DETAIL:  Expected comma or end of multirange.
+select '{[z,a]}'::textmultirange;
+ERROR:  range lower bound must be less than or equal to range upper bound
+LINE 1: select '{[z,a]}'::textmultirange;
+               ^
+-- should succeed
+select '{}'::textmultirange;
+ textmultirange 
+----------------
+ {}
+(1 row)
+
+select '  {}  '::textmultirange;
+ textmultirange 
+----------------
+ {}
+(1 row)
+
+select ' { empty, empty }  '::textmultirange;
+ textmultirange 
+----------------
+ {}
+(1 row)
+
+select ' {( " a " " a ", " z " " z " )  }'::textmultirange;
+       textmultirange       
+----------------------------
+ {("  a   a ","  z   z  ")}
+(1 row)
+
+select '{(,z)}'::textmultirange;
+ textmultirange 
+----------------
+ {(,z)}
+(1 row)
+
+select '{(a,)}'::textmultirange;
+ textmultirange 
+----------------
+ {(a,)}
+(1 row)
+
+select '{[,z]}'::textmultirange;
+ textmultirange 
+----------------
+ {(,z]}
+(1 row)
+
+select '{[a,]}'::textmultirange;
+ textmultirange 
+----------------
+ {[a,)}
+(1 row)
+
+select '{(,)}'::textmultirange;
+ textmultirange 
+----------------
+ {(,)}
+(1 row)
+
+select '{[ , ]}'::textmultirange;
+ textmultirange 
+----------------
+ {[" "," "]}
+(1 row)
+
+select '{["",""]}'::textmultirange;
+ textmultirange 
+----------------
+ {["",""]}
+(1 row)
+
+select '{[",",","]}'::textmultirange;
+ textmultirange 
+----------------
+ {[",",","]}
+(1 row)
+
+select '{["\\","\\"]}'::textmultirange;
+ textmultirange 
+----------------
+ {["\\","\\"]}
+(1 row)
+
+select '{["""","\""]}'::textmultirange;
+ textmultirange 
+----------------
+ {["""",""""]}
+(1 row)
+
+select '{(\\,a)}'::textmultirange;
+ textmultirange 
+----------------
+ {("\\",a)}
+(1 row)
+
+select '{((,z)}'::textmultirange;
+ textmultirange 
+----------------
+ {("(",z)}
+(1 row)
+
+select '{([,z)}'::textmultirange;
+ textmultirange 
+----------------
+ {("[",z)}
+(1 row)
+
+select '{(!,()}'::textmultirange;
+ textmultirange 
+----------------
+ {(!,"(")}
+(1 row)
+
+select '{(!,[)}'::textmultirange;
+ textmultirange 
+----------------
+ {(!,"[")}
+(1 row)
+
+select '{[a,a]}'::textmultirange;
+ textmultirange 
+----------------
+ {[a,a]}
+(1 row)
+
+select '{[a,a],[a,b]}'::textmultirange;
+ textmultirange 
+----------------
+ {[a,b]}
+(1 row)
+
+select '{[a,b), [b,e]}'::textmultirange;
+ textmultirange 
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [b,f]}'::textmultirange;
+ textmultirange 
+----------------
+ {[a,f]}
+(1 row)
+
+select '{[a,a],[b,b]}'::textmultirange;
+ textmultirange 
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+ textmultirange 
+----------------
+ {[a,a],[b,b]}
+(1 row)
+
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+ int4multirange 
+----------------
+ {[1,5)}
+(1 row)
+
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+   textmultirange    
+---------------------
+ {[a,a],[b,b],[c,c]}
+(1 row)
+
+select '{[a,d], [b,e]}'::textmultirange;
+ textmultirange 
+----------------
+ {[a,e]}
+(1 row)
+
+select '{[a,d), [d,e)}'::textmultirange;
+ textmultirange 
+----------------
+ {[a,e)}
+(1 row)
+
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+ textmultirange 
+----------------
+ {}
+(1 row)
+
+select '{(a,a]}'::textmultirange;
+ textmultirange 
+----------------
+ {}
+(1 row)
+
+select '{(a,a)}'::textmultirange;
+ textmultirange 
+----------------
+ {}
+(1 row)
+
+--
+-- test the constructor
+---
+select textmultirange();
+ textmultirange 
+----------------
+ {}
+(1 row)
+
+select textmultirange(textrange('a', 'c'));
+ textmultirange 
+----------------
+ {[a,c)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+ textmultirange 
+----------------
+ {[a,c),[f,g)}
+(1 row)
+
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+ textmultirange 
+----------------
+ {[a,d)}
+(1 row)
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+ int4multirange 
+----------------
+ {}
+(1 row)
+
+select int4range(1, 3)::int4multirange;
+ int4range 
+-----------
+ {[1,3)}
+(1 row)
+
+select int4range(1, null)::int4multirange;
+ int4range 
+-----------
+ {[1,)}
+(1 row)
+
+select int4range(null, null)::int4multirange;
+ int4range 
+-----------
+ {(,)}
+(1 row)
+
+select 'empty'::textrange::textmultirange;
+ textmultirange 
+----------------
+ {}
+(1 row)
+
+select textrange('a', 'c')::textmultirange;
+ textrange 
+-----------
+ {[a,c)}
+(1 row)
+
+select textrange('a', null)::textmultirange;
+ textrange 
+-----------
+ {[a,)}
+(1 row)
+
+select textrange(null, null)::textmultirange;
+ textrange 
+-----------
+ {(,)}
+(1 row)
+
+--
+-- create some test data and test the operators
+--
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+          nmr          | isempty | lower | upper 
+-----------------------+---------+-------+-------
+ {}                    | t       |       |      
+ {}                    | t       |       |      
+ {}                    | t       |       |      
+ {}                    | t       |       |      
+ {(,5)}                | f       |       |     5
+ {(,)}                 | f       |       |      
+ {(,)}                 | f       |       |      
+ {[1.1,2.2)}           | f       |   1.1 |   2.2
+ {[1.7,1.7],[1.9,2.1)} | f       |   1.7 |   2.1
+ {[1.7,1.9)}           | f       |   1.7 |   1.9
+ {[3,)}                | f       |     3 |      
+(11 rows)
+
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+          nmr          | lower_inc | lower_inf | upper_inc | upper_inf 
+-----------------------+-----------+-----------+-----------+-----------
+ {}                    | f         | f         | f         | f
+ {}                    | f         | f         | f         | f
+ {}                    | f         | f         | f         | f
+ {}                    | f         | f         | f         | f
+ {(,5)}                | f         | t         | f         | f
+ {(,)}                 | f         | t         | f         | t
+ {(,)}                 | f         | t         | f         | t
+ {[1.1,2.2)}           | t         | f         | f         | f
+ {[1.7,1.7],[1.9,2.1)} | t         | f         | f         | f
+ {[1.7,1.9)}           | t         | f         | f         | f
+ {[3,)}                | t         | f         | f         | t
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+ nmr 
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+  nmr   
+--------
+ {(,5)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+  nmr   
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+ nmr 
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+          nmr          
+-----------------------
+ {[1.7,1.7],[1.9,2.1)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+ nmr 
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+  nmr   
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+  nmr   
+--------
+ {}
+ {(,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+          nmr          
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+ nmr 
+-----
+ {}
+ {}
+ {}
+ {}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+          nmr          
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+          nmr          
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(11 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+  nmr   
+--------
+ {[3,)}
+(1 row)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+          nmr          
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+          nmr          
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+          nmr          
+-----------------------
+ {[3,)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+ nmr 
+-----
+(0 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+          nmr          
+-----------------------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(7 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+          nmr          
+-----------------------
+ {}
+ {(,)}
+ {[3,)}
+ {(,)}
+ {}
+ {}
+ {[1.1,2.2)}
+ {}
+ {[1.7,1.9)}
+ {[1.7,1.7],[1.9,2.1)}
+(10 rows)
+
+select nummultirange(numrange(2.0, 1.0));
+ERROR:  range lower bound must be less than or equal to range upper bound
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+     nummultirange     
+-----------------------
+ {[1.0,2.0),[5.0,6.0)}
+(1 row)
+
+analyze nummultirange_test;
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+ {(,5)}
+(4 rows)
+
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+  nmr   
+--------
+ {(,)}
+ {[3,)}
+ {(,)}
+(3 rows)
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() && nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange() @> 'empty'::numrange;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+ ?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,5);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+ ?column? 
+----------
+ t
+(1 row)
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'empty'::numrange <@ nummultirange();
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
+ ?column? 
+----------
+ t
+(1 row)
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+ ?column? 
+----------
+ f
+(1 row)
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+ ?column? 
+----------
+ t
+(1 row)
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange() << 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange() << numrange(1,2);
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) << numrange(3,6);
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,2)) << numrange(3,6);
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange() << nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange() << nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+ ?column? 
+----------
+ f
+(1 row)
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange() >> numrange(1,2);
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+ ?column? 
+----------
+ f
+(1 row)
+
+select 'empty'::numrange >> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(1,2) >> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(3,4));
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+ ?column? 
+----------
+ f
+(1 row)
+
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange() >> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange();
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange() >> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ f
+(1 row)
+
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+ ?column? 
+----------
+ t
+(1 row)
+
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+ ?column? 
+----------
+ f
+(1 row)
+
+-- union
+SELECT nummultirange() + nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() + nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+ ?column? 
+----------
+ {[1,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+   ?column?    
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+ ?column? 
+----------
+ {[1,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+   ?column?    
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+ ?column? 
+----------
+ {[0,9)}
+(1 row)
+
+-- merge
+SELECT range_merge(nummultirange());
+ range_merge 
+-------------
+ empty
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2)));
+ range_merge 
+-------------
+ [1,2)
+(1 row)
+
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+ range_merge 
+-------------
+ [1,8)
+(1 row)
+
+-- minus
+SELECT nummultirange() - nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() - nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+   ?column?    
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+   ?column?    
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+ ?column? 
+----------
+ {[2,4)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+   ?column?    
+---------------
+ {[2,3),[4,8)}
+(1 row)
+
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+   ?column?    
+---------------
+ {[1,2),[3,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+   ?column?    
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+   ?column?    
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+   ?column?    
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+   ?column?    
+---------------
+ {[1,2),[4,5)}
+(1 row)
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange() * nummultirange(numrange(1,2));
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+ ?column? 
+----------
+ {}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+ ?column? 
+----------
+ {[1,3)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+ ?column? 
+----------
+ {[1,2)}
+(1 row)
+
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+ ?column? 
+----------
+ {[2,3)}
+(1 row)
+
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+   ?column?    
+---------------
+ {[1,2),[3,4)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+       ?column?       
+----------------------
+ {[1,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+ ?column? 
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+ ?column? 
+----------
+ {[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+          ?column?          
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
+          ?column?          
+----------------------------
+ {[1,2),[3,4),[7,8),[9,10)}
+(1 row)
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT   room_id, range_agg(booked_during)
+FROM     reservations
+GROUP BY room_id
+ORDER BY room_id;
+ room_id |                     range_agg                     
+---------+---------------------------------------------------
+       1 | {[07-01-2018,07-14-2018),[07-20-2018,07-22-2018)}
+       2 | {[07-01-2018,07-03-2018)}
+       3 | 
+       4 | 
+       5 | {[07-01-2018,07-03-2018)}
+       6 | {[07-01-2018,07-10-2018)}
+       7 | {[07-01-2018,07-14-2018)}
+       8 | {}
+(8 rows)
+
+-- range_agg on a custom range type too
+SELECT  range_agg(r)
+FROM    (VALUES
+          ('[a,c]'::textrange),
+          ('[b,b]'::textrange),
+          ('[c,f]'::textrange),
+          ('[g,h)'::textrange),
+          ('[h,j)'::textrange)
+        ) t(r);
+   range_agg   
+---------------
+ {[a,f],[g,j)}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test;
+ range_intersect_agg 
+---------------------
+ {}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where false;
+ range_intersect_agg 
+---------------------
+(1 row)
+
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+ range_intersect_agg 
+---------------------
+ {[1,2]}
+(1 row)
+
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+ range_intersect_agg 
+---------------------
+ {[3,5)}
+(1 row)
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+select * from nummultirange_test2 where nmr = '{}';
+ nmr 
+-----
+ {}
+(1 row)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+     nmr     
+-------------
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(2 rows)
+
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+ nmr 
+-----
+(0 rows)
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+     nmr     
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+     nmr     
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+     nmr     
+-------------
+ {}
+ {}
+ {}
+ {}
+ {(,5)}
+ {[1.1,2.2)}
+ {[1.1,2.2)}
+(7 rows)
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+DROP TABLE nummultirange_test2;
+--
+-- Test user-defined multirange of floats
+--
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+ ?column? 
+----------
+ t
+(1 row)
+
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+           f8mr            | i  
+---------------------------+----
+ {[-100.00007,1111113000)} | 42
+(1 row)
+
+drop table float8multirange_test;
+--
+-- Test multirange types over domains
+--
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+ ?column? 
+----------
+ t
+(1 row)
+
+drop domain mydomain cascade;
+NOTICE:  drop cascades to type mydomainrange
+--
+-- Test domains over multirange types
+--
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+ ?column? 
+----------
+ f
+(1 row)
+
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+ERROR:  value for domain restrictedmultirange violates check constraint "restrictedmultirange_check"
+drop domain restrictedmultirange;
+---
+-- Check automatic naming of multiranges
+---
+create type intr as range(subtype=int);
+select intr_multirange(intr(1,10));
+ intr_multirange 
+-----------------
+ {[1,10)}
+(1 row)
+
+drop type intr;
+create type intmultirange as (x int, y int);
+create type intrange as range(subtype=int); -- should fail
+ERROR:  type "intmultirange" already exists
+DETAIL:  Failed while creating a multirange type for type "intrange".
+HINT:  You can manually specify a multirange type name using the "multirange_type_name" attribute
+drop type intmultirange;
+create type intr_multirange as (x int, y int);
+create type intr as range(subtype=int); -- should fail
+ERROR:  type "intr_multirange" already exists
+DETAIL:  Failed while creating a multirange type for type "intr".
+HINT:  You can manually specify a multirange type name using the "multirange_type_name" attribute
+drop type intr_multirange;
+--
+-- Test multiple multirange types over the same subtype and manual naming of
+-- the multirange type.
+--
+-- should fail
+create type textrange1 as range(subtype=text, multirange_type_name=int, collation="C");
+ERROR:  type "int4" already exists
+-- should pass
+create type textrange1 as range(subtype=text, multirange_type_name=multirange_of_text, collation="C");
+-- should pass, because existing _textrange1 is automatically renamed
+create type textrange2 as range(subtype=text, multirange_type_name=_textrange1, collation="C");
+select multirange_of_text(textrange2('a','Z'));  -- should fail
+ERROR:  function multirange_of_text(textrange2) does not exist
+LINE 1: select multirange_of_text(textrange2('a','Z'));
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select multirange_of_text(textrange1('a','Z')) @> 'b'::text;
+ERROR:  range lower bound must be less than or equal to range upper bound
+select _textrange1(textrange2('a','z')) @> 'b'::text;
+ ?column? 
+----------
+ t
+(1 row)
+
+drop type textrange1;
+drop type textrange2;
+--
+-- Test polymorphic type system
+--
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+  returns anyelement as 'select $1[1] + lower($2);' language sql;
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+ anyarray_anymultirange_func 
+-----------------------------
+                          11
+(1 row)
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+ERROR:  function anyarray_anymultirange_func(integer[], nummultirange) does not exist
+LINE 1: select anyarray_anymultirange_func(ARRAY[1,2], nummultirange...
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+-- should fail
+create function bogus_func(anyelement)
+  returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+-- should fail
+create function bogus_func(int)
+  returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+create function range_add_bounds(anymultirange)
+  returns anyelement as 'select lower($1) + upper($1)' language sql;
+select range_add_bounds(int4multirange(int4range(1, 17)));
+ range_add_bounds 
+------------------
+               18
+(1 row)
+
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+ range_add_bounds 
+------------------
+         124.1231
+(1 row)
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+  as $$ select upper($1) + $2[1] $$
+  language sql;
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+ multirangetypes_sql 
+---------------------
+                  12
+(1 row)
+
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]);  -- match failure
+ERROR:  function multirangetypes_sql(nummultirange, integer[]) does not exist
+LINE 1: select multirangetypes_sql(nummultirange(numrange(1,10)), AR...
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+create function anycompatiblearray_anycompatiblemultirange_func(a anycompatiblearray, mr anycompatiblemultirange)
+  returns anycompatible as 'select $1[1] + lower($2);' language sql;
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(int4range(10,20)));
+ anycompatiblearray_anycompatiblemultirange_func 
+-------------------------------------------------
+                                              11
+(1 row)
+
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(numrange(10,20)));
+ anycompatiblearray_anycompatiblemultirange_func 
+-------------------------------------------------
+                                              11
+(1 row)
+
+-- should fail
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1.1,2], multirange(int4range(10,20)));
+ERROR:  function anycompatiblearray_anycompatiblemultirange_func(numeric[], int4multirange) does not exist
+LINE 1: select anycompatiblearray_anycompatiblemultirange_func(ARRAY...
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anycompatiblearray_anycompatiblemultirange_func(anycompatiblearray, anycompatiblemultirange);
+create function anycompatiblerange_anycompatiblemultirange_func(r anycompatiblerange, mr anycompatiblemultirange)
+  returns anycompatible as 'select lower($1) + lower($2);' language sql;
+select anycompatiblerange_anycompatiblemultirange_func(int4range(1,2), multirange(int4range(10,20)));
+ anycompatiblerange_anycompatiblemultirange_func 
+-------------------------------------------------
+                                              11
+(1 row)
+
+-- should fail
+select anycompatiblerange_anycompatiblemultirange_func(numrange(1,2), multirange(int4range(10,20)));
+ERROR:  function anycompatiblerange_anycompatiblemultirange_func(numrange, int4multirange) does not exist
+LINE 1: select anycompatiblerange_anycompatiblemultirange_func(numra...
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anycompatiblerange_anycompatiblemultirange_func(anycompatiblerange, anycompatiblemultirange);
+-- should fail
+create function bogus_func(anycompatible)
+  returns anycompatiblerange as 'select int4range(1,10)' language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
+--
+-- Arrays of multiranges
+--
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+              array               
+----------------------------------
+ {"{[1.1,1.2)}","{[12.3,155.5)}"}
+(1 row)
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+ f1 |           f2            
+----+-------------------------
+ 42 | {"{[1,10)}","{[2,20)}"}
+(1 row)
+
+drop table i8mr_array;
+--
+-- Multiranges of arrays
+--
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+   arraymultirange   
+---------------------
+ {["{1,2}","{2,1}")}
+(1 row)
+
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2]));  -- fail
+ERROR:  range lower bound must be less than or equal to range upper bound
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column? 
+----------
+ f
+(1 row)
+
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+ ?column? 
+----------
+ t
+(1 row)
+
+--
+-- Ranges of composites
+--
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+  (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+          (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+          t          |       u       
+---------------------+---------------
+ {["(1,2)","(3,4)")} | {"a":3,"b":4}
+ {["(5,6)","(7,8)")} | {"a":7,"b":8}
+(2 rows)
+
+drop type two_ints cascade;
+NOTICE:  drop cascades to type two_ints_range
+--
+-- Check behavior when subtype lacks a hash function
+--
+set enable_sort = off;  -- try to make it pick a hash setop implementation
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+ cashmultirange  
+-----------------
+ {($2.00,$5.00)}
+(1 row)
+
+reset enable_sort;
+--
+-- OUT/INOUT/TABLE functions
+--
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+  as $$ select $1, 'foo'::text $$ language sql;
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+    r    |  t  
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+  as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+  r  |  t  
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+  as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+   r   |  t  
+-------+-----
+ [1,2) | foo
+(1 row)
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+  as $$ select multirange($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed4(int4range(1,2));
+    r    |  t  
+---------+-----
+ {[1,2)} | foo
+(1 row)
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+  as $$ select upper($1), $1 $$ language sql;
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+ i |    r    
+---+---------
+ 2 | {[1,2)}
+(1 row)
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+  as $$ select $1, $2 $$ language sql;
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+  i  |    r     
+-----+----------
+ 123 | {[1,11)}
+(1 row)
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+  as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+ mr_polymorphic 
+----------------
+ {[1,4)}
+(1 row)
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+  as $$ select '[1,10]', 'foo' $$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+  as $$ select $1, '[1,10]' $$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anymultirange requires at least one input of type anyrange or anymultirange.
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+  as $$ select $1, '[1,10]' $$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anymultirange requires at least one input of type anyrange or anymultirange.
index 507b474b1bb36a1c824dc7d0084d81375d63359c..254ca06d3dd5d9ab50894e6180357b97defa6bc9 100644 (file)
@@ -39,6 +39,10 @@ begin
     if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
     then return true; end if;
   end if;
+  if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+    if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+    then return true; end if;
+  end if;
   return false;
 end
 $$ language plpgsql strict stable;
@@ -183,7 +187,8 @@ WHERE p1.oid < p2.oid AND
 -- need to be modified whenever new pairs of types are made binary-equivalent,
 -- or when new polymorphic built-in functions are added!
 -- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function.  Likewise, ignore range constructor functions.
+-- dummy built-in function.  Likewise, ignore range and multirange constructor
+-- functions.
 SELECT DISTINCT p1.prorettype::regtype, p2.prorettype::regtype
 FROM pg_proc AS p1, pg_proc AS p2
 WHERE p1.oid != p2.oid AND
@@ -192,6 +197,8 @@ WHERE p1.oid != p2.oid AND
     p1.prokind != 'a' AND p2.prokind != 'a' AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
+    p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+    p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
     (p1.prorettype < p2.prorettype)
 ORDER BY 1, 2;
          prorettype          |        prorettype        
@@ -210,6 +217,8 @@ WHERE p1.oid != p2.oid AND
     p1.prokind != 'a' AND p2.prokind != 'a' AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
+    p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+    p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
     (p1.proargtypes[0] < p2.proargtypes[0])
 ORDER BY 1, 2;
          proargtypes         |       proargtypes        
@@ -230,6 +239,8 @@ WHERE p1.oid != p2.oid AND
     p1.prokind != 'a' AND p2.prokind != 'a' AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
+    p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+    p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
     (p1.proargtypes[1] < p2.proargtypes[1])
 ORDER BY 1, 2;
          proargtypes         |       proargtypes        
@@ -342,7 +353,8 @@ WHERE p1.prorettype IN
      'anyarray'::regtype = ANY (p1.proargtypes) OR
      'anynonarray'::regtype = ANY (p1.proargtypes) OR
      'anyenum'::regtype = ANY (p1.proargtypes) OR
-     'anyrange'::regtype = ANY (p1.proargtypes))
+     'anyrange'::regtype = ANY (p1.proargtypes) OR
+     'anymultirange'::regtype = ANY (p1.proargtypes))
 ORDER BY 2;
  oid  |    proname     
 ------+----------------
@@ -357,20 +369,25 @@ ORDER BY 2;
  3532 | enum_recv
 (9 rows)
 
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
 SELECT p1.oid, p1.proname
 FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
   AND NOT
-     'anyrange'::regtype = ANY (p1.proargtypes)
+    ('anyrange'::regtype = ANY (p1.proargtypes) OR
+      'anymultirange'::regtype = ANY (p1.proargtypes))
 ORDER BY 2;
  oid  |     proname      
 ------+------------------
+ 4229 | anymultirange_in
  3832 | anyrange_in
+ 4231 | multirange_in
+ 4233 | multirange_recv
  3876 | range_gist_union
  3834 | range_in
  3836 | range_recv
-(4 rows)
+(7 rows)
 
 -- similarly for the anycompatible family
 SELECT p1.oid, p1.proname
@@ -2204,13 +2221,14 @@ ORDER BY 1, 2, 3;
                     | float_ops        | float4_ops       | real
                     | float_ops        | float8_ops       | double precision
                     | jsonb_ops        | jsonb_ops        | jsonb
+                    | multirange_ops   | multirange_ops   | anymultirange
                     | numeric_ops      | numeric_ops      | numeric
                     | range_ops        | range_ops        | anyrange
                     | record_image_ops | record_image_ops | record
                     | record_ops       | record_ops       | record
                     | tsquery_ops      | tsquery_ops      | tsquery
                     | tsvector_ops     | tsvector_ops     | tsvector
-(14 rows)
+(15 rows)
 
 -- **************** pg_index ****************
 -- Look for illegal values in pg_index fields.
index d0a6b630b8f3e13f5a5ab746e9f6ef7822e6ec59..d55006d8c950a81b39e888d6160a40406c062e9f 100644 (file)
@@ -1811,7 +1811,7 @@ begin
   return array[x + 1, x + 2];
 end$$ language plpgsql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anyrange requires at least one input of type anyrange.
+DETAIL:  A result of type anyrange requires at least one input of type anyrange or anymultirange.
 create function f1(x anyrange) returns anyarray as $$
 begin
   return array[lower(x), upper(x)];
@@ -1856,7 +1856,7 @@ begin
   return array[x + 1, x + 2];
 end$$ language plpgsql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
 create function f1(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
 begin
   return x;
index f5dfdf617fd95b5e712e7b7b875dc6a123ea7c10..772345584f07b5164089aab9b4f21b0aefcbe905 100644 (file)
@@ -61,7 +61,7 @@ create function polyf(x anyelement) returns anyrange as $$
   select array[x + 1, x + 2]
 $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anyrange requires at least one input of type anyrange.
+DETAIL:  A result of type anyrange requires at least one input of type anyrange or anymultirange.
 create function polyf(x anyrange) returns anyarray as $$
   select array[lower(x), upper(x)]
 $$ language sql;
@@ -97,12 +97,27 @@ LINE 1: select polyf(int4range(42, 49), 11, 4.5) as fail;
                ^
 HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
 drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
+create function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+  select array[lower(x), upper(x), y, z]
+$$ language sql;
+select polyf(multirange(int4range(42, 49)), 11, 2::smallint) as int, polyf(multirange(float8range(4.5, 7.8)), 7.8, 11::real) as num;
+     int      |       num        
+--------------+------------------
+ {42,49,11,2} | {4.5,7.8,7.8,11}
+(1 row)
+
+select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail;  -- range type doesn't fit
+ERROR:  function polyf(int4multirange, integer, numeric) does not exist
+LINE 1: select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail...
+               ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible);
 -- fail, can't infer type:
 create function polyf(x anycompatible) returns anycompatiblerange as $$
   select array[x + 1, x + 2]
 $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
 create function polyf(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
   select x
 $$ language sql;
@@ -113,6 +128,22 @@ select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8),
 (1 row)
 
 drop function polyf(x anycompatiblerange, y anycompatiblearray);
+-- fail, can't infer type:
+create function polyf(x anycompatible) returns anycompatiblemultirange as $$
+  select array[x + 1, x + 2]
+$$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anycompatiblemultirange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
+create function polyf(x anycompatiblemultirange, y anycompatiblearray) returns anycompatiblemultirange as $$
+  select x
+$$ language sql;
+select polyf(multirange(int4range(42, 49)), array[11]) as int, polyf(multirange(float8range(4.5, 7.8)), array[7]) as num;
+    int    |     num     
+-----------+-------------
+ {[42,49)} | {[4.5,7.8)}
+(1 row)
+
+drop function polyf(x anycompatiblemultirange, y anycompatiblearray);
 create function polyf(a anyelement, b anyarray,
                       c anycompatible, d anycompatible,
                       OUT x anyarray, OUT y anycompatiblearray)
@@ -231,7 +262,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
 CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --     N    P
 -- should CREATE
 CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -243,11 +274,11 @@ CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[],
 CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --    Case2 (R = P) && ((B = P) || (B = N))
 --    -------------------------------------
 --    S    tf1      B    tf2
@@ -302,13 +333,13 @@ ERROR:  function tfp(integer[], anyelement) does not exist
 CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --    P    N        N    P
 -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
 CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --    P    N        P    N
 -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
 CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -324,21 +355,21 @@ ERROR:  function tf2p(anyarray, anyelement) does not exist
 CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --    P    P        N    P
 -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
 CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --    P    P        P    N
 -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
 CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -368,11 +399,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
 CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --     N    P
 -- should CREATE
 CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -382,7 +413,7 @@ CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
 CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --    Case4 (R = N) && ((B = P) || (B = N))
 --    -------------------------------------
 --    S    tf1      B    tf2
@@ -436,21 +467,21 @@ ERROR:  function tfp(integer[], anyelement) does not exist
 CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --    P    N        N    P
 -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
 CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --    P    N        P    N
 -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
 CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -472,13 +503,13 @@ ERROR:  function tf2p(anyarray, anyelement) does not exist
 CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --    P    P        N    P
 -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
 CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 --    P    P        P    N
 -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
 CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -1855,7 +1886,59 @@ returns anycompatiblerange as $$
   select $1
 $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
+create function anyctest(anycompatible, anycompatiblemultirange)
+returns anycompatiblemultirange as $$
+  select $2
+$$ language sql;
+select x, pg_typeof(x) from anyctest(11, multirange(int4range(4,7))) x;
+    x    |   pg_typeof    
+---------+----------------
+ {[4,7)} | int4multirange
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, multirange(numrange(4,7))) x;
+    x    |   pg_typeof   
+---------+---------------
+ {[4,7)} | nummultirange
+(1 row)
+
+select x, pg_typeof(x) from anyctest(11, 12) x;  -- fail
+ERROR:  function anyctest(integer, integer) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x;
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11.2, multirange(int4range(4,7))) x;  -- fail
+ERROR:  function anyctest(numeric, int4multirange) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(11.2, multirange(int4ra...
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select x, pg_typeof(x) from anyctest(11.2, '{[4,7)}') x;  -- fail
+ERROR:  could not identify anycompatiblemultirange type
+drop function anyctest(anycompatible, anycompatiblemultirange);
+create function anyctest(anycompatiblemultirange, anycompatiblemultirange)
+returns anycompatible as $$
+  select lower($1) + upper($2)
+$$ language sql;
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(int4range(4,7))) x;
+ x  | pg_typeof 
+----+-----------
+ 18 | integer
+(1 row)
+
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(numrange(4,7))) x; -- fail
+ERROR:  function anyctest(int4multirange, nummultirange) does not exist
+LINE 1: select x, pg_typeof(x) from anyctest(multirange(int4range(11...
+                                    ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function anyctest(anycompatiblemultirange, anycompatiblemultirange);
+-- fail, can't infer result type:
+create function anyctest(anycompatible)
+returns anycompatiblemultirange as $$
+  select $1
+$$ language sql;
+ERROR:  cannot determine result data type
+DETAIL:  A result of type anycompatiblemultirange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
 create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
 returns anycompatiblearray as $$
   select array[$1, $2]
index 06bd129fd22816b2b741ebfd1b480ec1f9f361aa..5bbd5f6c0bd614182262e94a4f0a433841f9745d 100644 (file)
@@ -1556,7 +1556,7 @@ DROP FUNCTION dup(anyelement);
 CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
 AS 'select $1, array[$1,$1]' LANGUAGE sql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
+DETAIL:  A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
 CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
 AS 'select $1, $2' LANGUAGE sql;
 SELECT dup(22, array[44]);
index c421f5394fe3594bfcefd6cfdc8f8e47c6312932..6a1bbadc91af2c7f19a59cd5641ea13096032271 100644 (file)
@@ -518,6 +518,25 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
  empty
 (1 row)
 
+select range_intersect_agg(nr) from numrange_test;
+ range_intersect_agg 
+---------------------
+ empty
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where false;
+ range_intersect_agg 
+---------------------
+(1 row)
+
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+ range_intersect_agg 
+---------------------
+ [3,5)
+(1 row)
+
+analyze numrange_test;
 create table numrange_test2(nr numrange);
 create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
 INSERT INTO numrange_test2 VALUES('[, 5)');
@@ -1371,12 +1390,12 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
 create function bogus_func(anyelement)
   returns anyrange as 'select int4range(1,10)' language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anyrange requires at least one input of type anyrange.
+DETAIL:  A result of type anyrange requires at least one input of type anyrange or anymultirange.
 -- should fail
 create function bogus_func(int)
   returns anyrange as 'select int4range(1,10)' language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anyrange requires at least one input of type anyrange.
+DETAIL:  A result of type anyrange requires at least one input of type anyrange or anymultirange.
 create function range_add_bounds(anyrange)
   returns anyelement as 'select lower($1) + upper($1)' language sql;
 select range_add_bounds(int4range(1, 17));
@@ -1430,7 +1449,7 @@ drop function anycompatiblearray_anycompatiblerange_func(anycompatiblearray, any
 create function bogus_func(anycompatible)
   returns anycompatiblerange as 'select int4range(1,10)' language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
+DETAIL:  A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
 --
 -- Arrays of ranges
 --
@@ -1508,6 +1527,7 @@ reset enable_sort;
 --
 -- OUT/INOUT/TABLE functions
 --
+-- infer anyrange from anyrange
 create function outparam_succeed(i anyrange, out r anyrange, out t text)
   as $$ select $1, 'foo'::text $$ language sql;
 select * from outparam_succeed(int4range(1,2));
@@ -1525,6 +1545,16 @@ select * from outparam2_succeed(int4range(1,11));
  {1,11} | {11,1}
 (1 row)
 
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+  as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+select * from outparam_succeed2(int4range(int4range(1,2)));
+  r  |  t  
+-----+-----
+ {2} | foo
+(1 row)
+
+-- infer anyelement from anyrange
 create function inoutparam_succeed(out i anyelement, inout r anyrange)
   as $$ select upper($1), $1 $$ language sql;
 select * from inoutparam_succeed(int4range(1,2));
@@ -1547,14 +1577,14 @@ select * from table_succeed(int4range(1,11));
 create function outparam_fail(i anyelement, out r anyrange, out t text)
   as $$ select '[1,10]', 'foo' $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anyrange requires at least one input of type anyrange.
+DETAIL:  A result of type anyrange requires at least one input of type anyrange or anymultirange.
 --should fail
 create function inoutparam_fail(inout i anyelement, out r anyrange)
   as $$ select $1, '[1,10]' $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anyrange requires at least one input of type anyrange.
+DETAIL:  A result of type anyrange requires at least one input of type anyrange or anymultirange.
 --should fail
 create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
   as $$ select $1, '[1,10]' $$ language sql;
 ERROR:  cannot determine result data type
-DETAIL:  A result of type anyrange requires at least one input of type anyrange.
+DETAIL:  A result of type anyrange requires at least one input of type anyrange or anymultirange.
index 192445878d21d823a02e8ef7e7a4d5f702c1cb99..d9ce961be2bb104e3377053fb8215026b9d87573 100644 (file)
@@ -92,6 +92,7 @@ num_exp_sqrt|t
 num_exp_sub|t
 num_input_test|f
 num_result|f
+nummultirange_test|t
 numrange_test|t
 onek|t
 onek2|t
@@ -172,6 +173,7 @@ quad_poly_tbl|t
 radix_text_tbl|t
 ramp|f
 real_city|f
+reservations|f
 road|t
 shighway|t
 slow_emp4000|f
index 13567ddf84b4d5f2a1c4c81a22f283b05522c7d6..0c74dc96a870b11564419fb4f5c6e995f9d4ca45 100644 (file)
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
 FROM pg_type as p1
 WHERE p1.typnamespace = 0 OR
     (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
-    (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+    (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
     NOT p1.typisdefined OR
     (p1.typalign not in ('c', 's', 'i', 'd')) OR
     (p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -196,18 +196,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
 -----+---------+-----+---------
 (0 rows)
 
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
 SELECT DISTINCT typtype, typinput
 FROM pg_type AS p1
 WHERE p1.typtype not in ('b', 'p')
 ORDER BY 1;
- typtype | typinput  
----------+-----------
+ typtype |   typinput    
+---------+---------------
  c       | record_in
  d       | domain_in
  e       | enum_in
+ m       | multirange_in
  r       | range_in
-(4 rows)
+(5 rows)
 
 -- Check for bogus typoutput routines
 -- As of 8.0, this check finds refcursor, which is borrowing
@@ -241,17 +242,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
 -----+---------+-----+---------
 (0 rows)
 
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
 SELECT DISTINCT typtype, typoutput
 FROM pg_type AS p1
 WHERE p1.typtype not in ('b', 'd', 'p')
 ORDER BY 1;
- typtype | typoutput  
----------+------------
+ typtype |   typoutput    
+---------+----------------
  c       | record_out
  e       | enum_out
+ m       | multirange_out
  r       | range_out
-(3 rows)
+(4 rows)
 
 -- Domains should have same typoutput as their base types
 SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -319,18 +321,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
 -----+---------+-----+---------
 (0 rows)
 
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
 SELECT DISTINCT typtype, typreceive
 FROM pg_type AS p1
 WHERE p1.typtype not in ('b', 'p')
 ORDER BY 1;
- typtype | typreceive  
----------+-------------
+ typtype |   typreceive    
+---------+-----------------
  c       | record_recv
  d       | domain_recv
  e       | enum_recv
+ m       | multirange_recv
  r       | range_recv
-(4 rows)
+(5 rows)
 
 -- Check for bogus typsend routines
 -- As of 7.4, this check finds refcursor, which is borrowing
@@ -364,17 +367,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
 -----+---------+-----+---------
 (0 rows)
 
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
 SELECT DISTINCT typtype, typsend
 FROM pg_type AS p1
 WHERE p1.typtype not in ('b', 'd', 'p')
 ORDER BY 1;
- typtype |   typsend   
----------+-------------
+ typtype |     typsend     
+---------+-----------------
  c       | record_send
  e       | enum_send
+ m       | multirange_send
  r       | range_send
-(3 rows)
+(4 rows)
 
 -- Domains should have same typsend as their base types
 SELECT p1.oid, p1.typname, p2.oid, p2.typname
@@ -660,3 +664,11 @@ WHERE pronargs != 2
 ----------+------------+---------
 (0 rows)
 
+-- every range should have a valid multirange
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
+ rngtypid | rngsubtype | rngmultitypid 
+----------+------------+---------------
+(0 rows)
+
index ae89ed7f0b40bd0a0213bdfef8b259074477220c..e0e1ef71dd790df542dc28ba7ecc5f810d58f45a 100644 (file)
@@ -19,15 +19,17 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
 # The second group of parallel tests
 # strings depends on char, varchar and text
 # numerology depends on int2, int4, int8, float4, float8
+# multirangetypes depends on rangetypes
+# multirangetypes shouldn't be in the one group with type_sanity
 # ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes xid
+test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
 
 # ----------
 # Another group of parallel tests
 # geometry depends on point, lseg, box, path, polygon and circle
 # horology depends on interval, timetz, timestamp, timestamptz
 # ----------
-test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions unicode
+test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions unicode xid
 
 # ----------
 # These four each depend on the previous one
index 525bdc804f61286dd4baccf627ef8c8dea0e8fe0..081fce32e75b616e55eee9f515478a03ad6624b7 100644 (file)
@@ -20,6 +20,7 @@ test: uuid
 test: enum
 test: money
 test: rangetypes
+test: multirangetypes
 test: pg_lsn
 test: regproc
 test: strings
index 681cc92ac20c6411ba6f4c34f9d0c44cb4351656..0292dedd15ae5076d170ff0e9ba8aa212954e172 100644 (file)
@@ -227,6 +227,16 @@ FROM   (VALUES (int4range(10, 20)), (int4range(23, 43)),
 WHERE  hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
        OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
 
+SELECT v as value, hash_multirange(v)::bit(32) as standard,
+          hash_multirange_extended(v, 0)::bit(32) as extended0,
+          hash_multirange_extended(v, 1)::bit(32) as extended1
+FROM   (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
+         ('{[5675, 550273)}'::int4multirange),
+                ('{[550274, 1550274)}'::int4multirange),
+                ('{[1550275, 208112489)}'::int4multirange)) x(v)
+WHERE  hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
+       OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
+
 CREATE TYPE hash_test_t1 AS (a int, b text);
 SELECT v as value, hash_record(v)::bit(32) as standard,
        hash_record_extended(v, 0)::bit(32) as extended0,
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644 (file)
index 0000000..9be26f1
--- /dev/null
@@ -0,0 +1,677 @@
+-- Tests for multirange data types.
+
+--
+-- test input parser
+--
+
+-- negative tests; should fail
+select ''::textmultirange;
+select '{,}'::textmultirange;
+select '{[a,c),}'::textmultirange;
+select '{,[a,c)}'::textmultirange;
+select '{-[a,z)}'::textmultirange;
+select '{[a,z) - }'::textmultirange;
+select '{(",a)}'::textmultirange;
+select '{(,,a)}'::textmultirange;
+select '{(),a)}'::textmultirange;
+select '{(a,))}'::textmultirange;
+select '{(],a)}'::textmultirange;
+select '{(a,])}'::textmultirange;
+select '{[z,a]}'::textmultirange;
+
+-- should succeed
+select '{}'::textmultirange;
+select '  {}  '::textmultirange;
+select ' { empty, empty }  '::textmultirange;
+select ' {( " a " " a ", " z " " z " )  }'::textmultirange;
+select '{(,z)}'::textmultirange;
+select '{(a,)}'::textmultirange;
+select '{[,z]}'::textmultirange;
+select '{[a,]}'::textmultirange;
+select '{(,)}'::textmultirange;
+select '{[ , ]}'::textmultirange;
+select '{["",""]}'::textmultirange;
+select '{[",",","]}'::textmultirange;
+select '{["\\","\\"]}'::textmultirange;
+select '{["""","\""]}'::textmultirange;
+select '{(\\,a)}'::textmultirange;
+select '{((,z)}'::textmultirange;
+select '{([,z)}'::textmultirange;
+select '{(!,()}'::textmultirange;
+select '{(!,[)}'::textmultirange;
+select '{[a,a]}'::textmultirange;
+select '{[a,a],[a,b]}'::textmultirange;
+select '{[a,b), [b,e]}'::textmultirange;
+select '{[a,d), [b,f]}'::textmultirange;
+select '{[a,a],[b,b]}'::textmultirange;
+-- without canonicalization, we can't join these:
+select '{[a,a], [b,b]}'::textmultirange;
+-- with canonicalization, we can join these:
+select '{[1,2], [3,4]}'::int4multirange;
+select '{[a,a], [b,b], [c,c]}'::textmultirange;
+select '{[a,d], [b,e]}'::textmultirange;
+select '{[a,d), [d,e)}'::textmultirange;
+-- these are allowed but normalize to empty:
+select '{[a,a)}'::textmultirange;
+select '{(a,a]}'::textmultirange;
+select '{(a,a)}'::textmultirange;
+
+--
+-- test the constructor
+---
+select textmultirange();
+select textmultirange(textrange('a', 'c'));
+select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
+select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
+
+--
+-- test casts, both a built-in range type and a user-defined one:
+--
+select 'empty'::int4range::int4multirange;
+select int4range(1, 3)::int4multirange;
+select int4range(1, null)::int4multirange;
+select int4range(null, null)::int4multirange;
+select 'empty'::textrange::textmultirange;
+select textrange('a', 'c')::textmultirange;
+select textrange('a', null)::textmultirange;
+select textrange(null, null)::textmultirange;
+
+--
+-- create some test data and test the operators
+--
+
+CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
+CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
+
+INSERT INTO nummultirange_test VALUES('{}');
+INSERT INTO nummultirange_test VALUES('{[,)}');
+INSERT INTO nummultirange_test VALUES('{[3,]}');
+INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
+INSERT INTO nummultirange_test VALUES('{[, 5)}');
+INSERT INTO nummultirange_test VALUES(nummultirange());
+INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test VALUES('{empty}');
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
+INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
+
+SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
+SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
+
+SELECT * FROM nummultirange_test WHERE nmr = '{}';
+SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
+SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
+SELECT * FROM nummultirange_test WHERE nmr < '{}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{}';
+SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{}';
+SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
+SELECT * FROM nummultirange_test WHERE nmr > '{}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
+SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{}';
+SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
+
+select nummultirange(numrange(2.0, 1.0));
+select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
+
+analyze nummultirange_test;
+
+-- overlaps
+SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
+SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
+
+-- mr contains x
+SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
+SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
+SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
+SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
+SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
+SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
+
+-- x is contained by mr
+SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
+SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
+SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
+SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
+SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
+SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
+
+-- overlaps
+SELECT 'empty'::numrange && nummultirange();
+SELECT 'empty'::numrange && nummultirange(numrange(1,2));
+SELECT nummultirange() && 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
+SELECT nummultirange() && nummultirange();
+SELECT nummultirange() && nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) && nummultirange();
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
+SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
+SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
+
+-- contains
+SELECT nummultirange() @> nummultirange();
+SELECT nummultirange() @> 'empty'::numrange;
+SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
+SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
+SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
+SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
+SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
+SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
+SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
+SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
+SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
+SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
+SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
+SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
+
+-- is contained by
+SELECT nummultirange() <@ nummultirange();
+SELECT 'empty'::numrange <@ nummultirange();
+SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
+SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
+SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
+SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
+SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
+SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
+SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
+SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
+SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
+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;
+
+-- overleft
+SELECT 'empty'::numrange &< nummultirange();
+SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
+SELECT nummultirange() &< 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
+SELECT nummultirange() &< nummultirange();
+SELECT nummultirange(numrange(1,2)) &< nummultirange();
+SELECT nummultirange() &< nummultirange(numrange(1,2));
+SELECT numrange(6,7) &< nummultirange(numrange(3,4));
+SELECT numrange(1,2) &< nummultirange(numrange(3,4));
+SELECT numrange(1,4) &< nummultirange(numrange(3,4));
+SELECT numrange(1,6) &< nummultirange(numrange(3,4));
+SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
+SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
+SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
+
+-- overright
+SELECT nummultirange() &> 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
+SELECT 'empty'::numrange &> nummultirange();
+SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
+SELECT nummultirange() &> nummultirange();
+SELECT nummultirange() &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) &> nummultirange();
+SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
+SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
+SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
+SELECT numrange(3,4) &> nummultirange(numrange(6,7));
+SELECT numrange(3,4) &> nummultirange(numrange(1,2));
+SELECT numrange(3,4) &> nummultirange(numrange(1,4));
+SELECT numrange(3,4) &> nummultirange(numrange(1,6));
+SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
+SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
+
+-- meets
+SELECT 'empty'::numrange -|- nummultirange();
+SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
+SELECT nummultirange() -|- 'empty'::numrange;
+SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
+SELECT nummultirange() -|- nummultirange();
+SELECT nummultirange(numrange(1,2)) -|- nummultirange();
+SELECT nummultirange() -|- nummultirange(numrange(1,2));
+SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
+SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
+SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
+SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
+
+-- strictly left
+select 'empty'::numrange << nummultirange();
+select numrange(1,2) << nummultirange();
+select numrange(1,2) << nummultirange(numrange(3,4));
+select numrange(1,2) << nummultirange(numrange(0,4));
+select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
+select nummultirange() << 'empty'::numrange;
+select nummultirange() << numrange(1,2);
+select nummultirange(numrange(3,4)) << numrange(3,6);
+select nummultirange(numrange(0,2)) << numrange(3,6);
+select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
+select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
+select nummultirange() << nummultirange();
+select nummultirange() << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange();
+select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
+select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
+select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
+
+-- strictly right
+select nummultirange() >> 'empty'::numrange;
+select nummultirange() >> numrange(1,2);
+select nummultirange(numrange(3,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4)) >> numrange(1,2);
+select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
+select 'empty'::numrange >> nummultirange();
+select numrange(1,2) >> nummultirange();
+select numrange(3,6) >> nummultirange(numrange(3,4));
+select numrange(3,6) >> nummultirange(numrange(0,2));
+select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
+select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
+select nummultirange() >> nummultirange();
+select nummultirange(numrange(1,2)) >> nummultirange();
+select nummultirange() >> nummultirange(numrange(1,2));
+select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
+select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
+
+-- union
+SELECT nummultirange() + nummultirange();
+SELECT nummultirange() + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange();
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
+
+-- merge
+SELECT range_merge(nummultirange());
+SELECT range_merge(nummultirange(numrange(1,2)));
+SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
+
+-- minus
+SELECT nummultirange() - nummultirange();
+SELECT nummultirange() - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange();
+SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
+SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
+SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
+SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
+SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
+
+-- intersection
+SELECT nummultirange() * nummultirange();
+SELECT nummultirange() * nummultirange(numrange(1,2));
+SELECT nummultirange(numrange(1,2)) * nummultirange();
+SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
+SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
+SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
+SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
+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;
+
+--
+-- range_agg function
+--
+create table reservations ( room_id integer not null, booked_during daterange );
+insert into reservations values
+-- 1: has a meets and a gap
+(1, daterange('2018-07-01', '2018-07-07')),
+(1, daterange('2018-07-07', '2018-07-14')),
+(1, daterange('2018-07-20', '2018-07-22')),
+-- 2: just a single row
+(2, daterange('2018-07-01', '2018-07-03')),
+-- 3: one null range
+(3, NULL),
+-- 4: two null ranges
+(4, NULL),
+(4, NULL),
+-- 5: a null range and a non-null range
+(5, NULL),
+(5, daterange('2018-07-01', '2018-07-03')),
+-- 6: has overlap
+(6, daterange('2018-07-01', '2018-07-07')),
+(6, daterange('2018-07-05', '2018-07-10')),
+-- 7: two ranges that meet: no gap or overlap
+(7, daterange('2018-07-01', '2018-07-07')),
+(7, daterange('2018-07-07', '2018-07-14')),
+-- 8: an empty range
+(8, 'empty'::daterange)
+;
+SELECT   room_id, range_agg(booked_during)
+FROM     reservations
+GROUP BY room_id
+ORDER BY room_id;
+
+-- range_agg on a custom range type too
+SELECT  range_agg(r)
+FROM    (VALUES
+          ('[a,c]'::textrange),
+          ('[b,b]'::textrange),
+          ('[c,f]'::textrange),
+          ('[g,h)'::textrange),
+          ('[h,j)'::textrange)
+        ) t(r);
+
+select range_intersect_agg(nmr) from nummultirange_test;
+select range_intersect_agg(nmr) from nummultirange_test where false;
+-- test with just one input:
+select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
+select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
+
+create table nummultirange_test2(nmr nummultirange);
+create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
+
+INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
+INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
+INSERT INTO nummultirange_test2 VALUES('{}');
+
+select * from nummultirange_test2 where nmr = '{}';
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
+select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
+
+set enable_nestloop=t;
+set enable_hashjoin=f;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=t;
+set enable_mergejoin=f;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+set enable_nestloop=f;
+set enable_hashjoin=f;
+set enable_mergejoin=t;
+select * from nummultirange_test natural join nummultirange_test2 order by nmr;
+
+set enable_nestloop to default;
+set enable_hashjoin to default;
+set enable_mergejoin to default;
+
+DROP TABLE nummultirange_test2;
+
+--
+-- Test user-defined multirange of floats
+--
+
+select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
+create table float8multirange_test(f8mr float8multirange, i int);
+insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
+select * from float8multirange_test;
+drop table float8multirange_test;
+
+--
+-- Test multirange types over domains
+--
+
+create domain mydomain as int4;
+create type mydomainrange as range(subtype=mydomain);
+select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
+drop domain mydomain cascade;
+
+--
+-- Test domains over multirange types
+--
+
+create domain restrictedmultirange as int4multirange check (upper(value) < 10);
+select '{[4,5)}'::restrictedmultirange @> 7;
+select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
+drop domain restrictedmultirange;
+
+---
+-- Check automatic naming of multiranges
+---
+
+create type intr as range(subtype=int);
+select intr_multirange(intr(1,10));
+drop type intr;
+create type intmultirange as (x int, y int);
+create type intrange as range(subtype=int); -- should fail
+drop type intmultirange;
+create type intr_multirange as (x int, y int);
+create type intr as range(subtype=int); -- should fail
+drop type intr_multirange;
+
+--
+-- Test multiple multirange types over the same subtype and manual naming of
+-- the multirange type.
+--
+
+-- should fail
+create type textrange1 as range(subtype=text, multirange_type_name=int, collation="C");
+-- should pass
+create type textrange1 as range(subtype=text, multirange_type_name=multirange_of_text, collation="C");
+-- should pass, because existing _textrange1 is automatically renamed
+create type textrange2 as range(subtype=text, multirange_type_name=_textrange1, collation="C");
+
+select multirange_of_text(textrange2('a','Z'));  -- should fail
+select multirange_of_text(textrange1('a','Z')) @> 'b'::text;
+select _textrange1(textrange2('a','z')) @> 'b'::text;
+
+drop type textrange1;
+drop type textrange2;
+
+--
+-- Test polymorphic type system
+--
+
+create function anyarray_anymultirange_func(a anyarray, r anymultirange)
+  returns anyelement as 'select $1[1] + lower($2);' language sql;
+
+select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
+
+-- should fail
+select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
+
+drop function anyarray_anymultirange_func(anyarray, anymultirange);
+
+-- should fail
+create function bogus_func(anyelement)
+  returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+-- should fail
+create function bogus_func(int)
+  returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
+
+create function range_add_bounds(anymultirange)
+  returns anyelement as 'select lower($1) + upper($1)' language sql;
+
+select range_add_bounds(int4multirange(int4range(1, 17)));
+select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
+
+create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
+  as $$ select upper($1) + $2[1] $$
+  language sql;
+
+select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
+select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]);  -- match failure
+
+create function anycompatiblearray_anycompatiblemultirange_func(a anycompatiblearray, mr anycompatiblemultirange)
+  returns anycompatible as 'select $1[1] + lower($2);' language sql;
+
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(int4range(10,20)));
+
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(numrange(10,20)));
+
+-- should fail
+select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1.1,2], multirange(int4range(10,20)));
+
+drop function anycompatiblearray_anycompatiblemultirange_func(anycompatiblearray, anycompatiblemultirange);
+
+create function anycompatiblerange_anycompatiblemultirange_func(r anycompatiblerange, mr anycompatiblemultirange)
+  returns anycompatible as 'select lower($1) + lower($2);' language sql;
+
+select anycompatiblerange_anycompatiblemultirange_func(int4range(1,2), multirange(int4range(10,20)));
+
+-- should fail
+select anycompatiblerange_anycompatiblemultirange_func(numrange(1,2), multirange(int4range(10,20)));
+
+drop function anycompatiblerange_anycompatiblemultirange_func(anycompatiblerange, anycompatiblemultirange);
+
+-- should fail
+create function bogus_func(anycompatible)
+  returns anycompatiblerange as 'select int4range(1,10)' language sql;
+
+--
+-- Arrays of multiranges
+--
+
+select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
+
+create table i8mr_array (f1 int, f2 int8multirange[]);
+insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
+select * from i8mr_array;
+drop table i8mr_array;
+
+--
+-- Multiranges of arrays
+--
+
+select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
+select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2]));  -- fail
+
+select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
+
+--
+-- Ranges of composites
+--
+
+create type two_ints as (a int, b int);
+create type two_ints_range as range (subtype = two_ints);
+
+-- with force_parallel_mode on, this exercises tqueue.c's range remapping
+select *, row_to_json(upper(t)) as u from
+  (values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
+          (two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
+
+drop type two_ints cascade;
+
+--
+-- Check behavior when subtype lacks a hash function
+--
+
+set enable_sort = off;  -- try to make it pick a hash setop implementation
+
+select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
+
+reset enable_sort;
+
+--
+-- OUT/INOUT/TABLE functions
+--
+
+-- infer anymultirange from anymultirange
+create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
+  as $$ select $1, 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyarray from anymultirange
+create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
+  as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
+
+-- infer anyrange from anymultirange
+create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
+  as $$ select range_merge($1), 'foo'::text $$ language sql;
+select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
+
+-- infer anymultirange from anyrange
+create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
+  as $$ select multirange($1), 'foo'::text $$ language sql;
+
+select * from mr_outparam_succeed4(int4range(1,2));
+
+-- infer anyelement from anymultirange
+create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
+  as $$ select upper($1), $1 $$ language sql;
+
+select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
+
+-- infer anyelement+anymultirange from anyelement+anymultirange
+create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
+  as $$ select $1, $2 $$ language sql;
+
+select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
+
+-- use anymultirange in plpgsql
+create function mr_polymorphic(i anyrange) returns anymultirange
+  as $$ begin return multirange($1); end; $$ language plpgsql;
+select mr_polymorphic(int4range(1, 4));
+
+-- should fail
+create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
+  as $$ select '[1,10]', 'foo' $$ language sql;
+
+--should fail
+create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
+  as $$ select $1, '[1,10]' $$ language sql;
+
+--should fail
+create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
+  as $$ select $1, '[1,10]' $$ language sql;
index 4189a5a4e09730cbf1c008a766b63b133e473128..bbd3834b6345b0111faa95808f3b129d5a548c0c 100644 (file)
@@ -42,6 +42,10 @@ begin
     if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
     then return true; end if;
   end if;
+  if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
+    if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
+    then return true; end if;
+  end if;
   return false;
 end
 $$ language plpgsql strict stable;
@@ -166,7 +170,8 @@ WHERE p1.oid < p2.oid AND
 -- need to be modified whenever new pairs of types are made binary-equivalent,
 -- or when new polymorphic built-in functions are added!
 -- Note: ignore aggregate functions here, since they all point to the same
--- dummy built-in function.  Likewise, ignore range constructor functions.
+-- dummy built-in function.  Likewise, ignore range and multirange constructor
+-- functions.
 
 SELECT DISTINCT p1.prorettype::regtype, p2.prorettype::regtype
 FROM pg_proc AS p1, pg_proc AS p2
@@ -176,6 +181,8 @@ WHERE p1.oid != p2.oid AND
     p1.prokind != 'a' AND p2.prokind != 'a' AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
+    p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+    p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
     (p1.prorettype < p2.prorettype)
 ORDER BY 1, 2;
 
@@ -187,6 +194,8 @@ WHERE p1.oid != p2.oid AND
     p1.prokind != 'a' AND p2.prokind != 'a' AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
+    p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+    p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
     (p1.proargtypes[0] < p2.proargtypes[0])
 ORDER BY 1, 2;
 
@@ -198,6 +207,8 @@ WHERE p1.oid != p2.oid AND
     p1.prokind != 'a' AND p2.prokind != 'a' AND
     p1.prosrc NOT LIKE E'range\\_constructor_' AND
     p2.prosrc NOT LIKE E'range\\_constructor_' AND
+    p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
+    p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
     (p1.proargtypes[1] < p2.proargtypes[1])
 ORDER BY 1, 2;
 
@@ -281,16 +292,19 @@ WHERE p1.prorettype IN
      'anyarray'::regtype = ANY (p1.proargtypes) OR
      'anynonarray'::regtype = ANY (p1.proargtypes) OR
      'anyenum'::regtype = ANY (p1.proargtypes) OR
-     'anyrange'::regtype = ANY (p1.proargtypes))
+     'anyrange'::regtype = ANY (p1.proargtypes) OR
+     'anymultirange'::regtype = ANY (p1.proargtypes))
 ORDER BY 2;
 
--- anyrange is tighter than the rest, can only resolve from anyrange input
+-- anyrange and anymultirange are tighter than the rest, can only resolve
+-- from each other
 
 SELECT p1.oid, p1.proname
 FROM pg_proc as p1
-WHERE p1.prorettype = 'anyrange'::regtype
+WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
   AND NOT
-     'anyrange'::regtype = ANY (p1.proargtypes)
+    ('anyrange'::regtype = ANY (p1.proargtypes) OR
+      'anymultirange'::regtype = ANY (p1.proargtypes))
 ORDER BY 2;
 
 -- similarly for the anycompatible family
index ff517fea41a29459e144b41fa60dda94b3cc6ec7..891b023ada09ee6b2e6c44cc0028a9f8ea8d2cfa 100644 (file)
@@ -71,6 +71,16 @@ select polyf(int4range(42, 49), 11, 4.5) as fail;  -- range type doesn't fit
 
 drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
 
+create function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
+  select array[lower(x), upper(x), y, z]
+$$ language sql;
+
+select polyf(multirange(int4range(42, 49)), 11, 2::smallint) as int, polyf(multirange(float8range(4.5, 7.8)), 7.8, 11::real) as num;
+
+select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail;  -- range type doesn't fit
+
+drop function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible);
+
 -- fail, can't infer type:
 create function polyf(x anycompatible) returns anycompatiblerange as $$
   select array[x + 1, x + 2]
@@ -84,6 +94,19 @@ select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8),
 
 drop function polyf(x anycompatiblerange, y anycompatiblearray);
 
+-- fail, can't infer type:
+create function polyf(x anycompatible) returns anycompatiblemultirange as $$
+  select array[x + 1, x + 2]
+$$ language sql;
+
+create function polyf(x anycompatiblemultirange, y anycompatiblearray) returns anycompatiblemultirange as $$
+  select x
+$$ language sql;
+
+select polyf(multirange(int4range(42, 49)), array[11]) as int, polyf(multirange(float8range(4.5, 7.8)), array[7]) as num;
+
+drop function polyf(x anycompatiblemultirange, y anycompatiblearray);
+
 create function polyf(a anyelement, b anyarray,
                       c anycompatible, d anycompatible,
                       OUT x anyarray, OUT y anycompatiblearray)
@@ -997,6 +1020,35 @@ returns anycompatiblerange as $$
   select $1
 $$ language sql;
 
+create function anyctest(anycompatible, anycompatiblemultirange)
+returns anycompatiblemultirange as $$
+  select $2
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(11, multirange(int4range(4,7))) x;
+select x, pg_typeof(x) from anyctest(11, multirange(numrange(4,7))) x;
+select x, pg_typeof(x) from anyctest(11, 12) x;  -- fail
+select x, pg_typeof(x) from anyctest(11.2, multirange(int4range(4,7))) x;  -- fail
+select x, pg_typeof(x) from anyctest(11.2, '{[4,7)}') x;  -- fail
+
+drop function anyctest(anycompatible, anycompatiblemultirange);
+
+create function anyctest(anycompatiblemultirange, anycompatiblemultirange)
+returns anycompatible as $$
+  select lower($1) + upper($2)
+$$ language sql;
+
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(int4range(4,7))) x;
+select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(numrange(4,7))) x; -- fail
+
+drop function anyctest(anycompatiblemultirange, anycompatiblemultirange);
+
+-- fail, can't infer result type:
+create function anyctest(anycompatible)
+returns anycompatiblemultirange as $$
+  select $1
+$$ language sql;
+
 create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
 returns anycompatiblearray as $$
   select array[$1, $2]
index 4048b1d185445abcdc0e2aab74702898f5c6043b..b69efede3ae4977e322c1349c02a5dc2f74b7cc4 100644 (file)
@@ -118,6 +118,12 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
 select numrange(1.0, 2.0) * numrange(1.5, 3.0);
 select numrange(1.0, 2.0) * numrange(2.5, 3.0);
 
+select range_intersect_agg(nr) from numrange_test;
+select range_intersect_agg(nr) from numrange_test where false;
+select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
+
+analyze numrange_test;
+
 create table numrange_test2(nr numrange);
 create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
 
@@ -528,6 +534,7 @@ reset enable_sort;
 -- OUT/INOUT/TABLE functions
 --
 
+-- infer anyrange from anyrange
 create function outparam_succeed(i anyrange, out r anyrange, out t text)
   as $$ select $1, 'foo'::text $$ language sql;
 
@@ -539,6 +546,13 @@ create function outparam2_succeed(r anyrange, out lu anyarray, out ul anyarray)
 
 select * from outparam2_succeed(int4range(1,11));
 
+-- infer anyarray from anyrange
+create function outparam_succeed2(i anyrange, out r anyarray, out t text)
+  as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
+
+select * from outparam_succeed2(int4range(int4range(1,2)));
+
+-- infer anyelement from anyrange
 create function inoutparam_succeed(out i anyelement, inout r anyrange)
   as $$ select upper($1), $1 $$ language sql;
 
index 8c6e614f20a1ab44a7f1e359ebbca3f046f22421..4739aca84a39067444583a681f535a209b75901e 100644 (file)
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
 FROM pg_type as p1
 WHERE p1.typnamespace = 0 OR
     (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
-    (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
+    (p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
     NOT p1.typisdefined OR
     (p1.typalign not in ('c', 's', 'i', 'd')) OR
     (p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -154,7 +154,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
 FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
 
--- Composites, domains, enums, ranges should all use the same input routines
+-- Composites, domains, enums, multiranges, ranges should all use the same input routines
 SELECT DISTINCT typtype, typinput
 FROM pg_type AS p1
 WHERE p1.typtype not in ('b', 'p')
@@ -183,7 +183,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
 FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
 
--- Composites, enums, ranges should all use the same output routines
+-- Composites, enums, multiranges, ranges should all use the same output routines
 SELECT DISTINCT typtype, typoutput
 FROM pg_type AS p1
 WHERE p1.typtype not in ('b', 'd', 'p')
@@ -235,7 +235,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
 FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
 
--- Composites, domains, enums, ranges should all use the same receive routines
+-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
 SELECT DISTINCT typtype, typreceive
 FROM pg_type AS p1
 WHERE p1.typtype not in ('b', 'p')
@@ -264,7 +264,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
 FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
 
--- Composites, enums, ranges should all use the same send routines
+-- Composites, enums, multiranges, ranges should all use the same send routines
 SELECT DISTINCT typtype, typsend
 FROM pg_type AS p1
 WHERE p1.typtype not in ('b', 'd', 'p')
@@ -489,3 +489,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
 WHERE pronargs != 2
     OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
     OR prorettype != 'pg_catalog.float8'::regtype;
+
+-- every range should have a valid multirange
+
+SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
+FROM pg_range p1
+WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
index a9dca717a6d26a4f51ef2b8f80bb1cd942e0a3a1..bca37c536eef1d88db88bd69d25116df284370db 100644 (file)
@@ -1395,6 +1395,9 @@ MultiXactMember
 MultiXactOffset
 MultiXactStateData
 MultiXactStatus
+MultirangeIOData
+MultirangeParseState
+MultirangeType
 NDBOX
 NODE
 NUMCacheEntry
@@ -3139,6 +3142,7 @@ mp_small
 mp_usmall
 mp_word
 mpz_t
+multirange_bsearch_comparison
 mxact
 mxtruncinfo
 needs_fmgr_hook_type