public inbox for [email protected]
help / color / mirror / Atom feedFrom: Alvaro Herrera <[email protected]>
Subject: [PATCH v12 1/5] Paul's main patch
Date: Mon, 9 Mar 2020 17:20:04 -0300
with minor changes by Alvaro
---
doc/src/sgml/extend.sgml | 28 +-
doc/src/sgml/func.sgml | 326 ++-
doc/src/sgml/rangetypes.sgml | 47 +-
src/backend/catalog/pg_proc.c | 17 +-
src/backend/catalog/pg_range.c | 10 +-
src/backend/catalog/pg_type.c | 119 +-
src/backend/commands/typecmds.c | 314 ++-
src/backend/parser/parse_coerce.c | 309 ++-
src/backend/utils/adt/Makefile | 1 +
src/backend/utils/adt/multirangetypes.c | 2169 +++++++++++++++
src/backend/utils/adt/pg_upgrade_support.c | 22 +
src/backend/utils/adt/pseudotypes.c | 25 +
src/backend/utils/adt/rangetypes.c | 349 ++-
src/backend/utils/cache/lsyscache.c | 62 +-
src/backend/utils/cache/syscache.c | 12 +
src/backend/utils/cache/typcache.c | 98 +-
src/backend/utils/fmgr/funcapi.c | 290 +-
src/bin/pg_dump/pg_dump.c | 150 +-
src/bin/pg_dump/pg_dump.h | 1 +
src/include/access/tupmacs.h | 4 +-
src/include/catalog/binary_upgrade.h | 2 +
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_aggregate.dat | 11 +
src/include/catalog/pg_amop.dat | 22 +
src/include/catalog/pg_amproc.dat | 12 +-
src/include/catalog/pg_cast.dat | 13 +
src/include/catalog/pg_opclass.dat | 4 +
src/include/catalog/pg_operator.dat | 169 ++
src/include/catalog/pg_opfamily.dat | 4 +
src/include/catalog/pg_proc.dat | 271 ++
src/include/catalog/pg_range.dat | 15 +-
src/include/catalog/pg_range.h | 5 +-
src/include/catalog/pg_type.dat | 39 +
src/include/catalog/pg_type.h | 7 +-
src/include/commands/typecmds.h | 2 +
src/include/utils/lsyscache.h | 3 +
src/include/utils/multirangetypes.h | 103 +
src/include/utils/rangetypes.h | 29 +-
src/include/utils/syscache.h | 1 +
src/include/utils/typcache.h | 6 +
src/pl/plpgsql/src/pl_comp.c | 6 +
src/test/regress/expected/dependency.out | 1 +
src/test/regress/expected/hash_func.out | 13 +
src/test/regress/expected/multirangetypes.out | 2395 +++++++++++++++++
src/test/regress/expected/opr_sanity.out | 26 +-
src/test/regress/expected/rangetypes.out | 38 +-
src/test/regress/expected/sanity_check.out | 2 +
src/test/regress/expected/type_sanity.out | 46 +-
src/test/regress/parallel_schedule | 3 +-
src/test/regress/serial_schedule | 1 +
src/test/regress/sql/hash_func.sql | 10 +
src/test/regress/sql/multirangetypes.sql | 630 +++++
src/test/regress/sql/opr_sanity.sql | 18 +-
src/test/regress/sql/rangetypes.sql | 13 +
src/test/regress/sql/type_sanity.sql | 16 +-
55 files changed, 8000 insertions(+), 292 deletions(-)
create mode 100644 src/backend/utils/adt/multirangetypes.c
create mode 100644 src/include/utils/multirangetypes.h
create mode 100644 src/test/regress/expected/multirangetypes.out
create mode 100644 src/test/regress/sql/multirangetypes.sql
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index 9ec1af780b..92a1a254ac 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -229,9 +229,9 @@
</indexterm>
<para>
- Five pseudo-types of special interest are <type>anyelement</type>,
+ Six pseudo-types of special interest are <type>anyelement</type>,
<type>anyarray</type>, <type>anynonarray</type>, <type>anyenum</type>,
- and <type>anyrange</type>,
+ <type>anyrange</type>, and <type>anymultirange</type>.
which are collectively called <firstterm>polymorphic types</firstterm>.
Any function declared using these types is said to be
a <firstterm>polymorphic function</firstterm>. A polymorphic function can
@@ -250,15 +250,15 @@
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>, 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.
<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.
@@ -267,6 +267,20 @@
be an enum type.
</para>
+ <para>
+ Similarly, if there are positions declared <type>anyrange</type>
+ and others declared <type>anyelement</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.
+ The type <type>anymultirange</type> accepts a multirange
+ whose contents match the other polymorphic types.
+ That is it must hold ranges matching <type>anyrange</type>
+ and base type elements matching <type>anyelement</type> (if either of
+ those are present). If <type>anyarray</type> is present its elements
+ must match the base type of the <type>anyrange</type> and/or the base
+ type of the ranges in an <type>anymultirange</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
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 323366feb6..cfe2c23e65 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -13546,7 +13546,7 @@ SELECT NULLIF(value, '(none)') ...
<row>
<entry>Operator</entry>
<entry>Description</entry>
- <entry>Example</entry>
+ <entry>Examples</entry>
<entry>Result</entry>
</row>
</thead>
@@ -14017,12 +14017,14 @@ NULL baz</literallayout>(3 rows)</entry>
<title>Range Functions and Operators</title>
<para>
- See <xref linkend="rangetypes"/> for an overview of range types.
+ See <xref linkend="rangetypes"/> for an overview of range and multirange types.
</para>
<para>
<xref linkend="range-operators-table"/> shows the operators
- available for range types.
+ available for range and multirange types.
+ Many of these operators will accept either a range or multirange
+ on either side.
</para>
<table id="range-operators-table">
@@ -14040,134 +14042,215 @@ NULL baz</literallayout>(3 rows)</entry>
<row>
<entry> <literal>=</literal> </entry>
<entry>equal</entry>
- <entry><literal>int4range(1,5) = '[1,4]'::int4range</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,5) = '[1,4]'::int4range
+'{[1,5)}'::int4multirange = '{[1,4]}'::int4multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><></literal> </entry>
<entry>not equal</entry>
- <entry><literal>numrange(1.1,2.2) <> numrange(1.1,2.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">numrange(1.1,2.2) <> numrange(1.1,2.3)
+'{[1.1,2.2)}'::nummultirange <> '{[1.1,2.3)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><</literal> </entry>
<entry>less than</entry>
- <entry><literal>int4range(1,10) < int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry><literallayout class="monospaced">int4range(1,10) < int4range(2,3)
+'{[1,10)}'::int4multirange < '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>></literal> </entry>
<entry>greater than</entry>
- <entry><literal>int4range(1,10) > int4range(1,5)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(1,10) > int4range(1,5)
+'{[1,10)}'::int4multirange > '{[1,5)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><=</literal> </entry>
<entry>less than or equal</entry>
- <entry><literal>numrange(1.1,2.2) <= numrange(1.1,2.2)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) <= numrange(1.1,2.2)
+'{[1.1,2.2)}'::nummultirange <= '{[1.1,2.2)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>=</literal> </entry>
<entry>greater than or equal</entry>
- <entry><literal>numrange(1.1,2.2) >= numrange(1.1,2.0)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) >= numrange(1.1,2.0)
+'{[1.1,2.2)}'::nummultirange >= '{[1.1,2.0)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal>@></literal> </entry>
+ <entry>contains multirange</entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> '{[2,3)}'::int4multirange
+'{[2,4)}'::int4multirange @> '{[2,3)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains range</entry>
- <entry><literal>int4range(2,4) @> int4range(2,3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) @> int4range(2,3)
+'{[2,4)}'::int4multirange @> int4range(2,3)</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>@></literal> </entry>
<entry>contains element</entry>
- <entry><literal>'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">'[2011-01-01,2011-03-01)'::tsrange @> '2011-01-10'::timestamp
+'{[2011-01-01,2011-03-01)}'::tsmultirange @> '2011-01-10'::timestamp</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
+ </row>
+
+ <row>
+ <entry> <literal><@</literal> </entry>
+ <entry>multirange is contained by</entry>
+ <entry>
+ <literallayout class="monospaced">'{[2,4)}'::int4multirange <@ int4range(1,7)
+'{[2,4)}'::int4multirange <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>range is contained by</entry>
- <entry><literal>int4range(2,4) <@ int4range(1,7)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int4range(2,4) <@ int4range(1,7)
+int4range(2,4) <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><@</literal> </entry>
<entry>element is contained by</entry>
- <entry><literal>42 <@ int4range(1,7)</literal></entry>
- <entry><literal>f</literal></entry>
+ <entry>
+ <literallayout class="monospaced">42 <@ int4range(1,7)
+42 <@ '{[1,7)}'::int4multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">f</literallayout></entry>
</row>
<row>
<entry> <literal>&&</literal> </entry>
<entry>overlap (have points in common)</entry>
- <entry><literal>int8range(3,7) && int8range(4,12)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(3,7) && int8range(4,12)
+int8range(3,7) && '{[4,12)}'::int8multirange
+'{[3,7)}'::int8multirange && int8range(4,12)
+'{[3,7)}'::int8multirange && '{[4,12)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal><<</literal> </entry>
<entry>strictly left of</entry>
- <entry><literal>int8range(1,10) << int8range(100,110)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,10) << int8range(100,110)
+int8range(1,10) << '{[100,110)}'::int8multirange
+'{[1,10)}'::int8multirange << int8range(100,110)
+'{[1,10)}'::int8multirange << '{[100,110)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>>></literal> </entry>
<entry>strictly right of</entry>
- <entry><literal>int8range(50,60) >> int8range(20,30)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(50,60) >> int8range(20,30)
+int8range(50,60) >> '{[20,30)}'::int8multirange
+'{[50,60)}'::int8multirange >> int8range(20,30)
+'{[50,60)}'::int8multirange >> '{[20,30)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&<</literal> </entry>
<entry>does not extend to the right of</entry>
- <entry><literal>int8range(1,20) &< int8range(18,20)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(1,20) &< int8range(18,20)
+int8range(1,20) &< '{[18,20)}'::int8multirange
+'{[1,20)}'::int8multirange &< int8range(18,20)
+'{[1,20)}'::int8multirange &< '{[18,20)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>&></literal> </entry>
<entry>does not extend to the left of</entry>
- <entry><literal>int8range(7,20) &> int8range(5,10)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">int8range(7,20) &> int8range(5,10)
+int8range(7,20) &> '{[5,10)}'::int8multirange
+'{[7,20)}'::int8multirange &> int8range(5,10)
+'{[7,20)}'::int8multirange &> '{[5,10)}'::int8multirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>-|-</literal> </entry>
<entry>is adjacent to</entry>
- <entry><literal>numrange(1.1,2.2) -|- numrange(2.2,3.3)</literal></entry>
- <entry><literal>t</literal></entry>
+ <entry>
+ <literallayout class="monospaced">numrange(1.1,2.2) -|- numrange(2.2,3.3)
+numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange
+'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)
+'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literallayout>
+ </entry>
+ <entry><literallayout class="monospaced">t</literallayout></entry>
</row>
<row>
<entry> <literal>+</literal> </entry>
<entry>union</entry>
- <entry><literal>numrange(5,15) + numrange(10,20)</literal></entry>
- <entry><literal>[5,20)</literal></entry>
+ <entry><literallayout class="monospaced">numrange(5,15) + numrange(10,20)
+'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,20)
+{[5,10), [15,20)}</literallayout></entry>
</row>
<row>
<entry> <literal>*</literal> </entry>
<entry>intersection</entry>
- <entry><literal>int8range(5,15) * int8range(10,20)</literal></entry>
- <entry><literal>[10,15)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) * int8range(10,20)
+'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[10,15)
+{[10,15)}</literallayout></entry>
</row>
<row>
<entry> <literal>-</literal> </entry>
<entry>difference</entry>
- <entry><literal>int8range(5,15) - int8range(10,20)</literal></entry>
- <entry><literal>[5,10)</literal></entry>
+ <entry><literallayout class="monospaced">int8range(5,15) - int8range(10,20)
+'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literallayout></entry>
+ <entry><literallayout class="monospaced">[5,10)
+{[5,10), [15,20)}</literallayout></entry>
</row>
</tbody>
@@ -14185,19 +14268,31 @@ NULL baz</literallayout>(3 rows)</entry>
<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 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.
+ 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.
+ available for use with range and multirange types.
</para>
<indexterm>
@@ -14246,6 +14341,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower(numrange(1.1,2.2))</literal></entry>
<entry><literal>1.1</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>lower bound of multirange</entry>
+ <entry><literal>lower('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>1.1</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14257,6 +14363,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper(numrange(1.1,2.2))</literal></entry>
<entry><literal>2.2</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry>multirange's element type</entry>
+ <entry>upper bound of multirange</entry>
+ <entry><literal>upper('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>2.2</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14268,6 +14385,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>isempty(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>isempty</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the multirange empty?</entry>
+ <entry><literal>isempty('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14279,6 +14407,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound inclusive?</entry>
+ <entry><literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14290,6 +14429,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inc(numrange(1.1,2.2))</literal></entry>
<entry><literal>false</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inc</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound inclusive?</entry>
+ <entry><literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal></entry>
+ <entry><literal>false</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14301,6 +14451,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>lower_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>lower_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the lower bound infinite?</entry>
+ <entry><literal>lower_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14312,6 +14473,17 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>upper_inf('(,)'::daterange)</literal></entry>
<entry><literal>true</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>upper_inf</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>boolean</type></entry>
+ <entry>is the upper bound infinite?</entry>
+ <entry><literal>upper_inf('{(,)}'::datemultirange)</literal></entry>
+ <entry><literal>true</literal></entry>
+ </row>
<row>
<entry>
<literal>
@@ -14323,16 +14495,38 @@ NULL baz</literallayout>(3 rows)</entry>
<entry><literal>range_merge('[1,2)'::int4range, '[3,4)'::int4range)</literal></entry>
<entry><literal>[1,4)</literal></entry>
</row>
+ <row>
+ <entry>
+ <literal>
+ <function>range_merge</function>(<type>anymultirange</type>)
+ </literal>
+ </entry>
+ <entry><type>anyrange</type></entry>
+ <entry>the smallest range which includes the entire multirange</entry>
+ <entry><literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal></entry>
+ <entry><literal>[1,4)</literal></entry>
+ </row>
+ <row>
+ <entry>
+ <literal>
+ <function>multirange</function>(<type>anyrange</type>)
+ </literal>
+ </entry>
+ <entry><type>anymultirange</type></entry>
+ <entry>a multirange containing just the given range</entry>
+ <entry><literal>multirange('[1,2)'::int4range)</literal></entry>
+ <entry><literal>{[1,2)}</literal></entry>
+ </row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower</function> and <function>upper</function> functions return null
- if the range is empty or the requested bound is infinite.
+ if the input range/multirange is empty or the requested bound is infinite.
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>
@@ -14651,6 +14845,44 @@ NULL baz</literallayout>(3 rows)</entry>
</entry>
</row>
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_agg</primary>
+ </indexterm>
+ <function>
+ range_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>Yes</entry>
+ <entry>union of the non-null input values</entry>
+ </row>
+
+ <row>
+ <entry>
+ <indexterm>
+ <primary>range_intersect_agg</primary>
+ </indexterm>
+ <function>
+ range_intersect_agg(<replaceable class="parameter">expression</replaceable>)
+ </function>
+ </entry>
+ <entry>
+ <type>anyrange</type>
+ </entry>
+ <entry>
+ <type>multirange</type> of the argument range type
+ </entry>
+ <entry>No</entry>
+ <entry>intersection of the non-null input values</entry>
+ </row>
+
<row>
<entry>
<indexterm>
diff --git a/doc/src/sgml/rangetypes.sgml b/doc/src/sgml/rangetypes.sgml
index b75fb3a392..c8784bdce3 100644
--- a/doc/src/sgml/rangetypes.sgml
+++ b/doc/src/sgml/rangetypes.sgml
@@ -27,6 +27,13 @@
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>
@@ -232,10 +239,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 +294,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 +381,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
diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c
index 423fd79d94..fb375cd3bf 100644
--- a/src/backend/catalog/pg_proc.c
+++ b/src/backend/catalog/pg_proc.c
@@ -192,6 +192,7 @@ ProcedureCreate(const char *procedureName,
genericInParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericInParam = true;
anyrangeInParam = true;
break;
@@ -219,6 +220,7 @@ ProcedureCreate(const char *procedureName,
genericOutParam = true;
break;
case ANYRANGEOID:
+ case ANYMULTIRANGEOID:
genericOutParam = true;
anyrangeOutParam = true;
break;
@@ -231,10 +233,10 @@ ProcedureCreate(const char *procedureName,
/*
* Do not allow polymorphic return type unless at least one input argument
- * is polymorphic. ANYRANGE return type is even stricter: must have an
- * ANYRANGE input (since we can't deduce the specific range type from
- * ANYELEMENT). Also, do not allow return type INTERNAL unless at least
- * one input argument is INTERNAL.
+ * is polymorphic. ANYRANGE and ANYMULTIRANGE return types are even
+ * stricter: must have an ANYRANGE or ANYMULTIRANGE input (since we can't
+ * deduce the specific range type from ANYELEMENT). Also, do not allow
+ * return type INTERNAL unless at least one input argument is INTERNAL.
*/
if ((IsPolymorphicType(returnType) || genericOutParam)
&& !genericInParam)
@@ -243,12 +245,13 @@ ProcedureCreate(const char *procedureName,
errmsg("cannot determine result data type"),
errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
- if ((returnType == ANYRANGEOID || anyrangeOutParam) &&
- !anyrangeInParam)
+ if ((returnType == ANYRANGEOID ||
+ returnType == ANYMULTIRANGEOID ||
+ anyrangeOutParam) && !anyrangeInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
- errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument.")));
+ errdetail("A function returning \"anyrange\" or \"anymultirange\" must have at least one \"anyrange\" or \"anymultirange\" argument.")));
if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
ereport(ERROR,
diff --git a/src/backend/catalog/pg_range.c b/src/backend/catalog/pg_range.c
index b5bc36c2bd..1217a6ddd0 100644
--- a/src/backend/catalog/pg_range.c
+++ b/src/backend/catalog/pg_range.c
@@ -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;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@@ -54,6 +55,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);
@@ -100,6 +102,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
+ /* 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);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index cd56714968..c359f27c74 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -27,6 +27,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"
@@ -36,6 +37,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;
@@ -809,31 +813,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;
+ char *arr;
- /*
- * 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;
- }
-
- 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\"",
@@ -904,3 +887,91 @@ 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 *mrname;
+ char *rangestr;
+
+ /*
+ * If the range type name contains "range" then change that to
+ * "multirange". Otherwise add "_multirange" to the end. After that,
+ * prepend underscores as needed until we make a name that doesn't collide
+ * with anything...
+ */
+ 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';
+
+ mrname = makeUniqueTypeName(buf, typeNamespace, true);
+ if (mrname == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_OBJECT),
+ errmsg("could not form multirange type name for type \"%s\"",
+ rangeTypeName)));
+
+ return mrname;
+}
+
+/*
+ * 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;
+}
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 8891b1d564..dfb2edd3d0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -105,9 +105,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);
@@ -738,7 +743,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",
@@ -1279,6 +1285,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)
@@ -1287,7 +1298,11 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
+ char *multirangeTypeName;
+ char *multirangeArrayName;
Oid rangeArrayOid;
+ Oid multirangeOid;
+ Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@@ -1304,6 +1319,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,
@@ -1452,8 +1469,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 =
@@ -1491,9 +1510,46 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == InvalidOid || typoid == address.objectId);
typoid = address.objectId;
+ /* Create the multirange that goes with it */
+ multirangeTypeName = makeMultirangeTypeName(typeName, typeNamespace);
+
+ mltrngaddress =
+ TypeCreate(multirangeOid, /* force assignment of this type OID */
+ multirangeTypeName, /* type name */
+ typeNamespace, /* 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, /* 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.
@@ -1534,8 +1590,53 @@ 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 */
+ typeNamespace, /* 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 */
+ 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;
}
@@ -1613,6 +1714,147 @@ 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(¶mModes, 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 functions for a type.
@@ -2045,6 +2287,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
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index 929f758ef4..27bfc4bf8c 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -187,18 +187,20 @@ coerce_type(ParseState *pstate, Node *node,
}
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
- targetTypeId == ANYRANGEOID)
+ targetTypeId == ANYRANGEOID ||
+ targetTypeId == ANYMULTIRANGEOID)
{
/*
* 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 anyarray_in,
- * anyenum_in, or anyrange_in, 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
+ * anyarray_in, anyenum_in, anyrange_in, or anymultirange_in, 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
@@ -1481,6 +1483,8 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid array_typelem;
Oid range_typeid = InvalidOid;
Oid range_typelem;
+ Oid multirange_typeid = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = false;
bool have_anynonarray = false;
bool have_anyenum = false;
@@ -1527,6 +1531,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;
+ }
}
/* Get the element type based on the array type, if we have one */
@@ -1577,6 +1590,41 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* otherwise, they better match */
return false;
}
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+ }
+ else
+ range_typelem = InvalidOid; /* keep compiler quiet */
+
+ /* Get the element type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ multirange_typelem = get_range_multirange_subtype(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 */
+ }
+ 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)
@@ -1680,13 +1728,17 @@ 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 array_typelem;
- Oid range_typelem;
+ Oid range_typelem = InvalidOid;
+ Oid multirange_typelem;
bool have_anyelement = (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
rettype == ANYENUMOID);
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
+ bool have_anyrange = (rettype == ANYRANGEOID);
+ bool have_anymultirange = (rettype == ANYMULTIRANGEOID);
/*
* Loop through the arguments to see if we have any that are polymorphic.
@@ -1744,7 +1796,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
else if (decl_type == ANYRANGEOID)
{
- have_generics = true;
+ have_generics = have_anyrange = true;
if (actual_type == UNKNOWNOID)
{
have_unknowns = true;
@@ -1762,6 +1814,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
+ else if (decl_type == ANYMULTIRANGEOID)
+ {
+ have_generics = have_anymultirange = true;
+ if (actual_type == UNKNOWNOID)
+ {
+ have_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;
+ }
}
/*
@@ -1812,9 +1884,12 @@ 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))
{
- if (range_typeid == ANYRANGEOID && !have_anyelement)
+ if (range_typeid == ANYRANGEOID && !have_anyelement && !have_anymultirange)
{
- /* Special case for ANYRANGE input: okay iff no ANYELEMENT */
+ /*
+ * Special case for ANYRANGE input: okay iff no ANYELEMENT or
+ * ANYMULTIRANGE
+ */
range_typelem = ANYELEMENTOID;
}
else
@@ -1846,6 +1921,71 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(range_typeid),
format_type_be(elem_typeid))));
}
+ else
+ range_typelem = InvalidOid;
+ }
+
+ /* Get the range type based on the multirange type, if we have one */
+ if (OidIsValid(multirange_typeid))
+ {
+ if (multirange_typeid == ANYMULTIRANGEOID && !have_anyrange && !have_anyelement)
+ {
+ /*
+ * Special case for ANYMULTIRANGE input: okay iff no ANYRANGE or
+ * ANYELEMENT
+ */
+ multirange_typelem = ANYRANGEOID;
+ }
+ else
+ {
+ multirange_typelem = get_range_multirange_subtype(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))));
+ }
+
+ /*
+ * Infer the element type too if needed (if there is an ANYMULTIRANGE
+ * and ANYELEMENT, with no ANYRANGE).
+ */
+ if (!OidIsValid(elem_typeid))
+ {
+ elem_typeid = get_range_subtype(range_typeid);
+ }
+ 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))));
+ }
}
if (!OidIsValid(elem_typeid))
@@ -1855,6 +1995,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
+ multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@@ -1927,6 +2068,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;
+ }
}
}
@@ -1958,6 +2110,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 ANYELEMENT use the appropriate argument type */
if (rettype == ANYELEMENTOID ||
rettype == ANYNONARRAYOID ||
@@ -2006,8 +2174,7 @@ resolve_generic_type(Oid declared_type,
}
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
- context_declared_type == ANYENUMOID ||
- context_declared_type == ANYRANGEOID)
+ context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
@@ -2019,11 +2186,37 @@ resolve_generic_type(Oid declared_type,
format_type_be(context_actual_type))));
return array_typeid;
}
+ else if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid range_typelem = get_range_subtype(context_base_type);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_base_type);
+ Oid range_typelem = get_range_subtype(multirange_typelem);
+ Oid array_typeid = get_array_type(range_typelem);
+
+ if (!OidIsValid(array_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(range_typelem))));
+ return array_typeid;
+ }
}
else if (declared_type == ANYELEMENTOID ||
declared_type == ANYNONARRAYOID ||
- declared_type == ANYENUMOID ||
- declared_type == ANYRANGEOID)
+ declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
@@ -2051,6 +2244,19 @@ resolve_generic_type(Oid declared_type,
"anyrange", format_type_be(context_base_type))));
return range_typelem;
}
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ /* Use the element type corresponding to actual type */
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_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(context_base_type))));
+ return multirange_typelem;
+ }
else if (context_declared_type == ANYELEMENTOID ||
context_declared_type == ANYNONARRAYOID ||
context_declared_type == ANYENUMOID)
@@ -2059,6 +2265,74 @@ resolve_generic_type(Oid declared_type,
return context_actual_type;
}
}
+ else if (declared_type == ANYRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typelem = get_range_multirange_subtype(context_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(context_base_type))));
+ return multirange_typelem;
+ }
+ else
+ {
+ /* We can't infer a range from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find range type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
+ else if (declared_type == ANYMULTIRANGEOID)
+ {
+ if (context_declared_type == ANYRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+ Oid multirange_typeid = get_range_multirange(context_base_type);
+
+ if (!OidIsValid(multirange_typeid))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a range type but type %s",
+ "anyrange", format_type_be(context_base_type))));
+ return multirange_typeid;
+ }
+ else if (context_declared_type == ANYMULTIRANGEOID)
+ {
+ Oid context_base_type = getBaseType(context_actual_type);
+
+ if (!OidIsValid(context_base_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("argument declared %s is not a multirange type but type %s",
+ "anymultirange", format_type_be(context_base_type))));
+ return context_base_type;
+ }
+ else
+ {
+ /* We can't infer a multirange from elem types */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find multirange type for data type %s",
+ format_type_be(context_actual_type))));
+ }
+ }
else
{
/* declared_type isn't polymorphic, so return it as-is */
@@ -2173,6 +2447,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)
+ if (type_is_multirange(srctype))
+ return true;
+
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 13efa9338c..7118dd0034 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -58,6 +58,7 @@ OBJS = \
mac.o \
mac8.o \
misc.o \
+ multirangetypes.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
index 0000000000..0c9afd5448
--- /dev/null
+++ b/src/backend/utils/adt/multirangetypes.c
@@ -0,0 +1,2169 @@
+/*-------------------------------------------------------------------------
+ *
+ * multirangetypes.c
+ * I/O functions, operators, and support functions for multirange types.
+ *
+ * The stored (serialized) format of a multirange value is:
+ *
+ * 4 bytes: varlena header
+ * 4 bytes: multirange type's OID
+ * 4 bytes: the number of ranges in the multirange
+ * The range values, each maxaligned.
+ *
+ *
+ * Portions Copyright (c) 1996-2019, 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 "lib/stringinfo.h"
+#include "libpq/pqformat.h"
+#include "miscadmin.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/int8.h"
+#include "utils/lsyscache.h"
+#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
+#include "utils/timestamp.h"
+#include "utils/array.h"
+
+
+/* fn_extra cache entry for one of the range I/O functions */
+typedef struct MultirangeIOData
+{
+ TypeCacheEntry *typcache; /* multirange type's typcache entry */
+ Oid typiofunc; /* range type's I/O function */
+ Oid typioparam; /* range type's I/O parameter */
+ FmgrInfo proc; /* lookup result for typiofunc */
+} 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;
+
+static MultirangeIOData * get_multirange_io_data(FunctionCallInfo fcinfo, Oid rngtypid,
+ 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 either 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 + 2;
+ range_str_copy = palloc0(range_str_len);
+ strlcpy(range_str_copy, 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->proc, 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;
+ Pointer ptr = (char *) multirange;
+ Pointer end = ptr + VARSIZE(multirange);
+ int32 range_count = 0;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_output);
+
+ initStringInfo(&buf);
+
+ appendStringInfoChar(&buf, '{');
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ while (ptr < end)
+ {
+ if (range_count > 0)
+ appendStringInfoChar(&buf, ',');
+ range = (RangeType *) ptr;
+ rangeStr = OutputFunctionCall(&cache->proc, RangeTypePGetDatum(range));
+ appendStringInfoString(&buf, rangeStr);
+ ptr += MAXALIGN(VARSIZE(range));
+ range_count++;
+ }
+
+ appendStringInfoChar(&buf, '}');
+
+ PG_RETURN_CSTRING(buf.data);
+}
+
+/*
+ * Binary representation: The first byte is the flags, then the lower bound
+ * (if present), then the upper bound (if present). Each bound is represented
+ * by a 4-byte length header and the binary representation of that bound (as
+ * returned by a call to the send function for the subtype).
+ */
+
+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;
+ TypeCacheEntry *rangetyp;
+ uint32 range_count;
+ RangeType **ranges;
+ MultirangeType *ret;
+ int i;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_receive);
+ rangetyp = cache->typcache->rngtype;
+
+ range_count = pq_getmsgint(buf, 4);
+ ranges = palloc0(range_count * sizeof(RangeType *));
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 range_len = pq_getmsgint(buf, 4);
+ const char *range_data = pq_getmsgbytes(buf, range_len);
+ StringInfoData range_buf;
+
+ initStringInfo(&range_buf);
+ appendBinaryStringInfo(&range_buf, range_data, range_len);
+
+ ranges[i] = DatumGetRangeTypeP(
+ ReceiveFunctionCall(&cache->proc,
+ &range_buf,
+ cache->typioparam,
+ typmod));
+ pfree(range_buf.data);
+ }
+
+ pq_getmsgend(buf);
+
+ ret = make_multirange(mltrngtypoid, rangetyp, 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;
+ int32 i;
+ MultirangeIOData *cache;
+
+ cache = get_multirange_io_data(fcinfo, mltrngtypoid, IOFunc_send);
+
+ /* construct output */
+ pq_begintypsend(buf);
+
+ pq_sendint32(buf, multirange->rangeCount);
+
+ multirange_deserialize(multirange, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ Datum range = RangeTypePGetDatum(ranges[i]);
+ uint32 range_len;
+ char *range_data;
+
+ range = PointerGetDatum(SendFunctionCall(&cache->proc, range));
+ range_len = VARSIZE(range) - VARHDRSZ;
+ range_data = VARDATA(range);
+
+ pq_sendint32(buf, range_len);
+ pq_sendbytes(buf, range_data, range_len);
+ }
+
+ PG_RETURN_BYTEA_P(pq_endtypsend(buf));
+}
+
+Datum
+multirange_typanalyze(PG_FUNCTION_ARGS)
+{
+ PG_RETURN_BOOL(true);
+}
+
+/*
+ * 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 rngtypid, IOFuncSelector func)
+{
+ MultirangeIOData *cache = (MultirangeIOData *) fcinfo->flinfo->fn_extra;
+
+ if (cache == NULL || cache->typcache->type_id != rngtypid)
+ {
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char typdelim;
+
+ cache = (MultirangeIOData *) MemoryContextAlloc(fcinfo->flinfo->fn_mcxt,
+ sizeof(MultirangeIOData));
+ cache->typcache = lookup_type_cache(rngtypid, TYPECACHE_MULTIRANGE_INFO);
+ if (cache->typcache->rngtype == NULL)
+ elog(ERROR, "type %u is not a multirange type", rngtypid);
+
+ /* 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,
+ &cache->typiofunc);
+
+ if (!OidIsValid(cache->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(cache->typiofunc, &cache->proc,
+ fcinfo->flinfo->fn_mcxt);
+
+ fcinfo->flinfo->fn_extra = (void *) cache;
+ }
+
+ return cache;
+}
+
+/*
+ *----------------------------------------------------------
+ * 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;
+}
+
+/*
+ * 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;
+ RangeType *range;
+ int i;
+ int32 bytelen;
+ Pointer ptr;
+
+ /* Sort and merge input ranges. */
+ range_count = multirange_canonicalize(rangetyp, range_count, ranges);
+
+ /*
+ * Count space for varlena header, multirange type's OID, other fields,
+ * and padding so that RangeTypes start aligned.
+ */
+ bytelen = MAXALIGN(sizeof(MultirangeType));
+
+ /* Count space for all ranges */
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ bytelen += MAXALIGN(VARSIZE(range));
+ }
+
+ /* Note: zero-fill is required here, just as in heap tuples */
+ multirange = palloc0(bytelen);
+ SET_VARSIZE(multirange, bytelen);
+
+ /* Now fill in the datum */
+ multirange->multirangetypid = mltrngtypoid;
+ multirange->rangeCount = range_count;
+
+ ptr = (char *) MAXALIGN(multirange + 1);
+ for (i = 0; i < range_count; i++)
+ {
+ range = ranges[i];
+ memcpy(ptr, range, VARSIZE(range));
+ ptr += MAXALIGN(VARSIZE(range));
+ }
+
+ return multirange;
+}
+
+/*
+ * Converts a list of any ranges you like 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;
+}
+
+/*
+ * 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(MultirangeType * multirange,
+ int32 *range_count, RangeType ***ranges)
+{
+ RangeType *r;
+ Pointer ptr;
+ Pointer end;
+ int32 i;
+
+ *range_count = multirange->rangeCount;
+ if (*range_count == 0)
+ {
+ ranges = NULL;
+ return;
+ }
+
+ *ranges = palloc0(*range_count * sizeof(RangeType *));
+
+ ptr = (char *) multirange;
+ end = ptr + VARSIZE(multirange);
+ ptr = (char *) MAXALIGN(multirange + 1);
+ i = 0;
+ while (ptr < end)
+ {
+ r = (RangeType *) ptr;
+ (*ranges)[i++] = r;
+
+ ptr += MAXALIGN(VARSIZE(r));
+ }
+}
+
+MultirangeType *
+make_empty_multirange(Oid mltrngtypoid, TypeCacheEntry *rangetyp)
+{
+ return make_multirange(mltrngtypoid, rangetyp, 0, NULL);
+}
+
+/*
+ *----------------------------------------------------------
+ * 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 range types 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, (errmsg("Can't construct multirange with a NULL input")));
+
+ rangeArray = PG_GETARG_ARRAYTYPE_P(0);
+
+ dims = ARR_NDIM(rangeArray);
+ if (dims > 1)
+ ereport(ERROR, (errmsg("Can't construct multirange with a multi-dimensional array")));
+
+ rngtypid = ARR_ELEMTYPE(rangeArray);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (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, (errmsg("Can't construct multirange with a NULL element")));
+
+ /* 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, (errmsg("Can't construct multirange with a NULL input")));
+
+ range = PG_GETARG_RANGE_P(0);
+
+ /* Make sure the range type matches. */
+ rngtypid = RangeTypeGetOid(range);
+ if (rngtypid != rangetyp->type_id)
+ ereport(ERROR, (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
+ ereport(ERROR, (errmsg("Zero-param multirange constructor shouldn't have 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(mr1, &range_count1, &ranges1);
+ multirange_deserialize(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(mr1, &range_count1, &ranges1);
+ multirange_deserialize(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(mr1, &range_count1, &ranges1);
+ multirange_deserialize(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, (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, (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(result, &range_count1, &ranges1);
+ multirange_deserialize(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;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_lower_internal(typcache->rngtype, ranges[0], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+/* extract upper bound value */
+Datum
+multirange_upper(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ bool isnull;
+ Datum result;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_NULL();
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ result = range_upper_internal(typcache->rngtype, ranges[range_count - 1], &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+
+/* multirange -> bool functions */
+
+/* is multirange empty? */
+Datum
+multirange_empty(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+
+ PG_RETURN_BOOL(mr->rangeCount == 0);
+}
+
+/* is lower bound inclusive? */
+Datum
+multirange_lower_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INC));
+}
+
+/* is upper bound inclusive? */
+Datum
+multirange_upper_inc(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INC));
+}
+
+/* is lower bound infinite? */
+Datum
+multirange_lower_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[0], RANGE_LB_INF));
+}
+
+/* is upper bound infinite? */
+Datum
+multirange_upper_inf(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ int32 range_count;
+ RangeType **ranges;
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_has_flag(ranges[range_count - 1], RANGE_UB_INF));
+}
+
+
+
+/* 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));
+}
+
+/*
+ * Test whether multirange mr contains a specific element value.
+ */
+bool
+multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType * mr, Datum val)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *r;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ r = ranges[i];
+ if (range_contains_elem_internal(rangetyp, r, val))
+ return true;
+ }
+
+ return false;
+}
+
+/* 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));
+}
+
+/*
+ * Test whether multirange mr contains a specific range r.
+ */
+bool
+multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType * mr, RangeType *r)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ RangeType **ranges;
+ RangeType *mrr;
+ int i;
+
+ rangetyp = typcache->rngtype;
+
+ /*
+ * Every multirange contains an infinite number of empty ranges, even an
+ * empty one.
+ */
+ if (RangeIsEmpty(r))
+ return true;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ for (i = 0; i < range_count; i++)
+ {
+ mrr = ranges[i];
+ if (range_contains_internal(rangetyp, mrr, r))
+ return true;
+ }
+
+ return false;
+}
+
+
+/* multirange, multirange -> bool functions */
+
+/* equality (internal version) */
+bool
+multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType * mr1, MultirangeType * mr2)
+{
+ int32 range_count_1;
+ int32 range_count_2;
+ int32 i;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /* Different types should be prevented by ANYMULTIRANGE matching rules */
+ if (MultirangeTypeGetOid(mr1) != MultirangeTypeGetOid(mr2))
+ elog(ERROR, "multirange types do not match");
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ if (range_count_1 != range_count_2)
+ return false;
+
+ for (i = 0; i < range_count_1; i++)
+ {
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ if (!range_eq_internal(typcache->rngtype, r1, r2))
+ 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));
+}
+
+bool
+range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r, MultirangeType * mr)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count;
+ int32 i;
+ RangeType **ranges;
+ RangeType *mrr;
+
+ /*
+ * 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;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ /* Scan through mrr and once it gets to r they either overlap or not */
+ mrr = ranges[0];
+
+ /* Discard mrrs while mrr << r */
+ i = 0;
+ while (range_before_internal(rangetyp, mrr, r))
+ {
+ if (++i >= range_count)
+ return false;
+ mrr = ranges[i];
+ }
+
+ /* Now either we overlap or we passed r */
+ return range_overlaps_internal(rangetyp, mrr, r);
+}
+
+bool
+multirange_overlaps_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ TypeCacheEntry *rangetyp;
+ int32 range_count1;
+ int32 range_count2;
+ int32 i1;
+ int32 i2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+
+ /*
+ * 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;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * 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.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * If r1 && r2, we're done, otherwise we failed to find an overlap for
+ * r2, so go to the next one.
+ */
+ if (range_overlaps_internal(rangetyp, r1, r2))
+ 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;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, r, ranges[range_count - 1]));
+}
+
+Datum
+multirange_overleft_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+Datum
+multirange_overleft_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;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overleft_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[range_count2 - 1]));
+}
+
+/* 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;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_overright_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges[0], r));
+}
+
+Datum
+multirange_overright_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;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ PG_RETURN_BOOL(false);
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_overright_internal(typcache->rngtype, ranges1[0], ranges2[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;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ int i1,
+ i2;
+
+ rangetyp = typcache->rngtype;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ /*
+ * 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.
+ */
+ r1 = ranges1[0];
+ for (i1 = 0, i2 = 0; i2 < range_count2; i2++)
+ {
+ r2 = ranges2[i2];
+
+ /* Discard r1s while r1 << r2 */
+ while (range_before_internal(rangetyp, r1, r2))
+ {
+ if (++i1 >= range_count1)
+ return false;
+ r1 = ranges1[i1];
+ }
+
+ /*
+ * 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_contains_internal(rangetyp, r1, r2))
+ 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)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_before_internal(typcache->rngtype, r, ranges[0]);
+}
+
+bool
+multirange_before_multirange_internal(TypeCacheEntry *typcache, MultirangeType * mr1,
+ MultirangeType * mr2)
+{
+ int32 range_count1;
+ int32 range_count2;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ return range_before_internal(typcache->rngtype, ranges1[range_count1 - 1],
+ ranges2[0]);
+}
+
+/* strictly right of? (internal version) */
+bool
+range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
+ MultirangeType * mr)
+{
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ return range_after_internal(typcache->rngtype, r, ranges[range_count - 1]);
+}
+
+/* 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;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, r, ranges[0]));
+}
+
+Datum
+multirange_adjacent_range(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ RangeType *r = PG_GETARG_RANGE_P(1);
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+
+ if (RangeIsEmpty(r) || MultirangeIsEmpty(mr))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges[range_count - 1], r));
+}
+
+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;
+ RangeType **ranges1;
+ RangeType **ranges2;
+
+ if (MultirangeIsEmpty(mr1) || MultirangeIsEmpty(mr2))
+ return false;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr1));
+
+ multirange_deserialize(mr1, &range_count1, &ranges1);
+ multirange_deserialize(mr2, &range_count2, &ranges2);
+
+ PG_RETURN_BOOL(range_adjacent_internal(typcache->rngtype, ranges1[range_count1 - 1], ranges2[0]));
+}
+
+/* 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;
+ RangeType **ranges1;
+ RangeType **ranges2;
+ RangeType *r1;
+ RangeType *r2;
+ 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));
+
+ multirange_deserialize(mr1, &range_count_1, &ranges1);
+ multirange_deserialize(mr2, &range_count_2, &ranges2);
+
+ /* Loop over source data */
+ range_count_max = Max(range_count_1, range_count_2);
+ for (i = 0; i < range_count_max; i++)
+ {
+ /*
+ * 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;
+ }
+ r1 = ranges1[i];
+ r2 = ranges2[i];
+
+ cmp = range_cmp_internal(typcache->rngtype, r1, r2);
+ 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;
+ int32 range_count;
+ RangeType **ranges;
+
+ typcache = multirange_get_typcache(fcinfo, mltrngtypoid);
+
+ if (MultirangeIsEmpty(mr))
+ PG_RETURN_RANGE_P(make_empty_range(typcache->rngtype));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+
+ PG_RETURN_RANGE_P(range_union_internal(typcache->rngtype, ranges[0],
+ ranges[range_count - 1], false));
+}
+
+/* Hash support */
+
+/* hash a multirange value */
+Datum
+hash_multirange(PG_FUNCTION_ARGS)
+{
+ MultirangeType *mr = PG_GETARG_MULTIRANGE_P(0);
+ uint32 result = 1;
+ TypeCacheEntry *typcache;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint32 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_internal(typcache->rngtype, r);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ 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;
+ int32 range_count;
+ RangeType **ranges;
+ int32 i;
+ RangeType *r;
+
+ typcache = multirange_get_typcache(fcinfo, MultirangeTypeGetOid(mr));
+
+ multirange_deserialize(mr, &range_count, &ranges);
+ for (i = 0; i < range_count; i++)
+ {
+ uint64 elthash;
+
+ r = ranges[i];
+ elthash = hash_range_extended_internal(typcache->rngtype, r, seed);
+
+ /*
+ * Use the same approach as hash_array to combine the individual
+ * elements' hash values:
+ */
+ result = (result << 5) - result + elthash;
+ }
+
+ PG_FREE_IF_COPY(mr, 0);
+
+ PG_RETURN_UINT64(result);
+}
diff --git a/src/backend/utils/adt/pg_upgrade_support.c b/src/backend/utils/adt/pg_upgrade_support.c
index 18f2ee8226..248ea7f44a 100644
--- a/src/backend/utils/adt/pg_upgrade_support.c
+++ b/src/backend/utils/adt/pg_upgrade_support.c
@@ -51,6 +51,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_toast_pg_type_oid(PG_FUNCTION_ARGS)
{
diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 4653fc33e6..c909c9dc8c 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
+#include "utils/multirangetypes.h"
/*
@@ -184,6 +185,30 @@ anyrange_out(PG_FUNCTION_ARGS)
return range_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_in - input routine for pseudo-type VOID.
*
diff --git a/src/backend/utils/adt/rangetypes.c b/src/backend/utils/adt/rangetypes.c
index 01ad8bc240..9d1ca13e32 100644
--- a/src/backend/utils/adt/rangetypes.c
+++ b/src/backend/utils/adt/rangetypes.c
@@ -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
{
@@ -431,19 +429,37 @@ range_lower(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_lower_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite lower bound */
if (empty || lower.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(lower.val);
+ *isnull = false;
+ return lower.val;
}
/* extract upper bound value */
@@ -452,19 +468,37 @@ range_upper(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
TypeCacheEntry *typcache;
+ bool isnull;
+ Datum result;
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+
+ result = range_upper_internal(typcache, r1, &isnull);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(result);
+}
+
+Datum
+range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1, bool *isnull)
+{
RangeBound lower;
RangeBound upper;
bool empty;
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
-
range_deserialize(typcache, r1, &lower, &upper, &empty);
/* Return NULL if there's no finite upper bound */
if (empty || upper.infinite)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return 0;
+ }
- PG_RETURN_DATUM(upper.val);
+ *isnull = false;
+ return upper.val;
}
@@ -475,9 +509,8 @@ Datum
range_empty(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_EMPTY);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_EMPTY));
}
/* is lower bound inclusive? */
@@ -485,9 +518,8 @@ Datum
range_lower_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INC));
}
/* is upper bound inclusive? */
@@ -495,9 +527,8 @@ Datum
range_upper_inc(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INC);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INC));
}
/* is lower bound infinite? */
@@ -505,9 +536,8 @@ Datum
range_lower_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_LB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_LB_INF));
}
/* is upper bound infinite? */
@@ -515,9 +545,8 @@ Datum
range_upper_inf(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
- char flags = range_get_flags(r1);
- PG_RETURN_BOOL(flags & RANGE_UB_INF);
+ PG_RETURN_BOOL(range_has_flag(r1, RANGE_UB_INF));
}
@@ -957,7 +986,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 +1016,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 +1034,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 +1142,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 +1164,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 +1180,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 */
@@ -1144,12 +1264,6 @@ range_cmp(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
- RangeBound lower1,
- lower2;
- RangeBound upper1,
- upper2;
- bool empty1,
- empty2;
int cmp;
check_stack_depth(); /* recurses when subtype is a range type */
@@ -1160,6 +1274,28 @@ range_cmp(PG_FUNCTION_ARGS)
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
+ cmp = range_cmp_internal(typcache, r1, r2);
+
+ PG_FREE_IF_COPY(r1, 0);
+ PG_FREE_IF_COPY(r2, 1);
+
+ PG_RETURN_INT32(cmp);
+}
+
+/*
+ * Internal version of range_cmp
+ */
+int
+range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
+{
+ RangeBound lower1,
+ lower2;
+ RangeBound upper1,
+ upper2;
+ bool empty1,
+ empty2;
+ int cmp;
+
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
@@ -1177,10 +1313,7 @@ range_cmp(PG_FUNCTION_ARGS)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
- PG_FREE_IF_COPY(r1, 0);
- PG_FREE_IF_COPY(r2, 1);
-
- PG_RETURN_INT32(cmp);
+ return cmp;
}
/* inequality operators using the range_cmp function */
@@ -1218,13 +1351,10 @@ range_gt(PG_FUNCTION_ARGS)
/* Hash support */
-/* hash a range value */
-Datum
-hash_range(PG_FUNCTION_ARGS)
+uint32
+hash_range_internal(TypeCacheEntry *typcache, const RangeType *r)
{
- RangeType *r = PG_GETARG_RANGE_P(0);
uint32 result;
- TypeCacheEntry *typcache;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1233,10 +1363,6 @@ hash_range(PG_FUNCTION_ARGS)
uint32 lower_hash;
uint32 upper_hash;
- check_stack_depth(); /* recurses when subtype is a range type */
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
/* deserialize */
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1278,20 +1404,27 @@ hash_range(PG_FUNCTION_ARGS)
result = (result << 1) | (result >> 31);
result ^= upper_hash;
- PG_RETURN_INT32(result);
+ return result;
}
-/*
- * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
- * Otherwise, similar to hash_range.
- */
+/* hash a range value */
Datum
-hash_range_extended(PG_FUNCTION_ARGS)
+hash_range(PG_FUNCTION_ARGS)
{
RangeType *r = PG_GETARG_RANGE_P(0);
- Datum seed = PG_GETARG_DATUM(1);
- uint64 result;
TypeCacheEntry *typcache;
+
+ check_stack_depth(); /* recurses when subtype is a range type */
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_INT32(hash_range_internal(typcache, r));
+}
+
+uint64
+hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r, Datum seed)
+{
+ uint64 result;
TypeCacheEntry *scache;
RangeBound lower;
RangeBound upper;
@@ -1300,10 +1433,6 @@ hash_range_extended(PG_FUNCTION_ARGS)
uint64 lower_hash;
uint64 upper_hash;
- check_stack_depth();
-
- typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
-
range_deserialize(typcache, r, &lower, &upper, &empty);
flags = range_get_flags(r);
@@ -1342,7 +1471,25 @@ hash_range_extended(PG_FUNCTION_ARGS)
result = ROTATE_HIGH_AND_LOW_32BITS(result);
result ^= upper_hash;
- PG_RETURN_UINT64(result);
+ return result;
+}
+
+/*
+ * Returns 64-bit value by hashing a value to a 64-bit value, with a seed.
+ * Otherwise, similar to hash_range.
+ */
+Datum
+hash_range_extended(PG_FUNCTION_ARGS)
+{
+ RangeType *r = PG_GETARG_RANGE_P(0);
+ Datum seed = PG_GETARG_DATUM(1);
+ TypeCacheEntry *typcache;
+
+ check_stack_depth();
+
+ typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r));
+
+ PG_RETURN_UINT64(hash_range_extended_internal(typcache, r, seed));
}
/*
@@ -1769,6 +1916,21 @@ range_get_flags(const RangeType *range)
return *((char *) range + VARSIZE(range) - 1);
}
+/*
+ * range_has_flag: set whether a range has a specific flag.
+ *
+ * This lets expose some of our functions that just check flags
+ * to the rest of the code base (like to multiranges)
+ * without writing full-fledged *_internal versions.
+ */
+bool
+range_has_flag(const RangeType *r1, char flag)
+{
+ char flags = range_get_flags(r1);
+
+ return flags & flag;
+}
+
/*
* range_set_contain_empty: set the RANGE_CONTAIN_EMPTY bit in the value.
*
@@ -1937,6 +2099,45 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
b1->val, b2->val));
}
+/*
+ * Compares two ranges so we can qsort them.
+ * This expects that you give qsort a RangeType **,
+ * so the RangeTypes can be in diverse locations,
+ * as long as you have a list of pointers to them all.
+ */
+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.
*/
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 27bbb58f56..c6c079c623 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -2494,6 +2494,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
*
@@ -3149,7 +3159,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
-/* ---------- PG_RANGE CACHE ---------- */
+/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@@ -3202,6 +3212,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_range_multirange_subtype
+ * Returns the subtype of a given multirange type
+ *
+ * Returns InvalidOid if the type is not a multirange type.
+ */
+Oid
+get_range_multirange_subtype(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 ---------- */
/*
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 53d9ddf159..7654331a59 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -651,6 +651,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
+ {RangeRelationId, /* RANGEMULTIRANGE */
+ RangeMultirangeTypidIndexId,
+ 1,
+ {
+ Anum_pg_range_rngmultitypid,
+ 0,
+ 0,
+ 0
+ },
+ 4
+ },
+
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 854f133f9b..160846cf10 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -286,6 +286,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);
@@ -302,6 +303,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);
@@ -553,8 +557,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))
@@ -591,7 +595,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))
@@ -616,7 +620,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))
@@ -641,7 +645,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))
@@ -693,6 +697,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_range_multirange_subtype(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
@@ -1527,11 +1571,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
@@ -1574,6 +1618,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'.
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 0201e4f8d3..b10cd99144 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -470,11 +470,13 @@ 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_anynonarray = false;
bool have_anyenum = false;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
Oid anycollation = InvalidOid;
int i;
@@ -500,12 +502,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
case ANYRANGEOID:
have_anyrange_result = true;
break;
+ case ANYMULTIRANGEOID:
+ have_anymultirange_result = true;
+ break;
default:
break;
}
}
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/*
@@ -533,6 +538,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (!OidIsValid(anyrange_type))
anyrange_type = get_call_expr_argtype(call_expr, i);
break;
+ case ANYMULTIRANGEOID:
+ if (!OidIsValid(anymultirange_type))
+ anymultirange_type = get_call_expr_argtype(call_expr, i);
+ break;
default:
break;
}
@@ -540,7 +549,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
/* If nothing found, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -561,19 +570,131 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_range_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+ anymultirange_type = mltrngtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
/* Enforce ANYNONARRAY if needed */
if (have_anynonarray && type_is_array(anyelement_type))
@@ -640,6 +761,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),
+ anymultirange_type,
+ -1,
+ 0);
+ /* no collation should be attached to a multirange type */
+ break;
default:
break;
}
@@ -664,9 +793,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;
Oid anyelement_type = InvalidOid;
Oid anyarray_type = InvalidOid;
Oid anyrange_type = InvalidOid;
+ Oid anymultirange_type = InvalidOid;
int inargno;
int i;
@@ -725,6 +856,21 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyrange_type;
}
break;
+ case ANYMULTIRANGEOID:
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ have_anymultirange_result = true;
+ else
+ {
+ if (!OidIsValid(anymultirange_type))
+ {
+ anymultirange_type = get_call_expr_argtype(call_expr,
+ inargno);
+ if (!OidIsValid(anymultirange_type))
+ return false;
+ }
+ argtypes[i] = anymultirange_type;
+ }
+ break;
default:
break;
}
@@ -734,12 +880,12 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
/* Done? */
if (!have_anyelement_result && !have_anyarray_result &&
- !have_anyrange_result)
+ !have_anyrange_result && !have_anymultirange_result)
return true;
/* If no input polymorphics, parser messed up */
if (!OidIsValid(anyelement_type) && !OidIsValid(anyarray_type) &&
- !OidIsValid(anyrange_type))
+ !OidIsValid(anyrange_type) && !OidIsValid(anymultirange_type))
return false;
/* If needed, deduce one polymorphic type from others */
@@ -760,19 +906,132 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
return false;
anyelement_type = subtype;
}
+
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
}
if (have_anyarray_result && !OidIsValid(anyarray_type))
- anyarray_type = resolve_generic_type(ANYARRAYOID,
- anyelement_type,
- ANYELEMENTOID);
+ {
+ if (OidIsValid(anyelement_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyelement_type,
+ ANYELEMENTOID);
+ }
+
+ /* An array type should have the same elemtype as the range type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anyrange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anyrange_type,
+ ANYRANGEOID);
+ }
+
+ /* An array type should have the same elemtype as the multirange type */
+ if (!OidIsValid(anyarray_type) && OidIsValid(anymultirange_type))
+ {
+ anyarray_type = resolve_generic_type(ANYARRAYOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+ }
+ }
/*
* We can't deduce a range type from other polymorphic inputs, because
- * there may be multiple range types for the same subtype.
+ * there may be multiple range types for the same subtype, but a
+ * multirange might help.
*/
if (have_anyrange_result && !OidIsValid(anyrange_type))
- return false;
+ {
+ if (OidIsValid(anymultirange_type))
+ {
+ Oid rngtype;
+ Oid subtype;
+
+ rngtype = resolve_generic_type(ANYRANGEOID,
+ anymultirange_type,
+ ANYMULTIRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and multirange results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /*
+ * Likewise we can't deduce a multirange from most polymorphic inputs, but
+ * a range type would work.
+ */
+ if (have_anymultirange_result && !OidIsValid(anymultirange_type))
+ {
+ if (OidIsValid(anyrange_type))
+ {
+ Oid subtype;
+ Oid mltrngtype;
+ Oid rngtype;
+
+ subtype = resolve_generic_type(ANYELEMENTOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent array and range results */
+ if (OidIsValid(anyelement_type) && anyelement_type != subtype)
+ return false;
+ anyelement_type = subtype;
+
+ mltrngtype = resolve_generic_type(ANYMULTIRANGEOID,
+ anyrange_type,
+ ANYRANGEOID);
+
+ /* check for inconsistent range and multirange results */
+ rngtype = get_range_multirange_subtype(mltrngtype);
+
+ if (OidIsValid(anyrange_type) && anyrange_type != rngtype)
+ return false;
+ anyrange_type = rngtype;
+
+ }
+ else
+ {
+ return false;
+ }
+ }
/* XXX do we need to enforce ANYNONARRAY or ANYENUM here? I think not */
@@ -792,6 +1051,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = anyrange_type;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = anymultirange_type;
+ break;
default:
break;
}
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index 28312e14ef..ba53ac13ba 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -271,7 +271,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 bool 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,
@@ -1580,7 +1581,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;
@@ -4339,16 +4340,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,
@@ -4369,33 +4403,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))
{
@@ -4406,6 +4414,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);
}
@@ -4439,7 +4487,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
- pg_type_oid, false);
+ pg_type_oid, false, false);
if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
{
@@ -4988,6 +5036,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);
@@ -8102,9 +8155,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)
@@ -8122,7 +8178,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, "
@@ -10310,7 +10379,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);
@@ -10436,7 +10505,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);
@@ -10542,7 +10611,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);
@@ -10747,7 +10816,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"
@@ -10934,7 +11003,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));
@@ -11122,7 +11192,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);
}
@@ -11396,7 +11466,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));
diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h
index e0c6444ef6..90a93b4cb2 100644
--- a/src/bin/pg_dump/pg_dump.h
+++ b/src/bin/pg_dump/pg_dump.h
@@ -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 */
diff --git a/src/include/access/tupmacs.h b/src/include/access/tupmacs.h
index 70157cf90a..c262265b95 100644
--- a/src/include/access/tupmacs.h
+++ b/src/include/access/tupmacs.h
@@ -139,8 +139,8 @@
* * 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.
diff --git a/src/include/catalog/binary_upgrade.h b/src/include/catalog/binary_upgrade.h
index 12d94fe1b3..01d95117cd 100644
--- a/src/include/catalog/binary_upgrade.h
+++ b/src/include/catalog/binary_upgrade.h
@@ -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_toast_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index 8be303870f..566f9364c5 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -327,6 +327,9 @@ DECLARE_UNIQUE_INDEX(pg_extension_name_index, 3081, on pg_extension using btree(
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, 8001, on pg_range using btree(rngmultitypid oid_ops));
+#define RangeMultirangeTypidIndexId 8001
+
DECLARE_UNIQUE_INDEX(pg_policy_oid_index, 3257, on pg_policy using btree(oid oid_ops));
#define PolicyOidIndexId 3257
diff --git a/src/include/catalog/pg_aggregate.dat b/src/include/catalog/pg_aggregate.dat
index ffabe275c0..03bab74253 100644
--- a/src/include/catalog/pg_aggregate.dat
+++ b/src/include/catalog/pg_aggregate.dat
@@ -538,6 +538,17 @@
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' },
diff --git a/src/include/catalog/pg_amop.dat b/src/include/catalog/pg_amop.dat
index 11aaa519c8..827ac4f03c 100644
--- a/src/include/catalog/pg_amop.dat
+++ b/src/include/catalog/pg_amop.dat
@@ -1356,6 +1356,28 @@
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)',
diff --git a/src/include/catalog/pg_amproc.dat b/src/include/catalog/pg_amproc.dat
index 75c0152b66..cf0cac5641 100644
--- a/src/include/catalog/pg_amproc.dat
+++ b/src/include/catalog/pg_amproc.dat
@@ -285,6 +285,8 @@
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' },
@@ -445,6 +447,11 @@
{ 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',
@@ -1263,12 +1270,13 @@
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',
diff --git a/src/include/catalog/pg_cast.dat b/src/include/catalog/pg_cast.dat
index 6ef8b8a4e7..14277c8fdf 100644
--- a/src/include/catalog/pg_cast.dat
+++ b/src/include/catalog/pg_cast.dat
@@ -512,4 +512,17 @@
{ 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' },
]
diff --git a/src/include/catalog/pg_opclass.dat b/src/include/catalog/pg_opclass.dat
index ab2f50c9eb..eebebbcdb4 100644
--- a/src/include/catalog/pg_opclass.dat
+++ b/src/include/catalog/pg_opclass.dat
@@ -226,6 +226,10 @@
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',
diff --git a/src/include/catalog/pg_operator.dat b/src/include/catalog/pg_operator.dat
index 7c135da3b1..24f4a32203 100644
--- a/src/include/catalog/pg_operator.dat
+++ b/src/include/catalog/pg_operator.dat
@@ -3297,5 +3297,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'contsel', oprjoin => 'contjoinsel' },
+{ oid => '8027', 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 => '8028', descr => 'not equal',
+ oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
+ oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
+ oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
+{ oid => '8029', 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 => 'scalarltsel', oprjoin => 'scalarltjoinsel' },
+{ oid => '8030', 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 => 'scalarlesel', oprjoin => 'scalarlejoinsel' },
+{ oid => '8031', 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 => 'scalargesel', oprjoin => 'scalargejoinsel' },
+{ oid => '8032', 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 => 'scalargtsel', oprjoin => 'scalargtjoinsel' },
+{ oid => '8073', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
+ descr => 'overlaps',
+ oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
+ oprcode => 'range_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8074', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
+ oprcode => 'multirange_overlaps_range', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8075', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
+ oprcode => 'multirange_overlaps_multirange', oprrest => 'areasel',
+ oprjoin => 'areajoinsel' },
+{ oid => '8064', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
+ descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
+ oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
+ oprcode => 'multirange_contains_elem', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8065', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
+ oprcode => 'multirange_contains_range', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8066', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
+ oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
+ oprcode => 'multirange_contains_multirange', oprrest => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8067', 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 => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8068', 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 => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8069', 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 => 'contsel',
+ oprjoin => 'contjoinsel' },
+{ oid => '8106', 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 => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8107', 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 => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8108', 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 => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8109', 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 => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8110', 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 => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8111', 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 => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8091', 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 => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8092', 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 => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8093', 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 => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8117', descr => 'multirange union',
+ oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
+ oprcode => 'multirange_union' },
+{ oid => '8123', descr => 'multirange minus',
+ oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcode => 'multirange_minus' },
+{ oid => '8129', descr => 'multirange intersect',
+ oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
+ oprcode => 'multirange_intersect' },
+{ oid => '8082', oid_symbol => 'OID_RANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
+ oprcode => 'range_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8083', oid_symbol => 'OID_MULTIRANGE_BEFORE_RANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
+ oprcode => 'multirange_before_range', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8084', oid_symbol => 'OID_MULTIRANGE_BEFORE_MULTIRANGE_OP', descr => 'is left of',
+ oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
+ oprcode => 'multirange_before_multirange', oprrest => 'scalarltsel',
+ oprjoin => 'scalarltjoinsel' },
+{ oid => '8085', oid_symbol => 'OID_RANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
+ oprcode => 'range_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8086', oid_symbol => 'OID_MULTIRANGE_AFTER_RANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
+ oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
+ oprcode => 'multirange_after_range', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
+{ oid => '8087', oid_symbol => 'OID_MULTIRANGE_AFTER_MULTIRANGE_OP', descr => 'is right of',
+ oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
+ oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
+ oprcode => 'multirange_after_multirange', oprrest => 'scalargtsel',
+ oprjoin => 'scalargtjoinsel' },
]
diff --git a/src/include/catalog/pg_opfamily.dat b/src/include/catalog/pg_opfamily.dat
index 26227df216..901fd7b303 100644
--- a/src/include/catalog/pg_opfamily.dat
+++ b/src/include/catalog/pg_opfamily.dat
@@ -226,5 +226,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
+{ oid => '8021',
+ opfmethod => 'btree', opfname => 'multirange_ops' },
+{ oid => '8035',
+ opfmethod => 'hash', opfname => 'multirange_ops' },
]
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index 7fb574f9dc..27f25d989b 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -9643,6 +9643,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
+{ oid => '8143',
+ 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' },
@@ -9692,6 +9696,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
+{ oid => '8133', descr => 'range aggregate by intersecting',
+ proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
+ proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
+{ oid => '8134', 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',
@@ -9760,6 +9771,258 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
+# functions for multiranges
+{ oid => '8002', descr => 'I/O',
+ proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
+{ oid => '8003', descr => 'I/O',
+ proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
+{ oid => '8004', descr => 'I/O',
+ proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
+{ oid => '8005', descr => 'I/O',
+ proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
+ proargtypes => 'anymultirange', prosrc => 'multirange_out' },
+{ oid => '8006', descr => 'I/O',
+ proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
+ proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
+{ oid => '8007', descr => 'I/O',
+ proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
+ proargtypes => 'anymultirange', prosrc => 'multirange_send' },
+{ oid => '8094', descr => 'lower bound of multirange',
+ proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower' },
+{ oid => '8095', descr => 'upper bound of multirange',
+ proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper' },
+{ oid => '8057', descr => 'is the multirange empty?',
+ proname => 'isempty', prorettype => 'bool',
+ proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
+{ oid => '8096', descr => 'is the multirange\'s lower bound inclusive?',
+ proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inc' },
+{ oid => '8097', descr => 'is the multirange\'s upper bound inclusive?',
+ proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inc' },
+{ oid => '8098', descr => 'is the multirange\'s lower bound infinite?',
+ proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_lower_inf' },
+{ oid => '8099', descr => 'is the multirange\'s upper bound infinite?',
+ proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
+ prosrc => 'multirange_upper_inf' },
+{ oid => '8008', descr => 'multirange typanalyze',
+ proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
+ proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
+{ oid => '8033',
+ proname => 'multirange_eq', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
+{ oid => '8034',
+ proname => 'multirange_ne', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
+{ oid => '8070',
+ proname => 'range_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
+{ oid => '8071',
+ proname => 'multirange_overlaps_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
+{ oid => '8072',
+ proname => 'multirange_overlaps_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
+{ oid => '8058',
+ proname => 'multirange_contains_elem', prorettype => 'bool',
+ proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
+{ oid => '8059',
+ proname => 'multirange_contains_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
+{ oid => '8060',
+ proname => 'multirange_contains_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
+{ oid => '8061',
+ proname => 'elem_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
+{ oid => '8062',
+ proname => 'range_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
+{ oid => '8063',
+ proname => 'multirange_contained_by_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
+{ oid => '8088',
+ proname => 'range_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
+{ oid => '8089',
+ proname => 'multirange_adjacent_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
+{ oid => '8090',
+ proname => 'multirange_adjacent_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
+{ oid => '8076',
+ proname => 'range_before_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
+{ oid => '8077',
+ proname => 'multirange_before_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
+{ oid => '8078',
+ proname => 'multirange_before_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
+{ oid => '8079',
+ proname => 'range_after_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
+{ oid => '8080',
+ proname => 'multirange_after_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
+{ oid => '8081',
+ proname => 'multirange_after_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
+{ oid => '8100',
+ proname => 'range_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
+{ oid => '8101',
+ proname => 'multirange_overleft_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
+{ oid => '8102',
+ proname => 'multirange_overleft_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
+{ oid => '8103',
+ proname => 'range_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
+{ oid => '8104',
+ proname => 'multirange_overright_range', prorettype => 'bool',
+ proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
+{ oid => '8105',
+ proname => 'multirange_overright_multirange', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
+{ oid => '8114',
+ proname => 'multirange_union', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
+{ oid => '8120',
+ proname => 'multirange_minus', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
+{ oid => '8126',
+ proname => 'multirange_intersect', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
+{ oid => '8022', descr => 'less-equal-greater',
+ proname => 'multirange_cmp', prorettype => 'int4',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
+{ oid => '8023',
+ proname => 'multirange_lt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
+{ oid => '8024',
+ proname => 'multirange_le', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
+{ oid => '8025',
+ proname => 'multirange_ge', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
+{ oid => '8026',
+ proname => 'multirange_gt', prorettype => 'bool',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
+{ oid => '8038', descr => 'hash a multirange',
+ proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
+ prosrc => 'hash_multirange' },
+{ oid => '8039', descr => 'hash a multirange',
+ proname => 'hash_multirange_extended', prorettype => 'int8',
+ proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
+
+{ oid => '8045', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8146', descr => 'int4multirange constructor',
+ proname => 'int4multirange', proisstrict => 't',
+ prorettype => 'int4multirange', proargtypes => 'int4range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8046', descr => 'int4multirange constructor',
+ proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
+ prorettype => 'int4multirange', proargtypes => '_int4range',
+ proallargtypes => '{_int4range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8047', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8147', descr => 'nummultirange constructor',
+ proname => 'nummultirange', proisstrict => 't',
+ prorettype => 'nummultirange', proargtypes => 'numrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8048', descr => 'nummultirange constructor',
+ proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
+ prorettype => 'nummultirange', proargtypes => '_numrange',
+ proallargtypes => '{_numrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8049', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8148', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', proisstrict => 't',
+ prorettype => 'tsmultirange', proargtypes => 'tsrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8050', descr => 'tsmultirange constructor',
+ proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
+ prorettype => 'tsmultirange', proargtypes => '_tsrange',
+ proallargtypes => '{_tsrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8051', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8149', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', proisstrict => 't',
+ prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8052', descr => 'tstzmultirange constructor',
+ proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
+ prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
+ proallargtypes => '{_tstzrange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8053', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8150', descr => 'datemultirange constructor',
+ proname => 'datemultirange', proisstrict => 't',
+ prorettype => 'datemultirange', proargtypes => 'daterange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8054', descr => 'datemultirange constructor',
+ proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
+ prorettype => 'datemultirange', proargtypes => '_daterange',
+ proallargtypes => '{_daterange}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8055', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '',
+ prosrc => 'multirange_constructor0' },
+{ oid => '8151', descr => 'int8multirange constructor',
+ proname => 'int8multirange', proisstrict => 't',
+ prorettype => 'int8multirange', proargtypes => 'int8range',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8056', descr => 'int8multirange constructor',
+ proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
+ prorettype => 'int8multirange', proargtypes => '_int8range',
+ proallargtypes => '{_int8range}', proargmodes => '{v}',
+ prosrc => 'multirange_constructor2' },
+{ oid => '8152', descr => 'anymultirange cast',
+ proname => 'multirange', proisstrict => 't',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'multirange_constructor1' },
+{ oid => '8130', descr => 'aggregate transition function',
+ proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
+{ oid => '8131', descr => 'aggregate final function',
+ proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
+ proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
+{ oid => '8132', descr => 'combine aggregate input into a multirange',
+ proname => 'range_agg', prokind => 'a', proisstrict => 'f',
+ prorettype => 'anymultirange', proargtypes => 'anyrange',
+ prosrc => 'aggregate_dummy' },
+{ oid => '8135', descr => 'range aggregate by intersecting',
+ proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
+ proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
+{ oid => '8136', 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',
@@ -10142,6 +10405,14 @@
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 => '8144', 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 => '8145', 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 => '3585', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
diff --git a/src/include/catalog/pg_range.dat b/src/include/catalog/pg_range.dat
index 479754c245..10060255c9 100644
--- a/src/include/catalog/pg_range.dat
+++ b/src/include/catalog/pg_range.dat
@@ -13,19 +13,22 @@
[
{ 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' },
]
diff --git a/src/include/catalog/pg_range.h b/src/include/catalog/pg_range.h
index 98172bb1b6..60a8fde518 100644
--- a/src/include/catalog/pg_range.h
+++ b/src/include/catalog/pg_range.h
@@ -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);
@@ -60,7 +63,7 @@ typedef FormData_pg_range *Form_pg_range;
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 */
diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat
index b00597d6ff..6e1cb796f1 100644
--- a/src/include/catalog/pg_type.dat
+++ b/src/include/catalog/pg_type.dat
@@ -486,6 +486,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
+# multirange types
+{ oid => '8009', 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 => '8011', 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 => '8013', 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 => '8015', 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 => '8017', 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 => '8019', 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
@@ -590,5 +624,10 @@
typname => 'anyrange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anyrange_in', typoutput => 'anyrange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
+{ oid => '8000',
+ 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' },
]
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 97890946c5..d18f19f55a 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -263,6 +263,7 @@ typedef FormData_pg_type *Form_pg_type;
#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 */
@@ -299,7 +300,8 @@ typedef FormData_pg_type *Form_pg_type;
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
- (typid) == ANYRANGEOID)
+ (typid) == ANYRANGEOID || \
+ (typid) == ANYMULTIRANGEOID)
#endif /* EXPOSE_TO_CLIENT_CODE */
@@ -358,4 +360,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 */
diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h
index 0162bc2ffe..80e3a5e399 100644
--- a/src/include/commands/typecmds.h
+++ b/src/include/commands/typecmds.h
@@ -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);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 4e646c55e9..80410dcd04 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -155,6 +155,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);
@@ -181,6 +182,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_range_multirange_subtype(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
index 0000000000..8779bd1f6e
--- /dev/null
+++ b/src/include/utils/multirangetypes.h
@@ -0,0 +1,103 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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"
+
+
+/*
+ * 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
+{
+ char vl_len_[4]; /* varlena header (do not touch directly!) */
+ Oid multirangetypid; /* multirange type's own OID */
+ uint32 rangeCount; /* the number of ranges */
+
+ /*
+ * Following the OID are the range objects themselves. 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 this macro in preference to fetching multirangetypid field 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 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(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);
+
+#endif /* MULTIRANGETYPES_H */
diff --git a/src/include/utils/rangetypes.h b/src/include/utils/rangetypes.h
index 0cbbf09033..204aee4054 100644
--- a/src/include/utils/rangetypes.h
+++ b/src/include/utils/rangetypes.h
@@ -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)
@@ -92,13 +94,21 @@ typedef struct
* prototypes for functions defined in rangetypes.c
*/
-extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
-
/* internal versions of the above */
+extern int range_cmp_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ const RangeType *r2);
+extern uint32 hash_range_internal(TypeCacheEntry *typcache, const RangeType *r);
+extern uint64 hash_range_extended_internal(TypeCacheEntry *typcache, const RangeType *r,
+ Datum seed);
+extern Datum range_lower_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
+extern Datum range_upper_internal(TypeCacheEntry *typcache, const RangeType *r1,
+ bool *isnull);
extern bool range_eq_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_ne_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
+extern bool range_contains_elem_internal(TypeCacheEntry *typcache, const RangeType *r, Datum val);
extern bool range_contains_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern bool range_contained_by_internal(TypeCacheEntry *typcache, const RangeType *r1,
@@ -115,6 +125,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,
@@ -125,6 +141,7 @@ extern void range_deserialize(TypeCacheEntry *typcache, const RangeType *range,
RangeBound *lower, RangeBound *upper,
bool *empty);
extern char range_get_flags(const RangeType *range);
+extern bool range_has_flag(const RangeType *range, char flag);
extern void range_set_contain_empty(RangeType *range);
extern RangeType *make_range(TypeCacheEntry *typcache, RangeBound *lower,
RangeBound *upper, bool empty);
@@ -132,8 +149,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 bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
- RangeBound bound2);
+extern int range_compare(const void *key1, const void *key2, void *arg);
+extern bool bounds_adjacent(TypeCacheEntry *typcache, const RangeBound bound1,
+ const 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 */
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index f27b73d76d..e8f393520a 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -79,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
+ RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index cdd20e56d7..f40a462b22 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -100,6 +100,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; /* underlying range type */
+
/*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
@@ -143,6 +148,7 @@ typedef struct TypeCacheEntry
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
+#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index c8e43e684f..2d59021d68 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -511,6 +511,8 @@ do_compile(FunctionCallInfo fcinfo,
rettypeid = INT4ARRAYOID;
else if (rettypeid == ANYRANGEOID)
rettypeid = INT4RANGEOID;
+ else if (rettypeid == ANYMULTIRANGEOID)
+ rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@@ -2087,6 +2089,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:
@@ -2501,6 +2504,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYRANGEOID:
argtypes[i] = INT4RANGEOID;
break;
+ case ANYMULTIRANGEOID:
+ argtypes[i] = INT4MULTIRANGEOID;
+ break;
default:
break;
}
diff --git a/src/test/regress/expected/dependency.out b/src/test/regress/expected/dependency.out
index b4a11b8aa9..778699a961 100644
--- a/src/test/regress/expected/dependency.out
+++ b/src/test/regress/expected/dependency.out
@@ -141,6 +141,7 @@ owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_range
+owner of type deptest_multirange
owner of table deptest2
owner of sequence ss1
owner of type deptest_t
diff --git a/src/test/regress/expected/hash_func.out b/src/test/regress/expected/hash_func.out
index da0948e95a..b446bfcbb7 100644
--- a/src/test/regress/expected/hash_func.out
+++ b/src/test/regress/expected/hash_func.out
@@ -298,3 +298,16 @@ 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)
+
diff --git a/src/test/regress/expected/multirangetypes.out b/src/test/regress/expected/multirangetypes.out
new file mode 100644
index 0000000000..723f2408b0
--- /dev/null
+++ b/src/test/regress/expected/multirangetypes.out
@@ -0,0 +1,2395 @@
+-- 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)
+
+-- 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;
+--
+-- Test multiple multirange types over the same subtype
+--
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+select textmultirange1(textrange2('a','Z')); -- should fail
+ERROR: function textmultirange1(textrange2) does not exist
+LINE 1: select textmultirange1(textrange2('a','Z'));
+ ^
+HINT: No function matches the given name and argument types. You might need to add explicit type casts.
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+ERROR: range lower bound must be less than or equal to range upper bound
+select textmultirange2(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 function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+-- 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 function returning a polymorphic type must have at least one polymorphic argument.
+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.
+--
+-- 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 function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--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 function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
+--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 function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 40468e8f49..1514f3af7c 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -38,6 +38,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;
@@ -181,7 +185,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, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@@ -190,6 +195,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
@@ -206,6 +213,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
@@ -224,6 +233,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
@@ -329,13 +340,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'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
------+------------------
@@ -343,16 +355,19 @@ ORDER BY 2;
2502 | anyarray_recv
2312 | anyelement_in
3504 | anyenum_in
+ 8002 | anymultirange_in
2777 | anynonarray_in
3832 | anyrange_in
750 | array_in
2400 | array_recv
3506 | enum_in
3532 | enum_recv
+ 8004 | multirange_in
+ 8006 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
-(13 rows)
+(16 rows)
-- Look for functions that accept cstring and are neither datatype input
-- functions nor encoding conversion functions. It's almost never a good
@@ -2139,13 +2154,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.
diff --git a/src/test/regress/expected/rangetypes.out b/src/test/regress/expected/rangetypes.out
index 220f2d96cb..6d00a3efab 100644
--- a/src/test/regress/expected/rangetypes.out
+++ b/src/test/regress/expected/rangetypes.out
@@ -518,6 +518,24 @@ 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)
+
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,7 +1389,7 @@ 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 function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
@@ -1482,6 +1500,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));
@@ -1490,6 +1509,16 @@ select * from outparam_succeed(int4range(1,2));
[1,2) | foo
(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));
@@ -1498,6 +1527,7 @@ select * from inoutparam_succeed(int4range(1,2));
2 | [1,2)
(1 row)
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
select * from table_succeed(123, int4range(1,11));
@@ -1510,14 +1540,14 @@ select * from table_succeed(123, 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 function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--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 function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
--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 function returning "anyrange" must have at least one "anyrange" argument.
+DETAIL: A function returning "anyrange" or "anymultirange" must have at least one "anyrange" or "anymultirange" argument.
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index 192445878d..d9ce961be2 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -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
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index cd7fc03b04..60a9b4d22f 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -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'));
@@ -193,18 +193,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
@@ -238,17 +239,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
@@ -316,18 +318,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
@@ -361,17 +364,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
@@ -629,3 +633,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)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index d2b17dd3ea..5355da7b01 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -19,8 +19,9 @@ 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
# ----------
-test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes
+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
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index acba391332..1f677f63af 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -19,6 +19,7 @@ test: uuid
test: enum
test: money
test: rangetypes
+test: multirangetypes
test: pg_lsn
test: regproc
test: strings
diff --git a/src/test/regress/sql/hash_func.sql b/src/test/regress/sql/hash_func.sql
index b7ce8b21a3..f8a09a25ff 100644
--- a/src/test/regress/sql/hash_func.sql
+++ b/src/test/regress/sql/hash_func.sql
@@ -220,3 +220,13 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
(int4range(550274, 1550274)), (int4range(1550275, 208112489))) x(v)
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);
diff --git a/src/test/regress/sql/multirangetypes.sql b/src/test/regress/sql/multirangetypes.sql
new file mode 100644
index 0000000000..0f70919e1a
--- /dev/null
+++ b/src/test/regress/sql/multirangetypes.sql
@@ -0,0 +1,630 @@
+-- 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));
+
+-- 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;
+
+--
+-- Test multiple multirange types over the same subtype
+--
+
+create type textrange1 as range(subtype=text, collation="C");
+create type textrange2 as range(subtype=text, collation="C");
+
+select textmultirange1(textrange2('a','Z')); -- should fail
+select textmultirange1(textrange1('a','Z')) @> 'b'::text;
+select textmultirange2(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
+
+--
+-- 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;
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index f06f245db3..4110a56ad2 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -41,6 +41,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;
@@ -164,7 +168,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, p2.prorettype
FROM pg_proc AS p1, pg_proc AS p2
@@ -174,6 +179,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;
@@ -185,6 +192,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;
@@ -196,6 +205,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;
@@ -273,13 +284,14 @@ SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype IN
('anyelement'::regtype, 'anyarray'::regtype, 'anynonarray'::regtype,
- 'anyenum'::regtype, 'anyrange'::regtype)
+ 'anyenum'::regtype, 'anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
('anyelement'::regtype = ANY (p1.proargtypes) OR
'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;
-- Look for functions that accept cstring and are neither datatype input
diff --git a/src/test/regress/sql/rangetypes.sql b/src/test/regress/sql/rangetypes.sql
index 72d80bc9d4..07c9bcf6d3 100644
--- a/src/test/regress/sql/rangetypes.sql
+++ b/src/test/regress/sql/rangetypes.sql
@@ -118,6 +118,10 @@ 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;
+
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@@ -512,16 +516,25 @@ 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));
+-- 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;
select * from inoutparam_succeed(int4range(1,2));
+-- infer anyelement+anyrange from anyelement+anyrange
create function table_succeed(i anyelement, r anyrange) returns table(i anyelement, r anyrange)
as $$ select $1, $2 $$ language sql;
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index fed413bf98..2e34bc8b65 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -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'));
@@ -151,7 +151,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')
@@ -180,7 +180,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')
@@ -232,7 +232,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')
@@ -261,7 +261,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')
@@ -465,3 +465,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;
--
2.20.1
--fdj2RfSjLxBAspz7
Content-Type: text/x-diff; charset=us-ascii
Content-Disposition: attachment;
filename="v12-0002-match-a77315fdf2a197a925e670be2d8b376c4ac02efc.patch"
view thread (8+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected]
Subject: Re: [PATCH v12 1/5] Paul's main patch
In-Reply-To: <no-message-id-1882534@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox