Multirange datatypes

Multiranges are basically sorted arrays of non-overlapping ranges with
set-theoretic operations defined over them.

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

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

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

Catversion is bumped.

Discussion: https://postgr.es/m/CALNJ-vSUpQ_Y%3DjXvTxt1VYFztaBSsWVXeF1y6gTYQ4bOiWDLgQ%40mail.gmail.com
Discussion: https://postgr.es/m/a0b8026459d1e6167933be2104a6174e7d40d0ab.camel%40j-davis.com#fe7218c83b08068bfffb0c5293eceda0
Author: Paul Jungwirth, revised by me
Reviewed-by: David Fetter, Corey Huinker, Jeff Davis, Pavel Stehule
Reviewed-by: Alvaro Herrera, Tom Lane, Isaac Morland, David G. Johnston
Reviewed-by: Zhihong Yu, Alexander Korotkov
This commit is contained in:
Alexander Korotkov 2020-12-20 07:20:33 +03:00
parent 08b01d4dd9
commit 6df7a9698b
67 changed files with 10568 additions and 270 deletions

View File

@ -6237,6 +6237,16 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>rngmultitypid</structfield> <type>oid</type>
(references <link linkend="catalog-pg-type"><structname>pg_type</structname></link>.<structfield>oid</structfield>)
</para>
<para>
OID of the multirange type for this range type
</para></entry>
</row>
<row>
<entry role="catalog_table_entry"><para role="column_definition">
<structfield>rngcollation</structfield> <type>oid</type>
@ -8671,8 +8681,9 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
<literal>c</literal> for a composite type (e.g., a table's row type),
<literal>d</literal> for a domain,
<literal>e</literal> for an enum type,
<literal>p</literal> for a pseudo-type, or
<literal>r</literal> for a range type.
<literal>p</literal> for a pseudo-type,
<literal>r</literal> for a range type, or
<literal>m</literal> for a multirange type.
See also <structfield>typrelid</structfield> and
<structfield>typbasetype</structfield>.
</para></entry>

View File

@ -4907,6 +4907,10 @@ SELECT * FROM pg_attribute
<primary>anyrange</primary>
</indexterm>
<indexterm zone="datatype-pseudo">
<primary>anymultirange</primary>
</indexterm>
<indexterm zone="datatype-pseudo">
<primary>anycompatible</primary>
</indexterm>
@ -4923,6 +4927,10 @@ SELECT * FROM pg_attribute
<primary>anycompatiblerange</primary>
</indexterm>
<indexterm zone="datatype-pseudo">
<primary>anycompatiblemultirange</primary>
</indexterm>
<indexterm zone="datatype-pseudo">
<primary>void</primary>
</indexterm>
@ -5034,6 +5042,13 @@ SELECT * FROM pg_attribute
<xref linkend="rangetypes"/>).</entry>
</row>
<row>
<entry><type>anymultirange</type></entry>
<entry>Indicates that a function accepts any multirange data type
(see <xref linkend="extend-types-polymorphic"/> and
<xref linkend="rangetypes"/>).</entry>
</row>
<row>
<entry><type>anycompatible</type></entry>
<entry>Indicates that a function accepts any data type,
@ -5063,6 +5078,14 @@ SELECT * FROM pg_attribute
<xref linkend="rangetypes"/>).</entry>
</row>
<row>
<entry><type>anycompatiblemultirange</type></entry>
<entry>Indicates that a function accepts any multirange data type,
with automatic promotion of multiple arguments to a common data type
(see <xref linkend="extend-types-polymorphic"/> and
<xref linkend="rangetypes"/>).</entry>
</row>
<row>
<entry><type>cstring</type></entry>
<entry>Indicates that a function accepts or returns a null-terminated C string.</entry>

View File

@ -288,6 +288,14 @@
</entry>
</row>
<row>
<entry><type>anymultirange</type></entry>
<entry>Simple</entry>
<entry>Indicates that a function accepts any multirange data type
(see <xref linkend="rangetypes"/>)
</entry>
</row>
<row>
<entry><type>anycompatible</type></entry>
<entry>Common</entry>
@ -319,6 +327,14 @@
with automatic promotion of multiple arguments to a common data type
</entry>
</row>
<row>
<entry><type>anycompatiblemultirange</type></entry>
<entry>Common</entry>
<entry>Indicates that a function accepts any multirange data type,
with automatic promotion of multiple arguments to a common data type
</entry>
</row>
</tbody>
</tgroup>
</table>
@ -346,17 +362,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> or <type>anyarray</type>,
the actual range type in the <type>anyrange</type> positions must be a
range whose subtype is the same type appearing in
the <type>anyelement</type> positions and the same as the element type
of the <type>anyarray</type> positions.
<type>anynonarray</type> is treated exactly the same as <type>anyelement</type>,
but adds the additional constraint that the actual type must not be
an array type.
@ -365,6 +379,19 @@
be an enum type.
</para>
<para>
Similarly, if there are positions declared <type>anyrange</type>
and others declared <type>anyelement</type> or <type>anyarray</type>,
the actual range type in the <type>anyrange</type> positions must be a
range whose subtype is the same type appearing in
the <type>anyelement</type> positions and the same as the element type
of the <type>anyarray</type> positions.
If there are positions declared <type>anymultirange</type>,
their actual multirange type must contain ranges matching parameters declared
<type>anyrange</type> and base elements matching parameters declared
<type>anyelement</type> and <type>anyarray</type>.
</para>
<para>
Thus, when more than one argument position is declared with a polymorphic
type, the net effect is that only certain combinations of actual argument
@ -420,7 +447,8 @@
Selection of the common type considers the actual types
of <type>anycompatible</type> and <type>anycompatiblenonarray</type>
inputs, the array element types of <type>anycompatiblearray</type>
inputs, and the range subtypes of <type>anycompatiblerange</type>
inputs, the range subtypes of <type>anycompatiblerange</type> inputs,
and the multirange subtypes of <type>anycompatiablemultirange</type>
inputs. If <type>anycompatiblenonarray</type> is present then the
common type is required to be a non-array type. Once a common type is
identified, arguments in <type>anycompatible</type>
@ -431,12 +459,15 @@
<para>
Since there is no way to select a range type knowing only its subtype,
use of <type>anycompatiblerange</type> requires that all arguments
declared with that type have the same actual range type, and that that
type's subtype agree with the selected common type, so that no casting
of the range values is required. As with <type>anyrange</type>, use
of <type>anycompatiblerange</type> as a function result type requires
that there be an <type>anycompatiblerange</type> argument.
use of <type>anycompatiblerange</type> and/or
<type>anycompatiblemultirange</type> requires that all arguments declared
with that type have the same actual range and/or multirange type, and that
that type's subtype agree with the selected common type, so that no casting
of the range values is required. As with <type>anyrange</type> and
<type>anymultirange</type>, use of <type>anycompatiblerange</type> and
<type>anymultirange</type> as a function result type requires that there be
an <type>anycompatiblerange</type> or <type>anycompatiblemultirange</type>
argument.
</para>
<para>

View File

@ -17884,12 +17884,15 @@ SELECT NULLIF(value, '(none)') ...
<para>
<xref linkend="range-operators-table"/> shows the specialized operators
available for range types.
<xref linkend="multirange-operators-table"/> shows the specialized operators
available for multirange types.
In addition to those, the usual comparison operators shown in
<xref linkend="functions-comparison-op-table"/> are available for range
types. The comparison operators order first by the range lower bounds, and
only if those are equal do they compare the upper bounds. This does not
usually result in a useful overall ordering, but the operators are provided
to allow unique indexes to be constructed on ranges.
and multirange types. The comparison operators order first by the range lower
bounds, and only if those are equal do they compare the upper bounds. The
multirange operators compare each range until one is unequal. This
does not usually result in a useful overall ordering, but the operators are
provided to allow unique indexes to be constructed on ranges.
</para>
<table id="range-operators-table">
@ -18099,15 +18102,449 @@ SELECT NULLIF(value, '(none)') ...
</tgroup>
</table>
<table id="multirange-operators-table">
<title>Multirange Operators</title>
<tgroup cols="1">
<thead>
<row>
<entry role="func_table_entry"><para role="func_signature">
Operator
</para>
<para>
Description
</para>
<para>
Example(s)
</para></entry>
</row>
</thead>
<tbody>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>@&gt;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the first multirange contain the second?
</para>
<para>
<literal>'{[2,4)}'::int4multirange @&gt; '{[2,3)}'::int4multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>@&gt;</literal> <type>anyrange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the multirange contain the range?
</para>
<para>
<literal>'{[2,4)}'::int4multirange @&gt; int4range(2,3)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>@&gt;</literal> <type>anyelement</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the multirange contain the element?
</para>
<para>
<literal>'{[2011-01-01,2011-03-01)}'::tsmultirange @&gt; '2011-01-10'::timestamp</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&lt;@</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the first multirange contained by the second?
</para>
<para>
<literal>'{[2,4)}'::int4multirange &lt;@ '{[1,7)}'::int4multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&lt;@</literal> <type>anyrange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the multirange contained by the range?
</para>
<para>
<literal>'{[2,4)}'::int4multirange &lt;@ int4range(1,7)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anyrange</type> <literal>&lt;@</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the range contained by the multirange?
</para>
<para>
<literal>int4range(2,4) &lt;@ '{[1,7)}'::int4multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anyelement</type> <literal>&lt;@</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the element contained by the multirange?
</para>
<para>
<literal>42 &lt;@ '{[1,7)}'::int4multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&amp;&amp;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Do the multiranges overlap, that is, have any elements in common?
</para>
<para>
<literal>'{[3,7)}'::int8multirange &amp;&amp; '{[4,12)}'::int8multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&amp;&amp;</literal> <type>anyrange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the multirange overlap the range?
</para>
<para>
<literal>'{[3,7)}'::int8multirange &amp;&amp; int8range(4,12)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anyrange</type> <literal>&amp;&amp;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the range overlap the multirange?
</para>
<para>
<literal>int8range(3,7) &amp;&amp; '{[4,12)}'::int8multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&lt;&lt;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the first multirange strictly left of the second?
</para>
<para>
<literal>'{[1,10)}'::int8multirange &lt;&lt; '{[100,110)}'::int8multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&lt;&lt;</literal> <type>anyrange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the multirange strictly left of the range?
</para>
<para>
<literal>'{[1,10)}'::int8multirange &lt;&lt; int8range(100,110)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anyrange</type> <literal>&lt;&lt;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the range strictly left of the multirange?
</para>
<para>
<literal>int8range(1,10) &lt;&lt; '{[100,110)}'::int8multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&gt;&gt;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the first multirange strictly right of the second?
</para>
<para>
<literal>'{[50,60)}'::int8multirange &gt;&gt; '{[20,30)}'::int8multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&gt;&gt;</literal> <type>anyrange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the multirange strictly right of the range?
</para>
<para>
<literal>'{[50,60)}'::int8multirange &gt;&gt; int8range(20,30)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anyrange</type> <literal>&gt;&gt;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the range strictly right of the multirange?
</para>
<para>
<literal>int8range(50,60) &gt;&gt; '{[20,30)}'::int8multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&amp;&lt;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the first multirange not extend to the right of the second?
</para>
<para>
<literal>'{[1,20)}'::int8multirange &amp;&lt; '{[18,20)}'::int8multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&amp;&lt;</literal> <type>anyrange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the multirange not extend to the right of the range?
</para>
<para>
<literal>'{[1,20)}'::int8multirange &amp;&lt; int8range(18,20)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anyrange</type> <literal>&amp;&lt;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the range not extend to the right of the multirange?
</para>
<para>
<literal>int8range(1,20) &amp;&lt; '{[18,20)}'::int8multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&amp;&gt;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the first multirange not extend to the left of the second?
</para>
<para>
<literal>'{[7,20)}'::int8multirange &amp;&gt; '{[5,10)}'::int8multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>&amp;&gt;</literal> <type>anyrange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the multirange not extend to the left of the range?
</para>
<para>
<literal>'{[7,20)}'::int8multirange &amp;&gt; int8range(5,10)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anyrange</type> <literal>&amp;&gt;</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Does the range not extend to the left of the multirange?
</para>
<para>
<literal>int8range(7,20) &amp;&gt; '{[5,10)}'::int8multirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>-|-</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Are the multiranges adjacent?
</para>
<para>
<literal>'{[1.1,2.2)}'::nummultirange -|- '{[2.2,3.3)}'::nummultirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>-|-</literal> <type>anyrange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the multirange adjacent to the range?
</para>
<para>
<literal>'{[1.1,2.2)}'::nummultirange -|- numrange(2.2,3.3)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anyrange</type> <literal>-|-</literal> <type>anymultirange</type>
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the range adjacent to the multirange?
</para>
<para>
<literal>numrange(1.1,2.2) -|- '{[2.2,3.3)}'::nummultirange</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>+</literal> <type>anymultirange</type>
<returnvalue>anymultirange</returnvalue>
</para>
<para>
Computes the union of the multiranges. The multiranges need not overlap
or be adjacent.
</para>
<para>
<literal>'{[5,10)}'::nummultirange + '{[15,20)}'::nummultirange</literal>
<returnvalue>{[5,10), [15,20)}</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>*</literal> <type>anymultirange</type>
<returnvalue>anymultirange</returnvalue>
</para>
<para>
Computes the intersection of the multiranges.
</para>
<para>
<literal>'{[5,15)}'::int8multirange * '{[10,20)}'::int8multirange</literal>
<returnvalue>{[10,15)}</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<type>anymultirange</type> <literal>-</literal> <type>anymultirange</type>
<returnvalue>anymultirange</returnvalue>
</para>
<para>
Computes the difference of the multiranges.
</para>
<para>
<literal>'{[5,20)}'::int8multirange - '{[10,15)}'::int8multirange</literal>
<returnvalue>{[5,10), [15,20)}</returnvalue>
</para></entry>
</row>
</tbody>
</tgroup>
</table>
<para>
The left-of/right-of/adjacent operators always return false when an empty
range is involved; that is, an empty range is not considered to be either
before or after any other range.
range or multirange is involved; that is, an empty range is not considered to
be either before or after any other range.
</para>
<para>
Elsewhere empty ranges and multiranges are treated as the additive identity:
anything unioned with an empty value is itself. Anything minus an empty
value is itself. An empty multirange has exactly the same points as an empty
range. Every range contains the empty range. Every multirange contains as many
empty ranges as you like.
</para>
<para>
The range union and difference operators will fail if the resulting range would
need to contain two disjoint sub-ranges, as such a range cannot be
represented. There are separate operators for union and difference that take
multirange parameters and return a multirange, and they do not fail even if
their arguments are disjoint. So if you need a union or difference operation
for ranges that may be disjoint, you can avoid errors by first casting your
ranges to multiranges.
</para>
<para>
<xref linkend="range-functions-table"/> shows the functions
available for use with range types.
<xref linkend="multirange-functions-table"/> shows the functions
available for use with multirange types.
</para>
<table id="range-functions-table">
@ -18269,10 +18706,185 @@ SELECT NULLIF(value, '(none)') ...
</tgroup>
</table>
<table id="multirange-functions-table">
<title>Multirange Functions</title>
<tgroup cols="1">
<thead>
<row>
<entry role="func_table_entry"><para role="func_signature">
Function
</para>
<para>
Description
</para>
<para>
Example(s)
</para></entry>
</row>
</thead>
<tbody>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>lower</primary>
</indexterm>
<function>lower</function> ( <type>anymultirange</type> )
<returnvalue>anyelement</returnvalue>
</para>
<para>
Extracts the lower bound of the multirange (<literal>NULL</literal> if the
multirange is empty or the lower bound is infinite).
</para>
<para>
<literal>lower('{[1.1,2.2)}'::nummultirange)</literal>
<returnvalue>1.1</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>upper</primary>
</indexterm>
<function>upper</function> ( <type>anymultirange</type> )
<returnvalue>anyelement</returnvalue>
</para>
<para>
Extracts the upper bound of the multirange (<literal>NULL</literal> if the
multirange is empty or the upper bound is infinite).
</para>
<para>
<literal>upper('{[1.1,2.2)}'::nummultirange)</literal>
<returnvalue>2.2</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>isempty</primary>
</indexterm>
<function>isempty</function> ( <type>anymultirange</type> )
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the multirange empty?
</para>
<para>
<literal>isempty('{[1.1,2.2)}'::nummultirange)</literal>
<returnvalue>f</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>lower_inc</primary>
</indexterm>
<function>lower_inc</function> ( <type>anymultirange</type> )
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the multirange's lower bound inclusive?
</para>
<para>
<literal>lower_inc('{[1.1,2.2)}'::nummultirange)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>upper_inc</primary>
</indexterm>
<function>upper_inc</function> ( <type>anymultirange</type> )
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the multirange's upper bound inclusive?
</para>
<para>
<literal>upper_inc('{[1.1,2.2)}'::nummultirange)</literal>
<returnvalue>f</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>lower_inf</primary>
</indexterm>
<function>lower_inf</function> ( <type>anymultirange</type> )
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the multirange's lower bound infinite?
</para>
<para>
<literal>lower_inf('{(,)}'::datemultirange)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>upper_inf</primary>
</indexterm>
<function>upper_inf</function> ( <type>anymultirange</type> )
<returnvalue>boolean</returnvalue>
</para>
<para>
Is the multirange's upper bound infinite?
</para>
<para>
<literal>upper_inf('{(,)}'::datemultirange)</literal>
<returnvalue>t</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>range_merge</primary>
</indexterm>
<function>range_merge</function> ( <type>anymultirange</type> )
<returnvalue>anyrange</returnvalue>
</para>
<para>
Computes the smallest range that includes the entire multirange.
</para>
<para>
<literal>range_merge('{[1,2), [3,4)}'::int4multirange)</literal>
<returnvalue>[1,4)</returnvalue>
</para></entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>multirange</primary>
</indexterm>
<function>multirange</function> ( <type>anyrange</type> )
<returnvalue>anymultirange</returnvalue>
</para>
<para>
Returns a multirange containing just the given range.
</para>
<para>
<literal>multirange('[1,2)'::int4range)</literal>
<returnvalue>{[1,2)}</returnvalue>
</para></entry>
</row>
</tbody>
</tgroup>
</table>
<para>
The <function>lower_inc</function>, <function>upper_inc</function>,
<function>lower_inf</function>, and <function>upper_inf</function>
functions all return false for an empty range.
functions all return false for an empty range or multirange.
</para>
</sect1>
@ -18604,6 +19216,36 @@ SELECT NULLIF(value, '(none)') ...
<entry>Yes</entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>range_agg</primary>
</indexterm>
<function>range_agg</function> ( <parameter>value</parameter>
<type>anyrange</type> )
<returnvalue>anymultirange</returnvalue>
</para>
<para>
Computes the union of the non-null input values.
</para></entry>
<entry>No</entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>
<primary>range_intersect_agg</primary>
</indexterm>
<function>range_intersect_agg</function> ( <parameter>value</parameter>
<type>anyrange</type> )
<returnvalue>anymultirange</returnvalue>
</para>
<para>
Computes the intersection of the non-null input values.
</para></entry>
<entry>No</entry>
</row>
<row>
<entry role="func_table_entry"><para role="func_signature">
<indexterm>

View File

@ -27,40 +27,53 @@
ranges from an instrument, and so forth can also be useful.
</para>
<para>
Every range type has a corresponding multirange type. A multirange is
an ordered list of non-continguous, non-empty, non-null ranges. Most
range operators also work on multiranges, and they have a few functions
of their own.
</para>
<sect2 id="rangetypes-builtin">
<title>Built-in Range Types</title>
<title>Built-in Range and Multirange Types</title>
<para>
PostgreSQL comes with the following built-in range types:
<itemizedlist>
<listitem>
<para>
<type>int4range</type> &mdash; Range of <type>integer</type>
<type>int4range</type> &mdash; Range of <type>integer</type>,
<type>int4multirange</type> &mdash; corresponding Multirange
</para>
</listitem>
<listitem>
<para>
<type>int8range</type> &mdash; Range of <type>bigint</type>
<type>int8range</type> &mdash; Range of <type>bigint</type>,
<type>int8multirange</type> &mdash; corresponding Multirange
</para>
</listitem>
<listitem>
<para>
<type>numrange</type> &mdash; Range of <type>numeric</type>
<type>numrange</type> &mdash; Range of <type>numeric</type>,
<type>nummultirange</type> &mdash; corresponding Multirange
</para>
</listitem>
<listitem>
<para>
<type>tsrange</type> &mdash; Range of <type>timestamp without time zone</type>
<type>tsrange</type> &mdash; Range of <type>timestamp without time zone</type>,
<type>tsmultirange</type> &mdash; corresponding Multirange
</para>
</listitem>
<listitem>
<para>
<type>tstzrange</type> &mdash; Range of <type>timestamp with time zone</type>
<type>tstzrange</type> &mdash; Range of <type>timestamp with time zone</type>,
<type>tstzmultirange</type> &mdash; corresponding Multirange
</para>
</listitem>
<listitem>
<para>
<type>daterange</type> &mdash; Range of <type>date</type>
<type>daterange</type> &mdash; Range of <type>date</type>,
<type>datemultirange</type> &mdash; corresponding Multirange
</para>
</listitem>
</itemizedlist>
@ -232,10 +245,30 @@ SELECT '[4,4]'::int4range;
SELECT '[4,4)'::int4range;
</programlisting>
</para>
<para>
The input for a multirange is curly brackets (<literal>{</literal> and
<literal>}</literal>) containing zero or more valid ranges,
separated by commas. Whitespace is permitted around the brackets and
commas. This is intended to be reminiscent of array syntax, although
multiranges are much simpler: they have just one dimension and there is
no need to quote their contents. (The bounds of their ranges may be
quoted as above however.)
</para>
<para>
Examples:
<programlisting>
SELECT '{}'::int4multirange;
SELECT '{[3,7)}'::int4multirange;
SELECT '{[3,7), [8,9)}'::int4multirange;
</programlisting>
</para>
</sect2>
<sect2 id="rangetypes-construct">
<title>Constructing Ranges</title>
<title>Constructing Ranges and Multiranges</title>
<para>
Each range type has a constructor function with the same name as the range
@ -267,6 +300,19 @@ SELECT int8range(1, 14, '(]');
-- Using NULL for either bound causes the range to be unbounded on that side.
SELECT numrange(NULL, 2.2);
</programlisting>
</para>
<para>
Each range type also has a multirange constructor with the same name as the
multirange type. The constructor function takes zero or more arguments
which are all ranges of the appropriate type.
For example:
<programlisting>
SELECT nummultirange();
SELECT nummultirange(numrange(1.0, 14.0));
SELECT nummultirange(numrange(1.0, 14.0), numrange(20.0, 25.0));
</programlisting>
</para>
</sect2>
@ -341,6 +387,11 @@ SELECT '[1.234, 5.678]'::floatrange;
function in this example.
</para>
<para>
When you define your own range you automatically get a corresponding
multirange type.
</para>
<para>
Defining your own range type also allows you to specify a different
subtype B-tree operator class or collation to use, so as to change the sort

View File

@ -33,6 +33,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> AS RANGE (
[ , COLLATION = <replaceable class="parameter">collation</replaceable> ]
[ , CANONICAL = <replaceable class="parameter">canonical_function</replaceable> ]
[ , SUBTYPE_DIFF = <replaceable class="parameter">subtype_diff_function</replaceable> ]
[ , MULTIRANGE_TYPE_NAME = <replaceable class="parameter">multirange_type_name</replaceable> ]
)
CREATE TYPE <replaceable class="parameter">name</replaceable> (
@ -174,6 +175,17 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
the range type. See <xref linkend="rangetypes-defining"/> for more
information.
</para>
<para>
The optional <replaceable class="parameter">multirange_type_name</replaceable>
parameter specifies the name of the corresponding multirange type. If not
specified, this name is chosen automatically as follows.
If range type name contains <literal>range</literal> substring, then
multirange type name is formed by replacement of the <literal>range</literal>
substring with <literal>multirange</literal> substring in the range
type name. Otherwise, multirange type name is formed by appending
<literal>_multirange</literal> suffix to the range type name.
</para>
</refsect2>
<refsect2>
@ -630,6 +642,15 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">multirange_type_name</replaceable></term>
<listitem>
<para>
The name of the corresponding multirange type.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><replaceable class="parameter">input_function</replaceable></term>
<listitem>

View File

@ -35,7 +35,7 @@
void
RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
RegProcedure rangeSubDiff)
RegProcedure rangeSubDiff, Oid multirangeTypeOid)
{
Relation pg_range;
Datum values[Natts_pg_range];
@ -43,6 +43,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
HeapTuple tup;
ObjectAddress myself;
ObjectAddress referenced;
ObjectAddress referencing;
ObjectAddresses *addrs;
pg_range = table_open(RangeRelationId, RowExclusiveLock);
@ -55,6 +56,7 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
values[Anum_pg_range_rngsubopc - 1] = ObjectIdGetDatum(rangeSubOpclass);
values[Anum_pg_range_rngcanonical - 1] = ObjectIdGetDatum(rangeCanonical);
values[Anum_pg_range_rngsubdiff - 1] = ObjectIdGetDatum(rangeSubDiff);
values[Anum_pg_range_rngmultitypid - 1] = ObjectIdGetDatum(multirangeTypeOid);
tup = heap_form_tuple(RelationGetDescr(pg_range), values, nulls);
@ -93,6 +95,12 @@ RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
record_object_address_dependencies(&myself, addrs, DEPENDENCY_NORMAL);
free_object_addresses(addrs);
/* record multirange type's dependency on the range type */
referencing.classId = TypeRelationId;
referencing.objectId = multirangeTypeOid;
referencing.objectSubId = 0;
recordDependencyOn(&referencing, &myself, DEPENDENCY_INTERNAL);
table_close(pg_range, RowExclusiveLock);
}

View File

@ -28,6 +28,7 @@
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/typecmds.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "parser/scansup.h"
#include "utils/acl.h"
@ -37,6 +38,9 @@
#include "utils/rel.h"
#include "utils/syscache.h"
static char *makeUniqueTypeName(const char *typeName, Oid typeNamespace,
bool tryOriginal);
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_pg_type_oid = InvalidOid;
@ -863,31 +867,10 @@ RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
char *
makeArrayTypeName(const char *typeName, Oid typeNamespace)
{
char *arr = (char *) palloc(NAMEDATALEN);
int namelen = strlen(typeName);
int i;
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\"",
@ -958,3 +941,90 @@ moveArrayTypeName(Oid typeOid, const char *typeName, Oid typeNamespace)
return true;
}
/*
* makeMultirangeTypeName
* - given a range type name, make a multirange type name for it
*
* caller is responsible for pfreeing the result
*/
char *
makeMultirangeTypeName(const char *rangeTypeName, Oid typeNamespace)
{
char *buf;
char *rangestr;
/*
* If the range type name contains "range" then change that to
* "multirange". Otherwise add "_multirange" to the end.
*/
rangestr = strstr(rangeTypeName, "range");
if (rangestr)
{
char *prefix = pnstrdup(rangeTypeName, rangestr - rangeTypeName);
buf = psprintf("%s%s%s", prefix, "multi", rangestr);
}
else
buf = psprintf("%s_multirange", pnstrdup(rangeTypeName, NAMEDATALEN - 12));
/* clip it at NAMEDATALEN-1 bytes */
buf[pg_mbcliplen(buf, strlen(buf), NAMEDATALEN - 1)] = '\0';
if (SearchSysCacheExists2(TYPENAMENSP,
CStringGetDatum(buf),
ObjectIdGetDatum(typeNamespace)))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("type \"%s\" already exists", buf),
errdetail("Failed while creating a multirange type for type \"%s\".", rangeTypeName),
errhint("You can manually specify a multirange type name using the \"multirange_type_name\" attribute")));
return pstrdup(buf);
}
/*
* makeUniqueTypeName
* Generate a unique name for a prospective new type
*
* Given a typeName, return a new palloc'ed name by preprending underscores
* until a non-conflicting name results.
*
* If tryOriginal, first try with zero underscores.
*/
static char *
makeUniqueTypeName(const char *typeName, Oid typeNamespace, bool tryOriginal)
{
int i;
int namelen;
char dest[NAMEDATALEN];
Assert(strlen(typeName) <= NAMEDATALEN - 1);
if (tryOriginal &&
!SearchSysCacheExists2(TYPENAMENSP,
CStringGetDatum(typeName),
ObjectIdGetDatum(typeNamespace)))
return pstrdup(typeName);
/*
* The idea is to prepend underscores as needed until we make a name that
* doesn't collide with anything ...
*/
namelen = strlen(typeName);
for (i = 1; i < NAMEDATALEN - 1; i++)
{
dest[i - 1] = '_';
strlcpy(dest + i, typeName, NAMEDATALEN - i);
if (namelen + i >= NAMEDATALEN)
truncate_identifier(dest, NAMEDATALEN, false);
if (!SearchSysCacheExists2(TYPENAMENSP,
CStringGetDatum(dest),
ObjectIdGetDatum(typeNamespace)))
return pstrdup(dest);
}
return NULL;
}

View File

@ -107,9 +107,14 @@ typedef struct
/* Potentially set by pg_upgrade_support functions */
Oid binary_upgrade_next_array_pg_type_oid = InvalidOid;
Oid binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
Oid binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
static void makeRangeConstructors(const char *name, Oid namespace,
Oid rangeOid, Oid subtype);
static void makeMultirangeConstructors(const char *name, Oid namespace,
Oid multirangeOid, Oid rangeOid,
Oid rangeArrayOid, Oid *castFuncOid);
static Oid findTypeInputFunction(List *procname, Oid typeOid);
static Oid findTypeOutputFunction(List *procname, Oid typeOid);
static Oid findTypeReceiveFunction(List *procname, Oid typeOid);
@ -772,7 +777,8 @@ DefineDomain(CreateDomainStmt *stmt)
typtype != TYPTYPE_COMPOSITE &&
typtype != TYPTYPE_DOMAIN &&
typtype != TYPTYPE_ENUM &&
typtype != TYPTYPE_RANGE)
typtype != TYPTYPE_RANGE &&
typtype != TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
@ -1323,6 +1329,11 @@ checkEnumOwner(HeapTuple tup)
/*
* DefineRange
* Registers a new range type.
*
* Perhaps it might be worthwhile to set pg_type.typelem to the base type,
* and likewise on multiranges to set it to the range type. But having a
* non-zero typelem is treated elsewhere as a synonym for being an array,
* and users might have queries with that same assumption.
*/
ObjectAddress
DefineRange(CreateRangeStmt *stmt)
@ -1331,7 +1342,12 @@ DefineRange(CreateRangeStmt *stmt)
Oid typeNamespace;
Oid typoid;
char *rangeArrayName;
char *multirangeTypeName = NULL;
char *multirangeArrayName;
Oid multirangeNamespace = InvalidOid;
Oid rangeArrayOid;
Oid multirangeOid;
Oid multirangeArrayOid;
Oid rangeSubtype = InvalidOid;
List *rangeSubOpclassName = NIL;
List *rangeCollationName = NIL;
@ -1348,6 +1364,8 @@ DefineRange(CreateRangeStmt *stmt)
AclResult aclresult;
ListCell *lc;
ObjectAddress address;
ObjectAddress mltrngaddress;
Oid castFuncOid;
/* Convert list of names to a name and namespace */
typeNamespace = QualifiedNameGetCreationNamespace(stmt->typeName,
@ -1431,6 +1449,16 @@ DefineRange(CreateRangeStmt *stmt)
errmsg("conflicting or redundant options")));
rangeSubtypeDiffName = defGetQualifiedName(defel);
}
else if (strcmp(defel->defname, "multirange_type_name") == 0)
{
if (multirangeTypeName != NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
/* we can look up the subtype name immediately */
multirangeNamespace = QualifiedNameGetCreationNamespace(defGetQualifiedName(defel),
&multirangeTypeName);
}
else
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@ -1496,8 +1524,10 @@ DefineRange(CreateRangeStmt *stmt)
/* alignment must be TYPALIGN_INT or TYPALIGN_DOUBLE for ranges */
alignment = (subtypalign == TYPALIGN_DOUBLE) ? TYPALIGN_DOUBLE : TYPALIGN_INT;
/* Allocate OID for array type */
/* Allocate OID for array type, its multirange, and its multirange array */
rangeArrayOid = AssignTypeArrayOid();
multirangeOid = AssignTypeMultirangeOid();
multirangeArrayOid = AssignTypeMultirangeArrayOid();
/* Create the pg_type entry */
address =
@ -1536,9 +1566,75 @@ DefineRange(CreateRangeStmt *stmt)
Assert(typoid == InvalidOid || typoid == address.objectId);
typoid = address.objectId;
/* Create the multirange that goes with it */
if (multirangeTypeName)
{
Oid old_typoid;
/*
* Look to see if multirange type already exists.
*/
old_typoid = GetSysCacheOid2(TYPENAMENSP, Anum_pg_type_oid,
CStringGetDatum(multirangeTypeName),
ObjectIdGetDatum(multirangeNamespace));
/*
* If it's not a shell, see if it's an autogenerated array type, and if so
* rename it out of the way.
*/
if (OidIsValid(old_typoid) && get_typisdefined(old_typoid))
{
if (!moveArrayTypeName(old_typoid, multirangeTypeName, multirangeNamespace))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("type \"%s\" already exists", multirangeTypeName)));
}
}
else
{
/* Generate multirange name automatically */
multirangeNamespace = typeNamespace;
multirangeTypeName = makeMultirangeTypeName(typeName, multirangeNamespace);
}
mltrngaddress =
TypeCreate(multirangeOid, /* force assignment of this type OID */
multirangeTypeName, /* type name */
multirangeNamespace, /* namespace */
InvalidOid, /* relation oid (n/a here) */
0, /* relation kind (ditto) */
GetUserId(), /* owner's ID */
-1, /* internal size (always varlena) */
TYPTYPE_MULTIRANGE, /* type-type (multirange type) */
TYPCATEGORY_RANGE, /* type-category (range type) */
false, /* multirange types are never preferred */
DEFAULT_TYPDELIM, /* array element delimiter */
F_MULTIRANGE_IN, /* input procedure */
F_MULTIRANGE_OUT, /* output procedure */
F_MULTIRANGE_RECV, /* receive procedure */
F_MULTIRANGE_SEND, /* send procedure */
InvalidOid, /* typmodin procedure - none */
InvalidOid, /* typmodout procedure - none */
F_MULTIRANGE_TYPANALYZE, /* analyze procedure */
InvalidOid, /* subscript procedure - none */
InvalidOid, /* element type ID - none */
false, /* this is not an array type */
multirangeArrayOid, /* array type we are about to create */
InvalidOid, /* base type ID (only for domains) */
NULL, /* never a default type value */
NULL, /* no binary form available either */
false, /* never passed by value */
alignment, /* alignment */
'x', /* TOAST strategy (always extended) */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
InvalidOid); /* type's collation (ranges never have one) */
Assert(multirangeOid == mltrngaddress.objectId);
/* Create the entry in pg_range */
RangeCreate(typoid, rangeSubtype, rangeCollation, rangeSubOpclass,
rangeCanonical, rangeSubtypeDiff);
rangeCanonical, rangeSubtypeDiff, multirangeOid);
/*
* Create the array type that goes with it.
@ -1580,8 +1676,54 @@ DefineRange(CreateRangeStmt *stmt)
pfree(rangeArrayName);
/* Create the multirange's array type */
multirangeArrayName = makeArrayTypeName(multirangeTypeName, typeNamespace);
TypeCreate(multirangeArrayOid, /* force assignment of this type OID */
multirangeArrayName, /* type name */
multirangeNamespace, /* namespace */
InvalidOid, /* relation oid (n/a here) */
0, /* relation kind (ditto) */
GetUserId(), /* owner's ID */
-1, /* internal size (always varlena) */
TYPTYPE_BASE, /* type-type (base type) */
TYPCATEGORY_ARRAY, /* type-category (array) */
false, /* array types are never preferred */
DEFAULT_TYPDELIM, /* array element delimiter */
F_ARRAY_IN, /* input procedure */
F_ARRAY_OUT, /* output procedure */
F_ARRAY_RECV, /* receive procedure */
F_ARRAY_SEND, /* send procedure */
InvalidOid, /* typmodin procedure - none */
InvalidOid, /* typmodout procedure - none */
F_ARRAY_TYPANALYZE, /* analyze procedure */
F_ARRAY_SUBSCRIPT_HANDLER, /* array subscript procedure */
multirangeOid, /* element type ID */
true, /* yes this is an array type */
InvalidOid, /* no further array type */
InvalidOid, /* base type ID */
NULL, /* never a default type value */
NULL, /* binary default isn't sent either */
false, /* never passed by value */
alignment, /* alignment - same as range's */
'x', /* ARRAY is always toastable */
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
InvalidOid); /* typcollation */
/* And create the constructor functions for this range type */
makeRangeConstructors(typeName, typeNamespace, typoid, rangeSubtype);
makeMultirangeConstructors(multirangeTypeName, typeNamespace,
multirangeOid, typoid, rangeArrayOid,
&castFuncOid);
/* Create cast from the range type to its multirange type */
CastCreate(typoid, multirangeOid, castFuncOid, 'e', 'f', DEPENDENCY_INTERNAL);
pfree(multirangeTypeName);
pfree(multirangeArrayName);
return address;
}
@ -1659,6 +1801,149 @@ makeRangeConstructors(const char *name, Oid namespace,
}
}
/*
* We make a separate multirange constructor for each range type
* so its name can include the base type, like range constructors do.
* If we had an anyrangearray polymorphic type we could use it here,
* but since each type has its own constructor name there's no need.
*
* Sets castFuncOid to the oid of the new constructor that can be used
* to cast from a range to a multirange.
*/
static void
makeMultirangeConstructors(const char *name, Oid namespace,
Oid multirangeOid, Oid rangeOid, Oid rangeArrayOid,
Oid *castFuncOid)
{
ObjectAddress myself,
referenced;
oidvector *argtypes;
Datum allParamTypes;
ArrayType *allParameterTypes;
Datum paramModes;
ArrayType *parameterModes;
referenced.classId = TypeRelationId;
referenced.objectId = multirangeOid;
referenced.objectSubId = 0;
/* 0-arg constructor - for empty multiranges */
argtypes = buildoidvector(NULL, 0);
myself = ProcedureCreate(name, /* name: same as multirange type */
namespace,
false, /* replace */
false, /* returns set */
multirangeOid, /* return type */
BOOTSTRAP_SUPERUSERID, /* proowner */
INTERNALlanguageId, /* language */
F_FMGR_INTERNAL_VALIDATOR,
"multirange_constructor0", /* prosrc */
NULL, /* probin */
PROKIND_FUNCTION,
false, /* security_definer */
false, /* leakproof */
false, /* isStrict */
PROVOLATILE_IMMUTABLE, /* volatility */
PROPARALLEL_SAFE, /* parallel safety */
argtypes, /* parameterTypes */
PointerGetDatum(NULL), /* allParameterTypes */
PointerGetDatum(NULL), /* parameterModes */
PointerGetDatum(NULL), /* parameterNames */
NIL, /* parameterDefaults */
PointerGetDatum(NULL), /* trftypes */
PointerGetDatum(NULL), /* proconfig */
InvalidOid, /* prosupport */
1.0, /* procost */
0.0); /* prorows */
/*
* Make the constructor internally-dependent on the multirange type so
* that they go away silently when the type is dropped. Note that pg_dump
* depends on this choice to avoid dumping the constructors.
*/
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
pfree(argtypes);
/*
* 1-arg constructor - for casts
*
* In theory we shouldn't need both this and the vararg (n-arg)
* constructor, but having a separate 1-arg function lets us define casts
* against it.
*/
argtypes = buildoidvector(&rangeOid, 1);
myself = ProcedureCreate(name, /* name: same as multirange type */
namespace,
false, /* replace */
false, /* returns set */
multirangeOid, /* return type */
BOOTSTRAP_SUPERUSERID, /* proowner */
INTERNALlanguageId, /* language */
F_FMGR_INTERNAL_VALIDATOR,
"multirange_constructor1", /* prosrc */
NULL, /* probin */
PROKIND_FUNCTION,
false, /* security_definer */
false, /* leakproof */
true, /* isStrict */
PROVOLATILE_IMMUTABLE, /* volatility */
PROPARALLEL_SAFE, /* parallel safety */
argtypes, /* parameterTypes */
PointerGetDatum(NULL), /* allParameterTypes */
PointerGetDatum(NULL), /* parameterModes */
PointerGetDatum(NULL), /* parameterNames */
NIL, /* parameterDefaults */
PointerGetDatum(NULL), /* trftypes */
PointerGetDatum(NULL), /* proconfig */
InvalidOid, /* prosupport */
1.0, /* procost */
0.0); /* prorows */
/* ditto */
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
pfree(argtypes);
*castFuncOid = myself.objectId;
/* n-arg constructor - vararg */
argtypes = buildoidvector(&rangeArrayOid, 1);
allParamTypes = ObjectIdGetDatum(rangeArrayOid);
allParameterTypes = construct_array(&allParamTypes,
1, OIDOID,
sizeof(Oid), true, 'i');
paramModes = CharGetDatum(FUNC_PARAM_VARIADIC);
parameterModes = construct_array(&paramModes, 1, CHAROID,
1, true, 'c');
myself = ProcedureCreate(name, /* name: same as multirange type */
namespace,
false, /* replace */
false, /* returns set */
multirangeOid, /* return type */
BOOTSTRAP_SUPERUSERID, /* proowner */
INTERNALlanguageId, /* language */
F_FMGR_INTERNAL_VALIDATOR,
"multirange_constructor2", /* prosrc */
NULL, /* probin */
PROKIND_FUNCTION,
false, /* security_definer */
false, /* leakproof */
false, /* isStrict */
PROVOLATILE_IMMUTABLE, /* volatility */
PROPARALLEL_SAFE, /* parallel safety */
argtypes, /* parameterTypes */
PointerGetDatum(allParameterTypes), /* allParameterTypes */
PointerGetDatum(parameterModes), /* parameterModes */
PointerGetDatum(NULL), /* parameterNames */
NIL, /* parameterDefaults */
PointerGetDatum(NULL), /* trftypes */
PointerGetDatum(NULL), /* proconfig */
InvalidOid, /* prosupport */
1.0, /* procost */
0.0); /* prorows */
/* ditto */
recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL);
pfree(argtypes);
pfree(allParameterTypes);
pfree(parameterModes);
}
/*
* Find suitable I/O and other support functions for a type.
@ -2152,6 +2437,72 @@ AssignTypeArrayOid(void)
return type_array_oid;
}
/*
* AssignTypeMultirangeOid
*
* Pre-assign the range type's multirange OID for use in pg_type.oid
*/
Oid
AssignTypeMultirangeOid(void)
{
Oid type_multirange_oid;
/* Use binary-upgrade override for pg_type.oid? */
if (IsBinaryUpgrade)
{
if (!OidIsValid(binary_upgrade_next_mrng_pg_type_oid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("pg_type multirange OID value not set when in binary upgrade mode")));
type_multirange_oid = binary_upgrade_next_mrng_pg_type_oid;
binary_upgrade_next_mrng_pg_type_oid = InvalidOid;
}
else
{
Relation pg_type = table_open(TypeRelationId, AccessShareLock);
type_multirange_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
Anum_pg_type_oid);
table_close(pg_type, AccessShareLock);
}
return type_multirange_oid;
}
/*
* AssignTypeMultirangeArrayOid
*
* Pre-assign the range type's multirange array OID for use in pg_type.typarray
*/
Oid
AssignTypeMultirangeArrayOid(void)
{
Oid type_multirange_array_oid;
/* Use binary-upgrade override for pg_type.oid? */
if (IsBinaryUpgrade)
{
if (!OidIsValid(binary_upgrade_next_mrng_array_pg_type_oid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("pg_type multirange array OID value not set when in binary upgrade mode")));
type_multirange_array_oid = binary_upgrade_next_mrng_array_pg_type_oid;
binary_upgrade_next_mrng_array_pg_type_oid = InvalidOid;
}
else
{
Relation pg_type = table_open(TypeRelationId, AccessShareLock);
type_multirange_array_oid = GetNewOidWithIndex(pg_type, TypeOidIndexId,
Anum_pg_type_oid);
table_close(pg_type, AccessShareLock);
}
return type_multirange_array_oid;
}
/*-------------------------------------------------------------------
* DefineCompositeType

View File

@ -1711,7 +1711,8 @@ check_sql_fn_retval(List *queryTreeLists,
if (fn_typtype == TYPTYPE_BASE ||
fn_typtype == TYPTYPE_DOMAIN ||
fn_typtype == TYPTYPE_ENUM ||
fn_typtype == TYPTYPE_RANGE)
fn_typtype == TYPTYPE_RANGE ||
fn_typtype == TYPTYPE_MULTIRANGE)
{
/*
* For scalar-type returns, the target list must have exactly one

View File

@ -190,19 +190,21 @@ coerce_type(ParseState *pstate, Node *node,
if (targetTypeId == ANYARRAYOID ||
targetTypeId == ANYENUMOID ||
targetTypeId == ANYRANGEOID ||
targetTypeId == ANYMULTIRANGEOID ||
targetTypeId == ANYCOMPATIBLEARRAYOID ||
targetTypeId == ANYCOMPATIBLERANGEOID)
targetTypeId == ANYCOMPATIBLERANGEOID ||
targetTypeId == ANYCOMPATIBLEMULTIRANGEOID)
{
/*
* Assume can_coerce_type verified that implicit coercion is okay.
*
* These cases are unlike the ones above because the exposed type of
* the argument must be an actual array, enum, or range type. In
* particular the argument must *not* be an UNKNOWN constant. If it
* is, we just fall through; below, we'll call the pseudotype's input
* function, which will produce an error. Also, if what we have is a
* domain over array, enum, or range, we have to relabel it to its
* base type.
* the argument must be an actual array, enum, range, or multirange
* type. In particular the argument must *not* be an UNKNOWN
* constant. If it is, we just fall through; below, we'll call the
* pseudotype's input function, which will produce an error. Also, if
* what we have is a domain over array, enum, range, or multirange, we
* have to relabel it to its base type.
*
* Note: currently, we can't actually see a domain-over-enum here,
* since the other functions in this file will not match such a
@ -1570,8 +1572,8 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
* 1) All arguments declared ANYELEMENT must have the same datatype.
* 2) All arguments declared ANYARRAY must have the same datatype,
* which must be a varlena array type.
* 3) All arguments declared ANYRANGE must have the same datatype,
* which must be a range type.
* 3) All arguments declared ANYRANGE or ANYMULTIRANGE must be a range or
* multirange type, all derived from the same base datatype.
* 4) If there are arguments of more than one of these polymorphic types,
* the array element type and/or range subtype must be the same as each
* other and the same as the ANYELEMENT type.
@ -1586,8 +1588,8 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
* to a common supertype (chosen as per select_common_type's rules).
* ANYCOMPATIBLENONARRAY works like ANYCOMPATIBLE but also requires the
* common supertype to not be an array. If there are ANYCOMPATIBLEARRAY
* or ANYCOMPATIBLERANGE arguments, their element types or subtypes are
* included while making the choice of common supertype.
* or ANYCOMPATIBLERANGE or ANYCOMPATIBLEMULTIRANGE arguments, their element
* types or subtypes are included while making the choice of common supertype.
* 8) The resolved type of ANYCOMPATIBLEARRAY arguments will be the array
* type over the common supertype (which might not be the same array type
* as any of the original arrays).
@ -1595,6 +1597,10 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
* (after domain flattening), since we have no preference rule that would
* let us choose one over another. Furthermore, that range's subtype
* must exactly match the common supertype chosen by rule 7.
* 10) All ANYCOMPATIBLEMULTIRANGE arguments must be the exact same multirange
* type (after domain flattening), since we have no preference rule that would
* let us choose one over another. Furthermore, that multirange's range's
* subtype must exactly match the common supertype chosen by rule 7.
*
* Domains over arrays match ANYARRAY, and are immediately flattened to their
* base type. (Thus, for example, we will consider it a match if one ANYARRAY
@ -1603,7 +1609,9 @@ select_common_typmod(ParseState *pstate, List *exprs, Oid common_type)
* for ANYCOMPATIBLEARRAY and ANYCOMPATIBLENONARRAY.
*
* Similarly, domains over ranges match ANYRANGE or ANYCOMPATIBLERANGE,
* and are immediately flattened to their base type.
* and are immediately flattened to their base type, and domains over
* multiranges match ANYMULTIRANGE or ANYCOMPATIBLEMULTIRANGE and are immediately
* flattened to their base type.
*
* Note that domains aren't currently considered to match ANYENUM,
* even if their base type would match.
@ -1621,8 +1629,12 @@ check_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
Oid multirange_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
Oid anycompatible_multirange_typeid = InvalidOid;
Oid anycompatible_multirange_typelem = InvalidOid;
Oid range_typelem = InvalidOid;
bool have_anynonarray = false;
bool have_anyenum = false;
bool have_anycompatible_nonarray = false;
@ -1671,6 +1683,15 @@ check_generic_type_consistency(const Oid *actual_arg_types,
return false;
range_typeid = actual_type;
}
else if (decl_type == ANYMULTIRANGEOID)
{
if (actual_type == UNKNOWNOID)
continue;
actual_type = getBaseType(actual_type); /* flatten domains */
if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
return false;
multirange_typeid = actual_type;
}
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@ -1715,6 +1736,45 @@ check_generic_type_consistency(const Oid *actual_arg_types,
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
}
else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
{
if (actual_type == UNKNOWNOID)
continue;
actual_type = getBaseType(actual_type); /* flatten domains */
if (OidIsValid(anycompatible_multirange_typeid))
{
/* All ANYCOMPATIBLEMULTIRANGE arguments must be the same type */
if (anycompatible_multirange_typeid != actual_type)
return false;
}
else
{
anycompatible_multirange_typeid = actual_type;
anycompatible_multirange_typelem = get_multirange_range(actual_type);
if (!OidIsValid(anycompatible_multirange_typelem))
return false; /* not a multirange type */
if (OidIsValid(anycompatible_range_typeid))
{
/*
* ANYCOMPATIBLEMULTIRANGE and ANYCOMPATIBLERANGE
* arguments must match
*/
if (anycompatible_range_typeid != anycompatible_multirange_typelem)
return false;
}
else
{
anycompatible_range_typeid = anycompatible_multirange_typelem;
anycompatible_range_typelem = get_range_subtype(anycompatible_range_typeid);
if (!OidIsValid(anycompatible_range_typelem))
return false; /* not a range type */
}
/* collect the subtype for common-supertype choice */
anycompatible_actual_types[n_anycompatible_args++] =
anycompatible_range_typelem;
}
}
}
/* Get the element type based on the array type, if we have one */
@ -1761,8 +1821,6 @@ check_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
Oid range_typelem;
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
return false; /* should be a range, but isn't */
@ -1781,6 +1839,45 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
}
/* Get the element type based on the multirange type, if we have one */
if (OidIsValid(multirange_typeid))
{
Oid multirange_typelem;
multirange_typelem = get_multirange_range(multirange_typeid);
if (!OidIsValid(multirange_typelem))
return false; /* should be a multirange, but isn't */
if (!OidIsValid(range_typeid))
{
/*
* If we don't have a range type yet, use the one we just got
*/
range_typeid = multirange_typelem;
range_typelem = get_range_subtype(multirange_typelem);
if (!OidIsValid(range_typelem))
return false; /* should be a range, but isn't */
}
else if (multirange_typelem != range_typeid)
{
/* otherwise, they better match */
return false;
}
if (!OidIsValid(elem_typeid))
{
/*
* If we don't have an element type yet, use the one we just got
*/
elem_typeid = range_typelem;
}
else if (range_typelem != elem_typeid)
{
/* otherwise, they better match */
return false;
}
}
if (have_anynonarray)
{
/* require the element type to not be an array or domain over array */
@ -1819,8 +1916,10 @@ check_generic_type_consistency(const Oid *actual_arg_types,
}
/*
* the anycompatible type must exactly match the range element type,
* if we were able to identify one
* The anycompatible type must exactly match the range element type,
* if we were able to identify one. This checks compatibility for
* anycompatiblemultirange too since that also sets
* anycompatible_range_typelem above.
*/
if (OidIsValid(anycompatible_range_typelem) &&
anycompatible_range_typelem != anycompatible_typeid)
@ -1859,21 +1958,27 @@ check_generic_type_consistency(const Oid *actual_arg_types,
* argument's actual type as the function's return type.
* 2) If return type is ANYARRAY, and any argument is ANYARRAY, use the
* argument's actual type as the function's return type.
* 3) Similarly, if return type is ANYRANGE, and any argument is ANYRANGE,
* use the argument's actual type as the function's return type.
* 4) Otherwise, if return type is ANYELEMENT or ANYARRAY, and there is
* 3) Similarly, if return type is ANYRANGE or ANYMULTIRANGE, and any
* argument is ANYRANGE or ANYMULTIRANGE, use that argument's
* actual type, range type or multirange type as the function's return
* type.
* 4) Otherwise, if return type is ANYMULTIRANGE, and any argument is
* ANYMULTIRANGE, use the argument's actual type as the function's return
* type. Or if any argument is ANYRANGE, use its multirange type as the
* function's return type.
* 5) Otherwise, if return type is ANYELEMENT or ANYARRAY, and there is
* at least one ANYELEMENT, ANYARRAY, or ANYRANGE input, deduce the
* return type from those inputs, or throw error if we can't.
* 5) Otherwise, if return type is ANYRANGE, throw error. (We have no way to
* select a specific range type if the arguments don't include ANYRANGE.)
* 6) ANYENUM is treated the same as ANYELEMENT except that if it is used
* 6) Otherwise, if return type is ANYRANGE or ANYMULTIRANGE, throw error.
* (We have no way to select a specific range type if the arguments don't
* include ANYRANGE.)
* (alone or in combination with plain ANYELEMENT), we add the extra
* condition that the ANYELEMENT type must be an enum.
* 7) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
* 8) ANYNONARRAY is treated the same as ANYELEMENT except that if it is used,
* we add the extra condition that the ANYELEMENT type must not be an array.
* (This is a no-op if used in combination with ANYARRAY or ANYENUM, but
* is an extra restriction if not.)
* 8) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLENONARRAY, and
* 9) ANYCOMPATIBLE, ANYCOMPATIBLEARRAY, ANYCOMPATIBLENONARRAY, and
* ANYCOMPATIBLERANGE are handled by resolving the common supertype
* of those arguments (or their element types/subtypes, for array and range
* inputs), and then coercing all those arguments to the common supertype,
@ -1927,10 +2032,15 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid range_typeid = InvalidOid;
Oid multirange_typeid = InvalidOid;
Oid anycompatible_typeid = InvalidOid;
Oid anycompatible_array_typeid = InvalidOid;
Oid anycompatible_range_typeid = InvalidOid;
Oid anycompatible_range_typelem = InvalidOid;
Oid anycompatible_multirange_typeid = InvalidOid;
Oid anycompatible_multirange_typelem = InvalidOid;
Oid range_typelem;
Oid multirange_typelem;
bool have_anynonarray = (rettype == ANYNONARRAYOID);
bool have_anyenum = (rettype == ANYENUMOID);
bool have_anycompatible_nonarray = (rettype == ANYCOMPATIBLENONARRAYOID);
@ -2015,6 +2125,26 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(actual_type))));
range_typeid = actual_type;
}
else if (decl_type == ANYMULTIRANGEOID)
{
n_poly_args++;
if (actual_type == UNKNOWNOID)
{
have_poly_unknowns = true;
continue;
}
if (allow_poly && decl_type == actual_type)
continue; /* no new information here */
actual_type = getBaseType(actual_type); /* flatten domains */
if (OidIsValid(multirange_typeid) && actual_type != multirange_typeid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("arguments declared \"anymultirange\" are not all alike"),
errdetail("%s versus %s",
format_type_be(multirange_typeid),
format_type_be(actual_type))));
multirange_typeid = actual_type;
}
else if (decl_type == ANYCOMPATIBLEOID ||
decl_type == ANYCOMPATIBLENONARRAYOID)
{
@ -2083,6 +2213,40 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
}
else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
{
have_poly_anycompatible = true;
if (actual_type == UNKNOWNOID)
continue;
if (allow_poly && decl_type == actual_type)
continue; /* no new information here */
actual_type = getBaseType(actual_type); /* flatten domains */
if (OidIsValid(anycompatible_multirange_typeid))
{
/* All ANYCOMPATIBLEMULTIRANGE arguments must be the same type */
if (anycompatible_multirange_typeid != actual_type)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("arguments declared \"anycompatiblemultirange\" are not all alike"),
errdetail("%s versus %s",
format_type_be(anycompatible_multirange_typeid),
format_type_be(actual_type))));
}
else
{
anycompatible_multirange_typeid = actual_type;
anycompatible_multirange_typelem = get_multirange_range(actual_type);
anycompatible_range_typelem = get_range_subtype(anycompatible_multirange_typelem);
if (!OidIsValid(anycompatible_multirange_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not a multirange type but type %s",
"anycompatiblemultirange",
format_type_be(actual_type))));
/* collect the subtype for common-supertype choice */
anycompatible_actual_types[n_anycompatible_args++] = anycompatible_range_typelem;
}
}
}
/*
@ -2151,8 +2315,6 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
/* Get the element type based on the range type, if we have one */
if (OidIsValid(range_typeid))
{
Oid range_typelem;
range_typelem = get_range_subtype(range_typeid);
if (!OidIsValid(range_typelem))
ereport(ERROR,
@ -2181,6 +2343,61 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
format_type_be(elem_typeid))));
}
}
else
range_typelem = InvalidOid;
/* Get the element type based on the multirange type, if we have one */
if (OidIsValid(multirange_typeid))
{
multirange_typelem = get_multirange_range(multirange_typeid);
if (!OidIsValid(multirange_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not a multirange type but type %s",
"anymultirange",
format_type_be(multirange_typeid))));
if (!OidIsValid(range_typeid))
{
/*
* If we don't have a range type yet, use the one we just got
*/
range_typeid = multirange_typelem;
range_typelem = get_range_subtype(range_typeid);
}
else if (multirange_typelem != range_typeid)
{
/* otherwise, they better match */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not consistent with argument declared %s",
"anymultirange", "anyrange"),
errdetail("%s versus %s",
format_type_be(multirange_typeid),
format_type_be(range_typeid))));
}
if (!OidIsValid(elem_typeid))
{
/*
* if we don't have an element type yet, use the one we just got
*/
elem_typeid = range_typelem;
}
else if (range_typelem != elem_typeid)
{
/* otherwise, they better match */
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not consistent with argument declared %s",
"anymultirange", "anyelement"),
errdetail("%s versus %s",
format_type_be(multirange_typeid),
format_type_be(elem_typeid))));
}
}
else
multirange_typelem = InvalidOid;
if (!OidIsValid(elem_typeid))
{
@ -2189,6 +2406,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
elem_typeid = ANYELEMENTOID;
array_typeid = ANYARRAYOID;
range_typeid = ANYRANGEOID;
multirange_typeid = ANYMULTIRANGEOID;
}
else
{
@ -2288,6 +2506,7 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
anycompatible_typeid = ANYCOMPATIBLEOID;
anycompatible_array_typeid = ANYCOMPATIBLEARRAYOID;
anycompatible_range_typeid = ANYCOMPATIBLERANGEOID;
anycompatible_multirange_typeid = ANYCOMPATIBLEMULTIRANGEOID;
}
else
{
@ -2319,6 +2538,8 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
declared_arg_types[j] = anycompatible_array_typeid;
else if (decl_type == ANYCOMPATIBLERANGEOID)
declared_arg_types[j] = anycompatible_range_typeid;
else if (decl_type == ANYCOMPATIBLEMULTIRANGEOID)
declared_arg_types[j] = anycompatible_multirange_typeid;
}
}
@ -2369,6 +2590,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
}
declared_arg_types[j] = range_typeid;
}
else if (decl_type == ANYMULTIRANGEOID)
{
if (!OidIsValid(multirange_typeid))
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find multirange type for data type %s",
format_type_be(elem_typeid))));
}
declared_arg_types[j] = multirange_typeid;
}
}
}
@ -2405,6 +2637,22 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return range_typeid;
}
/* if we return ANYMULTIRANGE use the appropriate argument type */
if (rettype == ANYMULTIRANGEOID)
{
if (!OidIsValid(multirange_typeid))
{
if (OidIsValid(range_typeid))
multirange_typeid = get_range_multirange(range_typeid);
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find multirange type for data type %s",
format_type_be(elem_typeid))));
}
return multirange_typeid;
}
/* if we return ANYCOMPATIBLE use the appropriate type */
if (rettype == ANYCOMPATIBLEOID ||
rettype == ANYCOMPATIBLENONARRAYOID)
@ -2439,6 +2687,17 @@ enforce_generic_type_consistency(const Oid *actual_arg_types,
return anycompatible_range_typeid;
}
/* if we return ANYCOMPATIBLEMULTIRANGE use the appropriate argument type */
if (rettype == ANYCOMPATIBLEMULTIRANGEOID)
{
/* this error is unreachable if the function signature is valid: */
if (!OidIsValid(anycompatible_multirange_typeid))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg_internal("could not identify anycompatiblemultirange type")));
return anycompatible_multirange_typeid;
}
/* we don't return a generic type; send back the original return type */
return rettype;
}
@ -2456,20 +2715,38 @@ check_valid_polymorphic_signature(Oid ret_type,
const Oid *declared_arg_types,
int nargs)
{
if (ret_type == ANYRANGEOID || ret_type == ANYCOMPATIBLERANGEOID)
if (ret_type == ANYRANGEOID || ret_type == ANYMULTIRANGEOID)
{
/*
* ANYRANGE requires an ANYRANGE input, else we can't tell which of
* several range types with the same element type to use. Likewise
* for ANYCOMPATIBLERANGE.
* ANYRANGE and ANYMULTIRANGE require an ANYRANGE or ANYMULTIRANGE
* input, else we can't tell which of several range types with the
* same element type to use.
*/
for (int i = 0; i < nargs; i++)
{
if (declared_arg_types[i] == ret_type)
if (declared_arg_types[i] == ANYRANGEOID ||
declared_arg_types[i] == ANYMULTIRANGEOID)
return NULL; /* OK */
}
return psprintf(_("A result of type %s requires at least one input of type %s."),
format_type_be(ret_type), format_type_be(ret_type));
return psprintf(_("A result of type %s requires at least one input of type anyrange or anymultirange."),
format_type_be(ret_type));
}
else if (ret_type == ANYCOMPATIBLERANGEOID || ret_type == ANYCOMPATIBLEMULTIRANGEOID)
{
/*
* ANYCOMPATIBLERANGE and ANYCOMPATIBLEMULTIRANGE require an
* ANYCOMPATIBLERANGE or ANYCOMPATIBLEMULTIRANGE input, else we can't
* tell which of several range types with the same element type to
* use.
*/
for (int i = 0; i < nargs; i++)
{
if (declared_arg_types[i] == ANYCOMPATIBLERANGEOID ||
declared_arg_types[i] == ANYCOMPATIBLEMULTIRANGEOID)
return NULL; /* OK */
}
return psprintf(_("A result of type %s requires at least one input of type anycompatiblerange or anycompatiblemultirange."),
format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily1(ret_type))
{
@ -2480,7 +2757,7 @@ check_valid_polymorphic_signature(Oid ret_type,
return NULL; /* OK */
}
/* Keep this list in sync with IsPolymorphicTypeFamily1! */
return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange."),
return psprintf(_("A result of type %s requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange."),
format_type_be(ret_type));
}
else if (IsPolymorphicTypeFamily2(ret_type))
@ -2632,6 +2909,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
if (type_is_range(srctype))
return true;
/* Also accept any multirange type as coercible to ANMULTIYRANGE */
if (targettype == ANYMULTIRANGEOID || targettype == ANYCOMPATIBLEMULTIRANGEOID)
if (type_is_multirange(srctype))
return true;
/* Also accept any composite type as coercible to RECORD */
if (targettype == RECORDOID)
if (ISCOMPLEX(srctype))

View File

@ -60,6 +60,8 @@ OBJS = \
mac8.o \
mcxtfuncs.o \
misc.o \
multirangetypes.o \
multirangetypes_selfuncs.o \
name.o \
network.o \
network_gist.o \

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -52,6 +52,28 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
PG_RETURN_VOID();
}
Datum
binary_upgrade_set_next_multirange_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
CHECK_IS_BINARY_UPGRADE;
binary_upgrade_next_mrng_pg_type_oid = typoid;
PG_RETURN_VOID();
}
Datum
binary_upgrade_set_next_multirange_array_pg_type_oid(PG_FUNCTION_ARGS)
{
Oid typoid = PG_GETARG_OID(0);
CHECK_IS_BINARY_UPGRADE;
binary_upgrade_next_mrng_array_pg_type_oid = typoid;
PG_RETURN_VOID();
}
Datum
binary_upgrade_set_next_heap_pg_class_oid(PG_FUNCTION_ARGS)
{

View File

@ -26,6 +26,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
#include "utils/multirangetypes.h"
/*
@ -227,6 +228,43 @@ anycompatiblerange_out(PG_FUNCTION_ARGS)
return range_out(fcinfo);
}
/*
* anycompatiblemultirange
*
* We may as well allow output, since multirange_out will in fact work.
*/
PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblemultirange);
Datum
anycompatiblemultirange_out(PG_FUNCTION_ARGS)
{
return multirange_out(fcinfo);
}
/*
* anymultirange_in - input routine for pseudo-type ANYMULTIRANGE.
*/
Datum
anymultirange_in(PG_FUNCTION_ARGS)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot accept a value of type %s", "anymultirange")));
PG_RETURN_VOID(); /* keep compiler quiet */
}
/*
* anymultirange_out - output routine for pseudo-type ANYMULTIRANGE.
*
* We may as well allow this, since multirange_out will in fact work.
*/
Datum
anymultirange_out(PG_FUNCTION_ARGS)
{
return multirange_out(fcinfo);
}
/*
* void
*

View File

@ -43,8 +43,6 @@
#include "utils/timestamp.h"
#define RANGE_EMPTY_LITERAL "empty"
/* fn_extra cache entry for one of the range I/O functions */
typedef struct RangeIOData
{
@ -957,7 +955,25 @@ range_minus(PG_FUNCTION_ARGS)
{
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
RangeType *ret;
TypeCacheEntry *typcache;
/* Different types should be prevented by ANYRANGE matching rules */
if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
elog(ERROR, "range types do not match");
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
ret = range_minus_internal(typcache, r1, r2);
if (ret)
PG_RETURN_RANGE_P(ret);
else
PG_RETURN_NULL();
}
RangeType *
range_minus_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2)
{
RangeBound lower1,
lower2;
RangeBound upper1,
@ -969,18 +985,12 @@ range_minus(PG_FUNCTION_ARGS)
cmp_u1l2,
cmp_u1u2;
/* Different types should be prevented by ANYRANGE matching rules */
if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
elog(ERROR, "range types do not match");
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
/* if either is empty, r1 is the correct answer */
if (empty1 || empty2)
PG_RETURN_RANGE_P(r1);
return r1;
cmp_l1l2 = range_cmp_bounds(typcache, &lower1, &lower2);
cmp_l1u2 = range_cmp_bounds(typcache, &lower1, &upper2);
@ -993,34 +1003,34 @@ range_minus(PG_FUNCTION_ARGS)
errmsg("result of range difference would not be contiguous")));
if (cmp_l1u2 > 0 || cmp_u1l2 < 0)
PG_RETURN_RANGE_P(r1);
return r1;
if (cmp_l1l2 >= 0 && cmp_u1u2 <= 0)
PG_RETURN_RANGE_P(make_empty_range(typcache));
return make_empty_range(typcache);
if (cmp_l1l2 <= 0 && cmp_u1l2 >= 0 && cmp_u1u2 <= 0)
{
lower2.inclusive = !lower2.inclusive;
lower2.lower = false; /* it will become the upper bound */
PG_RETURN_RANGE_P(make_range(typcache, &lower1, &lower2, false));
return make_range(typcache, &lower1, &lower2, false);
}
if (cmp_l1l2 >= 0 && cmp_u1u2 >= 0 && cmp_l1u2 <= 0)
{
upper2.inclusive = !upper2.inclusive;
upper2.lower = true; /* it will become the lower bound */
PG_RETURN_RANGE_P(make_range(typcache, &upper2, &upper1, false));
return make_range(typcache, &upper2, &upper1, false);
}
elog(ERROR, "unexpected case in range_minus");
PG_RETURN_NULL();
return NULL;
}
/*
* Set union. If strict is true, it is an error that the two input ranges
* are not adjacent or overlapping.
*/
static RangeType *
RangeType *
range_union_internal(TypeCacheEntry *typcache, RangeType *r1, RangeType *r2,
bool strict)
{
@ -1101,6 +1111,19 @@ range_intersect(PG_FUNCTION_ARGS)
RangeType *r1 = PG_GETARG_RANGE_P(0);
RangeType *r2 = PG_GETARG_RANGE_P(1);
TypeCacheEntry *typcache;
/* Different types should be prevented by ANYRANGE matching rules */
if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
elog(ERROR, "range types do not match");
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
PG_RETURN_RANGE_P(range_intersect_internal(typcache, r1, r2));
}
RangeType *
range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2)
{
RangeBound lower1,
lower2;
RangeBound upper1,
@ -1110,17 +1133,11 @@ range_intersect(PG_FUNCTION_ARGS)
RangeBound *result_lower;
RangeBound *result_upper;
/* Different types should be prevented by ANYRANGE matching rules */
if (RangeTypeGetOid(r1) != RangeTypeGetOid(r2))
elog(ERROR, "range types do not match");
typcache = range_get_typcache(fcinfo, RangeTypeGetOid(r1));
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
if (empty1 || empty2 || !DatumGetBool(range_overlaps(fcinfo)))
PG_RETURN_RANGE_P(make_empty_range(typcache));
if (empty1 || empty2 || !range_overlaps_internal(typcache, r1, r2))
return make_empty_range(typcache);
if (range_cmp_bounds(typcache, &lower1, &lower2) >= 0)
result_lower = &lower1;
@ -1132,9 +1149,81 @@ range_intersect(PG_FUNCTION_ARGS)
else
result_upper = &upper2;
PG_RETURN_RANGE_P(make_range(typcache, result_lower, result_upper, false));
return make_range(typcache, result_lower, result_upper, false);
}
/* range, range -> range, range functions */
/*
* range_split_internal - if r2 intersects the middle of r1, leaving non-empty
* ranges on both sides, then return true and set output1 and output2 to the
* results of r1 - r2 (in order). Otherwise return false and don't set output1
* or output2. Neither input range should be empty.
*/
bool
range_split_internal(TypeCacheEntry *typcache, const RangeType *r1, const RangeType *r2,
RangeType **output1, RangeType **output2)
{
RangeBound lower1,
lower2;
RangeBound upper1,
upper2;
bool empty1,
empty2;
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
if (range_cmp_bounds(typcache, &lower1, &lower2) < 0 &&
range_cmp_bounds(typcache, &upper1, &upper2) > 0)
{
/*
* Need to invert inclusive/exclusive for the lower2 and upper2
* points. They can't be infinite though. We're allowed to overwrite
* these RangeBounds since they only exist locally.
*/
lower2.inclusive = !lower2.inclusive;
lower2.lower = false;
upper2.inclusive = !upper2.inclusive;
upper2.lower = true;
*output1 = make_range(typcache, &lower1, &lower2, false);
*output2 = make_range(typcache, &upper2, &upper1, false);
return true;
}
return false;
}
/* range -> range aggregate functions */
Datum
range_intersect_agg_transfn(PG_FUNCTION_ARGS)
{
MemoryContext aggContext;
Oid rngtypoid;
TypeCacheEntry *typcache;
RangeType *result;
RangeType *current;
if (!AggCheckCallContext(fcinfo, &aggContext))
elog(ERROR, "range_intersect_agg_transfn called in non-aggregate context");
rngtypoid = get_fn_expr_argtype(fcinfo->flinfo, 1);
if (!type_is_range(rngtypoid))
ereport(ERROR, (errmsg("range_intersect_agg must be called with a range")));
typcache = range_get_typcache(fcinfo, rngtypoid);
/* strictness ensures these are non-null */
result = PG_GETARG_RANGE_P(0);
current = PG_GETARG_RANGE_P(1);
result = range_intersect_internal(typcache, result, current);
PG_RETURN_RANGE_P(result);
}
/* Btree support */
/* btree comparator */
@ -1937,6 +2026,46 @@ range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
b1->val, b2->val));
}
/*
* qsort callback for sorting ranges.
*
* Two empty ranges compare equal; an empty range sorts to the left of any
* non-empty range. Two non-empty ranges are sorted by lower bound first
* and by upper bound next.
*/
int
range_compare(const void *key1, const void *key2, void *arg)
{
RangeType *r1 = *(RangeType **) key1;
RangeType *r2 = *(RangeType **) key2;
TypeCacheEntry *typcache = (TypeCacheEntry *) arg;
RangeBound lower1;
RangeBound upper1;
RangeBound lower2;
RangeBound upper2;
bool empty1;
bool empty2;
int cmp;
range_deserialize(typcache, r1, &lower1, &upper1, &empty1);
range_deserialize(typcache, r2, &lower2, &upper2, &empty2);
if (empty1 && empty2)
cmp = 0;
else if (empty1)
cmp = -1;
else if (empty2)
cmp = 1;
else
{
cmp = range_cmp_bounds(typcache, &lower1, &lower2);
if (cmp == 0)
cmp = range_cmp_bounds(typcache, &upper1, &upper2);
}
return cmp;
}
/*
* Build an empty range value of the type indicated by the typcache entry.
*/

View File

@ -30,11 +30,13 @@
#include "utils/fmgrprotos.h"
#include "utils/lsyscache.h"
#include "utils/rangetypes.h"
#include "utils/multirangetypes.h"
static int float8_qsort_cmp(const void *a1, const void *a2);
static int range_bound_qsort_cmp(const void *a1, const void *a2, void *arg);
static void compute_range_stats(VacAttrStats *stats,
AnalyzeAttrFetchFunc fetchfunc, int samplerows, double totalrows);
AnalyzeAttrFetchFunc fetchfunc, int samplerows,
double totalrows);
/*
* range_typanalyze -- typanalyze function for range columns
@ -60,6 +62,33 @@ range_typanalyze(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(true);
}
/*
* multirange_typanalyze -- typanalyze function for multirange columns
*
* We do the same analysis as for ranges, but on the smallest range that
* completely includes the multirange.
*/
Datum
multirange_typanalyze(PG_FUNCTION_ARGS)
{
VacAttrStats *stats = (VacAttrStats *) PG_GETARG_POINTER(0);
TypeCacheEntry *typcache;
Form_pg_attribute attr = stats->attr;
/* Get information about multirange type; note column might be a domain */
typcache = multirange_get_typcache(fcinfo, getBaseType(stats->attrtypid));
if (attr->attstattarget < 0)
attr->attstattarget = default_statistics_target;
stats->compute_stats = compute_range_stats;
stats->extra_data = typcache;
/* same as in std_typanalyze */
stats->minrows = 300 * attr->attstattarget;
PG_RETURN_BOOL(true);
}
/*
* Comparison function for sorting float8s, used for range lengths.
*/
@ -98,7 +127,8 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
int samplerows, double totalrows)
{
TypeCacheEntry *typcache = (TypeCacheEntry *) stats->extra_data;
bool has_subdiff = OidIsValid(typcache->rng_subdiff_finfo.fn_oid);
TypeCacheEntry *mltrng_typcache = NULL;
bool has_subdiff;
int null_cnt = 0;
int non_null_cnt = 0;
int non_empty_cnt = 0;
@ -112,6 +142,15 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
*uppers;
double total_width = 0;
if (typcache->typtype == TYPTYPE_MULTIRANGE)
{
mltrng_typcache = typcache;
typcache = typcache->rngtype;
}
else
Assert(typcache->typtype == TYPTYPE_RANGE);
has_subdiff = OidIsValid(typcache->rng_subdiff_finfo.fn_oid);
/* Allocate memory to hold range bounds and lengths of the sample ranges. */
lowers = (RangeBound *) palloc(sizeof(RangeBound) * samplerows);
uppers = (RangeBound *) palloc(sizeof(RangeBound) * samplerows);
@ -123,6 +162,7 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
Datum value;
bool isnull,
empty;
MultirangeType *multirange;
RangeType *range;
RangeBound lower,
upper;
@ -145,8 +185,31 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
total_width += VARSIZE_ANY(DatumGetPointer(value));
/* Get range and deserialize it for further analysis. */
range = DatumGetRangeTypeP(value);
range_deserialize(typcache, range, &lower, &upper, &empty);
if (mltrng_typcache != NULL)
{
/* Treat multiranges like a big range without gaps. */
multirange = DatumGetMultirangeTypeP(value);
if (!MultirangeIsEmpty(multirange))
{
RangeBound tmp;
multirange_get_bounds(typcache, multirange, 0,
&lower, &tmp);
multirange_get_bounds(typcache, multirange,
multirange->rangeCount - 1,
&tmp, &upper);
empty = false;
}
else
{
empty = true;
}
}
else
{
range = DatumGetRangeTypeP(value);
range_deserialize(typcache, range, &lower, &upper, &empty);
}
if (!empty)
{
@ -262,6 +325,13 @@ compute_range_stats(VacAttrStats *stats, AnalyzeAttrFetchFunc fetchfunc,
stats->stakind[slot_idx] = STATISTIC_KIND_BOUNDS_HISTOGRAM;
stats->stavalues[slot_idx] = bound_hist_values;
stats->numvalues[slot_idx] = num_hist;
/* Store ranges even if we're analyzing a multirange column */
stats->statypid[slot_idx] = typcache->type_id;
stats->statyplen[slot_idx] = typcache->typlen;
stats->statypbyval[slot_idx] = typcache->typbyval;
stats->statypalign[slot_idx] = 'd';
slot_idx++;
}

View File

@ -2610,6 +2610,16 @@ type_is_range(Oid typid)
return (get_typtype(typid) == TYPTYPE_RANGE);
}
/*
* type_is_multirange
* Returns true if the given type is a multirange type.
*/
bool
type_is_multirange(Oid typid)
{
return (get_typtype(typid) == TYPTYPE_MULTIRANGE);
}
/*
* get_type_category_preferred
*
@ -3308,7 +3318,7 @@ get_namespace_name_or_temp(Oid nspid)
return get_namespace_name(nspid);
}
/* ---------- PG_RANGE CACHE ---------- */
/* ---------- PG_RANGE CACHES ---------- */
/*
* get_range_subtype
@ -3361,6 +3371,56 @@ get_range_collation(Oid rangeOid)
return InvalidOid;
}
/*
* get_range_multirange
* Returns the multirange type of a given range type
*
* Returns InvalidOid if the type is not a range type.
*/
Oid
get_range_multirange(Oid rangeOid)
{
HeapTuple tp;
tp = SearchSysCache1(RANGETYPE, ObjectIdGetDatum(rangeOid));
if (HeapTupleIsValid(tp))
{
Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
Oid result;
result = rngtup->rngmultitypid;
ReleaseSysCache(tp);
return result;
}
else
return InvalidOid;
}
/*
* get_multirange_range
* Returns the range type of a given multirange
*
* Returns InvalidOid if the type is not a multirange.
*/
Oid
get_multirange_range(Oid multirangeOid)
{
HeapTuple tp;
tp = SearchSysCache1(RANGEMULTIRANGE, ObjectIdGetDatum(multirangeOid));
if (HeapTupleIsValid(tp))
{
Form_pg_range rngtup = (Form_pg_range) GETSTRUCT(tp);
Oid result;
result = rngtup->rngtypid;
ReleaseSysCache(tp);
return result;
}
else
return InvalidOid;
}
/* ---------- PG_INDEX CACHE ---------- */
/*

View File

@ -650,6 +650,18 @@ static const struct cachedesc cacheinfo[] = {
},
64
},
{RangeRelationId, /* RANGEMULTIRANGE */
RangeMultirangeTypidIndexId,
1,
{
Anum_pg_range_rngmultitypid,
0,
0,
0
},
4
},
{RangeRelationId, /* RANGETYPE */
RangeTypidIndexId,
1,

View File

@ -287,6 +287,7 @@ static uint64 tupledesc_id_counter = INVALID_TUPLEDESC_IDENTIFIER;
static void load_typcache_tupdesc(TypeCacheEntry *typentry);
static void load_rangetype_info(TypeCacheEntry *typentry);
static void load_multirangetype_info(TypeCacheEntry *typentry);
static void load_domaintype_info(TypeCacheEntry *typentry);
static int dcs_cmp(const void *a, const void *b);
static void decr_dcc_refcount(DomainConstraintCache *dcc);
@ -305,6 +306,9 @@ static void cache_record_field_properties(TypeCacheEntry *typentry);
static bool range_element_has_hashing(TypeCacheEntry *typentry);
static bool range_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_range_element_properties(TypeCacheEntry *typentry);
static bool multirange_element_has_hashing(TypeCacheEntry *typentry);
static bool multirange_element_has_extended_hashing(TypeCacheEntry *typentry);
static void cache_multirange_element_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void TypeCacheTypCallback(Datum arg, int cacheid, uint32 hashvalue);
static void TypeCacheOpcCallback(Datum arg, int cacheid, uint32 hashvalue);
@ -557,8 +561,8 @@ lookup_type_cache(Oid type_id, int flags)
* to see if the element type or column types support equality. If
* not, array_eq or record_eq would fail at runtime, so we don't want
* to report that the type has equality. (We can omit similar
* checking for ranges because ranges can't be created in the first
* place unless their subtypes support equality.)
* checking for ranges and multiranges because ranges can't be created
* in the first place unless their subtypes support equality.)
*/
if (eq_opr == ARRAY_EQ_OP &&
!array_element_has_equality(typentry))
@ -595,7 +599,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
* we need no special check for ranges.
* we need no special check for ranges or multiranges.
*/
if (lt_opr == ARRAY_LT_OP &&
!array_element_has_compare(typentry))
@ -620,7 +624,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
* we need no special check for ranges.
* we need no special check for ranges or multiranges.
*/
if (gt_opr == ARRAY_GT_OP &&
!array_element_has_compare(typentry))
@ -645,7 +649,7 @@ lookup_type_cache(Oid type_id, int flags)
/*
* As above, make sure array_cmp or record_cmp will succeed; but again
* we need no special check for ranges.
* we need no special check for ranges or multiranges.
*/
if (cmp_proc == F_BTARRAYCMP &&
!array_element_has_compare(typentry))
@ -695,6 +699,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_hashing(typentry))
hash_proc = InvalidOid;
/*
* Likewise for hash_multirange.
*/
if (hash_proc == F_HASH_MULTIRANGE &&
!multirange_element_has_hashing(typentry))
hash_proc = InvalidOid;
/* Force update of hash_proc_finfo only if we're changing state */
if (typentry->hash_proc != hash_proc)
typentry->hash_proc_finfo.fn_oid = InvalidOid;
@ -737,6 +748,13 @@ lookup_type_cache(Oid type_id, int flags)
!range_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
/*
* Likewise for hash_multirange_extended.
*/
if (hash_extended_proc == F_HASH_MULTIRANGE_EXTENDED &&
!multirange_element_has_extended_hashing(typentry))
hash_extended_proc = InvalidOid;
/* Force update of proc finfo only if we're changing state */
if (typentry->hash_extended_proc != hash_extended_proc)
typentry->hash_extended_proc_finfo.fn_oid = InvalidOid;
@ -816,6 +834,16 @@ lookup_type_cache(Oid type_id, int flags)
(void) lookup_type_cache(typentry->rngelemtype->type_id, 0);
}
/*
* If requested, get information about a multirange type
*/
if ((flags & TYPECACHE_MULTIRANGE_INFO) &&
typentry->rngtype == NULL &&
typentry->typtype == TYPTYPE_MULTIRANGE)
{
load_multirangetype_info(typentry);
}
/*
* If requested, get information about a domain type
*/
@ -927,6 +955,22 @@ load_rangetype_info(TypeCacheEntry *typentry)
typentry->rngelemtype = lookup_type_cache(subtypeOid, 0);
}
/*
* load_multirangetype_info --- helper routine to set up multirange type
* information
*/
static void
load_multirangetype_info(TypeCacheEntry *typentry)
{
Oid rangetypeOid;
rangetypeOid = get_multirange_range(typentry->type_id);
if (!OidIsValid(rangetypeOid))
elog(ERROR, "cache lookup failed for multirange type %u",
typentry->type_id);
typentry->rngtype = lookup_type_cache(rangetypeOid, TYPECACHE_RANGE_INFO);
}
/*
* load_domaintype_info --- helper routine to set up domain constraint info
@ -1559,11 +1603,11 @@ cache_record_field_properties(TypeCacheEntry *typentry)
}
/*
* Likewise, some helper functions for range types.
* Likewise, some helper functions for range and multirange types.
*
* We can borrow the flag bits for array element properties to use for range
* element properties, since those flag bits otherwise have no use in a
* range type's typcache entry.
* range or multirange type's typcache entry.
*/
static bool
@ -1606,6 +1650,46 @@ cache_range_element_properties(TypeCacheEntry *typentry)
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
static bool
multirange_element_has_hashing(TypeCacheEntry *typentry)
{
if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
cache_multirange_element_properties(typentry);
return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
}
static bool
multirange_element_has_extended_hashing(TypeCacheEntry *typentry)
{
if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
cache_multirange_element_properties(typentry);
return (typentry->flags & TCFLAGS_HAVE_ELEM_EXTENDED_HASHING) != 0;
}
static void
cache_multirange_element_properties(TypeCacheEntry *typentry)
{
/* load up range link if we didn't already */
if (typentry->rngtype == NULL &&
typentry->typtype == TYPTYPE_MULTIRANGE)
load_multirangetype_info(typentry);
if (typentry->rngtype != NULL && typentry->rngtype->rngelemtype != NULL)
{
TypeCacheEntry *elementry;
/* might need to calculate subtype's hash function properties */
elementry = lookup_type_cache(typentry->rngtype->rngelemtype->type_id,
TYPECACHE_HASH_PROC |
TYPECACHE_HASH_EXTENDED_PROC);
if (OidIsValid(elementry->hash_proc))
typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
if (OidIsValid(elementry->hash_extended_proc))
typentry->flags |= TCFLAGS_HAVE_ELEM_EXTENDED_HASHING;
}
typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
}
/*
* Make sure that RecordCacheArray and RecordIdentifierArray are large enough
* to store 'typmod'.

View File

@ -35,6 +35,7 @@ typedef struct polymorphic_actuals
Oid anyelement_type; /* anyelement mapping, if known */
Oid anyarray_type; /* anyarray mapping, if known */
Oid anyrange_type; /* anyrange mapping, if known */
Oid anymultirange_type; /* anymultirange mapping, if known */
} polymorphic_actuals;
static void shutdown_MultiFuncCall(Datum arg);
@ -46,6 +47,7 @@ static TypeFuncClass internal_get_result_type(Oid funcid,
static void resolve_anyelement_from_others(polymorphic_actuals *actuals);
static void resolve_anyarray_from_others(polymorphic_actuals *actuals);
static void resolve_anyrange_from_others(polymorphic_actuals *actuals);
static void resolve_anymultirange_from_others(polymorphic_actuals *actuals);
static bool resolve_polymorphic_tupdesc(TupleDesc tupdesc,
oidvector *declared_args,
Node *call_expr);
@ -503,6 +505,34 @@ resolve_anyelement_from_others(polymorphic_actuals *actuals)
format_type_be(range_base_type))));
actuals->anyelement_type = range_typelem;
}
else if (OidIsValid(actuals->anymultirange_type))
{
/* Use the element type based on the multirange type */
Oid multirange_base_type;
Oid multirange_typelem;
Oid range_base_type;
Oid range_typelem;
multirange_base_type = getBaseType(actuals->anymultirange_type);
multirange_typelem = get_multirange_range(multirange_base_type);
if (!OidIsValid(multirange_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not a multirange type but type %s",
"anymultirange",
format_type_be(multirange_base_type))));
range_base_type = getBaseType(multirange_typelem);
range_typelem = get_range_subtype(range_base_type);
if (!OidIsValid(range_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s does not contain a range type but type %s",
"anymultirange",
format_type_be(range_base_type))));
actuals->anyelement_type = range_typelem;
}
else
elog(ERROR, "could not determine polymorphic type");
}
@ -540,10 +570,53 @@ static void
resolve_anyrange_from_others(polymorphic_actuals *actuals)
{
/*
* We can't deduce a range type from other polymorphic inputs, because
* there may be multiple range types with the same subtype.
* We can't deduce a range type from other polymorphic array or base
* types, because there may be multiple range types with the same subtype,
* but we can deduce it from a polymorphic multirange type.
*/
elog(ERROR, "could not determine polymorphic type");
if (OidIsValid(actuals->anymultirange_type))
{
/* Use the element type based on the multirange type */
Oid multirange_base_type = getBaseType(actuals->anymultirange_type);
Oid multirange_typelem = get_multirange_range(multirange_base_type);
if (!OidIsValid(multirange_typelem))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("argument declared %s is not a multirange type but type %s",
"anymultirange",
format_type_be(multirange_base_type))));
actuals->anyrange_type = multirange_typelem;
}
else
elog(ERROR, "could not determine polymorphic type");
}
/*
* Resolve actual type of ANYMULTIRANGE from other polymorphic inputs
*/
static void
resolve_anymultirange_from_others(polymorphic_actuals *actuals)
{
/*
* We can't deduce a multirange type from polymorphic array or base types,
* because there may be multiple range types with the same subtype, but we
* can deduce it from a polymorphic range type.
*/
if (OidIsValid(actuals->anyrange_type))
{
Oid range_base_type = getBaseType(actuals->anyrange_type);
Oid multirange_typeid = get_range_multirange(range_base_type);
if (!OidIsValid(multirange_typeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("could not find multirange type for data type %s",
format_type_be(actuals->anyrange_type))));
actuals->anymultirange_type = multirange_typeid;
}
else
elog(ERROR, "could not determine polymorphic type");
}
/*
@ -566,9 +639,11 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
bool have_anycompatible_multirange_result = false;
polymorphic_actuals poly_actuals;
polymorphic_actuals anyc_actuals;
Oid anycollation = InvalidOid;
@ -594,6 +669,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anyrange_result = true;
break;
case ANYMULTIRANGEOID:
have_polymorphic_result = true;
have_anymultirange_result = true;
break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
have_polymorphic_result = true;
@ -607,6 +686,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
have_polymorphic_result = true;
have_anycompatible_range_result = true;
break;
case ANYCOMPATIBLEMULTIRANGEOID:
have_polymorphic_result = true;
have_anycompatible_multirange_result = true;
break;
default:
break;
}
@ -660,6 +743,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
case ANYMULTIRANGEOID:
if (!OidIsValid(poly_actuals.anymultirange_type))
{
poly_actuals.anymultirange_type =
get_call_expr_argtype(call_expr, i);
if (!OidIsValid(poly_actuals.anymultirange_type))
return false;
}
break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (!OidIsValid(anyc_actuals.anyelement_type))
@ -688,6 +780,15 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
return false;
}
break;
case ANYCOMPATIBLEMULTIRANGEOID:
if (!OidIsValid(anyc_actuals.anymultirange_type))
{
anyc_actuals.anymultirange_type =
get_call_expr_argtype(call_expr, i);
if (!OidIsValid(anyc_actuals.anymultirange_type))
return false;
}
break;
default:
break;
}
@ -703,6 +804,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
resolve_anymultirange_from_others(&poly_actuals);
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@ -712,6 +816,9 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
resolve_anyrange_from_others(&anyc_actuals);
if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
resolve_anymultirange_from_others(&anyc_actuals);
/*
* Identify the collation to use for polymorphic OUT parameters. (It'll
* necessarily be the same for both anyelement and anyarray, likewise for
@ -780,6 +887,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
case ANYMULTIRANGEOID:
TupleDescInitEntry(tupdesc, i + 1,
NameStr(att->attname),
poly_actuals.anymultirange_type,
-1,
0);
/* no collation should be attached to a multirange type */
break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
TupleDescInitEntry(tupdesc, i + 1,
@ -805,6 +920,14 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
0);
/* no collation should be attached to a range type */
break;
case ANYCOMPATIBLEMULTIRANGEOID:
TupleDescInitEntry(tupdesc, i + 1,
NameStr(att->attname),
anyc_actuals.anymultirange_type,
-1,
0);
/* no collation should be attached to a multirange type */
break;
default:
break;
}
@ -834,9 +957,11 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
bool have_anyelement_result = false;
bool have_anyarray_result = false;
bool have_anyrange_result = false;
bool have_anymultirange_result = false;
bool have_anycompatible_result = false;
bool have_anycompatible_array_result = false;
bool have_anycompatible_range_result = false;
bool have_anycompatible_multirange_result = false;
polymorphic_actuals poly_actuals;
polymorphic_actuals anyc_actuals;
int inargno;
@ -912,6 +1037,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = poly_actuals.anyrange_type;
}
break;
case ANYMULTIRANGEOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
{
have_polymorphic_result = true;
have_anymultirange_result = true;
}
else
{
if (!OidIsValid(poly_actuals.anymultirange_type))
{
poly_actuals.anymultirange_type =
get_call_expr_argtype(call_expr, inargno);
if (!OidIsValid(poly_actuals.anymultirange_type))
return false;
}
argtypes[i] = poly_actuals.anymultirange_type;
}
break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
@ -967,6 +1110,24 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
argtypes[i] = anyc_actuals.anyrange_type;
}
break;
case ANYCOMPATIBLEMULTIRANGEOID:
if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
{
have_polymorphic_result = true;
have_anycompatible_multirange_result = true;
}
else
{
if (!OidIsValid(anyc_actuals.anymultirange_type))
{
anyc_actuals.anymultirange_type =
get_call_expr_argtype(call_expr, inargno);
if (!OidIsValid(anyc_actuals.anymultirange_type))
return false;
}
argtypes[i] = anyc_actuals.anymultirange_type;
}
break;
default:
break;
}
@ -988,6 +1149,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anyrange_result && !OidIsValid(poly_actuals.anyrange_type))
resolve_anyrange_from_others(&poly_actuals);
if (have_anymultirange_result && !OidIsValid(poly_actuals.anymultirange_type))
resolve_anymultirange_from_others(&poly_actuals);
if (have_anycompatible_result && !OidIsValid(anyc_actuals.anyelement_type))
resolve_anyelement_from_others(&anyc_actuals);
@ -997,6 +1161,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
if (have_anycompatible_range_result && !OidIsValid(anyc_actuals.anyrange_type))
resolve_anyrange_from_others(&anyc_actuals);
if (have_anycompatible_multirange_result && !OidIsValid(anyc_actuals.anymultirange_type))
resolve_anymultirange_from_others(&anyc_actuals);
/* And finally replace the output column types as needed */
for (i = 0; i < numargs; i++)
{
@ -1013,6 +1180,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYRANGEOID:
argtypes[i] = poly_actuals.anyrange_type;
break;
case ANYMULTIRANGEOID:
argtypes[i] = poly_actuals.anymultirange_type;
break;
case ANYCOMPATIBLEOID:
case ANYCOMPATIBLENONARRAYOID:
argtypes[i] = anyc_actuals.anyelement_type;
@ -1023,6 +1193,9 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
case ANYCOMPATIBLERANGEOID:
argtypes[i] = anyc_actuals.anyrange_type;
break;
case ANYCOMPATIBLEMULTIRANGEOID:
argtypes[i] = anyc_actuals.anymultirange_type;
break;
default:
break;
}
@ -1052,6 +1225,7 @@ get_type_func_class(Oid typid, Oid *base_typeid)
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
case TYPTYPE_MULTIRANGE:
return TYPEFUNC_SCALAR;
case TYPTYPE_DOMAIN:
*base_typeid = typid = getBaseType(typid);

View File

@ -272,7 +272,8 @@ static void dumpSearchPath(Archive *AH);
static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
bool force_array_type);
bool force_array_type,
bool include_multirange_type);
static void binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
static void binary_upgrade_set_pg_class_oids(Archive *fout,
@ -1653,7 +1654,7 @@ selectDumpableType(TypeInfo *tyinfo, Archive *fout)
}
/* skip auto-generated array types */
if (tyinfo->isArray)
if (tyinfo->isArray || tyinfo->isMultirange)
{
tyinfo->dobj.objType = DO_DUMMY_TYPE;
@ -4461,16 +4462,49 @@ append_depends_on_extension(Archive *fout,
}
}
static Oid
get_next_possible_free_pg_type_oid(Archive *fout, PQExpBuffer upgrade_query)
{
/*
* If the old version didn't assign an array type, but the new version
* does, we must select an unused type OID to assign. This currently only
* happens for domains, when upgrading pre-v11 to v11 and up.
*
* Note: local state here is kind of ugly, but we must have some, since we
* mustn't choose the same unused OID more than once.
*/
static Oid next_possible_free_oid = FirstNormalObjectId;
PGresult *res;
bool is_dup;
do
{
++next_possible_free_oid;
printfPQExpBuffer(upgrade_query,
"SELECT EXISTS(SELECT 1 "
"FROM pg_catalog.pg_type "
"WHERE oid = '%u'::pg_catalog.oid);",
next_possible_free_oid);
res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
PQclear(res);
} while (is_dup);
return next_possible_free_oid;
}
static void
binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQExpBuffer upgrade_buffer,
Oid pg_type_oid,
bool force_array_type)
bool force_array_type,
bool include_multirange_type)
{
PQExpBuffer upgrade_query = createPQExpBuffer();
PGresult *res;
Oid pg_type_array_oid;
Oid pg_type_multirange_oid;
Oid pg_type_multirange_array_oid;
appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
@ -4491,33 +4525,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
PQclear(res);
if (!OidIsValid(pg_type_array_oid) && force_array_type)
{
/*
* If the old version didn't assign an array type, but the new version
* does, we must select an unused type OID to assign. This currently
* only happens for domains, when upgrading pre-v11 to v11 and up.
*
* Note: local state here is kind of ugly, but we must have some,
* since we mustn't choose the same unused OID more than once.
*/
static Oid next_possible_free_oid = FirstNormalObjectId;
bool is_dup;
do
{
++next_possible_free_oid;
printfPQExpBuffer(upgrade_query,
"SELECT EXISTS(SELECT 1 "
"FROM pg_catalog.pg_type "
"WHERE oid = '%u'::pg_catalog.oid);",
next_possible_free_oid);
res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
is_dup = (PQgetvalue(res, 0, 0)[0] == 't');
PQclear(res);
} while (is_dup);
pg_type_array_oid = next_possible_free_oid;
}
pg_type_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
if (OidIsValid(pg_type_array_oid))
{
@ -4528,6 +4536,46 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
pg_type_array_oid);
}
/*
* Pre-set the multirange type oid and its own array type oid.
*/
if (include_multirange_type)
{
if (fout->remoteVersion >= 130000)
{
appendPQExpBuffer(upgrade_query,
"SELECT t.oid, t.typarray "
"FROM pg_catalog.pg_type t "
"JOIN pg_catalog.pg_range r "
"ON t.oid = r.rngmultitypid "
"WHERE r.rngtypid = '%u'::pg_catalog.oid;",
pg_type_oid);
res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
pg_type_multirange_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "oid")));
pg_type_multirange_array_oid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typarray")));
PQclear(res);
}
else
{
pg_type_multirange_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
pg_type_multirange_array_oid = get_next_possible_free_pg_type_oid(fout, upgrade_query);
}
appendPQExpBufferStr(upgrade_buffer,
"\n-- For binary upgrade, must preserve multirange pg_type oid\n");
appendPQExpBuffer(upgrade_buffer,
"SELECT pg_catalog.binary_upgrade_set_next_multirange_pg_type_oid('%u'::pg_catalog.oid);\n\n",
pg_type_multirange_oid);
appendPQExpBufferStr(upgrade_buffer,
"\n-- For binary upgrade, must preserve multirange pg_type array oid\n");
appendPQExpBuffer(upgrade_buffer,
"SELECT pg_catalog.binary_upgrade_set_next_multirange_array_pg_type_oid('%u'::pg_catalog.oid);\n\n",
pg_type_multirange_array_oid);
}
destroyPQExpBuffer(upgrade_query);
}
@ -4552,7 +4600,7 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
if (OidIsValid(pg_type_oid))
binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
pg_type_oid, false);
pg_type_oid, false, false);
PQclear(upgrade_res);
destroyPQExpBuffer(upgrade_query);
@ -5097,6 +5145,11 @@ getTypes(Archive *fout, int *numTypes)
else
tyinfo[i].isArray = false;
if (tyinfo[i].typtype == 'm')
tyinfo[i].isMultirange = true;
else
tyinfo[i].isMultirange = false;
/* Decide whether we want to dump it */
selectDumpableType(&tyinfo[i], fout);
@ -8326,9 +8379,12 @@ getProcLangs(Archive *fout, int *numProcLangs)
/*
* getCasts
* get basic information about every cast in the system
* get basic information about most casts in the system
*
* numCasts is set to the number of casts read in
*
* Skip casts from a range to its multirange, since we'll create those
* automatically.
*/
CastInfo *
getCasts(Archive *fout, int *numCasts)
@ -8346,7 +8402,20 @@ getCasts(Archive *fout, int *numCasts)
int i_castcontext;
int i_castmethod;
if (fout->remoteVersion >= 80400)
if (fout->remoteVersion >= 130000)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
"castmethod "
"FROM pg_cast c "
"WHERE NOT EXISTS ( "
"SELECT 1 FROM pg_range r "
"WHERE c.castsource = r.rngtypid "
"AND c.casttarget = r.rngmultitypid "
") "
"ORDER BY 3,4");
}
else if (fout->remoteVersion >= 80400)
{
appendPQExpBufferStr(query, "SELECT tableoid, oid, "
"castsource, casttarget, castfunc, castcontext, "
@ -10496,7 +10565,7 @@ dumpEnumType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
false);
false, false);
appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (",
qualtypname);
@ -10594,7 +10663,17 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
char *procname;
appendPQExpBuffer(query,
"SELECT pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
"SELECT ");
if (fout->remoteVersion >= 140000)
appendPQExpBuffer(query,
"pg_catalog.format_type(rngmultitypid, NULL) AS rngmultitype, ");
else
appendPQExpBuffer(query,
"NULL AS rngmultitype, ");
appendPQExpBuffer(query,
"pg_catalog.format_type(rngsubtype, NULL) AS rngsubtype, "
"opc.opcname AS opcname, "
"(SELECT nspname FROM pg_catalog.pg_namespace nsp "
" WHERE nsp.oid = opc.opcnamespace) AS opcnsp, "
@ -10622,7 +10701,7 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
false);
false, true);
appendPQExpBuffer(q, "CREATE TYPE %s AS RANGE (",
qualtypname);
@ -10630,6 +10709,10 @@ dumpRangeType(Archive *fout, TypeInfo *tyinfo)
appendPQExpBuffer(q, "\n subtype = %s",
PQgetvalue(res, 0, PQfnumber(res, "rngsubtype")));
if (PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")))
appendPQExpBuffer(q, ",\n multirange_type_name = %s",
PQgetvalue(res, 0, PQfnumber(res, "rngmultitype")));
/* print subtype_opclass only if not default for subtype */
if (PQgetvalue(res, 0, PQfnumber(res, "opcdefault"))[0] != 't')
{
@ -10728,7 +10811,7 @@ dumpUndefinedType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
false);
false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
qualtypname);
@ -10913,7 +10996,7 @@ dumpBaseType(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
false);
false, false);
appendPQExpBuffer(q,
"CREATE TYPE %s (\n"
@ -11103,7 +11186,8 @@ dumpDomain(Archive *fout, TypeInfo *tyinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
true); /* force array type */
true, /* force array type */
false); /* force multirange type */
qtypname = pg_strdup(fmtId(tyinfo->dobj.name));
qualtypname = pg_strdup(fmtQualifiedDumpable(tyinfo));
@ -11291,7 +11375,7 @@ dumpCompositeType(Archive *fout, TypeInfo *tyinfo)
{
binary_upgrade_set_type_oids_by_type_oid(fout, q,
tyinfo->dobj.catId.oid,
false);
false, false);
binary_upgrade_set_pg_class_oids(fout, q, tyinfo->typrelid, false);
}
@ -11565,7 +11649,7 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo)
if (dopt->binary_upgrade)
binary_upgrade_set_type_oids_by_type_oid(fout, q,
stinfo->baseType->dobj.catId.oid,
false);
false, false);
appendPQExpBuffer(q, "CREATE TYPE %s;\n",
fmtQualifiedDumpable(stinfo));

View File

@ -176,6 +176,7 @@ typedef struct _typeInfo
char typrelkind; /* 'r', 'v', 'c', etc */
char typtype; /* 'b', 'c', etc */
bool isArray; /* true if auto-generated array type */
bool isMultirange; /* true if auto-generated multirange type */
bool isDefined; /* true if typisdefined */
/* If needed, we'll create a "shell type" entry for it; link that here: */
struct _shellTypeInfo *shellType; /* shell-type entry, or NULL */

View File

@ -1601,6 +1601,7 @@ my %tests = (
\QOPERATOR 3 dump_test.~~(integer,integer);\E\n.+
\QCREATE TYPE dump_test.range_type_custom AS RANGE (\E\n\s+
\Qsubtype = integer,\E\n\s+
\Qmultirange_type_name = dump_test.multirange_type_custom,\E\n\s+
\Qsubtype_opclass = dump_test.op_class_custom\E\n
\Q);\E
/xms,
@ -1698,6 +1699,7 @@ my %tests = (
regexp => qr/^
\QCREATE TYPE dump_test.textrange AS RANGE (\E
\n\s+\Qsubtype = text,\E
\n\s+\Qmultirange_type_name = dump_test.textmultirange,\E
\n\s+\Qcollation = pg_catalog."C"\E
\n\);/xm,
like =>

View File

@ -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.

View File

@ -16,6 +16,8 @@
extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_mrng_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_mrng_array_pg_type_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
extern PGDLLIMPORT Oid binary_upgrade_next_index_pg_class_oid;

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202012091
#define CATALOG_VERSION_NO 202012201
#endif

View File

@ -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' },

View File

@ -1374,6 +1374,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)',

View File

@ -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' },
{ amprocfamily => 'btree/xid8_ops', amproclefttype => 'xid8',
@ -457,6 +459,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',
@ -1280,12 +1287,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',

View File

@ -530,4 +530,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' },
]

View File

@ -232,6 +232,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',

View File

@ -3277,5 +3277,174 @@
oprname => '@@', oprleft => 'jsonb', oprright => 'jsonpath',
oprresult => 'bool', oprcode => 'jsonb_path_match_opr(jsonb,jsonpath)',
oprrest => 'matchingsel', oprjoin => 'matchingjoinsel' },
{ oid => '2860', descr => 'equal',
oprname => '=', oprcanmerge => 't', oprcanhash => 't', oprleft => 'anymultirange',
oprright => 'anymultirange', oprresult => 'bool', oprcom => '=(anymultirange,anymultirange)',
oprnegate => '<>(anymultirange,anymultirange)', oprcode => 'multirange_eq',
oprrest => 'eqsel', oprjoin => 'eqjoinsel' },
{ oid => '2861', descr => 'not equal',
oprname => '<>', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '<>(anymultirange,anymultirange)',
oprnegate => '=(anymultirange,anymultirange)', oprcode => 'multirange_ne',
oprrest => 'neqsel', oprjoin => 'neqjoinsel' },
{ oid => '2862', oid_symbol => 'OID_MULTIRANGE_LESS_OP', descr => 'less than',
oprname => '<', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '>(anymultirange,anymultirange)',
oprnegate => '>=(anymultirange,anymultirange)', oprcode => 'multirange_lt',
oprrest => 'multirangesel', oprjoin => 'scalarltjoinsel' },
{ oid => '2863', oid_symbol => 'OID_MULTIRANGE_LESS_EQUAL_OP',
descr => 'less than or equal',
oprname => '<=', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '>=(anymultirange,anymultirange)',
oprnegate => '>(anymultirange,anymultirange)', oprcode => 'multirange_le',
oprrest => 'multirangesel', oprjoin => 'scalarlejoinsel' },
{ oid => '2864', oid_symbol => 'OID_MULTIRANGE_GREATER_EQUAL_OP',
descr => 'greater than or equal',
oprname => '>=', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '<=(anymultirange,anymultirange)',
oprnegate => '<(anymultirange,anymultirange)', oprcode => 'multirange_ge',
oprrest => 'multirangesel', oprjoin => 'scalargejoinsel' },
{ oid => '2865', oid_symbol => 'OID_MULTIRANGE_GREATER_OP',
descr => 'greater than',
oprname => '>', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '<(anymultirange,anymultirange)',
oprnegate => '<=(anymultirange,anymultirange)', oprcode => 'multirange_gt',
oprrest => 'multirangesel', oprjoin => 'scalargtjoinsel' },
{ oid => '2866', oid_symbol => 'OID_RANGE_OVERLAPS_MULTIRANGE_OP',
descr => 'overlaps',
oprname => '&&', oprleft => 'anyrange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '&&(anymultirange,anyrange)',
oprcode => 'range_overlaps_multirange', oprrest => 'multirangesel',
oprjoin => 'areajoinsel' },
{ oid => '2867', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RANGE_OP', descr => 'contains',
oprname => '&&', oprleft => 'anymultirange', oprright => 'anyrange',
oprresult => 'bool', oprcom => '&&(anyrange,anymultirange)',
oprcode => 'multirange_overlaps_range', oprrest => 'multirangesel',
oprjoin => 'areajoinsel' },
{ oid => '2868', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_MULTIRANGE_OP', descr => 'contains',
oprname => '&&', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '&&(anymultirange,anymultirange)',
oprcode => 'multirange_overlaps_multirange', oprrest => 'multirangesel',
oprjoin => 'areajoinsel' },
{ oid => '2869', oid_symbol => 'OID_MULTIRANGE_CONTAINS_ELEM_OP',
descr => 'contains',
oprname => '@>', oprleft => 'anymultirange', oprright => 'anyelement',
oprresult => 'bool', oprcom => '<@(anyelement,anymultirange)',
oprcode => 'multirange_contains_elem', oprrest => 'multirangesel',
oprjoin => 'contjoinsel' },
{ oid => '2870', oid_symbol => 'OID_MULTIRANGE_CONTAINS_RANGE_OP', descr => 'contains',
oprname => '@>', oprleft => 'anymultirange', oprright => 'anyrange',
oprresult => 'bool', oprcom => '<@(anyrange,anymultirange)',
oprcode => 'multirange_contains_range', oprrest => 'multirangesel',
oprjoin => 'contjoinsel' },
{ oid => '2871', oid_symbol => 'OID_MULTIRANGE_CONTAINS_MULTIRANGE_OP', descr => 'contains',
oprname => '@>', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '<@(anymultirange,anymultirange)',
oprcode => 'multirange_contains_multirange', oprrest => 'multirangesel',
oprjoin => 'contjoinsel' },
{ oid => '2872', oid_symbol => 'OID_MULTIRANGE_ELEM_CONTAINED_OP',
descr => 'is contained by',
oprname => '<@', oprleft => 'anyelement', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '@>(anymultirange,anyelement)',
oprcode => 'elem_contained_by_multirange', oprrest => 'multirangesel',
oprjoin => 'contjoinsel' },
{ oid => '2873', oid_symbol => 'OID_MULTIRANGE_RANGE_CONTAINED_OP',
descr => 'is contained by',
oprname => '<@', oprleft => 'anyrange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '@>(anymultirange,anyrange)',
oprcode => 'range_contained_by_multirange', oprrest => 'multirangesel',
oprjoin => 'contjoinsel' },
{ oid => '2874', oid_symbol => 'OID_MULTIRANGE_MULTIRANGE_CONTAINED_OP',
descr => 'is contained by',
oprname => '<@', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '@>(anymultirange,anymultirange)',
oprcode => 'multirange_contained_by_multirange', oprrest => 'multirangesel',
oprjoin => 'contjoinsel' },
{ oid => '2875', oid_symbol => 'OID_RANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
descr => 'overlaps or is left of',
oprname => '&<', oprleft => 'anyrange', oprright => 'anymultirange',
oprresult => 'bool', oprcode => 'range_overleft_multirange', oprrest => 'multirangesel',
oprjoin => 'scalarltjoinsel' },
{ oid => '2876', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_RANGE_OP',
descr => 'overlaps or is left of',
oprname => '&<', oprleft => 'anymultirange', oprright => 'anyrange',
oprresult => 'bool', oprcode => 'multirange_overleft_range', oprrest => 'multirangesel',
oprjoin => 'scalarltjoinsel' },
{ oid => '2877', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_LEFT_MULTIRANGE_OP',
descr => 'overlaps or is left of',
oprname => '&<', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcode => 'multirange_overleft_multirange', oprrest => 'multirangesel',
oprjoin => 'scalargtjoinsel' },
{ oid => '3585', oid_symbol => 'OID_RANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
descr => 'overlaps or is right of',
oprname => '&>', oprleft => 'anyrange', oprright => 'anymultirange',
oprresult => 'bool', oprcode => 'range_overright_multirange', oprrest => 'multirangesel',
oprjoin => 'scalargtjoinsel' },
{ oid => '4035', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_RANGE_OP',
descr => 'overlaps or is right of',
oprname => '&>', oprleft => 'anymultirange', oprright => 'anyrange',
oprresult => 'bool', oprcode => 'multirange_overright_range', oprrest => 'multirangesel',
oprjoin => 'scalargtjoinsel' },
{ oid => '4142', oid_symbol => 'OID_MULTIRANGE_OVERLAPS_RIGHT_MULTIRANGE_OP',
descr => 'overlaps or is right of',
oprname => '&>', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcode => 'multirange_overright_multirange', oprrest => 'multirangesel',
oprjoin => 'scalargtjoinsel' },
{ oid => '4179', oid_symbol => 'OID_RANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
oprname => '-|-', oprleft => 'anyrange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '-|-(anymultirange,anyrange)',
oprcode => 'range_adjacent_multirange', oprrest => 'matchingsel',
oprjoin => 'matchingjoinsel' },
{ oid => '4180', oid_symbol => 'OID_MULTIRANGE_ADJACENT_RANGE_OP', descr => 'is adjacent to',
oprname => '-|-', oprleft => 'anymultirange', oprright => 'anyrange',
oprresult => 'bool', oprcom => '-|-(anyrange,anymultirange)',
oprcode => 'multirange_adjacent_range', oprrest => 'matchingsel',
oprjoin => 'matchingjoinsel' },
{ oid => '4198', oid_symbol => 'OID_MULTIRANGE_ADJACENT_MULTIRANGE_OP', descr => 'is adjacent to',
oprname => '-|-', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '-|-(anymultirange,anymultirange)',
oprcode => 'multirange_adjacent_multirange', oprrest => 'matchingsel',
oprjoin => 'matchingjoinsel' },
{ oid => '4392', descr => 'multirange union',
oprname => '+', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'anymultirange', oprcom => '+(anymultirange,anymultirange)',
oprcode => 'multirange_union' },
{ oid => '4393', descr => 'multirange minus',
oprname => '-', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'anymultirange', oprcode => 'multirange_minus' },
{ oid => '4394', descr => 'multirange intersect',
oprname => '*', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'anymultirange', oprcom => '*(anymultirange,anymultirange)',
oprcode => 'multirange_intersect' },
{ oid => '4395', oid_symbol => 'OID_RANGE_LEFT_MULTIRANGE_OP', descr => 'is left of',
oprname => '<<', oprleft => 'anyrange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '>>(anymultirange,anyrange)',
oprcode => 'range_before_multirange', oprrest => 'multirangesel',
oprjoin => 'scalarltjoinsel' },
{ oid => '4396', oid_symbol => 'OID_MULTIRANGE_LEFT_RANGE_OP', descr => 'is left of',
oprname => '<<', oprleft => 'anymultirange', oprright => 'anyrange',
oprresult => 'bool', oprcom => '>>(anyrange,anymultirange)',
oprcode => 'multirange_before_range', oprrest => 'multirangesel',
oprjoin => 'scalarltjoinsel' },
{ oid => '4397', oid_symbol => 'OID_MULTIRANGE_LEFT_MULTIRANGE_OP', descr => 'is left of',
oprname => '<<', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '>>(anymultirange,anymultirange)',
oprcode => 'multirange_before_multirange', oprrest => 'multirangesel',
oprjoin => 'scalarltjoinsel' },
{ oid => '4398', oid_symbol => 'OID_RANGE_RIGHT_MULTIRANGE_OP', descr => 'is right of',
oprname => '>>', oprleft => 'anyrange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '<<(anymultirange,anyrange)',
oprcode => 'range_after_multirange', oprrest => 'multirangesel',
oprjoin => 'scalargtjoinsel' },
{ oid => '4399', oid_symbol => 'OID_MULTIRANGE_RIGHT_RANGE_OP', descr => 'is right of',
oprname => '>>', oprleft => 'anymultirange', oprright => 'anyrange',
oprresult => 'bool', oprcom => '<<(anyrange,anymultirange)',
oprcode => 'multirange_after_range', oprrest => 'multirangesel',
oprjoin => 'scalargtjoinsel' },
{ oid => '4400', oid_symbol => 'OID_MULTIRANGE_RIGHT_MULTIRANGE_OP', descr => 'is right of',
oprname => '>>', oprleft => 'anymultirange', oprright => 'anymultirange',
oprresult => 'bool', oprcom => '<<(anymultirange,anymultirange)',
oprcode => 'multirange_after_multirange', oprrest => 'multirangesel',
oprjoin => 'scalargtjoinsel' },
]

View File

@ -232,5 +232,9 @@
opfmethod => 'spgist', opfname => 'box_ops' },
{ oid => '5008',
opfmethod => 'spgist', opfname => 'poly_ops' },
{ oid => '4199',
opfmethod => 'btree', opfname => 'multirange_ops' },
{ oid => '4225',
opfmethod => 'hash', opfname => 'multirange_ops' },
]

View File

@ -7253,6 +7253,14 @@
proname => 'anycompatiblerange_out', provolatile => 's',
prorettype => 'cstring', proargtypes => 'anycompatiblerange',
prosrc => 'anycompatiblerange_out' },
{ oid => '4226', descr => 'I/O',
proname => 'anycompatiblemultirange_in', provolatile => 's',
prorettype => 'anycompatiblemultirange', proargtypes => 'cstring oid int4',
prosrc => 'anycompatiblemultirange_in' },
{ oid => '4227', descr => 'I/O',
proname => 'anycompatiblemultirange_out', provolatile => 's',
prorettype => 'cstring', proargtypes => 'anycompatiblemultirange',
prosrc => 'anycompatiblemultirange_out' },
# tablesample method handlers
{ oid => '3313', descr => 'BERNOULLI tablesample method handler',
@ -9873,6 +9881,10 @@
descr => 'the smallest range which includes both of the given ranges',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_merge' },
{ oid => '4228',
descr => 'the smallest range which includes the whole multirange',
proname => 'range_merge', prorettype => 'anyrange',
proargtypes => 'anymultirange', prosrc => 'range_merge_from_multirange' },
{ oid => '3868',
proname => 'range_intersect', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect' },
@ -9922,6 +9934,13 @@
{ oid => '3169', descr => 'restriction selectivity for range operators',
proname => 'rangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'rangesel' },
{ oid => '4401', descr => 'range aggregate by intersecting',
proname => 'range_intersect_agg_transfn', prorettype => 'anyrange',
proargtypes => 'anyrange anyrange', prosrc => 'range_intersect_agg_transfn'},
{ oid => '4450', descr => 'range aggregate by intersecting',
proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
prorettype => 'anyrange', proargtypes => 'anyrange',
prosrc => 'aggregate_dummy'},
{ oid => '3914', descr => 'convert an int4 range to canonical form',
proname => 'int4range_canonical', prorettype => 'int4range',
@ -9990,6 +10009,261 @@
proname => 'int8range', proisstrict => 'f', prorettype => 'int8range',
proargtypes => 'int8 int8 text', prosrc => 'range_constructor3' },
# functions for multiranges
{ oid => '4229', descr => 'I/O',
proname => 'anymultirange_in', provolatile => 's', prorettype => 'anymultirange',
proargtypes => 'cstring oid int4', prosrc => 'anymultirange_in' },
{ oid => '4230', descr => 'I/O',
proname => 'anymultirange_out', provolatile => 's', prorettype => 'cstring',
proargtypes => 'anymultirange', prosrc => 'anymultirange_out' },
{ oid => '4231', descr => 'I/O',
proname => 'multirange_in', provolatile => 's', prorettype => 'anymultirange',
proargtypes => 'cstring oid int4', prosrc => 'multirange_in' },
{ oid => '4232', descr => 'I/O',
proname => 'multirange_out', provolatile => 's', prorettype => 'cstring',
proargtypes => 'anymultirange', prosrc => 'multirange_out' },
{ oid => '4233', descr => 'I/O',
proname => 'multirange_recv', provolatile => 's', prorettype => 'anymultirange',
proargtypes => 'internal oid int4', prosrc => 'multirange_recv' },
{ oid => '4234', descr => 'I/O',
proname => 'multirange_send', provolatile => 's', prorettype => 'bytea',
proargtypes => 'anymultirange', prosrc => 'multirange_send' },
{ oid => '4235', descr => 'lower bound of multirange',
proname => 'lower', prorettype => 'anyelement', proargtypes => 'anymultirange',
prosrc => 'multirange_lower' },
{ oid => '4236', descr => 'upper bound of multirange',
proname => 'upper', prorettype => 'anyelement', proargtypes => 'anymultirange',
prosrc => 'multirange_upper' },
{ oid => '4237', descr => 'is the multirange empty?',
proname => 'isempty', prorettype => 'bool',
proargtypes => 'anymultirange', prosrc => 'multirange_empty' },
{ oid => '4238', descr => 'is the multirange\'s lower bound inclusive?',
proname => 'lower_inc', prorettype => 'bool', proargtypes => 'anymultirange',
prosrc => 'multirange_lower_inc' },
{ oid => '4239', descr => 'is the multirange\'s upper bound inclusive?',
proname => 'upper_inc', prorettype => 'bool', proargtypes => 'anymultirange',
prosrc => 'multirange_upper_inc' },
{ oid => '4240', descr => 'is the multirange\'s lower bound infinite?',
proname => 'lower_inf', prorettype => 'bool', proargtypes => 'anymultirange',
prosrc => 'multirange_lower_inf' },
{ oid => '4241', descr => 'is the multirange\'s upper bound infinite?',
proname => 'upper_inf', prorettype => 'bool', proargtypes => 'anymultirange',
prosrc => 'multirange_upper_inf' },
{ oid => '4242', descr => 'multirange typanalyze',
proname => 'multirange_typanalyze', provolatile => 's', prorettype => 'bool',
proargtypes => 'internal', prosrc => 'multirange_typanalyze' },
{ oid => '4243', descr => 'restriction selectivity for multirange operators',
proname => 'multirangesel', provolatile => 's', prorettype => 'float8',
proargtypes => 'internal oid internal int4', prosrc => 'multirangesel' },
{ oid => '4244',
proname => 'multirange_eq', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_eq' },
{ oid => '4245',
proname => 'multirange_ne', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ne' },
{ oid => '4246',
proname => 'range_overlaps_multirange', prorettype => 'bool',
proargtypes => 'anyrange anymultirange', prosrc => 'range_overlaps_multirange' },
{ oid => '4247',
proname => 'multirange_overlaps_range', prorettype => 'bool',
proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overlaps_range' },
{ oid => '4248',
proname => 'multirange_overlaps_multirange', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overlaps_multirange' },
{ oid => '4249',
proname => 'multirange_contains_elem', prorettype => 'bool',
proargtypes => 'anymultirange anyelement', prosrc => 'multirange_contains_elem' },
{ oid => '4250',
proname => 'multirange_contains_range', prorettype => 'bool',
proargtypes => 'anymultirange anyrange', prosrc => 'multirange_contains_range' },
{ oid => '4251',
proname => 'multirange_contains_multirange', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contains_multirange' },
{ oid => '4252',
proname => 'elem_contained_by_multirange', prorettype => 'bool',
proargtypes => 'anyelement anymultirange', prosrc => 'elem_contained_by_multirange' },
{ oid => '4253',
proname => 'range_contained_by_multirange', prorettype => 'bool',
proargtypes => 'anyrange anymultirange', prosrc => 'range_contained_by_multirange' },
{ oid => '4254',
proname => 'multirange_contained_by_multirange', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_contained_by_multirange' },
{ oid => '4255',
proname => 'range_adjacent_multirange', prorettype => 'bool',
proargtypes => 'anyrange anymultirange', prosrc => 'range_adjacent_multirange' },
{ oid => '4256',
proname => 'multirange_adjacent_multirange', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_adjacent_multirange' },
{ oid => '4257',
proname => 'multirange_adjacent_range', prorettype => 'bool',
proargtypes => 'anymultirange anyrange', prosrc => 'multirange_adjacent_range' },
{ oid => '4258',
proname => 'range_before_multirange', prorettype => 'bool',
proargtypes => 'anyrange anymultirange', prosrc => 'range_before_multirange' },
{ oid => '4259',
proname => 'multirange_before_range', prorettype => 'bool',
proargtypes => 'anymultirange anyrange', prosrc => 'multirange_before_range' },
{ oid => '4260',
proname => 'multirange_before_multirange', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_before_multirange' },
{ oid => '4261',
proname => 'range_after_multirange', prorettype => 'bool',
proargtypes => 'anyrange anymultirange', prosrc => 'range_after_multirange' },
{ oid => '4262',
proname => 'multirange_after_range', prorettype => 'bool',
proargtypes => 'anymultirange anyrange', prosrc => 'multirange_after_range' },
{ oid => '4263',
proname => 'multirange_after_multirange', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_after_multirange' },
{ oid => '4264',
proname => 'range_overleft_multirange', prorettype => 'bool',
proargtypes => 'anyrange anymultirange', prosrc => 'range_overleft_multirange' },
{ oid => '4265',
proname => 'multirange_overleft_range', prorettype => 'bool',
proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overleft_range' },
{ oid => '4266',
proname => 'multirange_overleft_multirange', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overleft_multirange' },
{ oid => '4267',
proname => 'range_overright_multirange', prorettype => 'bool',
proargtypes => 'anyrange anymultirange', prosrc => 'range_overright_multirange' },
{ oid => '4268',
proname => 'multirange_overright_range', prorettype => 'bool',
proargtypes => 'anymultirange anyrange', prosrc => 'multirange_overright_range' },
{ oid => '4269',
proname => 'multirange_overright_multirange', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_overright_multirange' },
{ oid => '4270',
proname => 'multirange_union', prorettype => 'anymultirange',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_union' },
{ oid => '4271',
proname => 'multirange_minus', prorettype => 'anymultirange',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_minus' },
{ oid => '4272',
proname => 'multirange_intersect', prorettype => 'anymultirange',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect' },
{ oid => '4273', descr => 'less-equal-greater',
proname => 'multirange_cmp', prorettype => 'int4',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_cmp' },
{ oid => '4274',
proname => 'multirange_lt', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_lt' },
{ oid => '4275',
proname => 'multirange_le', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_le' },
{ oid => '4276',
proname => 'multirange_ge', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_ge' },
{ oid => '4277',
proname => 'multirange_gt', prorettype => 'bool',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_gt' },
{ oid => '4278', descr => 'hash a multirange',
proname => 'hash_multirange', prorettype => 'int4', proargtypes => 'anymultirange',
prosrc => 'hash_multirange' },
{ oid => '4279', descr => 'hash a multirange',
proname => 'hash_multirange_extended', prorettype => 'int8',
proargtypes => 'anymultirange int8', prosrc => 'hash_multirange_extended' },
{ oid => '4280', descr => 'int4multirange constructor',
proname => 'int4multirange', proisstrict => 'f',
prorettype => 'int4multirange', proargtypes => '',
prosrc => 'multirange_constructor0' },
{ oid => '4281', descr => 'int4multirange constructor',
proname => 'int4multirange', proisstrict => 't',
prorettype => 'int4multirange', proargtypes => 'int4range',
prosrc => 'multirange_constructor1' },
{ oid => '4282', descr => 'int4multirange constructor',
proname => 'int4multirange', provariadic => 'int4range', proisstrict => 'f',
prorettype => 'int4multirange', proargtypes => '_int4range',
proallargtypes => '{_int4range}', proargmodes => '{v}',
prosrc => 'multirange_constructor2' },
{ oid => '4283', descr => 'nummultirange constructor',
proname => 'nummultirange', proisstrict => 'f',
prorettype => 'nummultirange', proargtypes => '',
prosrc => 'multirange_constructor0' },
{ oid => '4284', descr => 'nummultirange constructor',
proname => 'nummultirange', proisstrict => 't',
prorettype => 'nummultirange', proargtypes => 'numrange',
prosrc => 'multirange_constructor1' },
{ oid => '4285', descr => 'nummultirange constructor',
proname => 'nummultirange', provariadic => 'numrange', proisstrict => 'f',
prorettype => 'nummultirange', proargtypes => '_numrange',
proallargtypes => '{_numrange}', proargmodes => '{v}',
prosrc => 'multirange_constructor2' },
{ oid => '4286', descr => 'tsmultirange constructor',
proname => 'tsmultirange', proisstrict => 'f',
prorettype => 'tsmultirange', proargtypes => '',
prosrc => 'multirange_constructor0' },
{ oid => '4287', descr => 'tsmultirange constructor',
proname => 'tsmultirange', proisstrict => 't',
prorettype => 'tsmultirange', proargtypes => 'tsrange',
prosrc => 'multirange_constructor1' },
{ oid => '4288', descr => 'tsmultirange constructor',
proname => 'tsmultirange', provariadic => 'tsrange', proisstrict => 'f',
prorettype => 'tsmultirange', proargtypes => '_tsrange',
proallargtypes => '{_tsrange}', proargmodes => '{v}',
prosrc => 'multirange_constructor2' },
{ oid => '4289', descr => 'tstzmultirange constructor',
proname => 'tstzmultirange', proisstrict => 'f',
prorettype => 'tstzmultirange', proargtypes => '',
prosrc => 'multirange_constructor0' },
{ oid => '4290', descr => 'tstzmultirange constructor',
proname => 'tstzmultirange', proisstrict => 't',
prorettype => 'tstzmultirange', proargtypes => 'tstzrange',
prosrc => 'multirange_constructor1' },
{ oid => '4291', descr => 'tstzmultirange constructor',
proname => 'tstzmultirange', provariadic => 'tstzrange', proisstrict => 'f',
prorettype => 'tstzmultirange', proargtypes => '_tstzrange',
proallargtypes => '{_tstzrange}', proargmodes => '{v}',
prosrc => 'multirange_constructor2' },
{ oid => '4292', descr => 'datemultirange constructor',
proname => 'datemultirange', proisstrict => 'f',
prorettype => 'datemultirange', proargtypes => '',
prosrc => 'multirange_constructor0' },
{ oid => '4293', descr => 'datemultirange constructor',
proname => 'datemultirange', proisstrict => 't',
prorettype => 'datemultirange', proargtypes => 'daterange',
prosrc => 'multirange_constructor1' },
{ oid => '4294', descr => 'datemultirange constructor',
proname => 'datemultirange', provariadic => 'daterange', proisstrict => 'f',
prorettype => 'datemultirange', proargtypes => '_daterange',
proallargtypes => '{_daterange}', proargmodes => '{v}',
prosrc => 'multirange_constructor2' },
{ oid => '4295', descr => 'int8multirange constructor',
proname => 'int8multirange', proisstrict => 'f',
prorettype => 'int8multirange', proargtypes => '',
prosrc => 'multirange_constructor0' },
{ oid => '4296', descr => 'int8multirange constructor',
proname => 'int8multirange', proisstrict => 't',
prorettype => 'int8multirange', proargtypes => 'int8range',
prosrc => 'multirange_constructor1' },
{ oid => '4297', descr => 'int8multirange constructor',
proname => 'int8multirange', provariadic => 'int8range', proisstrict => 'f',
prorettype => 'int8multirange', proargtypes => '_int8range',
proallargtypes => '{_int8range}', proargmodes => '{v}',
prosrc => 'multirange_constructor2' },
{ oid => '4298', descr => 'anymultirange cast',
proname => 'multirange', proisstrict => 't',
prorettype => 'anymultirange', proargtypes => 'anyrange',
prosrc => 'multirange_constructor1' },
{ oid => '4299', descr => 'aggregate transition function',
proname => 'range_agg_transfn', proisstrict => 'f', prorettype => 'internal',
proargtypes => 'internal anyrange', prosrc => 'range_agg_transfn' },
{ oid => '4300', descr => 'aggregate final function',
proname => 'range_agg_finalfn', proisstrict => 'f', prorettype => 'anymultirange',
proargtypes => 'internal anyrange', prosrc => 'range_agg_finalfn' },
{ oid => '4301', descr => 'combine aggregate input into a multirange',
proname => 'range_agg', prokind => 'a', proisstrict => 'f',
prorettype => 'anymultirange', proargtypes => 'anyrange',
prosrc => 'aggregate_dummy' },
{ oid => '4388', descr => 'range aggregate by intersecting',
proname => 'multirange_intersect_agg_transfn', prorettype => 'anymultirange',
proargtypes => 'anymultirange anymultirange', prosrc => 'multirange_intersect_agg_transfn'},
{ oid => '4389', descr => 'range aggregate by intersecting',
proname => 'range_intersect_agg', prokind => 'a', proisstrict => 'f',
prorettype => 'anymultirange', proargtypes => 'anymultirange',
prosrc => 'aggregate_dummy'},
# date, time, timestamp constructors
{ oid => '3846', descr => 'construct date',
proname => 'make_date', prorettype => 'date', proargtypes => 'int4 int4 int4',
@ -10372,6 +10646,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 => '4390', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_multirange_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_multirange_pg_type_oid' },
{ oid => '4391', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_multirange_array_pg_type_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
prosrc => 'binary_upgrade_set_next_multirange_array_pg_type_oid' },
{ oid => '3586', descr => 'for use by pg_upgrade',
proname => 'binary_upgrade_set_next_heap_pg_class_oid', provolatile => 'v',
proparallel => 'r', prorettype => 'void', proargtypes => 'oid',

View File

@ -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' },
]

View File

@ -34,6 +34,9 @@ CATALOG(pg_range,3541,RangeRelationId)
/* OID of range's element type (subtype) */
Oid rngsubtype BKI_LOOKUP(pg_type);
/* OID of the range's multirange type */
Oid rngmultitypid BKI_LOOKUP(pg_type);
/* collation for this range type, or 0 */
Oid rngcollation BKI_DEFAULT(0);
@ -57,13 +60,17 @@ typedef FormData_pg_range *Form_pg_range;
DECLARE_UNIQUE_INDEX(pg_range_rngtypid_index, 3542, on pg_range using btree(rngtypid oid_ops));
#define RangeTypidIndexId 3542
DECLARE_UNIQUE_INDEX(pg_range_rngmultitypid_index, 2228, on pg_range using btree(rngmultitypid oid_ops));
#define RangeMultirangeTypidIndexId 2228
/*
* prototypes for functions in pg_range.c
*/
extern void RangeCreate(Oid rangeTypeOid, Oid rangeSubType, Oid rangeCollation,
Oid rangeSubOpclass, RegProcedure rangeCanonical,
RegProcedure rangeSubDiff);
RegProcedure rangeSubDiff, Oid multirangeTypeOid);
extern void RangeDelete(Oid rangeTypeOid);
#endif /* PG_RANGE_H */

View File

@ -496,6 +496,40 @@
typreceive => 'range_recv', typsend => 'range_send',
typanalyze => 'range_typanalyze', typalign => 'd', typstorage => 'x' },
# multirange types
{ oid => '4451', array_type_oid => '8010', descr => 'multirange of integers',
typname => 'int4multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
{ oid => '4532', array_type_oid => '8012', descr => 'multirange of numerics',
typname => 'nummultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
{ oid => '4533', array_type_oid => '8014',
descr => 'multirange of timestamps without time zone',
typname => 'tsmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
{ oid => '4534', array_type_oid => '8016',
descr => 'multirange of timestamps with time zone',
typname => 'tstzmultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
{ oid => '4535', array_type_oid => '8018', descr => 'multirange of dates',
typname => 'datemultirange', typlen => '-1', typbyval => 'f', typtype => 'm',
typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'i', typstorage => 'x' },
{ oid => '4536', array_type_oid => '8020', descr => 'multirange of bigints',
typname => 'int8multirange', typlen => '-1', typbyval => 'f', typtype => 'm',
typcategory => 'R', typinput => 'multirange_in', typoutput => 'multirange_out',
typreceive => 'multirange_recv', typsend => 'multirange_send',
typanalyze => 'multirange_typanalyze', typalign => 'd', typstorage => 'x' },
# pseudo-types
# types with typtype='p' represent various special cases in the type system.
# These cannot be used to define table columns, but are valid as function
@ -626,5 +660,16 @@
typtype => 'p', typcategory => 'P', typinput => 'anycompatiblerange_in',
typoutput => 'anycompatiblerange_out', typreceive => '-', typsend => '-',
typalign => 'd', typstorage => 'x' },
{ oid => '4537',
descr => 'pseudo-type representing a polymorphic base type that is a multirange',
typname => 'anymultirange', typlen => '-1', typbyval => 'f', typtype => 'p',
typcategory => 'P', typinput => 'anymultirange_in', typoutput => 'anymultirange_out',
typreceive => '-', typsend => '-', typalign => 'd', typstorage => 'x' },
{ oid => '4538',
descr => 'pseudo-type representing a multirange over a polymorphic common type',
typname => 'anycompatiblemultirange', typlen => '-1', typbyval => 'f',
typtype => 'p', typcategory => 'P', typinput => 'anycompatiblemultirange_in',
typoutput => 'anycompatiblemultirange_out', typreceive => '-', typsend => '-',
typalign => 'd', typstorage => 'x' },
]

View File

@ -276,6 +276,7 @@ DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typ
#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */
#define TYPTYPE_DOMAIN 'd' /* domain over another type */
#define TYPTYPE_ENUM 'e' /* enumerated type */
#define TYPTYPE_MULTIRANGE 'm' /* multirange type */
#define TYPTYPE_PSEUDO 'p' /* pseudo-type */
#define TYPTYPE_RANGE 'r' /* range type */
@ -317,13 +318,15 @@ DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index, 2704, on pg_type using btree(typ
(typid) == ANYARRAYOID || \
(typid) == ANYNONARRAYOID || \
(typid) == ANYENUMOID || \
(typid) == ANYRANGEOID)
(typid) == ANYRANGEOID || \
(typid) == ANYMULTIRANGEOID)
#define IsPolymorphicTypeFamily2(typid) \
((typid) == ANYCOMPATIBLEOID || \
(typid) == ANYCOMPATIBLEARRAYOID || \
(typid) == ANYCOMPATIBLENONARRAYOID || \
(typid) == ANYCOMPATIBLERANGEOID)
(typid) == ANYCOMPATIBLERANGEOID || \
(typid) == ANYCOMPATIBLEMULTIRANGEOID)
/* Is this a "true" array type? (Requires fmgroids.h) */
#define IsTrueArrayType(typeForm) \
@ -397,4 +400,7 @@ extern char *makeArrayTypeName(const char *typeName, Oid typeNamespace);
extern bool moveArrayTypeName(Oid typeOid, const char *typeName,
Oid typeNamespace);
extern char *makeMultirangeTypeName(const char *rangeTypeName,
Oid typeNamespace);
#endif /* PG_TYPE_H */

View File

@ -29,6 +29,8 @@ extern ObjectAddress DefineRange(CreateRangeStmt *stmt);
extern ObjectAddress AlterEnum(AlterEnumStmt *stmt);
extern ObjectAddress DefineCompositeType(RangeVar *typevar, List *coldeflist);
extern Oid AssignTypeArrayOid(void);
extern Oid AssignTypeMultirangeOid(void);
extern Oid AssignTypeMultirangeArrayOid(void);
extern ObjectAddress AlterDomainDefault(List *names, Node *defaultRaw);
extern ObjectAddress AlterDomainNotNull(List *names, bool notNull);

View File

@ -161,6 +161,7 @@ extern char get_typtype(Oid typid);
extern bool type_is_rowtype(Oid typid);
extern bool type_is_enum(Oid typid);
extern bool type_is_range(Oid typid);
extern bool type_is_multirange(Oid typid);
extern void get_type_category_preferred(Oid typid,
char *typcategory,
bool *typispreferred);
@ -190,6 +191,8 @@ extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
extern Oid get_range_collation(Oid rangeOid);
extern Oid get_range_multirange(Oid rangeOid);
extern Oid get_multirange_range(Oid multirangeOid);
extern Oid get_index_column_opclass(Oid index_oid, int attno);
extern bool get_index_isreplident(Oid index_oid);
extern bool get_index_isvalid(Oid index_oid);

View File

@ -0,0 +1,116 @@
/*-------------------------------------------------------------------------
*
* multirangetypes.h
* Declarations for Postgres multirange types.
*
*
* Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/utils/multirangetypes.h
*
*-------------------------------------------------------------------------
*/
#ifndef MULTIRANGETYPES_H
#define MULTIRANGETYPES_H
#include "utils/typcache.h"
#include "utils/expandeddatum.h"
/*
* Multiranges are varlena objects, so must meet the varlena convention that
* the first int32 of the object contains the total object size in bytes.
* Be sure to use VARSIZE() and SET_VARSIZE() to access it, though!
*/
typedef struct
{
int32 vl_len_; /* varlena header (do not touch directly!) */
Oid multirangetypid; /* multirange type's own OID */
uint32 rangeCount; /* the number of ranges */
/*
* Following the count are the range objects themselves, as ShortRangeType
* structs. Note that ranges are varlena too, depending on whether they
* have lower/upper bounds and because even their base types can be
* varlena. So we can't really index into this list.
*/
} MultirangeType;
/* Use these macros in preference to accessing these fields directly */
#define MultirangeTypeGetOid(mr) ((mr)->multirangetypid)
#define MultirangeIsEmpty(mr) ((mr)->rangeCount == 0)
/*
* fmgr macros for multirange type objects
*/
#define DatumGetMultirangeTypeP(X) ((MultirangeType *) PG_DETOAST_DATUM(X))
#define DatumGetMultirangeTypePCopy(X) ((MultirangeType *) PG_DETOAST_DATUM_COPY(X))
#define MultirangeTypePGetDatum(X) PointerGetDatum(X)
#define PG_GETARG_MULTIRANGE_P(n) DatumGetMultirangeTypeP(PG_GETARG_DATUM(n))
#define PG_GETARG_MULTIRANGE_P_COPY(n) DatumGetMultirangeTypePCopy(PG_GETARG_DATUM(n))
#define PG_RETURN_MULTIRANGE_P(x) return MultirangeTypePGetDatum(x)
/*
* prototypes for functions defined in multirangetypes.c
*/
/* internal versions of the above */
extern bool multirange_eq_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
MultirangeType *mr2);
extern bool multirange_ne_internal(TypeCacheEntry *typcache, MultirangeType *mr1,
MultirangeType *mr2);
extern bool multirange_contains_elem_internal(TypeCacheEntry *typcache, MultirangeType *mr,
Datum elem);
extern bool multirange_contains_range_internal(TypeCacheEntry *typcache, MultirangeType *mr,
RangeType *r);
extern bool multirange_contains_multirange_internal(TypeCacheEntry *typcache,
MultirangeType *mr1,
MultirangeType *mr2);
extern bool range_overlaps_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
MultirangeType *mr);
extern bool multirange_overlaps_multirange_internal(TypeCacheEntry *typcache,
MultirangeType *mr1,
MultirangeType *mr2);
extern bool range_before_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
MultirangeType *mr);
extern bool range_after_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
MultirangeType *mr);
extern bool range_adjacent_multirange_internal(TypeCacheEntry *typcache, RangeType *r,
MultirangeType *mr);
extern bool multirange_before_multirange_internal(TypeCacheEntry *typcache,
MultirangeType *mr1,
MultirangeType *mr2);
extern MultirangeType *multirange_minus_internal(Oid mltrngtypoid,
TypeCacheEntry *rangetyp,
int32 range_count1,
RangeType **ranges1,
int32 range_count2,
RangeType **ranges2);
extern MultirangeType *multirange_intersect_internal(Oid mltrngtypoid,
TypeCacheEntry *rangetyp,
int32 range_count1,
RangeType **ranges1,
int32 range_count2,
RangeType **ranges2);
/* assorted support functions */
extern TypeCacheEntry *multirange_get_typcache(FunctionCallInfo fcinfo,
Oid mltrngtypid);
extern void multirange_deserialize(TypeCacheEntry *rangetyp,
const MultirangeType *range,
int32 *range_count,
RangeType ***ranges);
extern MultirangeType *make_multirange(Oid mltrngtypoid,
TypeCacheEntry *typcache,
int32 range_count, RangeType **ranges);
extern MultirangeType *make_empty_multirange(Oid mltrngtypoid,
TypeCacheEntry *rangetyp);
extern void multirange_get_bounds(TypeCacheEntry *rangetyp,
const MultirangeType *multirange,
uint32 i,
RangeBound *lower, RangeBound *upper);
extern RangeType *multirange_get_range(TypeCacheEntry *rangetyp,
const MultirangeType *multirange, int i);
#endif /* MULTIRANGETYPES_H */

View File

@ -29,6 +29,8 @@ typedef struct
/* Following the OID are zero to two bound values, then a flags byte */
} RangeType;
#define RANGE_EMPTY_LITERAL "empty"
/* Use this macro in preference to fetching rangetypid field directly */
#define RangeTypeGetOid(r) ((r)->rangetypid)
@ -115,6 +117,12 @@ extern bool range_overleft_internal(TypeCacheEntry *typcache, const RangeType *r
const RangeType *r2);
extern bool range_overright_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
extern RangeType *range_union_internal(TypeCacheEntry *typcache, RangeType *r1,
RangeType *r2, bool strict);
extern RangeType *range_minus_internal(TypeCacheEntry *typcache, RangeType *r1,
RangeType *r2);
extern RangeType *range_intersect_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2);
/* assorted support functions */
extern TypeCacheEntry *range_get_typcache(FunctionCallInfo fcinfo,
@ -132,8 +140,12 @@ extern int range_cmp_bounds(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_cmp_bound_values(TypeCacheEntry *typcache, const RangeBound *b1,
const RangeBound *b2);
extern int range_compare(const void *key1, const void *key2, void *arg);
extern bool bounds_adjacent(TypeCacheEntry *typcache, RangeBound bound1,
RangeBound bound2);
extern RangeType *make_empty_range(TypeCacheEntry *typcache);
extern bool range_split_internal(TypeCacheEntry *typcache, const RangeType *r1,
const RangeType *r2, RangeType **output1,
RangeType **output2);
#endif /* RANGETYPES_H */

View File

@ -39,6 +39,9 @@
/* default selectivity estimate for range inequalities "A > b AND A < c" */
#define DEFAULT_RANGE_INEQ_SEL 0.005
/* default selectivity estimate for multirange inequalities "A > b AND A < c" */
#define DEFAULT_MULTIRANGE_INEQ_SEL 0.005
/* default selectivity estimate for pattern-match operators such as LIKE */
#define DEFAULT_MATCH_SEL 0.005

View File

@ -79,6 +79,7 @@ enum SysCacheIdentifier
PUBLICATIONOID,
PUBLICATIONREL,
PUBLICATIONRELMAP,
RANGEMULTIRANGE,
RANGETYPE,
RELNAMENSP,
RELOID,

View File

@ -101,6 +101,11 @@ typedef struct TypeCacheEntry
FmgrInfo rng_canonical_finfo; /* canonicalization function, if any */
FmgrInfo rng_subdiff_finfo; /* difference function, if any */
/*
* Fields computed when TYPCACHE_MULTIRANGE_INFO is required.
*/
struct TypeCacheEntry *rngtype; /* multirange's range underlying type */
/*
* Domain's base type and typmod if it's a domain type. Zeroes if not
* domain, or if information hasn't been requested.
@ -128,22 +133,23 @@ typedef struct TypeCacheEntry
} TypeCacheEntry;
/* Bit flags to indicate which fields a given caller needs to have set */
#define TYPECACHE_EQ_OPR 0x0001
#define TYPECACHE_LT_OPR 0x0002
#define TYPECACHE_GT_OPR 0x0004
#define TYPECACHE_CMP_PROC 0x0008
#define TYPECACHE_HASH_PROC 0x0010
#define TYPECACHE_EQ_OPR_FINFO 0x0020
#define TYPECACHE_CMP_PROC_FINFO 0x0040
#define TYPECACHE_HASH_PROC_FINFO 0x0080
#define TYPECACHE_TUPDESC 0x0100
#define TYPECACHE_BTREE_OPFAMILY 0x0200
#define TYPECACHE_HASH_OPFAMILY 0x0400
#define TYPECACHE_RANGE_INFO 0x0800
#define TYPECACHE_DOMAIN_BASE_INFO 0x1000
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x2000
#define TYPECACHE_HASH_EXTENDED_PROC 0x4000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x8000
#define TYPECACHE_EQ_OPR 0x00001
#define TYPECACHE_LT_OPR 0x00002
#define TYPECACHE_GT_OPR 0x00004
#define TYPECACHE_CMP_PROC 0x00008
#define TYPECACHE_HASH_PROC 0x00010
#define TYPECACHE_EQ_OPR_FINFO 0x00020
#define TYPECACHE_CMP_PROC_FINFO 0x00040
#define TYPECACHE_HASH_PROC_FINFO 0x00080
#define TYPECACHE_TUPDESC 0x00100
#define TYPECACHE_BTREE_OPFAMILY 0x00200
#define TYPECACHE_HASH_OPFAMILY 0x00400
#define TYPECACHE_RANGE_INFO 0x00800
#define TYPECACHE_DOMAIN_BASE_INFO 0x01000
#define TYPECACHE_DOMAIN_CONSTR_INFO 0x02000
#define TYPECACHE_HASH_EXTENDED_PROC 0x04000
#define TYPECACHE_HASH_EXTENDED_PROC_FINFO 0x08000
#define TYPECACHE_MULTIRANGE_INFO 0x10000
/* This value will not equal any valid tupledesc identifier, nor 0 */
#define INVALID_TUPLEDESC_IDENTIFIER ((uint64) 1)

View File

@ -515,6 +515,8 @@ do_compile(FunctionCallInfo fcinfo,
else if (rettypeid == ANYRANGEOID ||
rettypeid == ANYCOMPATIBLERANGEOID)
rettypeid = INT4RANGEOID;
else if (rettypeid == ANYMULTIRANGEOID)
rettypeid = INT4MULTIRANGEOID;
else /* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
rettypeid = INT4OID;
/* XXX what could we use for ANYENUM? */
@ -2109,6 +2111,7 @@ build_datatype(HeapTuple typeTup, int32 typmod,
case TYPTYPE_BASE:
case TYPTYPE_ENUM:
case TYPTYPE_RANGE:
case TYPTYPE_MULTIRANGE:
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case TYPTYPE_COMPOSITE:
@ -2526,6 +2529,9 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
case ANYCOMPATIBLERANGEOID:
argtypes[i] = INT4RANGEOID;
break;
case ANYMULTIRANGEOID:
argtypes[i] = INT4MULTIRANGEOID;
break;
default:
break;
}

View File

@ -140,6 +140,7 @@ owner of sequence deptest_a_seq
owner of table deptest
owner of function deptest_func()
owner of type deptest_enum
owner of type deptest_multirange
owner of type deptest_range
owner of table deptest2
owner of sequence ss1

View File

@ -305,6 +305,19 @@ WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
-------+----------+-----------+-----------
(0 rows)
SELECT v as value, hash_multirange(v)::bit(32) as standard,
hash_multirange_extended(v, 0)::bit(32) as extended0,
hash_multirange_extended(v, 1)::bit(32) as extended1
FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
('{[5675, 550273)}'::int4multirange),
('{[550274, 1550274)}'::int4multirange),
('{[1550275, 208112489)}'::int4multirange)) x(v)
WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
value | standard | extended0 | extended1
-------+----------+-----------+-----------
(0 rows)
CREATE TYPE hash_test_t1 AS (a int, b text);
SELECT v as value, hash_record(v)::bit(32) as standard,
hash_record_extended(v, 0)::bit(32) as extended0,

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
then return true; end if;
end if;
return false;
end
$$ language plpgsql strict stable;
@ -183,7 +187,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
-- dummy built-in function. Likewise, ignore range constructor functions.
-- dummy built-in function. Likewise, ignore range and multirange constructor
-- functions.
SELECT DISTINCT p1.prorettype::regtype, p2.prorettype::regtype
FROM pg_proc AS p1, pg_proc AS p2
WHERE p1.oid != p2.oid AND
@ -192,6 +197,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
prorettype | prorettype
@ -210,6 +217,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
proargtypes | proargtypes
@ -230,6 +239,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
proargtypes | proargtypes
@ -342,7 +353,8 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
'anyrange'::regtype = ANY (p1.proargtypes))
'anyrange'::regtype = ANY (p1.proargtypes) OR
'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+----------------
@ -357,20 +369,25 @@ ORDER BY 2;
3532 | enum_recv
(9 rows)
-- anyrange is tighter than the rest, can only resolve from anyrange input
-- anyrange and anymultirange are tighter than the rest, can only resolve
-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype = 'anyrange'::regtype
WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
'anyrange'::regtype = ANY (p1.proargtypes)
('anyrange'::regtype = ANY (p1.proargtypes) OR
'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
oid | proname
------+------------------
4229 | anymultirange_in
3832 | anyrange_in
4231 | multirange_in
4233 | multirange_recv
3876 | range_gist_union
3834 | range_in
3836 | range_recv
(4 rows)
(7 rows)
-- similarly for the anycompatible family
SELECT p1.oid, p1.proname
@ -2204,13 +2221,14 @@ ORDER BY 1, 2, 3;
| float_ops | float4_ops | real
| float_ops | float8_ops | double precision
| jsonb_ops | jsonb_ops | jsonb
| multirange_ops | multirange_ops | anymultirange
| numeric_ops | numeric_ops | numeric
| range_ops | range_ops | anyrange
| record_image_ops | record_image_ops | record
| record_ops | record_ops | record
| tsquery_ops | tsquery_ops | tsquery
| tsvector_ops | tsvector_ops | tsvector
(14 rows)
(15 rows)
-- **************** pg_index ****************
-- Look for illegal values in pg_index fields.

View File

@ -1811,7 +1811,7 @@ begin
return array[x + 1, x + 2];
end$$ language plpgsql;
ERROR: cannot determine result data type
DETAIL: A result of type anyrange requires at least one input of type anyrange.
DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function f1(x anyrange) returns anyarray as $$
begin
return array[lower(x), upper(x)];
@ -1856,7 +1856,7 @@ begin
return array[x + 1, x + 2];
end$$ language plpgsql;
ERROR: cannot determine result data type
DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function f1(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
begin
return x;

View File

@ -61,7 +61,7 @@ create function polyf(x anyelement) returns anyrange as $$
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anyrange requires at least one input of type anyrange.
DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function polyf(x anyrange) returns anyarray as $$
select array[lower(x), upper(x)]
$$ language sql;
@ -97,12 +97,27 @@ LINE 1: select polyf(int4range(42, 49), 11, 4.5) as fail;
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
create function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
select array[lower(x), upper(x), y, z]
$$ language sql;
select polyf(multirange(int4range(42, 49)), 11, 2::smallint) as int, polyf(multirange(float8range(4.5, 7.8)), 7.8, 11::real) as num;
int | num
--------------+------------------
{42,49,11,2} | {4.5,7.8,7.8,11}
(1 row)
select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail; -- range type doesn't fit
ERROR: function polyf(int4multirange, integer, numeric) does not exist
LINE 1: select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
drop function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible);
-- fail, can't infer type:
create function polyf(x anycompatible) returns anycompatiblerange as $$
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function polyf(x anycompatiblerange, y anycompatiblearray) returns anycompatiblerange as $$
select x
$$ language sql;
@ -113,6 +128,22 @@ select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8),
(1 row)
drop function polyf(x anycompatiblerange, y anycompatiblearray);
-- fail, can't infer type:
create function polyf(x anycompatible) returns anycompatiblemultirange as $$
select array[x + 1, x + 2]
$$ language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anycompatiblemultirange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function polyf(x anycompatiblemultirange, y anycompatiblearray) returns anycompatiblemultirange as $$
select x
$$ language sql;
select polyf(multirange(int4range(42, 49)), array[11]) as int, polyf(multirange(float8range(4.5, 7.8)), array[7]) as num;
int | num
-----------+-------------
{[42,49)} | {[4.5,7.8)}
(1 row)
drop function polyf(x anycompatiblemultirange, y anycompatiblearray);
create function polyf(a anyelement, b anyarray,
c anycompatible, d anycompatible,
OUT x anyarray, OUT y anycompatiblearray)
@ -231,7 +262,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
@ -243,11 +274,11 @@ CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case2 (R = P) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@ -302,13 +333,13 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
@ -324,21 +355,21 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
@ -368,11 +399,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- N P
-- should CREATE
CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
@ -382,7 +413,7 @@ CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- Case4 (R = N) && ((B = P) || (B = N))
-- -------------------------------------
-- S tf1 B tf2
@ -436,21 +467,21 @@ ERROR: function tfp(integer[], anyelement) does not exist
CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N N P
-- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P N P N
-- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
@ -472,13 +503,13 @@ ERROR: function tf2p(anyarray, anyelement) does not exist
CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P N P
-- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
FINALFUNC = ffnp, INITCOND = '{}');
ERROR: cannot determine transition data type
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyarray requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
-- P P P N
-- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
@ -1855,7 +1886,59 @@ returns anycompatiblerange as $$
select $1
$$ language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function anyctest(anycompatible, anycompatiblemultirange)
returns anycompatiblemultirange as $$
select $2
$$ language sql;
select x, pg_typeof(x) from anyctest(11, multirange(int4range(4,7))) x;
x | pg_typeof
---------+----------------
{[4,7)} | int4multirange
(1 row)
select x, pg_typeof(x) from anyctest(11, multirange(numrange(4,7))) x;
x | pg_typeof
---------+---------------
{[4,7)} | nummultirange
(1 row)
select x, pg_typeof(x) from anyctest(11, 12) x; -- fail
ERROR: function anyctest(integer, integer) does not exist
LINE 1: select x, pg_typeof(x) from anyctest(11, 12) x;
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
select x, pg_typeof(x) from anyctest(11.2, multirange(int4range(4,7))) x; -- fail
ERROR: function anyctest(numeric, int4multirange) does not exist
LINE 1: select x, pg_typeof(x) from anyctest(11.2, multirange(int4ra...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
select x, pg_typeof(x) from anyctest(11.2, '{[4,7)}') x; -- fail
ERROR: could not identify anycompatiblemultirange type
drop function anyctest(anycompatible, anycompatiblemultirange);
create function anyctest(anycompatiblemultirange, anycompatiblemultirange)
returns anycompatible as $$
select lower($1) + upper($2)
$$ language sql;
select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(int4range(4,7))) x;
x | pg_typeof
----+-----------
18 | integer
(1 row)
select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(numrange(4,7))) x; -- fail
ERROR: function anyctest(int4multirange, nummultirange) does not exist
LINE 1: select x, pg_typeof(x) from anyctest(multirange(int4range(11...
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
drop function anyctest(anycompatiblemultirange, anycompatiblemultirange);
-- fail, can't infer result type:
create function anyctest(anycompatible)
returns anycompatiblemultirange as $$
select $1
$$ language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anycompatiblemultirange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
returns anycompatiblearray as $$
select array[$1, $2]

View File

@ -1556,7 +1556,7 @@ DROP FUNCTION dup(anyelement);
CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
AS 'select $1, array[$1,$1]' LANGUAGE sql;
ERROR: cannot determine result data type
DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, or anyrange.
DETAIL: A result of type anyelement requires at least one input of type anyelement, anyarray, anynonarray, anyenum, anyrange, or anymultirange.
CREATE FUNCTION dup (f1 anycompatible, f2 anycompatiblearray, f3 out anycompatible, f4 out anycompatiblearray)
AS 'select $1, $2' LANGUAGE sql;
SELECT dup(22, array[44]);

View File

@ -518,6 +518,25 @@ select numrange(1.0, 2.0) * numrange(2.5, 3.0);
empty
(1 row)
select range_intersect_agg(nr) from numrange_test;
range_intersect_agg
---------------------
empty
(1 row)
select range_intersect_agg(nr) from numrange_test where false;
range_intersect_agg
---------------------
(1 row)
select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
range_intersect_agg
---------------------
[3,5)
(1 row)
analyze numrange_test;
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
INSERT INTO numrange_test2 VALUES('[, 5)');
@ -1371,12 +1390,12 @@ drop function anyarray_anyrange_func(anyarray, anyrange);
create function bogus_func(anyelement)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anyrange requires at least one input of type anyrange.
DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
-- should fail
create function bogus_func(int)
returns anyrange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anyrange requires at least one input of type anyrange.
DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
create function range_add_bounds(anyrange)
returns anyelement as 'select lower($1) + upper($1)' language sql;
select range_add_bounds(int4range(1, 17));
@ -1430,7 +1449,7 @@ drop function anycompatiblearray_anycompatiblerange_func(anycompatiblearray, any
create function bogus_func(anycompatible)
returns anycompatiblerange as 'select int4range(1,10)' language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange.
DETAIL: A result of type anycompatiblerange requires at least one input of type anycompatiblerange or anycompatiblemultirange.
--
-- Arrays of ranges
--
@ -1508,6 +1527,7 @@ reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from outparam_succeed(int4range(1,2));
@ -1525,6 +1545,16 @@ select * from outparam2_succeed(int4range(1,11));
{1,11} | {11,1}
(1 row)
-- infer anyarray from anyrange
create function outparam_succeed2(i anyrange, out r anyarray, out t text)
as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
select * from outparam_succeed2(int4range(int4range(1,2)));
r | t
-----+-----
{2} | foo
(1 row)
-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;
select * from inoutparam_succeed(int4range(1,2));
@ -1547,14 +1577,14 @@ select * from table_succeed(int4range(1,11));
create function outparam_fail(i anyelement, out r anyrange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anyrange requires at least one input of type anyrange.
DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function inoutparam_fail(inout i anyelement, out r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anyrange requires at least one input of type anyrange.
DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.
--should fail
create function table_fail(i anyelement) returns table(i anyelement, r anyrange)
as $$ select $1, '[1,10]' $$ language sql;
ERROR: cannot determine result data type
DETAIL: A result of type anyrange requires at least one input of type anyrange.
DETAIL: A result of type anyrange requires at least one input of type anyrange or anymultirange.

View File

@ -92,6 +92,7 @@ num_exp_sqrt|t
num_exp_sub|t
num_input_test|f
num_result|f
nummultirange_test|t
numrange_test|t
onek|t
onek2|t
@ -172,6 +173,7 @@ quad_poly_tbl|t
radix_text_tbl|t
ramp|f
real_city|f
reservations|f
road|t
shighway|t
slow_emp4000|f

View File

@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
(p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
(p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@ -196,18 +196,19 @@ WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
-- Composites, domains, enums, ranges should all use the same input routines
-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
typtype | typinput
---------+-----------
typtype | typinput
---------+---------------
c | record_in
d | domain_in
e | enum_in
m | multirange_in
r | range_in
(4 rows)
(5 rows)
-- Check for bogus typoutput routines
-- As of 8.0, this check finds refcursor, which is borrowing
@ -241,17 +242,18 @@ WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
-- Composites, enums, ranges should all use the same output routines
-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
typtype | typoutput
---------+------------
typtype | typoutput
---------+----------------
c | record_out
e | enum_out
m | multirange_out
r | range_out
(3 rows)
(4 rows)
-- Domains should have same typoutput as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@ -319,18 +321,19 @@ WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
-- Composites, domains, enums, ranges should all use the same receive routines
-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
ORDER BY 1;
typtype | typreceive
---------+-------------
typtype | typreceive
---------+-----------------
c | record_recv
d | domain_recv
e | enum_recv
m | multirange_recv
r | range_recv
(4 rows)
(5 rows)
-- Check for bogus typsend routines
-- As of 7.4, this check finds refcursor, which is borrowing
@ -364,17 +367,18 @@ WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-----+---------+-----+---------
(0 rows)
-- Composites, enums, ranges should all use the same send routines
-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
ORDER BY 1;
typtype | typsend
---------+-------------
typtype | typsend
---------+-----------------
c | record_send
e | enum_send
m | multirange_send
r | range_send
(3 rows)
(4 rows)
-- Domains should have same typsend as their base types
SELECT p1.oid, p1.typname, p2.oid, p2.typname
@ -660,3 +664,11 @@ WHERE pronargs != 2
----------+------------+---------
(0 rows)
-- every range should have a valid multirange
SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
FROM pg_range p1
WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;
rngtypid | rngsubtype | rngmultitypid
----------+------------+---------------
(0 rows)

View File

@ -19,15 +19,17 @@ test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeri
# The second group of parallel tests
# strings depends on char, varchar and text
# numerology depends on int2, int4, int8, float4, float8
# multirangetypes depends on rangetypes
# multirangetypes shouldn't be in the one group with type_sanity
# ----------
test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes xid
test: strings numerology point lseg line box path polygon circle date time timetz timestamp timestamptz interval inet macaddr macaddr8 tstypes multirangetypes
# ----------
# Another group of parallel tests
# geometry depends on point, lseg, box, path, polygon and circle
# horology depends on interval, timetz, timestamp, timestamptz
# ----------
test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions unicode
test: geometry horology regex oidjoins type_sanity opr_sanity misc_sanity comments expressions unicode xid
# ----------
# These four each depend on the previous one

View File

@ -20,6 +20,7 @@ test: uuid
test: enum
test: money
test: rangetypes
test: multirangetypes
test: pg_lsn
test: regproc
test: strings

View File

@ -227,6 +227,16 @@ FROM (VALUES (int4range(10, 20)), (int4range(23, 43)),
WHERE hash_range(v)::bit(32) != hash_range_extended(v, 0)::bit(32)
OR hash_range(v)::bit(32) = hash_range_extended(v, 1)::bit(32);
SELECT v as value, hash_multirange(v)::bit(32) as standard,
hash_multirange_extended(v, 0)::bit(32) as extended0,
hash_multirange_extended(v, 1)::bit(32) as extended1
FROM (VALUES ('{[10,20)}'::int4multirange), ('{[23, 43]}'::int4multirange),
('{[5675, 550273)}'::int4multirange),
('{[550274, 1550274)}'::int4multirange),
('{[1550275, 208112489)}'::int4multirange)) x(v)
WHERE hash_multirange(v)::bit(32) != hash_multirange_extended(v, 0)::bit(32)
OR hash_multirange(v)::bit(32) = hash_multirange_extended(v, 1)::bit(32);
CREATE TYPE hash_test_t1 AS (a int, b text);
SELECT v as value, hash_record(v)::bit(32) as standard,
hash_record_extended(v, 0)::bit(32) as extended0,

View File

@ -0,0 +1,677 @@
-- Tests for multirange data types.
--
-- test input parser
--
-- negative tests; should fail
select ''::textmultirange;
select '{,}'::textmultirange;
select '{[a,c),}'::textmultirange;
select '{,[a,c)}'::textmultirange;
select '{-[a,z)}'::textmultirange;
select '{[a,z) - }'::textmultirange;
select '{(",a)}'::textmultirange;
select '{(,,a)}'::textmultirange;
select '{(),a)}'::textmultirange;
select '{(a,))}'::textmultirange;
select '{(],a)}'::textmultirange;
select '{(a,])}'::textmultirange;
select '{[z,a]}'::textmultirange;
-- should succeed
select '{}'::textmultirange;
select ' {} '::textmultirange;
select ' { empty, empty } '::textmultirange;
select ' {( " a " " a ", " z " " z " ) }'::textmultirange;
select '{(,z)}'::textmultirange;
select '{(a,)}'::textmultirange;
select '{[,z]}'::textmultirange;
select '{[a,]}'::textmultirange;
select '{(,)}'::textmultirange;
select '{[ , ]}'::textmultirange;
select '{["",""]}'::textmultirange;
select '{[",",","]}'::textmultirange;
select '{["\\","\\"]}'::textmultirange;
select '{["""","\""]}'::textmultirange;
select '{(\\,a)}'::textmultirange;
select '{((,z)}'::textmultirange;
select '{([,z)}'::textmultirange;
select '{(!,()}'::textmultirange;
select '{(!,[)}'::textmultirange;
select '{[a,a]}'::textmultirange;
select '{[a,a],[a,b]}'::textmultirange;
select '{[a,b), [b,e]}'::textmultirange;
select '{[a,d), [b,f]}'::textmultirange;
select '{[a,a],[b,b]}'::textmultirange;
-- without canonicalization, we can't join these:
select '{[a,a], [b,b]}'::textmultirange;
-- with canonicalization, we can join these:
select '{[1,2], [3,4]}'::int4multirange;
select '{[a,a], [b,b], [c,c]}'::textmultirange;
select '{[a,d], [b,e]}'::textmultirange;
select '{[a,d), [d,e)}'::textmultirange;
-- these are allowed but normalize to empty:
select '{[a,a)}'::textmultirange;
select '{(a,a]}'::textmultirange;
select '{(a,a)}'::textmultirange;
--
-- test the constructor
---
select textmultirange();
select textmultirange(textrange('a', 'c'));
select textmultirange(textrange('a', 'c'), textrange('f', 'g'));
select textmultirange(textrange('a', 'c'), textrange('b', 'd'));
--
-- test casts, both a built-in range type and a user-defined one:
--
select 'empty'::int4range::int4multirange;
select int4range(1, 3)::int4multirange;
select int4range(1, null)::int4multirange;
select int4range(null, null)::int4multirange;
select 'empty'::textrange::textmultirange;
select textrange('a', 'c')::textmultirange;
select textrange('a', null)::textmultirange;
select textrange(null, null)::textmultirange;
--
-- create some test data and test the operators
--
CREATE TABLE nummultirange_test (nmr NUMMULTIRANGE);
CREATE INDEX nummultirange_test_btree ON nummultirange_test(nmr);
INSERT INTO nummultirange_test VALUES('{}');
INSERT INTO nummultirange_test VALUES('{[,)}');
INSERT INTO nummultirange_test VALUES('{[3,]}');
INSERT INTO nummultirange_test VALUES('{[,), [3,]}');
INSERT INTO nummultirange_test VALUES('{[, 5)}');
INSERT INTO nummultirange_test VALUES(nummultirange());
INSERT INTO nummultirange_test VALUES(nummultirange(variadic '{}'::numrange[]));
INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.1, 2.2)));
INSERT INTO nummultirange_test VALUES('{empty}');
INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.7, 1.9)));
INSERT INTO nummultirange_test VALUES(nummultirange(numrange(1.7, 1.7, '[]'), numrange(1.9, 2.1)));
SELECT nmr, isempty(nmr), lower(nmr), upper(nmr) FROM nummultirange_test ORDER BY nmr;
SELECT nmr, lower_inc(nmr), lower_inf(nmr), upper_inc(nmr), upper_inf(nmr) FROM nummultirange_test ORDER BY nmr;
SELECT * FROM nummultirange_test WHERE nmr = '{}';
SELECT * FROM nummultirange_test WHERE nmr = '{(,5)}';
SELECT * FROM nummultirange_test WHERE nmr = '{[3,)}';
SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7]}';
SELECT * FROM nummultirange_test WHERE nmr = '{[1.7,1.7],[1.9,2.1)}';
SELECT * FROM nummultirange_test WHERE nmr < '{}';
SELECT * FROM nummultirange_test WHERE nmr < '{[-1000.0, -1000.0]}';
SELECT * FROM nummultirange_test WHERE nmr < '{[0.0, 1.0]}';
SELECT * FROM nummultirange_test WHERE nmr < '{[1000.0, 1001.0]}';
SELECT * FROM nummultirange_test WHERE nmr <= '{}';
SELECT * FROM nummultirange_test WHERE nmr <= '{[3,)}';
SELECT * FROM nummultirange_test WHERE nmr >= '{}';
SELECT * FROM nummultirange_test WHERE nmr >= '{[3,)}';
SELECT * FROM nummultirange_test WHERE nmr > '{}';
SELECT * FROM nummultirange_test WHERE nmr > '{[-1000.0, -1000.0]}';
SELECT * FROM nummultirange_test WHERE nmr > '{[0.0, 1.0]}';
SELECT * FROM nummultirange_test WHERE nmr > '{[1000.0, 1001.0]}';
SELECT * FROM nummultirange_test WHERE nmr <> '{}';
SELECT * FROM nummultirange_test WHERE nmr <> '{(,5)}';
select nummultirange(numrange(2.0, 1.0));
select nummultirange(numrange(5.0, 6.0), numrange(1.0, 2.0));
analyze nummultirange_test;
-- overlaps
SELECT * FROM nummultirange_test WHERE range_overlaps_multirange(numrange(4.0, 4.2), nmr);
SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) && nmr;
SELECT * FROM nummultirange_test WHERE multirange_overlaps_range(nmr, numrange(4.0, 4.2));
SELECT * FROM nummultirange_test WHERE nmr && numrange(4.0, 4.2);
SELECT * FROM nummultirange_test WHERE multirange_overlaps_multirange(nmr, nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0)));
SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(4.0, 4.2), numrange(6.0, 7.0));
SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0));
SELECT * FROM nummultirange_test WHERE nmr && nummultirange(numrange(6.0, 7.0), numrange(8.0, 9.0));
-- mr contains x
SELECT * FROM nummultirange_test WHERE multirange_contains_elem(nmr, 4.0);
SELECT * FROM nummultirange_test WHERE nmr @> 4.0;
SELECT * FROM nummultirange_test WHERE multirange_contains_range(nmr, numrange(4.0, 4.2));
SELECT * FROM nummultirange_test WHERE nmr @> numrange(4.0, 4.2);
SELECT * FROM nummultirange_test WHERE multirange_contains_multirange(nmr, '{[4.0,4.2), [6.0, 8.0)}');
SELECT * FROM nummultirange_test WHERE nmr @> '{[4.0,4.2), [6.0, 8.0)}'::nummultirange;
-- x is contained by mr
SELECT * FROM nummultirange_test WHERE elem_contained_by_multirange(4.0, nmr);
SELECT * FROM nummultirange_test WHERE 4.0 <@ nmr;
SELECT * FROM nummultirange_test WHERE range_contained_by_multirange(numrange(4.0, 4.2), nmr);
SELECT * FROM nummultirange_test WHERE numrange(4.0, 4.2) <@ nmr;
SELECT * FROM nummultirange_test WHERE multirange_contained_by_multirange('{[4.0,4.2), [6.0, 8.0)}', nmr);
SELECT * FROM nummultirange_test WHERE '{[4.0,4.2), [6.0, 8.0)}'::nummultirange <@ nmr;
-- overlaps
SELECT 'empty'::numrange && nummultirange();
SELECT 'empty'::numrange && nummultirange(numrange(1,2));
SELECT nummultirange() && 'empty'::numrange;
SELECT nummultirange(numrange(1,2)) && 'empty'::numrange;
SELECT nummultirange() && nummultirange();
SELECT nummultirange() && nummultirange(numrange(1,2));
SELECT nummultirange(numrange(1,2)) && nummultirange();
SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(7,8));
SELECT nummultirange(numrange(1,2), numrange(7,8)) && nummultirange(numrange(3,4));
SELECT nummultirange(numrange(3,4)) && nummultirange(numrange(1,2), numrange(3.5,8));
SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && numrange(3,4);
SELECT nummultirange(numrange(1,2), numrange(3.5,8)) && nummultirange(numrange(3,4));
-- contains
SELECT nummultirange() @> nummultirange();
SELECT nummultirange() @> 'empty'::numrange;
SELECT nummultirange(numrange(null,null)) @> numrange(1,2);
SELECT nummultirange(numrange(null,null)) @> numrange(null,2);
SELECT nummultirange(numrange(null,null)) @> numrange(2,null);
SELECT nummultirange(numrange(null,5)) @> numrange(null,3);
SELECT nummultirange(numrange(null,5)) @> numrange(null,8);
SELECT nummultirange(numrange(5,null)) @> numrange(8,null);
SELECT nummultirange(numrange(5,null)) @> numrange(3,null);
SELECT nummultirange(numrange(1,5)) @> numrange(8,9);
SELECT nummultirange(numrange(1,5)) @> numrange(3,9);
SELECT nummultirange(numrange(1,5)) @> numrange(1,4);
SELECT nummultirange(numrange(1,5)) @> numrange(1,5);
SELECT nummultirange(numrange(-4,-2), numrange(1,5)) @> numrange(1,5);
SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(1,5);
SELECT nummultirange(numrange(1,5), numrange(8,9)) @> numrange(6,7);
SELECT nummultirange(numrange(1,5), numrange(6,9)) @> numrange(6,7);
SELECT '{[1,5)}'::nummultirange @> '{[1,5)}';
SELECT '{[-4,-2), [1,5)}'::nummultirange @> '{[1,5)}';
SELECT '{[1,5), [8,9)}'::nummultirange @> '{[1,5)}';
SELECT '{[1,5), [8,9)}'::nummultirange @> '{[6,7)}';
SELECT '{[1,5), [6,9)}'::nummultirange @> '{[6,7)}';
-- is contained by
SELECT nummultirange() <@ nummultirange();
SELECT 'empty'::numrange <@ nummultirange();
SELECT numrange(1,2) <@ nummultirange(numrange(null,null));
SELECT numrange(null,2) <@ nummultirange(numrange(null,null));
SELECT numrange(2,null) <@ nummultirange(numrange(null,null));
SELECT numrange(null,3) <@ nummultirange(numrange(null,5));
SELECT numrange(null,8) <@ nummultirange(numrange(null,5));
SELECT numrange(8,null) <@ nummultirange(numrange(5,null));
SELECT numrange(3,null) <@ nummultirange(numrange(5,null));
SELECT numrange(8,9) <@ nummultirange(numrange(1,5));
SELECT numrange(3,9) <@ nummultirange(numrange(1,5));
SELECT numrange(1,4) <@ nummultirange(numrange(1,5));
SELECT numrange(1,5) <@ nummultirange(numrange(1,5));
SELECT numrange(1,5) <@ nummultirange(numrange(-4,-2), numrange(1,5));
SELECT numrange(1,5) <@ nummultirange(numrange(1,5), numrange(8,9));
SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(8,9));
SELECT numrange(6,7) <@ nummultirange(numrange(1,5), numrange(6,9));
SELECT '{[1,5)}' <@ '{[1,5)}'::nummultirange;
SELECT '{[1,5)}' <@ '{[-4,-2), [1,5)}'::nummultirange;
SELECT '{[1,5)}' <@ '{[1,5), [8,9)}'::nummultirange;
SELECT '{[6,7)}' <@ '{[1,5), [8,9)}'::nummultirange;
SELECT '{[6,7)}' <@ '{[1,5), [6,9)}'::nummultirange;
-- overleft
SELECT 'empty'::numrange &< nummultirange();
SELECT 'empty'::numrange &< nummultirange(numrange(1,2));
SELECT nummultirange() &< 'empty'::numrange;
SELECT nummultirange(numrange(1,2)) &< 'empty'::numrange;
SELECT nummultirange() &< nummultirange();
SELECT nummultirange(numrange(1,2)) &< nummultirange();
SELECT nummultirange() &< nummultirange(numrange(1,2));
SELECT numrange(6,7) &< nummultirange(numrange(3,4));
SELECT numrange(1,2) &< nummultirange(numrange(3,4));
SELECT numrange(1,4) &< nummultirange(numrange(3,4));
SELECT numrange(1,6) &< nummultirange(numrange(3,4));
SELECT numrange(3.5,6) &< nummultirange(numrange(3,4));
SELECT nummultirange(numrange(6,7)) &< numrange(3,4);
SELECT nummultirange(numrange(1,2)) &< numrange(3,4);
SELECT nummultirange(numrange(1,4)) &< numrange(3,4);
SELECT nummultirange(numrange(1,6)) &< numrange(3,4);
SELECT nummultirange(numrange(3.5,6)) &< numrange(3,4);
SELECT nummultirange(numrange(6,7)) &< nummultirange(numrange(3,4));
SELECT nummultirange(numrange(1,2)) &< nummultirange(numrange(3,4));
SELECT nummultirange(numrange(1,4)) &< nummultirange(numrange(3,4));
SELECT nummultirange(numrange(1,6)) &< nummultirange(numrange(3,4));
SELECT nummultirange(numrange(3.5,6)) &< nummultirange(numrange(3,4));
-- overright
SELECT nummultirange() &> 'empty'::numrange;
SELECT nummultirange(numrange(1,2)) &> 'empty'::numrange;
SELECT 'empty'::numrange &> nummultirange();
SELECT 'empty'::numrange &> nummultirange(numrange(1,2));
SELECT nummultirange() &> nummultirange();
SELECT nummultirange() &> nummultirange(numrange(1,2));
SELECT nummultirange(numrange(1,2)) &> nummultirange();
SELECT nummultirange(numrange(3,4)) &> numrange(6,7);
SELECT nummultirange(numrange(3,4)) &> numrange(1,2);
SELECT nummultirange(numrange(3,4)) &> numrange(1,4);
SELECT nummultirange(numrange(3,4)) &> numrange(1,6);
SELECT nummultirange(numrange(3,4)) &> numrange(3.5,6);
SELECT numrange(3,4) &> nummultirange(numrange(6,7));
SELECT numrange(3,4) &> nummultirange(numrange(1,2));
SELECT numrange(3,4) &> nummultirange(numrange(1,4));
SELECT numrange(3,4) &> nummultirange(numrange(1,6));
SELECT numrange(3,4) &> nummultirange(numrange(3.5,6));
SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(6,7));
SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,2));
SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,4));
SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(1,6));
SELECT nummultirange(numrange(3,4)) &> nummultirange(numrange(3.5,6));
-- meets
SELECT 'empty'::numrange -|- nummultirange();
SELECT 'empty'::numrange -|- nummultirange(numrange(1,2));
SELECT nummultirange() -|- 'empty'::numrange;
SELECT nummultirange(numrange(1,2)) -|- 'empty'::numrange;
SELECT nummultirange() -|- nummultirange();
SELECT nummultirange(numrange(1,2)) -|- nummultirange();
SELECT nummultirange() -|- nummultirange(numrange(1,2));
SELECT numrange(1,2) -|- nummultirange(numrange(2,4));
SELECT numrange(1,2) -|- nummultirange(numrange(3,4));
SELECT nummultirange(numrange(1,2)) -|- numrange(2,4);
SELECT nummultirange(numrange(1,2)) -|- numrange(3,4);
SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4));
SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(3,4));
SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(3,4));
SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(6,7));
SELECT nummultirange(numrange(1,2), numrange(5,6)) -|- nummultirange(numrange(8,9));
SELECT nummultirange(numrange(1,2)) -|- nummultirange(numrange(2,4), numrange(6,7));
-- strictly left
select 'empty'::numrange << nummultirange();
select numrange(1,2) << nummultirange();
select numrange(1,2) << nummultirange(numrange(3,4));
select numrange(1,2) << nummultirange(numrange(0,4));
select numrange(1,2) << nummultirange(numrange(0,4), numrange(7,8));
select nummultirange() << 'empty'::numrange;
select nummultirange() << numrange(1,2);
select nummultirange(numrange(3,4)) << numrange(3,6);
select nummultirange(numrange(0,2)) << numrange(3,6);
select nummultirange(numrange(0,2), numrange(7,8)) << numrange(3,6);
select nummultirange(numrange(-4,-2), numrange(0,2)) << numrange(3,6);
select nummultirange() << nummultirange();
select nummultirange() << nummultirange(numrange(1,2));
select nummultirange(numrange(1,2)) << nummultirange();
select nummultirange(numrange(1,2)) << nummultirange(numrange(1,2));
select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4));
select nummultirange(numrange(1,2)) << nummultirange(numrange(3,4), numrange(7,8));
select nummultirange(numrange(1,2), numrange(4,5)) << nummultirange(numrange(3,4), numrange(7,8));
-- strictly right
select nummultirange() >> 'empty'::numrange;
select nummultirange() >> numrange(1,2);
select nummultirange(numrange(3,4)) >> numrange(1,2);
select nummultirange(numrange(0,4)) >> numrange(1,2);
select nummultirange(numrange(0,4), numrange(7,8)) >> numrange(1,2);
select 'empty'::numrange >> nummultirange();
select numrange(1,2) >> nummultirange();
select numrange(3,6) >> nummultirange(numrange(3,4));
select numrange(3,6) >> nummultirange(numrange(0,2));
select numrange(3,6) >> nummultirange(numrange(0,2), numrange(7,8));
select numrange(3,6) >> nummultirange(numrange(-4,-2), numrange(0,2));
select nummultirange() >> nummultirange();
select nummultirange(numrange(1,2)) >> nummultirange();
select nummultirange() >> nummultirange(numrange(1,2));
select nummultirange(numrange(1,2)) >> nummultirange(numrange(1,2));
select nummultirange(numrange(3,4)) >> nummultirange(numrange(1,2));
select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2));
select nummultirange(numrange(3,4), numrange(7,8)) >> nummultirange(numrange(1,2), numrange(4,5));
-- union
SELECT nummultirange() + nummultirange();
SELECT nummultirange() + nummultirange(numrange(1,2));
SELECT nummultirange(numrange(1,2)) + nummultirange();
SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(1,2));
SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(2,4));
SELECT nummultirange(numrange(1,2)) + nummultirange(numrange(3,4));
SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(2,4));
SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(3,4));
SELECT nummultirange(numrange(1,2), numrange(4,5)) + nummultirange(numrange(0,9));
-- merge
SELECT range_merge(nummultirange());
SELECT range_merge(nummultirange(numrange(1,2)));
SELECT range_merge(nummultirange(numrange(1,2), numrange(7,8)));
-- minus
SELECT nummultirange() - nummultirange();
SELECT nummultirange() - nummultirange(numrange(1,2));
SELECT nummultirange(numrange(1,2)) - nummultirange();
SELECT nummultirange(numrange(1,2), numrange(3,4)) - nummultirange();
SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(1,2));
SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(2,4));
SELECT nummultirange(numrange(1,2)) - nummultirange(numrange(3,4));
SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(1,2));
SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(2,3));
SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,8));
SELECT nummultirange(numrange(1,4)) - nummultirange(numrange(0,2));
SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(0,2), numrange(3,4));
SELECT nummultirange(numrange(1,8)) - nummultirange(numrange(2,3), numrange(5,null));
SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0));
SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(2,4));
SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(3,5));
SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(0,9));
SELECT nummultirange(numrange(1,3), numrange(4,5)) - nummultirange(numrange(2,9));
SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(8,9));
SELECT nummultirange(numrange(1,2), numrange(4,5)) - nummultirange(numrange(-2,0), numrange(8,9));
-- intersection
SELECT nummultirange() * nummultirange();
SELECT nummultirange() * nummultirange(numrange(1,2));
SELECT nummultirange(numrange(1,2)) * nummultirange();
SELECT '{[1,3)}'::nummultirange * '{[1,5)}'::nummultirange;
SELECT '{[1,3)}'::nummultirange * '{[0,5)}'::nummultirange;
SELECT '{[1,3)}'::nummultirange * '{[0,2)}'::nummultirange;
SELECT '{[1,3)}'::nummultirange * '{[2,5)}'::nummultirange;
SELECT '{[1,4)}'::nummultirange * '{[2,3)}'::nummultirange;
SELECT '{[1,4)}'::nummultirange * '{[0,2), [3,5)}'::nummultirange;
SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,8), [9,12)}'::nummultirange;
SELECT '{[1,4), [7,10)}'::nummultirange * '{[9,12)}'::nummultirange;
SELECT '{[1,4), [7,10)}'::nummultirange * '{[-5,-4), [5,6), [9,12)}'::nummultirange;
SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
SELECT '{[1,4), [7,10)}'::nummultirange * '{[0,2), [3,8), [9,12)}'::nummultirange;
--
-- range_agg function
--
create table reservations ( room_id integer not null, booked_during daterange );
insert into reservations values
-- 1: has a meets and a gap
(1, daterange('2018-07-01', '2018-07-07')),
(1, daterange('2018-07-07', '2018-07-14')),
(1, daterange('2018-07-20', '2018-07-22')),
-- 2: just a single row
(2, daterange('2018-07-01', '2018-07-03')),
-- 3: one null range
(3, NULL),
-- 4: two null ranges
(4, NULL),
(4, NULL),
-- 5: a null range and a non-null range
(5, NULL),
(5, daterange('2018-07-01', '2018-07-03')),
-- 6: has overlap
(6, daterange('2018-07-01', '2018-07-07')),
(6, daterange('2018-07-05', '2018-07-10')),
-- 7: two ranges that meet: no gap or overlap
(7, daterange('2018-07-01', '2018-07-07')),
(7, daterange('2018-07-07', '2018-07-14')),
-- 8: an empty range
(8, 'empty'::daterange)
;
SELECT room_id, range_agg(booked_during)
FROM reservations
GROUP BY room_id
ORDER BY room_id;
-- range_agg on a custom range type too
SELECT range_agg(r)
FROM (VALUES
('[a,c]'::textrange),
('[b,b]'::textrange),
('[c,f]'::textrange),
('[g,h)'::textrange),
('[h,j)'::textrange)
) t(r);
select range_intersect_agg(nmr) from nummultirange_test;
select range_intersect_agg(nmr) from nummultirange_test where false;
-- test with just one input:
select range_intersect_agg(nmr) from (values ('{[1,2]}'::nummultirange)) t(nmr);
select range_intersect_agg(nmr) from nummultirange_test where nmr @> 4.0;
create table nummultirange_test2(nmr nummultirange);
create index nummultirange_test2_hash_idx on nummultirange_test2 using hash (nmr);
INSERT INTO nummultirange_test2 VALUES('{[, 5)}');
INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2)));
INSERT INTO nummultirange_test2 VALUES(nummultirange(numrange(1.1, 2.2,'()')));
INSERT INTO nummultirange_test2 VALUES('{}');
select * from nummultirange_test2 where nmr = '{}';
select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.2));
select * from nummultirange_test2 where nmr = nummultirange(numrange(1.1, 2.3));
set enable_nestloop=t;
set enable_hashjoin=f;
set enable_mergejoin=f;
select * from nummultirange_test natural join nummultirange_test2 order by nmr;
set enable_nestloop=f;
set enable_hashjoin=t;
set enable_mergejoin=f;
select * from nummultirange_test natural join nummultirange_test2 order by nmr;
set enable_nestloop=f;
set enable_hashjoin=f;
set enable_mergejoin=t;
select * from nummultirange_test natural join nummultirange_test2 order by nmr;
set enable_nestloop to default;
set enable_hashjoin to default;
set enable_mergejoin to default;
DROP TABLE nummultirange_test2;
--
-- Test user-defined multirange of floats
--
select '{[123.001, 5.e9)}'::float8multirange @> 888.882::float8;
create table float8multirange_test(f8mr float8multirange, i int);
insert into float8multirange_test values(float8multirange(float8range(-100.00007, '1.111113e9')), 42);
select * from float8multirange_test;
drop table float8multirange_test;
--
-- Test multirange types over domains
--
create domain mydomain as int4;
create type mydomainrange as range(subtype=mydomain);
select '{[4,50)}'::mydomainmultirange @> 7::mydomain;
drop domain mydomain cascade;
--
-- Test domains over multirange types
--
create domain restrictedmultirange as int4multirange check (upper(value) < 10);
select '{[4,5)}'::restrictedmultirange @> 7;
select '{[4,50)}'::restrictedmultirange @> 7; -- should fail
drop domain restrictedmultirange;
---
-- Check automatic naming of multiranges
---
create type intr as range(subtype=int);
select intr_multirange(intr(1,10));
drop type intr;
create type intmultirange as (x int, y int);
create type intrange as range(subtype=int); -- should fail
drop type intmultirange;
create type intr_multirange as (x int, y int);
create type intr as range(subtype=int); -- should fail
drop type intr_multirange;
--
-- Test multiple multirange types over the same subtype and manual naming of
-- the multirange type.
--
-- should fail
create type textrange1 as range(subtype=text, multirange_type_name=int, collation="C");
-- should pass
create type textrange1 as range(subtype=text, multirange_type_name=multirange_of_text, collation="C");
-- should pass, because existing _textrange1 is automatically renamed
create type textrange2 as range(subtype=text, multirange_type_name=_textrange1, collation="C");
select multirange_of_text(textrange2('a','Z')); -- should fail
select multirange_of_text(textrange1('a','Z')) @> 'b'::text;
select _textrange1(textrange2('a','z')) @> 'b'::text;
drop type textrange1;
drop type textrange2;
--
-- Test polymorphic type system
--
create function anyarray_anymultirange_func(a anyarray, r anymultirange)
returns anyelement as 'select $1[1] + lower($2);' language sql;
select anyarray_anymultirange_func(ARRAY[1,2], int4multirange(int4range(10,20)));
-- should fail
select anyarray_anymultirange_func(ARRAY[1,2], nummultirange(numrange(10,20)));
drop function anyarray_anymultirange_func(anyarray, anymultirange);
-- should fail
create function bogus_func(anyelement)
returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
-- should fail
create function bogus_func(int)
returns anymultirange as 'select int4multirange(int4range(1,10))' language sql;
create function range_add_bounds(anymultirange)
returns anyelement as 'select lower($1) + upper($1)' language sql;
select range_add_bounds(int4multirange(int4range(1, 17)));
select range_add_bounds(nummultirange(numrange(1.0001, 123.123)));
create function multirangetypes_sql(q anymultirange, b anyarray, out c anyelement)
as $$ select upper($1) + $2[1] $$
language sql;
select multirangetypes_sql(int4multirange(int4range(1,10)), ARRAY[2,20]);
select multirangetypes_sql(nummultirange(numrange(1,10)), ARRAY[2,20]); -- match failure
create function anycompatiblearray_anycompatiblemultirange_func(a anycompatiblearray, mr anycompatiblemultirange)
returns anycompatible as 'select $1[1] + lower($2);' language sql;
select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(int4range(10,20)));
select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1,2], multirange(numrange(10,20)));
-- should fail
select anycompatiblearray_anycompatiblemultirange_func(ARRAY[1.1,2], multirange(int4range(10,20)));
drop function anycompatiblearray_anycompatiblemultirange_func(anycompatiblearray, anycompatiblemultirange);
create function anycompatiblerange_anycompatiblemultirange_func(r anycompatiblerange, mr anycompatiblemultirange)
returns anycompatible as 'select lower($1) + lower($2);' language sql;
select anycompatiblerange_anycompatiblemultirange_func(int4range(1,2), multirange(int4range(10,20)));
-- should fail
select anycompatiblerange_anycompatiblemultirange_func(numrange(1,2), multirange(int4range(10,20)));
drop function anycompatiblerange_anycompatiblemultirange_func(anycompatiblerange, anycompatiblemultirange);
-- should fail
create function bogus_func(anycompatible)
returns anycompatiblerange as 'select int4range(1,10)' language sql;
--
-- Arrays of multiranges
--
select ARRAY[nummultirange(numrange(1.1, 1.2)), nummultirange(numrange(12.3, 155.5))];
create table i8mr_array (f1 int, f2 int8multirange[]);
insert into i8mr_array values (42, array[int8multirange(int8range(1,10)), int8multirange(int8range(2,20))]);
select * from i8mr_array;
drop table i8mr_array;
--
-- Multiranges of arrays
--
select arraymultirange(arrayrange(ARRAY[1,2], ARRAY[2,1]));
select arraymultirange(arrayrange(ARRAY[2,1], ARRAY[1,2])); -- fail
select array[1,1] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
select array[1,3] <@ arraymultirange(arrayrange(array[1,2], array[2,1]));
--
-- Ranges of composites
--
create type two_ints as (a int, b int);
create type two_ints_range as range (subtype = two_ints);
-- with force_parallel_mode on, this exercises tqueue.c's range remapping
select *, row_to_json(upper(t)) as u from
(values (two_ints_multirange(two_ints_range(row(1,2), row(3,4)))),
(two_ints_multirange(two_ints_range(row(5,6), row(7,8))))) v(t);
drop type two_ints cascade;
--
-- Check behavior when subtype lacks a hash function
--
set enable_sort = off; -- try to make it pick a hash setop implementation
select '{(2,5)}'::cashmultirange except select '{(5,6)}'::cashmultirange;
reset enable_sort;
--
-- OUT/INOUT/TABLE functions
--
-- infer anymultirange from anymultirange
create function mr_outparam_succeed(i anymultirange, out r anymultirange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
select * from mr_outparam_succeed(int4multirange(int4range(1,2)));
-- infer anyarray from anymultirange
create function mr_outparam_succeed2(i anymultirange, out r anyarray, out t text)
as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
select * from mr_outparam_succeed2(int4multirange(int4range(1,2)));
-- infer anyrange from anymultirange
create function mr_outparam_succeed3(i anymultirange, out r anyrange, out t text)
as $$ select range_merge($1), 'foo'::text $$ language sql;
select * from mr_outparam_succeed3(int4multirange(int4range(1,2)));
-- infer anymultirange from anyrange
create function mr_outparam_succeed4(i anyrange, out r anymultirange, out t text)
as $$ select multirange($1), 'foo'::text $$ language sql;
select * from mr_outparam_succeed4(int4range(1,2));
-- infer anyelement from anymultirange
create function mr_inoutparam_succeed(out i anyelement, inout r anymultirange)
as $$ select upper($1), $1 $$ language sql;
select * from mr_inoutparam_succeed(int4multirange(int4range(1,2)));
-- infer anyelement+anymultirange from anyelement+anymultirange
create function mr_table_succeed(i anyelement, r anymultirange) returns table(i anyelement, r anymultirange)
as $$ select $1, $2 $$ language sql;
select * from mr_table_succeed(123, int4multirange(int4range(1,11)));
-- use anymultirange in plpgsql
create function mr_polymorphic(i anyrange) returns anymultirange
as $$ begin return multirange($1); end; $$ language plpgsql;
select mr_polymorphic(int4range(1, 4));
-- should fail
create function mr_outparam_fail(i anyelement, out r anymultirange, out t text)
as $$ select '[1,10]', 'foo' $$ language sql;
--should fail
create function mr_inoutparam_fail(inout i anyelement, out r anymultirange)
as $$ select $1, '[1,10]' $$ language sql;
--should fail
create function mr_table_fail(i anyelement) returns table(i anyelement, r anymultirange)
as $$ select $1, '[1,10]' $$ language sql;

View File

@ -42,6 +42,10 @@ begin
if (select typtype from pg_catalog.pg_type where oid = $1) = 'r'
then return true; end if;
end if;
if $2 = 'pg_catalog.anymultirange'::pg_catalog.regtype then
if (select typtype from pg_catalog.pg_type where oid = $1) = 'm'
then return true; end if;
end if;
return false;
end
$$ language plpgsql strict stable;
@ -166,7 +170,8 @@ WHERE p1.oid < p2.oid AND
-- need to be modified whenever new pairs of types are made binary-equivalent,
-- or when new polymorphic built-in functions are added!
-- Note: ignore aggregate functions here, since they all point to the same
-- dummy built-in function. Likewise, ignore range constructor functions.
-- dummy built-in function. Likewise, ignore range and multirange constructor
-- functions.
SELECT DISTINCT p1.prorettype::regtype, p2.prorettype::regtype
FROM pg_proc AS p1, pg_proc AS p2
@ -176,6 +181,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.prorettype < p2.prorettype)
ORDER BY 1, 2;
@ -187,6 +194,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[0] < p2.proargtypes[0])
ORDER BY 1, 2;
@ -198,6 +207,8 @@ WHERE p1.oid != p2.oid AND
p1.prokind != 'a' AND p2.prokind != 'a' AND
p1.prosrc NOT LIKE E'range\\_constructor_' AND
p2.prosrc NOT LIKE E'range\\_constructor_' AND
p1.prosrc NOT LIKE E'multirange\\_constructor_' AND
p2.prosrc NOT LIKE E'multirange\\_constructor_' AND
(p1.proargtypes[1] < p2.proargtypes[1])
ORDER BY 1, 2;
@ -281,16 +292,19 @@ WHERE p1.prorettype IN
'anyarray'::regtype = ANY (p1.proargtypes) OR
'anynonarray'::regtype = ANY (p1.proargtypes) OR
'anyenum'::regtype = ANY (p1.proargtypes) OR
'anyrange'::regtype = ANY (p1.proargtypes))
'anyrange'::regtype = ANY (p1.proargtypes) OR
'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- anyrange is tighter than the rest, can only resolve from anyrange input
-- anyrange and anymultirange are tighter than the rest, can only resolve
-- from each other
SELECT p1.oid, p1.proname
FROM pg_proc as p1
WHERE p1.prorettype = 'anyrange'::regtype
WHERE p1.prorettype IN ('anyrange'::regtype, 'anymultirange'::regtype)
AND NOT
'anyrange'::regtype = ANY (p1.proargtypes)
('anyrange'::regtype = ANY (p1.proargtypes) OR
'anymultirange'::regtype = ANY (p1.proargtypes))
ORDER BY 2;
-- similarly for the anycompatible family

View File

@ -71,6 +71,16 @@ select polyf(int4range(42, 49), 11, 4.5) as fail; -- range type doesn't fit
drop function polyf(x anycompatiblerange, y anycompatible, z anycompatible);
create function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible) returns anycompatiblearray as $$
select array[lower(x), upper(x), y, z]
$$ language sql;
select polyf(multirange(int4range(42, 49)), 11, 2::smallint) as int, polyf(multirange(float8range(4.5, 7.8)), 7.8, 11::real) as num;
select polyf(multirange(int4range(42, 49)), 11, 4.5) as fail; -- range type doesn't fit
drop function polyf(x anycompatiblemultirange, y anycompatible, z anycompatible);
-- fail, can't infer type:
create function polyf(x anycompatible) returns anycompatiblerange as $$
select array[x + 1, x + 2]
@ -84,6 +94,19 @@ select polyf(int4range(42, 49), array[11]) as int, polyf(float8range(4.5, 7.8),
drop function polyf(x anycompatiblerange, y anycompatiblearray);
-- fail, can't infer type:
create function polyf(x anycompatible) returns anycompatiblemultirange as $$
select array[x + 1, x + 2]
$$ language sql;
create function polyf(x anycompatiblemultirange, y anycompatiblearray) returns anycompatiblemultirange as $$
select x
$$ language sql;
select polyf(multirange(int4range(42, 49)), array[11]) as int, polyf(multirange(float8range(4.5, 7.8)), array[7]) as num;
drop function polyf(x anycompatiblemultirange, y anycompatiblearray);
create function polyf(a anyelement, b anyarray,
c anycompatible, d anycompatible,
OUT x anyarray, OUT y anycompatiblearray)
@ -997,6 +1020,35 @@ returns anycompatiblerange as $$
select $1
$$ language sql;
create function anyctest(anycompatible, anycompatiblemultirange)
returns anycompatiblemultirange as $$
select $2
$$ language sql;
select x, pg_typeof(x) from anyctest(11, multirange(int4range(4,7))) x;
select x, pg_typeof(x) from anyctest(11, multirange(numrange(4,7))) x;
select x, pg_typeof(x) from anyctest(11, 12) x; -- fail
select x, pg_typeof(x) from anyctest(11.2, multirange(int4range(4,7))) x; -- fail
select x, pg_typeof(x) from anyctest(11.2, '{[4,7)}') x; -- fail
drop function anyctest(anycompatible, anycompatiblemultirange);
create function anyctest(anycompatiblemultirange, anycompatiblemultirange)
returns anycompatible as $$
select lower($1) + upper($2)
$$ language sql;
select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(int4range(4,7))) x;
select x, pg_typeof(x) from anyctest(multirange(int4range(11,12)), multirange(numrange(4,7))) x; -- fail
drop function anyctest(anycompatiblemultirange, anycompatiblemultirange);
-- fail, can't infer result type:
create function anyctest(anycompatible)
returns anycompatiblemultirange as $$
select $1
$$ language sql;
create function anyctest(anycompatiblenonarray, anycompatiblenonarray)
returns anycompatiblearray as $$
select array[$1, $2]

View File

@ -118,6 +118,12 @@ select numrange(1.0, 2.0) * numrange(2.0, 3.0);
select numrange(1.0, 2.0) * numrange(1.5, 3.0);
select numrange(1.0, 2.0) * numrange(2.5, 3.0);
select range_intersect_agg(nr) from numrange_test;
select range_intersect_agg(nr) from numrange_test where false;
select range_intersect_agg(nr) from numrange_test where nr @> 4.0;
analyze numrange_test;
create table numrange_test2(nr numrange);
create index numrange_test2_hash_idx on numrange_test2 using hash (nr);
@ -528,6 +534,7 @@ reset enable_sort;
-- OUT/INOUT/TABLE functions
--
-- infer anyrange from anyrange
create function outparam_succeed(i anyrange, out r anyrange, out t text)
as $$ select $1, 'foo'::text $$ language sql;
@ -539,6 +546,13 @@ create function outparam2_succeed(r anyrange, out lu anyarray, out ul anyarray)
select * from outparam2_succeed(int4range(1,11));
-- infer anyarray from anyrange
create function outparam_succeed2(i anyrange, out r anyarray, out t text)
as $$ select ARRAY[upper($1)], 'foo'::text $$ language sql;
select * from outparam_succeed2(int4range(int4range(1,2)));
-- infer anyelement from anyrange
create function inoutparam_succeed(out i anyelement, inout r anyrange)
as $$ select upper($1), $1 $$ language sql;

View File

@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
(p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r')) OR
(p1.typtype not in ('b', 'c', 'd', 'e', 'p', 'r', 'm')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
@ -154,7 +154,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-- Composites, domains, enums, ranges should all use the same input routines
-- Composites, domains, enums, multiranges, ranges should all use the same input routines
SELECT DISTINCT typtype, typinput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@ -183,7 +183,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p2.provolatile NOT IN ('i', 's');
-- Composites, enums, ranges should all use the same output routines
-- Composites, enums, multiranges, ranges should all use the same output routines
SELECT DISTINCT typtype, typoutput
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@ -235,7 +235,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typreceive = p2.oid AND p2.provolatile NOT IN ('i', 's');
-- Composites, domains, enums, ranges should all use the same receive routines
-- Composites, domains, enums, multiranges, ranges should all use the same receive routines
SELECT DISTINCT typtype, typreceive
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'p')
@ -264,7 +264,7 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typsend = p2.oid AND p2.provolatile NOT IN ('i', 's');
-- Composites, enums, ranges should all use the same send routines
-- Composites, enums, multiranges, ranges should all use the same send routines
SELECT DISTINCT typtype, typsend
FROM pg_type AS p1
WHERE p1.typtype not in ('b', 'd', 'p')
@ -489,3 +489,9 @@ FROM pg_range p1 JOIN pg_proc p ON p.oid = p1.rngsubdiff
WHERE pronargs != 2
OR proargtypes[0] != rngsubtype OR proargtypes[1] != rngsubtype
OR prorettype != 'pg_catalog.float8'::regtype;
-- every range should have a valid multirange
SELECT p1.rngtypid, p1.rngsubtype, p1.rngmultitypid
FROM pg_range p1
WHERE p1.rngmultitypid IS NULL OR p1.rngmultitypid = 0;

View File

@ -1395,6 +1395,9 @@ MultiXactMember
MultiXactOffset
MultiXactStateData
MultiXactStatus
MultirangeIOData
MultirangeParseState
MultirangeType
NDBOX
NODE
NUMCacheEntry
@ -3139,6 +3142,7 @@ mp_small
mp_usmall
mp_word
mpz_t
multirange_bsearch_comparison
mxact
mxtruncinfo
needs_fmgr_hook_type